面试官可能会问:“给 Agent 新增一个搜索工具,你会改哪些地方?工具越来越多以后,怎么注册、禁用、鉴权和排查?”
如果回答是“写一个函数,再把它放进 tools 数组”,只能说明会做最小 Demo。真实工具系统至少面对两类消费者:模型需要一份容易理解的工具说明,后端需要一个可执行、可鉴权、可观测的服务入口。这两者有关联,却不能混为一体。
Tool Schema 解决“模型怎样知道工具能做什么、参数怎么填”;Tool Registry 解决“程序怎样找到真实执行器,并读取权限、超时、副作用和版本等元数据”;工具网关解决“真正调用时怎样统一校验、限流、日志和容错”。把三层拆开,新增工具才不需要修改 Agent 主循环,敏感工具也不会因为出现在 Prompt 里就被任何用户调用。
先给出 30 秒回答
我会把 Agent 工具拆成三部分:给模型看的 Tool Schema、给程序用的 Tool Registry、真正执行的后端适配器。Schema 包含稳定名称、明确描述、参数约束和返回语义,帮助模型选工具和填参数;Registry 把工具名映射到执行器,并保存版本、权限等级、是否有副作用、超时、重试、租户范围和可用状态;运行时网关根据当前用户和任务只暴露允许的 Schema,收到 tool call 后再做名称、参数、权限和业务校验,最后执行并返回统一 Observation。这样工具可以独立上线、灰度、禁用和审计,Agent loop 只依赖抽象工具协议。
装饰器只是注册入口。真正需要讲清的是模型说明、运行元数据和真实执行如何相互解耦。

模型只需要看说明书,后端必须掌握执行权和安全元数据。
一个工具到底由哪些部分组成
最小教程通常说,一个工具由名字、描述和参数组成。这是从模型视角看。进入工程实现后,还要增加返回契约、执行器和运行策略。
稳定名称
名称用于模型选择,也用于后端分发。它应该短、可理解、可版本管理,例如 search_web、visit_page、query_order。不要让同一工具在不同地方出现多个别名,也不要频繁重命名。名称变化会影响 Prompt、评估集、日志查询和历史 Trace。
清楚描述
描述不是普通代码注释,而是模型选择工具的重要依据。至少说明工具做什么、什么时候用、输入和输出边界,以及什么情况不要用。
例如“搜索信息”太宽泛;“搜索公开网页,返回标题、摘要与链接,只用于发现资料线索,不返回网页完整正文”更容易与 visit_page 区分。后者让模型知道先搜索找线索,再访问拿细节。
参数 Schema
参数需要类型、必填字段、枚举、长度与范围。搜索工具的 top_k 不应允许任意整数,排序方式应使用有限枚举,日期和语言要有明确格式。
Schema 只能约束结构,不能替代业务校验。一个字符串符合格式,不代表用户有权查询对应订单;一个 URL 格式正确,也不代表允许访问内网地址。
返回契约
很多工具只规范输入,不规范输出,最后每个执行器返回完全不同的对象。Agent 运行时需要稳定 Observation,例如:成功状态、数据、来源、错误类型、是否可重试、结果摘要和工件引用。
返回给模型的内容要可理解、大小受控,返回给日志的内容要可追踪但不能泄露敏感信息。两者可以来自同一次执行,但不一定是同一份文本。
执行器
执行器是真正访问数据库、搜索服务、文件系统或业务 API 的代码。模型看不到它,也不应拿到它的凭证。执行器可以是本地函数、远程 RPC 或网关适配器,只要遵守统一输入输出协议。
运行策略
包括权限等级、是否只读、是否有副作用、超时、重试、缓存、并发限制、审计要求和可用环境。运行策略不该散落在 Prompt 和多个 if 中,而应成为注册信息的一部分。
为什么不能把 Schema 和函数直接绑死
在最小 Demo 里,可以用字典把工具名映射到函数。这种方式有助于理解 Function Calling,但规模扩大后会出现问题。
同一个搜索抽象可能有多个供应商实现,需要按环境或故障状态切换;同一个执行器可能对不同用户暴露不同参数范围;一个工具可能临时禁用,但模型端缓存的 Schema 仍然存在;工具升级返回格式后,旧运行实例未必能立刻迁移。
因此更稳的分层是:工具定义描述能力,注册表描述当前可用实现和策略,执行器负责具体调用。Agent loop 只依赖工具名和统一结果,不关心底层是 HTTP、RPC 还是本地函数。
这种解耦还带来一个好处:可以在不修改 Agent Prompt 的前提下更换供应商、增加缓存、接入限流或补充审计。反过来,优化工具描述也不需要改业务执行代码。
Tool Registry 最少要保存哪些元数据
一份注册记录可以包含下面这些字段。具体项目按需要裁剪,不是字段越多越专业。
ToolSpec(
name="visit_page",
version="v1",
description="访问公开网页并抽取与问题相关的段落",
input_schema={...},
output_schema={...},
executor=visit_adapter,
permission="public_read",
side_effect="none",
timeout_policy="visit_default",
retry_policy="read_retry",
cache_policy="freshness_aware",
enabled=True,
)
名称和版本用于定位契约;输入输出 Schema 用于校验;执行器用于分发;权限与副作用决定能否暴露和是否需要审批;超时、重试与缓存由统一策略引用;enabled 支持紧急下线。
不要把 API Key、数据库密码或用户令牌放进发给模型的 Schema。凭证由执行环境管理,运行时根据身份注入。模型只看到它被允许知道的能力说明。
注册流程怎样设计
启动时显式注册
服务启动时逐个注册工具,遇到重名、Schema 不合法或缺少策略就拒绝启动。这种方式简单、可预测,适合工具数量有限的单体应用。
装饰器或注解注册
执行函数通过装饰器声明元数据,启动时扫描并构建注册表。它减少样板代码,但仍要防止同名覆盖,并把自动生成的 Schema 纳入审查。
配置或服务发现
工具是独立服务时,可以从配置中心或服务发现加载端点和版本。动态更新需要快照语义:一次 Agent 运行最好绑定一份工具集合版本,避免执行到一半工具定义突然变化。
无论使用哪种方式,注册阶段都应完成静态校验:名称唯一、描述非空、输入输出 Schema 可解析、执行器存在、策略引用有效、高风险工具声明审批要求。把错误挡在启动或发布阶段,比等模型调用时才发现可靠得多。

