Agent 系列(16):工具链设计——让 LLM 用对工具的五个原则

作者:冬奇Lab日期:2026/6/9

工具文档是写给 LLM 的,不是写给人的

你有没有写过这样的工具文档:

1@lc_tool
2def get_data(query: str) -> str:
3    """Get data."""
4    ...
5

这对人类来说是糟糕的文档,对 LLM 来说更糟——它不知道这个工具做什么、什么时候调它、传什么参数。

工具设计有三条核心维度:描述质量(LLM 选不选你)、错误处理(出错时崩不崩)、粒度设计(参数好不好提取)。本文用实验数据说话。


Demo 1:描述质量——真正影响工具选择的条件

对比同一个天气工具的两个版本:

1# 版本 A:模糊
2@lc_tool
3def weather_vague(city: str) -> str:
4    """Get data."""
5    ...
6
7# 版本 B:精准
8@lc_tool
9def weather_precise(city: str) -> str:
10    """Get current weather for a city.
11
12    Returns temperature (Celsius) and condition (sunny / cloudy / rainy / unknown).
13    Use this whenever the user asks about weather, temperature, or sky conditions
14    for a specific city. Pass the city name as a plain string, e.g. 'Beijing'.
15    """
16    ...
17

用 5 条天气查询对比两个版本的工具调用率:

1Query                                            Vague      Precise
2------------------------------------------------ ---------- ----------
3What's the weather in Beijing today?              called    called
4Is it raining in Shanghai right now?              called    called
5What temperature should I expect in Shenzhen?     called    called
6Should I bring an umbrella to Beijing?            called    called
7How's the sky in Shanghai?                        called    called
8
9Tool call rate  Vague: 5/5  Precise: 5/5
10

两者都是 5/5。

这是一个反直觉的结果,背后有重要的前提条件:当 Agent 只有一个工具时,LLM 别无选择,无论描述多糟糕都会用它。 描述质量的差距只在 LLM 需要从多个工具中选择时才显现——这才是生产系统的常态。

一个 Agent 挂了 10 个工具,用户问"帮我查一下北京的天气",LLM 要在 10 个 docstring 里找出谁最匹配。此时精准描述的工具胜率远高于模糊工具。

描述质量的黄金格式:

1"""<一句话说做什么>
2
3返回:<返回值的格式和含义>
4使用时机:<什么类型的用户问题应该触发这个工具>
5参数说明:<参数名 + 传入格式示例>
6"""
7

Demo 2:错误处理——raise 还是 return?

两个版本的工具,逻辑相同,出错行为不同:

1# 抛出异常  危险
2@lc_tool
3def weather_raises(city: str) -> str:
4    """Get current weather for a city."""
5    if city.lower() not in MOCK_WEATHER:
6        raise ValueError(f"City '{city}' not found in database.")
7    ...
8
9# 返回错误字符串  安全
10@lc_tool
11def weather_returns_error(city: str) -> str:
12    """Get current weather for a city. Returns error message if city not found."""
13    data = MOCK_WEATHER.get(city.lower())
14    if data is None:
15        return (f"City '{city}' not found. "
16                f"Available cities: {list(MOCK_WEATHER.keys())}. "
17                f"Please ask the user to confirm the city name.")
18    ...
19

三个测试用例:

已知城市(Beijing): 两者结果相同,正常返回天气。

未知城市(Atlantis):

1raises : [CRASHED] ValueError: City 'Atlantis' not found in database.
2returns: I'm sorry, but I couldn't find the weather information for Atlantis.
3         Please make sure the city name is correct...
4

weather_raises 直接崩溃,整个 Agent run 终止;weather_returns_error 的 LLM 读到错误字符串,组织了一条友好的回复。

拼写错误(Shanghia):

1raises : The current weather in Shanghai is cloudy with a temperature of 22°C.
2returns: The current weather in Shanghai is 22°C with a cloudy condition.
3

两者都正确——因为 LLM 在调工具之前就把 "Shanghia" 自动纠正成了 "Shanghai",工具接收到的是正确城市名。这说明 LLM 有一定的输入自愈能力,但不能依赖它。

