导语
上一篇,我们走完了一次 Tool Calling:应用提供工具定义,模型返回 Tool Call,Runtime 校验并执行,再把结果送回模型。
其中有一个环节被刻意略过了:模型怎样知道该选哪个工具,参数又该怎么填?
假设一个开发 Agent 同时拥有两个工具:
search_code search_logs用户说:
帮我找出 INVALID_SIGNATURE 是从哪里产生的。它应该搜代码,还是搜运行日志?如果工具描述都只写“搜索内容”,模型只能猜。
Tool Schema 就是模型看到的操作说明书。它用工具名、用途描述和参数约束,回答三件事:
- 这个工具是做什么的;
- 什么情况下应该用它;
- 调用时需要提供哪些结构化参数。
Schema 设计不好,强模型也可能选错工具、漏传参数或扩大查询范围。本文会用一组“代码搜索与日志搜索”工具,完整演示怎样把模糊能力改造成清晰接口。
一、先看一个看似能用的坏 Schema
constname'search'description'搜索信息'parameterstype'object'propertiesinputtype'string'这份定义没有语法错误,却几乎没有帮助模型做决策。
模型不知道:
- 搜索的是源码、日志、文档还是互联网;
input是关键词、正则表达式还是一段自然语言;- 搜索范围在哪里;
- 参数是不是必填;
- 结果为空时意味着什么。
更麻烦的是,如果工具实现允许input同时表达动作和参数,它就会成为一个万能入口:
search({ input: "在生产日志里找错误并顺便删除重复记录" })Runtime 很难对这种自由文本做可靠校验和细粒度授权。
一个能通过 JSON Schema 校验的定义,不一定是一个适合模型使用的 Tool Schema。
二、工具名应该表达一个清晰动作
工具名是模型做选择时最先看到的信号之一。
相比:
github data_tool handle execute下面这些名字更容易形成稳定边界:
get_pull_request search_application_logs read_file create_review_draft实用的命名习惯是“动词 + 对象”:
get:获取一个已知对象;list:列出一组对象;search:根据条件查找未知位置;create:创建新资源;update:修改已有资源;delete:删除资源。
这不是为了追求英文整齐,而是让动作的副作用和返回预期更明显。
例如get_order与update_order应该分开。前者可以自动读取,后者可能需要确认。如果合成order_tool,权限系统还要再次解析参数才能判断风险。
名字不要承担全部解释
不要为了写清边界,造出过长的名字:
search_staging_application_logs_by_exact_error_code_only工具名负责识别,完整条件交给 description 和参数 schema。三者要协作,而不是把所有信息压进名字。
三、Description 要写选择边界,不是宣传语
下面这种描述信息量很低:
强大、智能、快速地搜索日志。模型真正需要知道的是:什么时候用、能查什么、不能做什么。
例如:
查询指定环境中的应用运行日志。 适用于根据时间范围、服务名、请求 ID 或错误码定位运行时问题。 不用于搜索源码;搜索源码请使用 search_code。 该工具只读,不会修改日志或服务状态。这段描述提供了四类信号:
- 能力:查询应用日志;
- 触发场景:定位运行时问题;
- 反边界:不搜索源码;
- 副作用:只读。
当两个工具容易混淆时,互相写清“不要在什么情况下使用”很有效。但不必给每个工具堆十条反例;只有真实存在选择冲突时才添加。
四、参数名要让模型知道“该填什么”
继续改造日志工具:
constname'search_application_logs'description'查询指定环境中的应用运行日志。''用于根据服务、时间范围、请求 ID 或错误码定位运行时问题。''不用于搜索源码;搜索源码请使用 search_code。''该工具只读。'join' 'parameterstype'object'propertiesenvironmenttype'string'enum'staging''production'description'要查询的部署环境'servicetype'string'description'服务名,例如 auth-api'querytype'string'minLength1description'日志查询表达式,可包含错误码或请求 ID'startTimetype'string'format'date-time'description'查询起始时间,ISO 8601 格式'endTimetype'string'format'date-time'description'查询结束时间,ISO 8601 格式'limittype'integer'minimum1maximum200default50description'最多返回多少条日志'required'environment''service''query''startTime''endTime'additionalPropertiesfalse这里每个字段只表达一个概念。
不要把几个概念塞进一个字符串:
{"filter":"staging auth-api last 30 minutes limit 50"}这种格式看起来省字段,实际上把解析工作重新推给模型或工具实现。拆成结构化参数后,Runtime 才能检查时间范围、环境和最大返回条数。
五、required、默认值和null不是一回事
JSON Schema 中,在properties里声明字段,并不自动代表它必填。必填字段要放入required。
{"properties":{"service":{"type":"string"}},"required":["service"]}还要区分三种状态:
字段缺失 字段存在但值为 null 字段存在且使用默认语义如果 schema 只允许string,传入null并不等同于省略字段。
默认值也不要只写在描述里。更稳妥的做法是:
- schema 用
default告诉读者和工具系统推荐值; - Runtime 或工具实现真正补齐默认值;
- 日志记录补齐后的最终参数。
不同校验器不一定会自动应用default,不能假设“写了 default 就一定改写输入”。
六、Enum、范围和格式是可执行边界
能枚举的值,不要让模型自由拼写:
{"environment":{"type":"string","enum":["staging","production"]}}相比任意字符串,它可以拦截:
prod online 正式环境 production-eu-secret数值也应该有合理范围:
{"limit":{"type":"integer","minimum":1,"maximum":200}}否则模型可能请求返回十万条日志,既慢又占满上下文。
格式约束能表达日期时间、URI 等常见结构,但要确认你使用的校验器是否实际启用了对应 format 检查。Schema 是契约,Runtime 使用的验证行为才是最终事实。
七、为什么建议关闭额外字段
JSON Schema 默认允许未声明的额外属性。也就是说,只写properties时,这类输入可能仍会通过:
{"service":"auth-api","query":"INVALID_SIGNATURE","deleteAfterRead":true}对于边界明确的工具,通常可以设置:
{"additionalProperties":false}这样模型多传字段时,Runtime 会明确拒绝,而不是静默忽略或把未知字段传给下游。
不过复杂 schema 使用allOf等组合关键字时,additionalProperties的作用域容易产生意外。不要机械添加后就结束;要用真实样例验证合法和非法输入。
八、一个工具应该做多大一件事
工具太大,模型难选择,权限也难控制:
github工具太碎,模型又需要在几十个近似动作中犹豫:
get_issue_title get_issue_body get_issue_author get_issue_labels更合适的边界通常对应一个可理解、可授权、可测试的业务动作:
get_issue list_issue_comments create_issue_comment update_issue_labels判断是否应该拆分,可以问四个问题:
- 不同动作的副作用是否不同?
- 是否需要不同权限或确认策略?
- 参数和错误类型是否明显不同?
- 模型是否经常只需要其中一部分能力?
任意两项差异很大时,拆开通常更清晰。
但这不是绝对公式。最终要用真实任务集测试,而不是只凭接口美感判断。
九、不要让模型填写系统已经知道的信息
假设用户已在产品里打开仓库acme/web-app,服务端也知道当前账号和组织。
这时工具参数未必需要再次暴露:
{"userId":"?","tenantId":"?","accessToken":"?","owner":"?","repo":"?"}可信信息应从执行上下文注入:
asyncfunctionexecute args:SearchCodeArgs,context:ToolContextreturnsearchtenantIdtenantIdrepositorycurrentRepositoryqueryquery这样既减少模型出错,也避免它把一次会话里的资源标识带到另一位用户或另一个租户。
原则是:模型只填写完成语义动作所需、且确实需要它判断的参数;身份、凭证和已确定的资源上下文由应用提供。
十、Schema 需要怎样测试
Schema 不是写完看着合理就结束。至少要准备三组测试。
选择测试
给模型一组真实用户请求,检查它是否选对工具:
“找出这段函数在哪里被调用” → search_code “查 request_id=abc 的线上错误” → search_application_logs还要加入容易混淆的反例。
参数测试
检查正常、边界和非法参数:
缺少必填字段 时间范围颠倒 limit 超过上限 多出未知字段 environment 使用未允许值JSON Schema 负责结构约束;像“startTime 必须早于 endTime”这样的跨字段业务规则,通常仍需要额外代码验证。
权限测试
同一份合法参数,在不同用户和环境下可能得到不同结果:
开发者查询 staging → 允许 开发者查询 production → 需要额外权限 外部用户查询内部服务 → 拒绝Schema 不能替代授权测试。
十一、前端和后端如何共同使用 Schema
后端把 Schema 用于模型提示、参数校验和工具路由。前端也可以从同一份元数据中获得帮助:
- 展示即将执行的工具名称;
- 把关键参数转成确认摘要;
- 为人工修正参数生成表单;
- 在提交前进行基础校验。
但不要直接把原始 Schema 无脑渲染给用户。query对模型可能很清楚,对普通用户却需要显示成“日志查询条件”。产品层可以维护安全、可本地化的展示元数据。
同一份底层契约可以服务多端,但模型描述、开发者文档和用户界面不必使用完全相同的文案。
十二、用一组工具检查是否理解
现在设计一个订单查询 Agent,已有三个工具:
get_order list_fulfillment_events cancel_order试着回答:
- 为什么不能合成一个
order_tool? cancel_order的描述应该怎样明确副作用?- 订单所属租户应该由模型传入吗?
reason是必填、可空还是可省略?依据是什么?- 哪些非法参数可以由 JSON Schema 拒绝,哪些仍需业务代码检查?
如果你能根据权限、动作边界和业务语义回答,而不只是堆更多字段,就已经掌握了 Schema 设计的核心。
十三、这一篇真正要记住的事
Tool Schema 不只是“把 TypeScript 类型翻译成 JSON”。它同时服务三个目标:
- 帮模型在相似能力中选对工具;
- 把自然语言意图约束成可校验参数;
- 给 Runtime 留下权限、测试和审计的清晰边界。
设计时优先检查:
名字是否表达明确动作 描述是否说明使用场景与反边界 字段是否一项一义 必填、枚举、范围和额外字段是否受控 可信身份是否来自服务端上下文 读写动作是否因权限和副作用而拆分Schema 解决的是“模型怎样提出一份清楚的调用请求”。下一篇继续看另一半:工具执行完后,怎样把结果返回给模型,才不会让它误判、浪费上下文或陷入重试。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~