工具上线不只是把函数写完,还要经过契约校验、权限配置、灰度和回归。
运行时不要把整个 Registry 原样发给模型
注册表是系统的完整能力清单,模型看到的应该是当前请求允许使用的子集。过滤至少考虑用户身份、租户、任务类型、环境和工具健康状态。
例如普通用户只能看到自己的只读查询工具;管理员在特定审批流程里才能看到配置修改工具;测试环境可以暴露 Dry-run 工具,生产环境不一定允许;某个工具故障时可以临时从候选集合移除,避免模型持续选择。
这一步叫能力投影或工具暴露。它解决两个问题:减少模型在无关工具之间选择的难度,也避免仅靠后端拒绝来保护敏感能力。
但“没有展示给模型”仍不是最终安全边界。模型可能生成一个没展示的工具名,恶意用户也可能直接构造请求。所以执行时必须重新从 Registry 解析,并再次做权限判断。工具可见性是第一层,后端授权是最终层。
工具网关怎样执行一次调用
一个统一入口大致经过下面步骤。
第一,接收任务身份、用户身份、工具名、参数、调用 ID 和 Trace ID。
第二,从绑定版本的 Registry 解析工具。找不到、已禁用或版本不兼容时,返回稳定错误。
第三,对参数做 JSON Schema 校验,再做业务校验。例如 URL 域名、文件路径、订单归属和时间范围。
第四,执行权限与副作用检查。高风险操作进入 Dry-run、人工确认或固定 Workflow,而不是直接调用执行器。
第五,应用超时、限流、熔断和必要重试。只对明确可重试、且幂等的操作自动重试。
第六,调用真实执行器,把结果转换成统一 Observation。大对象放工件存储,模型得到相关摘要和引用。
第七,记录审计与指标。日志包含工具名、版本、调用者、参数摘要、结果状态和耗时,但不泄露密钥与敏感正文。
这个网关让 Agent 与工具实现解耦。底层更换搜索供应商、增加缓存或限制并发时,Agent loop 不需要知道具体变化。
如何写一份不让模型混淆的工具描述
工具描述要避免三种问题。
第一,多个工具描述高度重叠。search_web 和 search_document 都写成“搜索内容”,模型无法知道边界。应明确数据域和返回粒度:一个搜公开网页线索,一个搜已授权内部文档片段。
第二,工具职责过宽。一个 do_research 同时搜索、访问、计算和生成结论,看似方便,却让参数和错误边界难以管理。单一职责工具更容易组合、测试和替换。
第三,只写能力,不写限制。“访问网页”需要补充只访问公开 HTTP 页面、不执行页面脚本、结果按当前问题提取、不能访问私网。限制既帮助模型选择,也方便评估。
修改描述后要跑回归集,而不是凭文案感觉。准备相似任务和难负例,观察工具选择、参数与无调用情况。描述是运行行为的一部分,应该像代码一样版本化和评审。
工具版本怎样管理
工具升级可能改变参数、语义或返回结构。若只是修复内部实现且契约不变,可以保持版本;若删除字段、改变默认行为或返回含义,就需要新版本。
一次 Agent 运行最好绑定工具清单版本。长任务恢复时仍能知道当时模型看到什么 Schema、后端使用什么执行器。旧版本可以在迁移期并存,达到保留期限后明确拒绝,而不是静默把旧参数解释成新语义。
Schema、Prompt、执行器和评估集的版本关系也要能追踪。一次回归变差时,只有知道哪一部分变化,才可能定位原因。
灰度、禁用和降级怎么做
新增工具不要一次暴露给所有任务。可以先只对内部测试任务开放,再按租户、用户组或流量比例灰度。观察工具选择、参数错误、超时和对最终任务的影响后再扩大。
紧急禁用要有两层:不再向新模型请求展示 Schema;执行网关拒绝新调用。只做前者,历史上下文中的工具定义仍可能让模型继续请求;只做后者,模型会反复选择一个不可用工具,浪费轮次。
降级可以是切换备用执行器、返回缓存、换成只读能力、让模型请求用户补充,或停止自动处理。具体策略取决于工具时效和业务风险。缓存不能冒充最新结果,备用工具的返回契约也应保持兼容。
谁对一项工具负责
Registry 里最好记录工具负责人、数据负责人和安全审批信息。Agent 平台团队可以维护协议与网关,但只有业务团队知道返回字段的真实含义、权限规则和不可接受的副作用。
工具发布前应由能力负责人确认描述和契约,由业务负责人确认授权与幂等,由平台侧确认超时、日志和降级。发生故障后,Trace 能定位到工具版本和负责人,避免所有问题都被模糊归为“模型不稳定”。
下线也要有责任边界。先统计哪些 Agent、评估样本和运行版本仍依赖旧工具,再停止新任务暴露,处理存量任务,最后关闭执行器。直接删除注册记录会让历史任务恢复时找不到原契约。

