工具定义写在代码注解里,还是存在数据库里?
简化版
两种都能用,差别在「发给模型的说明从哪来」。写在注解里(@Tool 描述、@P / @ToolParam 参数说明):说明和实现写在一起,参数类型从方法签名生成,类型安全、不会对不上,但改一个字就要发版;通常再配一张表只管启用开关和返回上限,表里的编码必须和方法名一字不差。存在数据库里:名称、说明、入参说明都存表,运行时拼成工具定义,改说明下一次执行就生效,适合频繁调优;但执行逻辑仍在代码里,要靠编码分发,表和代码可能对不上,入参类型也要自己维护。选择看两点:说明需要多频繁地调,以及团队能否接受「改说明要发版」。
详细版
| 维度 | 注解定义 + 表管开关 | 表驱动定义 |
|---|---|---|
| 发给模型的说明 | 注解文字 | 表里的 description |
| 参数 Schema | 方法签名 + 参数注解自动生成 | 表里的入参说明,自己转成 Schema |
| 改说明 | 改代码、发版 | 页面改,下一次执行生效 |
| 类型安全 | 高,编译期检查 | 靠约定,写错要容错 |
| 执行 | 框架反射调用同一个方法 | 分发器按编码 switch 到实现 |
| 容易出错的地方 | 表里编码和方法名对不上;代理类扫不到注解 | 表里有、代码没实现;入参类型推断不准 |
| 适合 | 工具稳定、团队习惯代码审查 | 说明需要频繁调、运营人员参与 |
完整版教学
一、两种方案的数据流
注解方案:
@Tool("说明") + @P("参数说明") 的方法
→ 框架扫描 → 工具规格(名称=方法名,说明=注解,参数=签名)
→ 表里查启用的编码,过滤工具规格 → 交给模型
→ 模型调用 → 框架反射调用同一个方法
表驱动方案:
表里一行:编码、说明、入参说明、状态
→ 运行时查启用的行 → 拼成工具规格(名称=编码,说明=表,参数=入参说明转 Schema)
→ 交给模型 → 模型调用 → 执行器按编码分发到代码里的实现
两者的共同点是执行逻辑都在代码里;区别在于「模型看到的说明和参数定义」是编译进代码,还是存在数据库。
二、注解方案的优点:一处定义,不会对不上
注解方案里,工具规格和执行器从同一个方法转出来:方法名就是工具名,参数签名就是 Schema,框架回调时也调这个方法。
// 示例写法
@Tool("查询两个点位之间的直线距离、路程和预计车程。不要用它穷举搜索最优顺序。")
public String queryDistanceTool(
@P("起点点位ID") Long fromPoiId,
@P("终点点位ID") Long toPoiId) { ... }
好处是:参数类型由编译器保证,改了参数签名 Schema 自动跟着变;说明写在方法旁边,改逻辑时顺手就能改说明,不会出现说明还在讲旧行为的情况。
三、注解方案的坑
坑一:表里的编码必须和方法名一字不差。 表只管开关时,过滤逻辑是「扫出的方法名 ∈ 启用的编码集合」。编码多一个空格、改了方法名没改表,工具就静默失效。稳妥的做法是:扫到的方法在表里找不到登记时直接报错,而不是悄悄跳过;编码在页面上不允许编辑。
坑二:表里的说明改了不影响模型。 管理员在页面上改了工具说明,以为调了模型的行为,其实模型看到的仍是注解里的文字。页面上要写清楚「这里的说明只给管理员看」。
坑三:代理类扫不到注解。 Spring 给 Bean 加了事务、异步等切面后,注入进来的是代理对象,代理类重写的方法上不带 @Tool:
扫描代理类 → 一个工具都扫不到 → 报「当前没有启用任何工具」
→ 人跑去工具中心看,发现开关都开着,白找一圈
正确做法是按目标类扫描注解,执行时仍然拿注入的代理对象去调,切面照常生效。
易错点:「没有启用任何工具」这个报错,原因可能根本不在工具中心,而在代理对象上。
四、表驱动方案的优点:说明可以在线调
工具说明本质上是 Prompt,调它和调 Prompt 是同一类工作:改一版、跑几个样例、看效果、再改。表驱动方案里改完说明,下一次执行就生效:
| 调整 | 注解方案 | 表驱动方案 |
|---|---|---|
| 改一句说明,看模型是否更少误调 | 改代码 → 构建 → 部署,十几分钟到几小时 | 页面保存,下一次执行生效 |
| 临时下线某个工具 | 表里开关(两种方案一样) | 表里开关 |
按一次调优的真实节奏算一笔账(时间为估算):
调一个工具的说明,通常要改 4 版左右才稳定
注解方案:每版 改代码 → 构建 → 部署 ≈ 20 分钟,4 版 ≈ 80 分钟,还要占用发布窗口
表驱动: 每版 页面保存 ≈ 几十秒,4 版几分钟内完成,改错了随时改回
说明需要频繁调、或者由不写代码的人参与调优时,表驱动更合适。
五、表驱动方案的坑
坑一:表里有、代码没实现。 管理员新增了一行 send_email,代码里的分发器没有这个分支。模型看到这个工具、决定调用、执行器报「未配置」,白白浪费一次调用。构建工具池时要过滤掉没有实现的工具,页面上标出来。
坑二:入参类型要自己维护。 表里的入参说明通常是 {"参数名": "说明"} 这样的简单格式,要转成 JSON Schema,类型从哪来就成了问题:
按字段名推断:以 Id 结尾 → integer,其余 → string
问题:布尔、数组、枚举都表达不了
更好的做法:表里直接存 JSON Schema,或至少允许声明类型
坑三:入参说明写错不能让整次执行崩。 入参说明是人在页面上手填的,括号写错很常见。解析失败时退化成无参工具或空 Schema,比整次 Agent 执行失败更好。
六、怎么选
工具数量少、说明稳定、团队习惯通过代码审查改动 → 注解 + 表管开关
说明需要频繁调优、运营或产品人员参与 → 表驱动
两者折中:注解定义参数(类型安全),说明允许表里覆盖(可在线调)
无论哪种,开关都应该在表里,并且在所有执行入口一致生效;执行逻辑都应该在代码里,不要把可执行的内容存进数据库。
七、两种方案都要有的「对账」
表和代码是两份数据,一定会出现不同步。两种方案都应该在启动或执行时对账,并把结果展示在页面上:
| 方案 | 对账项 |
|---|---|
| 注解方案 | 每个扫到的方法在表里有登记;表里的编码都能找到方法 |
| 表驱动方案 | 表里每个启用的编码在分发器里有实现;有实现的工具不能被删除 |
对账失败时要大声报错,而不是静默跳过,因为「某个工具悄悄没了」是最难排查的问题。
八、常见误区与追问
- 误区:改了工具中心页面上的说明,模型就会看到。 注解方案里模型看到的是注解文字,表里的说明只给管理员看。
- 误区:表驱动就是把工具逻辑存进数据库。 表里只存定义和开关,执行逻辑必须在代码里按编码分发。
- 误区:扫描不到工具就是没启用。 可能是扫描了代理类,代理类的方法上没有注解,要按目标类扫描。
- 误区:入参说明按字段名推断类型就够了。 布尔、数组、枚举表达不了,最好直接存 JSON Schema 或允许声明类型。
- 误区:表和代码对不上时跳过就行。 静默跳过会让工具悄悄失效,对账失败要明确报错。
- 追问:能不能两种结合? 可以用注解生成参数 Schema 保证类型,再允许表里的说明覆盖注解说明,兼顾类型安全和在线调优。
- 追问:注解方案里编码为什么不允许在页面上编辑? 编码必须和方法名一致,改了就对不上,工具会失效或执行时报错。
九、加强记忆
注解方案里说明和参数来自注解与方法签名,一处定义不会对不上,类型安全,但改说明要发版;表只管开关,编码要和方法名一字不差,页面说明不影响模型,扫描要按目标类避开代理。表驱动方案里说明和入参说明存表可在线调,执行按编码分发,要处理表里有代码没实现、入参类型推断不准、说明写错不能崩。说明需要频繁调选表驱动,稳定选注解,也可以折中。两种都要把开关放表里、逻辑放代码里,并做对账、失败时报错。
项目实战落地
项目里怎么做的
《AI Agent旅游行程智能规划平台》用的是「注解定义 + 表管开关」:
- 说明来自注解:8 个工具的描述写在
@Tool注解里,参数说明写在@P注解里;function_tool表里的说明和入参说明只给管理员看,改它不影响模型看到的内容; - 表只管两件事:
enabled决定放不放进工具池,max_return_rows决定列表工具单次返回多少行; - 编码和方法名对账:
loadEnabledTools扫出所有@Tool方法,方法名在表里找不到登记就直接报错「没有登记到 function_tool 表」,找得到但停用就不放进工具池;编辑弹窗里工具编码只读; - 按目标类扫描:注入的 Bean 可能是加了切面的代理对象,代理类的方法上没有
@Tool,所以按目标类找方法,执行器仍然拿注入的对象去调。
《AI Agent 智慧医院智能导诊就诊系统》用的是表驱动:mcp_tool 的 description 和 input_schema 直接发给模型,改完下一次执行生效;执行时分发器按 tool_code 用 switch 路由到具体方法,表里有编码但分发器还没接入的,执行时返回「工具执行器未接入」并记一条失败日志。
为什么这样取舍
- 旅游项目选注解:工具规格和执行器从同一个方法转出,发给模型的工具名、参数和真正被调用的方法不会对不上;不用自己拼工具 JSON、自己解析调用。
- 医院项目选表驱动:分诊时模型是否调用症状检索、何时查排班,全看说明写得准不准,需要能在后台改完立即验证。
面试官还会追问
- 在工具中心删掉某条工具登记,但代码里还保留着对应的
@Tool方法,下一次发起编排会怎样?怎么恢复? - 工具中心的编辑弹窗里,工具编码为什么不允许修改?
学完《AI Agent旅游行程智能规划平台》,上面这些追问你都会迎刃而解。