小节三:让 Schema 与 Registry 合体——一个 Tool 只定义一次,模型与 Executor 共用同一份信息

上一节我们的 Tool:

@dataclass
class Tool:
    name: str
    description: str
    func: Callable

现在很自然可以加:

schema: dict

但是更好的方式不是手写:

schema = {...}

而是在注册时自动:

schema = function_to_schema(func)

这样 Tool 定义只写一次。

理想流程

Python Function
 ↓
@tool
 ↓
Registry.register
 ↓
自动提取:
- name
- description
- signature
- type hints
 ↓
生成 Tool Schema
 ↓
保存 Tool

以后:

registry.list_schemas()

直接给 Provider。

执行:

registry.execute(tool_call)

也是同一 Registry。

这就形成了单一事实来源。

把 Registry 和 Schema 合在一起以后,系统里就不应该再出现两套彼此独立的 Tool 定义。可以把下面这张图理解成“一份 Tool,两条出口”:一条通过 list_schemas() 去模型侧,另一条通过 execute() 去真实函数侧。

图片[1]-小节三:让 Schema 与 Registry 合体——一个 Tool 只定义一次,模型与 Executor 共用同一份信息-大赛人网

这样做真正省掉的不是几行代码,而是配置漂移。函数参数一旦变化,只要 Schema 从真实函数生成,模型看到的协议和 Harness 最终调用的 Callable 就更不容易错位。

完整 Tool + Registry + Schema 实现

下面是一段非常重要的完整代码。

建议你保存为:

tool_registry_schema_demo.py

它不依赖任何第三方库。

这一段代码最值得理解什么?

不是 inspect API。

而是:

@tool
 ↓
Registry
 ├─ 保存真实 func
 └─ 自动生成 schema

从此以后:

模型知道什么

和:

Harness 能执行什么

来自同一个 Tool。

这会极大减少配置漂移。

Schema 要不要直接存 Registry?

可以。

也可以只在需要时动态生成。

两种都合理。

课程里先存起来,更直观。

真实项目如果函数定义不会变化,注册时生成一次就够。

Tool Schema 与 Provider 的关系

我们内部定义:

registry.list_schemas()

Provider 可能还需要包装成特定 API 形式。

例如内部是:

{
  "name": "...",
  "description": "...",
  "parameters": {...}
}

某个 Provider 可能要求外面再套一层。

这属于 Provider Adapter 的职责。

所以边界是:

Registry:
内部统一 Tool Schema

Provider:
转换成目标模型 API 的 Tool 格式

这样 Registry 不依赖具体模型厂商。

这和上一部分的 Provider 解耦正好接上。

例如,我们内部保留:

{
  "name": "read_file",
  "description": "读取指定文本文件",
  "parameters": {
    "type": "object",
    "properties": {}
  }
}

OpenAI Compatible Provider 再按接口要求包装成:

{
  "type": "function",
  "function": {
    "name": "read_file",
    "description": "读取指定文本文件",
    "parameters": {
      "type": "object",
      "properties": {}
    }
  }
}

注意,外层 type 和 function 是传输格式,不应该反过来污染 Registry 的内部模型。将来切换 Provider 时,我们只换包装方式,不必重写所有 Tool。

这里可以把 Provider Adapter 看成一个“翻译层”。Registry 保存的是课程内部统一协议,到了不同模型厂商那里,再按各自 API 要求套外壳。下面这张图把这个边界放进完整调用链里看,会更容易理解为什么 Registry 不应该直接绑定某一家 SDK。

图片[2]-小节三:让 Schema 与 Registry 合体——一个 Tool 只定义一次,模型与 Executor 共用同一份信息-大赛人网

换句话说,Schema 的业务含义属于 Tool 系统,厂商需要的包装格式属于 Provider。 两层分开以后,换模型时主要调整 Adapter,而不是回头修改每个工具。

回到配套项目看一遍

当前项目的 ToolSpec 已经保存了 name、description、parameters 和真实 func,Registry.schemas() 会把内部 Schema 统一列出来。阶段项目 03 为了让结构更直观,仍然手写 parameters,本节的 function_to_schema() 是我们接下来可以接入的增强方案,两者不要误认为已经自动连在一起。

在项目根目录运行:

python3 --version
python3 projects/03_tool_registry_schema/main.py

第一行应显示 Python 3.9.x;随后会打印 add 工具的 Schema,并输出执行结果 5。这一步先确认“登记—导出—执行”链路畅通,至于自动生成和执行前校验,我们分别在本节示例与后续 Harness 中补齐。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像-大赛人网
欢迎您留下宝贵的见解!
提交
头像-大赛人网

昵称

取消
昵称

    请登录后查看评论内容