工具是否可调用,不能只由模型决定;权限、副作用和健康状态共同决定执行路径。
最容易出现的工程问题
同名工具被覆盖
动态扫描时后注册工具悄悄覆盖前一个,导致不同环境行为不一致。注册阶段应拒绝重复名称,或要求显式版本与命名空间。
Schema 与执行器漂移
模型看到的字段和真实函数参数不一致。可以从单一契约生成双方适配,或在发布时做契约测试,不能靠人工记住同步。
描述变更没有回归
开发者认为只是改了一句文案,实际改变了模型选择。工具描述和参数说明都应进入版本与评估流程。
错误直接抛堆栈
模型拿到内部异常既难以判断,也可能看到敏感信息。网关应把异常映射为有限错误类型,详细堆栈只进入受控日志。
所有工具都自动重试
只读临时失败可以重试,写操作和非幂等动作可能造成重复副作用。重试策略要成为工具元数据,并由执行层强制。
Registry 成为安全名单的唯一来源
工具注册了不等于任何人都能用。可见性、授权、业务对象权限和审批需要在请求上下文里判断。
怎样验证工具系统真的可维护
先做契约测试。每个 Schema 准备合法、缺字段、越界和多余字段样本,确认校验结果稳定;执行器返回也要符合输出契约。
再做选择测试。给模型一组应该调用、容易混淆和不应调用的任务,记录可见工具集合、模型选择和参数。工具新增后要跑全量回归,确认没有挤压原工具。
然后做权限测试。不同身份看到的 Schema 是否正确,直接构造未授权工具名能否被网关拒绝,高风险动作是否进入审批。
还要做故障测试。执行器超时、返回空、格式变化、被禁用和备用服务失败时,Observation 和 Agent 路由是否符合预期。
最后检查可观测性。用一次调用 ID 能否查到工具版本、参数摘要、权限判断、执行器、耗时和结果状态;能否从失败 Trace 还原“模型选错”还是“工具执行错”。

