核心小节二:Tool 不只是函数——为什么 Registry 最好保存 Tool 对象和 Metadata

上一小节我们保存的是:

name → function

够不够?

短期够。

但很快你会想知道:

这个 Tool 是干什么的?
有没有副作用?
属于哪一类?
是否需要审批?
超时多少?
后面 Schema 怎么生成?

如果 Registry 只保存 Callable,这些信息没地方放。

所以更好的方式是定义:

Tool

对象。

一个 Tool 最少可以有什么?

当我们说“注册一个工具”时,真正被放进 Registry 的,最好不是孤零零的函数指针,而是一个带上下文信息的 Tool 对象。这样后面的描述、参数、权限控制才能自然长出来。

图片[1]-核心小节二:Tool 不只是函数——为什么 Registry 最好保存 Tool 对象和 Metadata-大赛人网

图里把这个关系拆得比较直观:函数只是 callable,本体之外还会带上 name、description、schema、permission、side_effect 等信息。这样 Registry 才不仅能“存起来”,还能真正支撑模型调用和工程治理。

例如:

name
description
func

再进一步:

category
risk_level
requires_approval

但这一节不要过度设计。

先做:

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

为什么 description 很重要?

因为后面模型不是只看到:

read_file

它还要知道,这个 Tool 到底什么时候用?

例如:

read_file:
读取工作目录内一个 UTF-8 文本文件的完整内容。

与:

search_files:
按关键词搜索文件名和文本内容。

如果描述模糊,模型 Tool 选择质量会明显下降。

所以 description 不是文档附属品。

它会直接影响 Agent 决策。

完整 Tool 数据类

from dataclasses import dataclass
from typing import Any, Callable


@dataclass
class Tool:
    name: str
    description: str
    func: Callable[..., Any]

Registry:

class ToolRegistry:

    def __init__(self):
        self._tools: dict[
            str,
            Tool
        ] = {}

这时 get() 返回的就不是函数,而是 Tool 对象。

执行:

tool = registry.get(name)
result = tool.func(**arguments)

一个完整可运行版本

现在 Registry 已经开始像一个真正工具目录。

Tool Metadata 为什么要逐步增加?

这一节特别适合配一张演进图来看。因为很多同学容易误会成:一上来就要把所有 metadata 设计得特别全。其实真实工程一般不是这么推进的。

图片[2]-核心小节二:Tool 不只是函数——为什么 Registry 最好保存 Tool 对象和 Metadata-大赛人网

更自然的方式是先让工具跑起来,再逐步补齐描述、参数 schema、标签、版本、权限、风险等级这些信息。也正因为是逐步长出来的,所以前期的设计要尽量留出扩展空间。

后面很自然会加:

schema
risk_level
timeout
tags
requires_approval

例如:

read_file
risk_level=low

write_file
risk_level=medium

run_shell
risk_level=high

然后 Middleware 就可以:

if tool.risk_level == "high":
    ...

这就是 Registry 作为统一入口的真正扩展价值。

Metadata 本身只是声明。给工具写上 risk_level="high",不会自动阻止任何调用;标记 idempotent=False,也不会自动获得防重能力。只有 Middleware、重试策略和 Trace 真正读取这些字段,它们才会变成运行约束。看到元数据已经存在时,记得再追问一句:谁在消费它?

Registry 和 Schema 的边界

这里很多人会串线:Registry、Schema、Executor 都和工具有关,但职责并不一样。把边界看清楚,后面设计代码结构就会顺很多。

图片[3]-核心小节二:Tool 不只是函数——为什么 Registry 最好保存 Tool 对象和 Metadata-大赛人网

简单说,Registry 负责存定义和统一来源;Schema 是从 Registry 里裁出来给模型看的可见视图;Executor 则拿到 tool_call 以后做校验、权限判断和真正执行。三者相关,但绝不是一回事。

先提前说清楚。

Registry 负责,有哪些 Tool,真实 Callable 是什么。

Schema 负责,怎么把 Tool 描述给模型。

例如 Registry 保存:

Tool(
  name="read_file",
  description="读取文件",
  func=read_file
)

下一节会从函数签名:

read_file(path: str)

生成:

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

两者紧密相关,但不是同一个概念。

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

昵称

取消
昵称

    请登录后查看评论内容