结论:工具只应返回字符串,永远不抛出异常。 异常会跳出 Agent 的控制流,LLM 没有机会处理它。错误字符串则可以被 LLM 读取、理解、然后决定下一步(重试、告知用户、换一个工具)。


Demo 3:粒度设计——胖工具 vs 细粒度工具

胖工具: 一个工具包办所有事,传入自由文本。

1@lc_tool
2def omnibus_lookup(query: str) -> str:
3    """Look up weather, product info, or evaluate math. Pass the full user question."""
4    q = query.lower()
5    for city in MOCK_WEATHER:
6        if city in q:
7            return json.dumps(MOCK_WEATHER[city])
8    for name in MOCK_PRODUCTS:
9        if name in q:
10            return json.dumps(MOCK_PRODUCTS[name])
11    # try math...
12

细粒度工具: 三个独立工具,各有精准类型参数。

四个测试用例的对比结果:

单步查询(天气、产品): 两者都能完成任务,差异不明显。

多步 — 天气 + 计算差值:

1Fat  tools=['omnibus_lookup', 'omnibus_lookup']
2      The temperature difference is 3 degrees Celsius.
3
4Fine tools=['get_weather', 'get_weather', 'calculator']
5      The difference is 3°C.  (3 separate calls)
6

Fat tool 调了两次,每次查一个城市,自己没法算差;Fine 工具调了两次天气 + 一次 calculator,明确分工。

多步 — 产品价格 + 年费计算:

1Fat  tools=['omnibus_lookup']   (只调一次!)
2      The monthly price is $299. The annual cost is $3588.
3
4Fine tools=['get_product_info', 'calculator']   (两次)
5      The monthly price is $299. The annual cost is $3588.
6

这是最有趣的结果:Fat tool 只调了一次就答对了。因为 omnibus 工具内部发现价格是 299,LLM 在后续回答里直接做了 299×12 的心算,没有触发工具里的数学逻辑。

这说明 Fat 工具并不总是更差——但它的执行路径不透明,不可追踪,无法测试,不可维护。

何时用细粒度,何时允许合并:

1选细粒度:
2  - 多个工具会被不同查询分别触发
3  - 工具参数有明确的结构化类型(city: str, amount: float)
4  - 需要可观测性(每个工具单独计时、记录入参)
5
6允许合并:
7  - 两个操作总是一起出现,从不单独使用
8  - 合并后参数仍然是结构化的(不是自由文本)
9  - 例如:get_weather_and_unit(city, unit: Literal["C","F"])
10

绝对不合并的情况: 合并后参数退化为自由文本 query: str——这把参数提取的负担推给了工具内部的文本解析,比让 LLM 提取结构化参数更脆。


五条工具设计黄金规则

1原则              错误示范                        正确示范
2──────────────────────────────────────────────────────────────────────
3描述              "Get data."                     What + When + How + param format
4错误处理          raise ValueError(...)           return "Error: city not found"
5粒度              omnibus(query: str)             get_weather(city: str)
6参数命名          lookup(q: str)                  get_weather(city: str)
7返回格式          raw dict / None                 JSON string or error string
8

黄金规则:为 LLM 设计工具,不是为人类。

LLM 通过三个信息决定如何调工具:

  1. docstring:决定选不选这个工具
  2. 参数类型和名称:决定传什么值
  3. 返回值:决定下一步怎么做

这三个信息设计好,工具自然被正确使用。


设计 Checklist