工具可维护性来自契约、版本、权限和回归,不来自注册表里有多少个名字。
面试官会怎样连续追问
追问一:为什么不用一个字典把函数都放进去
小规模 Demo 可以这样做。工程系统还需要版本、权限、副作用、超时、重试、灰度、禁用和审计等元数据。Registry 的价值是统一管理这些运行契约,而不只是函数查找。
追问二:Schema 校验通过为什么还要业务校验
Schema 只证明结构和基本范围合法,不能证明订单属于当前用户、URL 可以访问或资源处于可修改状态。业务与权限必须由后端检查。
追问三:模型应该看到全部工具吗
不应该。运行时根据身份、任务和环境投影出允许的子集,减少选择噪声和敏感能力暴露。但后端仍要再次授权,因为模型也可能生成未展示的名字。
追问四:如何动态下线一个故障工具
同时从新请求的可见 Schema 中移除,并让网关拒绝调用;对正在运行的任务返回稳定、可路由错误,按策略切备用、降级或人工处理。
追问五:工具描述怎样评估
准备包含正例、难负例、相似工具和无调用场景的任务集,比较工具选择、参数和最终结果。描述变更与 Schema 变更一样进入版本和回归。
追问六:Registry 和工具网关有什么区别
Registry 管理工具定义、实现映射和策略元数据;网关是在一次真实调用中读取这些信息,执行校验、授权、限流、调用、结果规范化和审计。
怎样向面试官讲 Tool Registry
Agent 工具我会拆成 Schema、Registry 和执行网关三层。Schema 是给模型看的说明书,包含稳定名称、明确的使用边界、参数约束和返回语义;Registry 给程序使用,把工具名与真实执行器、版本、权限、是否有副作用、超时、重试、缓存和可用状态关联;运行时根据用户、租户和任务只投影允许的 Schema。模型返回 tool call 后,网关重新解析 Registry,完成名称、Schema、业务和权限校验,高风险动作进入审批,再调用执行器并把结果转换成统一 Observation。工具描述、Schema 和执行器都版本化,新增工具先灰度,故障时同时停止暴露和执行。验证会覆盖契约、相似工具选择、权限绕过、故障注入和完整 Trace。这样 Agent loop 只依赖抽象协议,底层工具可以独立替换和治理。
Tool Registry 是否真能维护:六项核验
- 工具说明、执行器和运行策略是否真的分层。
- Schema 是否写清使用条件、限制、参数和返回语义。
- Registry 是否支持版本、禁用、权限、副作用和重试策略。
- 模型看到的是否只是当前身份允许的工具子集。
- 网关是否再次做业务校验和授权,不能信任模型参数。
- 描述或 Schema 变化后,是否有选择回归和失败 Trace。
工具数量不能证明系统成熟。更可靠的标志是每个工具都能被准确选择、安全执行、独立演进,并在出错时明确知道问题发生在说明、参数、权限还是执行器。