小节四:把 Registry 接入 Agent Harness——让模型看到的 Tool 和 Harness 能执行的 Tool 来自同一个地方

这一节的核心思想就一句话:模型能看到什么工具,Harness 就应该从同一个来源里去执行什么工具。只要来源分裂,后面就很容易出现“模型看得到但系统调不到”,或者“系统能执行但模型根本不知道”的问题。

图片[1]-小节四:把 Registry 接入 Agent Harness——让模型看到的 Tool 和 Harness 能执行的 Tool 来自同一个地方-大赛人网

图里把这件事拆得很清楚:上面一支是给模型看的 Tool Schema,下面一支是给 Harness 用的 get(name)、权限校验和执行逻辑,但它们都从同一个 Registry 出来。这样,工具目录才真正成为整个 Harness 的单一事实来源。

到这里我们已经有:

ToolRegistry
Tool
@tool

现在要解决最后一个特别重要的问题,模型看到的 Tool 列表,和 Harness 实际能执行的 Tool,怎么保证一致?

很多 Agent Bug 都来自这两套列表不同步。

比如 Prompt 写:

你可以使用:
read_file
search_files
run_python

但 Registry 实际只有:

read_file
run_python

模型一调用:

search_files

就 UnknownTool。

最稳的思路是,模型可见工具列表直接从 Registry 生成。

也就是说:

Registry
  ├─ 给 Provider 生成 Tool Schemas
  └─ 给 Executor 提供真实 Callable

一个来源,两种用途。

执行时也必须回 Registry

Main Loop:

response = provider.chat(
    messages,
    tools=schemas,
)

模型返回:

read_file

Executor 不自己写 if/elif。

而是:

registry.execute(
    name,
    arguments,
)

这样整个 Tool 生命周期是统一的。

一个完整 Harness Demo

下面我们把:

Registry
@tool
FakeProvider
Main Loop

全部接起来。

这段代码已经具备一个非常重要的结构:

工具定义
 ↓
@tool
 ↓
Registry
 ├─ list_tools
 └─ execute

以后再加 Tool:

@tool()
def search_files(...):
    ...

Main Loop 不需要增加:

elif name == "search_files":

这就是抽象真正起作用的地方。

Registry 为什么还是安全控制的挂载点?

再往后走一步,Registry 就不只是“收工具”的地方了,它还是权限、暴露范围、可见性控制的天然挂载点。

图片[2]-小节四:把 Registry 接入 Agent Harness——让模型看到的 Tool 和 Harness 能执行的 Tool 来自同一个地方-大赛人网

这张图很好理解:同一个 Registry 里可以统一存放工具定义,但面向分析师、运营、管理员时,暴露出去的能力集合并不一样。也就是说,Registry 既承担了统一管理,也承担了能力裁剪的入口作用。

后面我们会加:

Tool.risk_level
Tool.requires_approval
Tool.timeout

于是执行前:

tool = registry.get(name)

middleware.check(tool, arguments)

所有 Tool 都经过同一入口。

这会比:

每个函数内部自己判断安全

稳定很多。

所以 Registry 最终会连接:

Tool Schema
Tool Executor
Middleware
Tracing
Permission
Human Approval

本节先把它做好,后面会越来越省力。

现在可以回到配套项目核对一次。项目中的方法名与前面教学版本略有不同:names() 返回工具名称,schemas() 返回给 Provider 使用的描述,execute(name, **kwargs) 负责真实调用。职责仍然对应 list_tools()、Schema 生成与统一执行,只是接口更贴近最终 Engine。

cd agent_harness_course_projects
python3 projects/03_tool_registry_schema/main.py

运行后先看到 add 的 Schema,再看到结果 5。可以再做一个很小的实验:重复注册同名 add,确认 Registry 明确报错;然后请求一个不存在的名称,观察 get() 返回的 KeyError。这两个失败都应该是可预测、可测试的,而不是静默覆盖或动态搜索其他函数。

还要留意,配套 ToolRegistry.execute() 当前只负责查找并调用,参数 Schema 校验和按用户授权尚未内置在 Registry 中。Registry 是这些能力的稳定挂载点,但“有挂载点”不等于相关控制已经全部生效;下一节会继续处理 Schema,第 24~26 节再补 Middleware、审批和 Sandbox。

工具注册之后,还要决定谁能看见和执行

假设我们同时接入两个文档服务,它们都提供 search。Registry 若只按裸名称存储,后注册的工具可能覆盖前一个。命名空间可以消除歧义,但暴露给模型的名称还要符合目标接口的命名限制。我们可以保留内部限定名,再由 Provider 映射为合法名称,回传时还原。

发现工具、注册工具、展示工具和执行授权是四个环节。一个工具已经注册,不代表所有用户都能看见;从可见列表隐藏它,也不能替代执行时鉴权。实际调用必须再次依据当前身份、资源归属和策略判断,因为用户权限可能在两次请求之间改变。

版本、风险与幂等字段也要找到消费位置:版本进入 Trace,风险供策略判断,幂等性影响重试。远程 MCP Server 的描述与 annotations 是外部声明,Host 不能直接把它们当作可信权限结论。配套适配器展示了允许列表的入口,完整授权和服务端验证仍需继续接入。

动手检查:同名与越权分别由谁处理

注册两个名为 search 的远程工具,再模拟一个用户只获得其中一个服务的权限。参考验收是:名称不会互相覆盖,模型仅看见允许的工具,直接构造另一个工具调用也会在执行层被拒绝。若只能通过“模型通常不会这样调用”来解释结果,授权链路还没有完成。

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

昵称

取消
昵称

    请登录后查看评论内容