Docstring

  • 第一句话说清楚工具做什么(动词开头)
  • 说明返回值格式(JSON / 纯文本 / 错误字符串)
  • 说明使用时机("when the user asks about...")
  • 给出参数示例(e.g. 'Beijing', e.g. '299 * 12'

错误处理

  • 工具只 return,永远不 raise
  • 错误消息要有行动指南("city not found. Available: [...]")
  • 区分"数据不存在"和"输入格式错误",给出不同提示

粒度

  • 参数是结构化类型(str with clear semantics, int, float),不是自由文本
  • 一个工具只做一件事——如果描述需要"以及"或"或者",考虑拆分
  • 多个工具之间互斥:不同查询触发不同工具

返回格式

  • 成功:JSON 字符串(方便 LLM 解析字段)
  • 失败:"Error: <原因>. <建议操作>" 格式
  • 不返回 None 或空字符串——LLM 不知道怎么处理空值

总结

五个核心结论:

  1. 描述质量的战场是多工具竞争:单工具时 LLM 别无选择,多工具时好 docstring 的工具胜率显著更高
  2. 工具只返回字符串,永远不抛异常:raise 让 Agent 崩溃,return 错误字符串让 LLM 有机会恢复
  3. LLM 有输入自愈能力,但不可依赖:"Shanghia" 被自动纠正为 "Shanghai",但这不是可靠的防线
  4. Fat 工具并不总是更差,但不可追踪:实测 omnibus 工具在某些场景只需一次调用就完成任务,但代价是执行路径不透明
  5. 参数类型决定参数质量city: str(明确语义)> q: str(自由文本)——参数类型越清晰,LLM 提取值越准确

下一篇:Agent 上下文工程进阶 —— 如何精确控制传给 LLM 的信息:系统提示词优化、few-shot 示例选择、动态上下文注入。


参考资料


欢迎访问 PrimeSkills —— 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。

更多实用知识和有趣产品,欢迎访问我的个人主页


Agent 系列(16):工具链设计——让 LLM 用对工具的五个原则》 是转载文章,点击查看原文


相关推荐


实战解析:如何用自然语言驱动混沌工程?Blade AI Agent 实现故障演练全链路自动化
阿里云云原生2026/6/1

作者:林曜、穹谷 混沌工程为什么难落地? 每个 SRE 团队都知道混沌工程的价值——在可控条件下主动注入故障,验证系统韧性,防患于未然。 但现实是,绝大多数团队的故障演练停留在“年度任务”而非“日常习惯”。原因很简单: 门槛太高,流程太碎。 一次完整演练五步:定位目标 → 拼装命令 → 确认安全 → 验证效果 → 善后清理。每一步都要查文档、写参数、跑命令。即使是经验丰富的工程师,单次演练也需要 20-30 分钟。而任何一步遗漏(忘了验证、忘了清理),后果都可能比不演练更糟。 Blade AI


策略周度复盘 | 2026年wk19
0xAI2026/5/12

本文观点仅供参考,不构成任何投资建议。投资有风险,入市需谨慎。 一、本周大盘走势 本周从周三开始开盘,只有3个交易日(5月6日-8日),但是整个大A还是实现了开门红。到周五收盘为止,整个大盘走势稳扎稳打,虽然有大涨,不过回调也比较有限,仍然维持着比较强势的多头态势。再加上外围美股市场AI科技大行其道,一片”涨声“,所以下周开盘,大概率还会延续本周的涨势。手上有票的朋友不必慌张,可以继续持股等着更大的涨幅。 接下来,还是老规矩,我们以真实数据说话,一图胜千言。本周三大股指本周仍然是以创业板为主,


🚀 2026 年 4 月 GitHub 十大热门项目排行榜 🔥
一点一木2026/5/2

欢迎来到 2026 年 4 月 GitHub 热门开源项目排行榜!本月榜单横跨 成长型通用智能体、Claude Code 技能与记忆、文档—Markdown 数据管线、Token 经济学 CLI、多智能体协作平台、Harness / 工作流治理、Agent-Native 教育 与 金融时序基础模型 等方向。这些项目共同指向:把编码智能体从「单次对话」推进到「可协作、可度量、可沉淀」;它们几乎全部围绕「更稳的 Harness、更省的 Token、更真的垂直数据」展开,不再是概念验证,而是可以立刻嵌


数据仓库是什么?怎么搭建数据仓库?
isNotNullX2026/4/23

我们每天都在跟数据打交道,但提到数据仓库这个词,大多数人的第一反应还是——听说过,但说不清到底是什么。 有人觉得它就是存数据的地方; 有人觉得它和数据库差不多; 也有不少人以为,只有大厂、只有数据团队才需要数据仓库。 实际上,只要企业存在多个业务系统、多个部门协同、多个分析口径,数据仓库几乎就会成为绕不开的一步。 这篇文章,我们就把数据仓库这件事彻底讲清楚: 数据仓库到底是什么?企业为什么需要它?怎么搭建?又能给企业带来什么价值? 开始之前,我整理了一份数据仓库建设解决方案,里面涵盖了从


C语言-----扫雷游戏
2026/4/14

扫雷游戏的功能说明 : • 使⽤控制台实现经典的扫雷游戏 • 游戏可以通过菜单实现继续玩或者退出游戏 • 扫雷的棋盘是9*9的格⼦ • 默认随机布置10个雷 • 可以排查雷: ◦ 如果位置不是雷,就显⽰周围有⼏个雷 ◦ 如果位置是雷,就炸死游戏结束 ◦ 把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束 test.c //⽂件中写游戏的测试逻辑 game.c //⽂件中写游戏中函数的实现等 game.h //⽂件中写游戏需要的数据类型和函数声明等 逻辑开始: 一、菜单 输入1进入游戏,输入


UniApp 页面跳转完全指南:5 种路由方式详解与实战对比
编程随想_Code2026/4/6

前言 在 UniApp 开发中,页面间的跳转是最常见的操作之一。UniApp 提供了 5 种路由 API,分别对应不同的跳转场景。选错跳转方式轻则体验变差,重则出现"无法返回""Tab 页跳转失败"等让人头疼的 bug。 本文将逐一讲解 5 种跳转方式的原理、适用场景与代码示例,并附上横向对比表,方便日常查阅。 一、uni.navigateTo — 保留式跳转 最常用的跳转方式。跳转到新页面时,当前页面并不会被销毁,而是压入页面栈(page stack),用户可以通过左滑或返回按钮回到


放过自己,降低预期,及时行乐
野生的码农2026/3/29

1 月初,发了篇文章《做好自己的份内工作,等着被裁》,文中提到: 距离公司上次「狼人杀 」,三年之期已到,今年会有「狼人杀 2.0」吗? 之后,因为自己的原因,工作异常忙碌,每天加班到很晚。真不是怕被刀,只是责任心使然,至少要对得起工资,再次断更了很久。 前几天,有位铁粉发来一张某司的毕业证,问我是否安好。很感谢他的关心,只是我没在那个公司。虽然它是合肥本地最知名的公司,但它只是厂大,并不是大厂。 我司只是个小厂,但同样不太平。都怪我这破嘴,就跟开了光似的。那篇文章发出后仅一周,狼人真的再次


HTML和CSS和JavaScript的区别
漫随流水2026/3/21

一、从代码外观直接区分 1.1 HTML:尖括号包裹的标签 HTML的特点是尖括号<>包围的标签,成对出现(开始标签和结束标签)。 <div>这是一个div容器</div> <p>这是一个段落</p> <h1>这是一个标题</h1> <img src="图片.jpg"> <!-- 自闭合标签 --> <a href="链接.html">这是一个链接</a> <table>...</table> <form>...</form> 识别口诀:看到<xxx>和</xxx>,这就是HTML。


音视频教程-第二节
glumes2026/3/13

音视频系列教程 课程目标 学习如何使用 FFmpeg 打开和读取媒体文件的基本信息,理解 AVFormatContext 的作用。 封装格式简介 在开始之前,先简单了解一下封装格式。 我们常见的视频文件(如 .mp4、.mkv、.avi)都是封装格式,它们把视频、音频、字幕等数据打包在一起。可以简单理解为:封装格式是"盒子",里面装着编码后的视频和音频数据。 封装格式 vs 编码格式: 封装格式(如 MP4、MKV):决定如何打包和组织数据 编码格式(如 H.264、AAC):决定如何压缩数


Interspeech2022论文解读 | CUSIDE:一个流式语音识别新框架,刷新SOTA
成都它思科技有限公司2026/3/4

简介 本文介绍清华大学语音处理与机器智能实验室(Speech Processing and Machine Intelligence, SPMI)与美团的联合工作 — CUSIDE:分块、模拟未来、解码的流式语音识别新框架,刷新了目前Aishell-1上流式模型的SOTA(State Of The Art,最好结果)。该工作已被语音领域的国际会议Interspeech2022接收,论文的作者是安柯宇、郑华焕、欧智坚、向鸿雨、丁科、万广鲁。 论文链接: http://oa.ee.

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读