1. 从工具到伙伴:Agent 范式跃迁的底层逻辑
1.1 为什么“工具”这个词已经不够用了
过去两年,我参与过不少 Agent 相关的项目,从最早的“给 LLM 套一个函数调用壳子”到后来做多步推理、任务编排、记忆管理,踩过的坑比写过的代码还多。最开始大家管这东西叫“工具调用”,本质上就是把大模型当成一个更聪明的 if-else 分支器:用户输入一句话,模型判断该调哪个 API,调完把结果拼回去,结束。这个阶段的核心矛盾是“模型能不能正确选对工具”,所以那会儿论文和工程博客都在卷 function calling 的准确率、参数抽取的 F1 值。
但做着做着就发现不对劲了。用户不满足于“帮我查天气”“帮我订会议室”这种单轮指令,他们开始说“帮我规划下周去杭州出差的行程,顺便看看有没有合适的客户拜访机会,回来之后把报销单填了”。这句话里隐含了时间推理、地点偏好、联系人匹配、表单填写等多个子任务,而且子任务之间还有依赖关系。这时候你再把 LLM 当“工具选择器”用,它就会在某个环节掉链子——要么忘了前面查过的航班时间,要么把报销单的金额填成了机票价格而不是总价。
这就是“工具”范式的天花板:它假设任务是可以被预先拆解成固定步骤的,而 LLM 只负责在每一步做局部决策。但真实世界的任务往往是模糊的、动态的、需要边做边调整的。于是“伙伴”范式开始冒头——Agent 不再是一个被动的执行器,而是一个能主动追问、能记住上下文、能在失败后换策略的协作实体。这个跃迁不是营销话术,而是工程上被逼出来的必然选择。
1.2 Harness、Skill、Tool 三者的关系到底怎么理
热词里反复出现 Harness、Skill、Tool,很多人搞不清它们的边界。我用自己的项目经验给一个粗暴但好用的定义:
- Tool是最底层的原子能力,比如“发送 HTTP 请求”“读取本地文件”“执行 SQL 查询”。它没有状态,输入输出确定,跟传统软件里的函数没本质区别。
- Skill是面向场景的能力封装,通常由多个 Tool 组合而成,带有一定的领域知识。比如“生成周报”这个 Skill,内部会调用“查询数据库”“格式化 Markdown”“发送邮件”三个 Tool,并且知道周报的模板长什么样、哪些指标必须包含。
- Harness是承载 Agent 运行的骨架环境,负责调度 Skill、管理记忆、处理异常、控制循环。你可以把它理解成 Agent 的“操作系统”——它不直接干活,但决定了活能不能干得稳。
这三者的关系有点像:Tool 是螺丝刀和扳手,Skill 是“换轮胎”这个操作流程,Harness 是修车师傅的工作台和工具箱管理规则。很多团队一上来就猛写 Tool,结果发现 Agent 还是笨,问题往往出在 Harness 层——没有好的记忆压缩策略、没有失败重试的退避机制、没有对 Skill 执行结果的校验逻辑。
提示:如果你刚开始做 Agent 项目,建议先把 Harness 的最小闭环跑通——能加载 Skill、能调用 Tool、能记录中间状态、能在失败时回滚。别急着堆 Tool 数量,那是最容易做但收益最低的部分。
1.3 范式跃迁带来的架构变化
从工具到伙伴,架构上最明显的变化是“控制流”的归属。工具范式下,控制流在代码里——你写一个 while 循环,让 LLM 在循环里做决策。伙伴范式下,控制流部分交给了 Agent 自己,代码只提供约束和兜底。这听起来很美好,但实际落地时会遇到一个致命问题:Agent 自己决定下一步做什么,它可能会陷入死循环、可能会忘记目标、可能会被中间结果带偏。
所以伙伴范式并不是“把控制权全给 LLM”,而是“在 Harness 层设计一套让 Agent 既能自主决策又不会跑飞的机制”。我目前项目里用的是“目标锚定 + 步骤预算 + 异常熔断”三件套:每次 Agent 要执行一个 Skill 之前,Harness 会检查当前步骤是否偏离原始目标超过阈值;如果连续三步没有产生有效进展,强制触发重新规划;如果某个 Tool 调用连续失败两次,直接熔断并返回降级结果。这套机制让 Agent 的自主性有了边界,既不会像工具范式那样死板,也不会像纯自由 Agent 那样不可控。
2. 工业界实战:一个 Agent 项目的完整落地记录
2.1 需求场景与选型决策
去年我接手了一个内部知识助手项目,目标是让非技术同事能用自然语言查询产品文档、生成竞品分析摘要、自动整理会议纪要。听起来像是典型的 RAG 场景,但实际用起来发现纯 RAG 根本不够——用户会追问“那这个功能在竞品里是怎么实现的”,会要求“把刚才提到的三个风险点整理成表格发给我”,这些都不是单次检索能解决的。
选型时我对比了三种方案:纯 Prompt 编排、LangChain 式链式调用、以及基于 Harness 的 Agent 架构。纯 Prompt 编排最轻,但一旦任务超过三步就不可维护;链式调用适合固定流程,但用户的问题往往不按套路出牌;最终选了 Agent 架构,核心原因是它允许“边执行边规划”,而且 Harness 层可以统一处理记忆和异常。
具体技术栈上,LLM 用的是内部部署的模型(避免数据外泄),Harness 是自己写的轻量级调度器,Skill 层用 Python 装饰器注册,Tool 层封装了文档检索、表格生成、邮件发送等基础能力。整个项目从立项到上线大概两个月,其中 Harness 的开发占了将近一半时间——这部分没有现成轮子,但恰恰是决定 Agent 稳不稳的关键。
2.2 Harness 层的核心模块拆解
我的 Harness 实现里主要有四个模块,每个模块都对应一类踩过的坑:
记忆管理模块负责维护对话历史、中间结果、以及长期偏好。这里最大的坑是“上下文爆炸”——Agent 执行十步之后,历史记录可能已经几千 token,再塞给 LLM 就会挤掉当前任务的指令。我的做法是分层压缩:最近三轮对话保留原文,更早的对话压缩成摘要,中间结果只保留最终值不保留过程。摘要用一个小模型生成,成本低且够用。
Skill 调度模块负责根据当前目标选择合适的 Skill。这里没有用复杂的向量检索,而是让 LLM 直接看 Skill 列表和描述做选择,因为 Skill 数量通常不超过二十个,LLM 的零样本选择准确率已经够高。但要注意 Skill 描述必须写得像“给同事看的说明书”,而不是“给机器看的接口文档”——后者会让 LLM 选错。
异常处理模块是 Harness 里最容易被忽视但最重要的部分。我的策略是分级处理:Tool 调用超时自动重试一次,重试失败返回结构化错误;Skill 执行结果不符合预期格式时,让 LLM 自己修正一次;如果连续两个 Skill 都失败,触发全局降级,返回“当前无法完成,建议您手动操作”并附上已收集到的信息。
审计日志模块记录每一步的输入输出、耗时、token 消耗。这个模块平时不显眼,但出问题时是唯一的排查依据。我建议日志里一定要记录 LLM 的原始输出,不要只记录解析后的结果——很多时候问题就出在解析逻辑上,而不是模型本身。
2.3 Skill 设计与编码规范
Skill 是 Agent 能力的直接体现,设计得好不好直接决定用户体验。我总结了几条硬性规范:
第一,一个 Skill 只做一件事。早期我把“查询文档并生成摘要”写成一个 Skill,结果发现用户只想查询不想摘要时,Agent 还是会多此一举。拆成“查询文档”和“生成摘要”两个 Skill 后,Agent 可以根据用户意图灵活组合。
第二,Skill 的输入输出必须结构化。输入用 JSON Schema 定义,输出也必须是 JSON,哪怕内容是一段自然语言,也要包在{"text": "..."}里。这样 Harness 才能做校验和重试,否则 Agent 拿到一个格式错乱的结果就懵了。
第三,Skill 描述里要写清楚“什么时候用”和“什么时候不用”。比如“生成摘要”这个 Skill,描述里要写“当用户明确要求摘要,或需要将长文档压缩后展示时使用;如果用户只是问一个具体问题,不要调用此 Skill”。这条经验是从无数次 Agent 乱调 Skill 的教训里总结出来的。
第四,Skill 要支持 dry-run 模式。在开发调试阶段,让 Skill 只返回“我会做什么”而不真正执行,可以快速验证 Agent 的规划逻辑是否正确,避免误操作真实数据。
2.4 Tool 层的封装与容错
Tool 层看起来最简单,但坑也不少。我封装 Tool 时遵循三个原则:
幂等性优先。查询类 Tool 天然幂等,但写入类 Tool 必须支持幂等键。比如“创建日程”这个 Tool,如果 Agent 因为超时重试了两次,不能创建出三个相同的日程。我的做法是让 Harness 为每次 Tool 调用生成一个唯一 ID,Tool 内部根据这个 ID 去重。
超时和重试要可配置。不同 Tool 的响应时间差异很大,文档检索可能几百毫秒,邮件发送可能几秒。我把超时和重试次数做成 Tool 注册时的参数,而不是全局统一配置。重试策略用指数退避,第一次等 1 秒,第二次等 2 秒,避免瞬间打爆下游服务。
错误信息要人话。Tool 返回的错误不能是“Error 500: Internal Server Error”,而应该是“文档检索服务暂时不可用,请稍后重试”。因为这条错误信息最终会进入 LLM 的上下文,如果太技术化,LLM 可能会做出奇怪的决策,比如反复重试同一个必然失败的调用。
3. 论文里的 Agent 与工业界的 Agent 差在哪
3.1 评测指标与真实体验的鸿沟
学术论文里的 Agent 评测通常看任务完成率、步骤效率、工具调用准确率这些指标。这些指标有意义,但和工业界的真实体验之间隔着一条河。我举个例子:论文里可能定义“任务完成”是最终答案正确,但工业界用户还会在意“中间过程是否让我等了太久”“是否问了我一堆不必要的问题”“失败时有没有给我有用的信息”。
我做过一个对比实验,同一个 Agent 在论文式评测里任务完成率 85%,但用户满意度只有 60%。差距主要来自三个方面:一是 Agent 在不确定时倾向于追问,而用户觉得“你明明可以猜一下”;二是 Agent 失败后的错误信息太笼统,用户不知道下一步该干嘛;三是 Agent 的执行过程不透明,用户不知道它卡在哪一步。
所以工业界做 Agent,不能只盯着论文指标,还要建立自己的体验度量体系。我目前用的指标包括:首次响应时间、平均交互轮次、用户主动中断率、失败后的用户重试率。这些指标比单纯的完成率更能反映真实质量。
3.2 论文里的“理想环境”与工业界的“脏数据”
论文里的 Agent 通常在干净的 benchmark 上跑,工具调用有明确的 schema,输入输出都是规整的。但工业界的数据是脏的:用户输入有错别字、有口语、有中英文混杂;下游 API 可能返回非标准 JSON;文档格式可能是扫描件 PDF 而不是干净的 Markdown。
我在项目里专门做了一个“输入清洗层”,放在 Agent 之前。这一层做三件事:拼写纠错、意图归一化、以及敏感信息脱敏。拼写纠错用一个小模型做,成本很低但效果明显;意图归一化是把“帮我看看”“给我查一下”“我想知道”都映射到同一个意图标签;脱敏是为了合规,把手机号、身份证号之类的信息替换成占位符,等 Agent 处理完再还原。
下游 API 的脏数据则靠 Tool 层的适配器解决。每个 Tool 内部都有一个“结果规范化”步骤,把各种奇怪的返回格式统一成 Harness 能处理的 JSON。这个适配器写起来很烦,但一旦写好,Agent 的稳定性会提升一个档次。
3.3 从论文里能抄什么,不能抄什么
论文里值得抄的是规划算法和记忆压缩策略。比如 ReAct 的交替推理-行动框架、Reflexion 的自我反思机制、以及各种基于向量检索的记忆召回方法,这些在工业界直接能用,而且效果经过验证。
不能直接抄的是评测方法和假设条件。论文里经常假设工具调用是零延迟的、LLM 输出是格式正确的、任务边界是清晰的,这些假设在工业界全都不成立。所以读论文时要带着“这个结论在脏环境下还成立吗”的问题去读,而不是照搬实现。
还有一个容易踩的坑是过度依赖 LLM 做决策。论文里经常让 LLM 决定每一步做什么,但在工业界,有些决策用规则做更稳、更快、更便宜。比如“用户问的是天气还是日程”这种分类,用一个小分类模型或者关键词匹配就能解决,没必要每次都调大模型。我的原则是:能用规则解决的不用模型,能用小模型解决的不用大模型,只有真正需要推理和生成的环节才用大模型。
4. 常见问题与排查技巧实录
4.1 Agent 陷入死循环怎么办
死循环是 Agent 项目里最常见的问题,表现是 Agent 反复调用同一个 Skill 或 Tool,每次都得到相似的结果,但就是不推进。我遇到过最离谱的一次是 Agent 连续调了十几次“查询文档”,每次都说“没找到相关信息”,然后换个关键词再查,直到把步骤预算耗尽。
排查思路分三步:先看日志里 Agent 的推理过程,确认它是不是在重复同一个子目标;再看 Skill 的返回结果,确认是不是返回了空值或错误但 Agent 没有正确处理;最后看 Harness 的步骤预算和熔断机制是否生效。
解决方法我用了三个:一是强制目标检查,每三步让 LLM 重新确认当前目标是否还和原始目标一致;二是结果去重,如果连续两次 Skill 调用的输入输出高度相似,直接跳过并提示 Agent 换策略;三是硬性步骤上限,超过上限就强制返回当前最佳结果并附上“未能完全完成”的说明。
注意:步骤上限不要设得太小,否则复杂任务会被误杀;也不要设得太大,否则死循环会浪费大量 token。我的经验值是简单任务 5 步,中等任务 10 步,复杂任务 20 步,超过就熔断。
4.2 Skill 选择错误怎么调
Skill 选择错误的表现是 Agent 调了一个不相关的 Skill,比如用户问“文档里有没有提到并发限制”,Agent 却调了“生成摘要”。这个问题通常不是 LLM 太笨,而是 Skill 描述写得不好。
我的调试方法是:把 Skill 列表和用户问题一起给 LLM,让它解释为什么选这个 Skill。如果解释里出现“因为用户提到了文档”这种表面匹配,说明描述里缺少对“意图”的说明。改进方式是在 Skill 描述里加一段“适用场景”和“不适用场景”,用自然语言写清楚。
另一个技巧是给 Skill 加负样本。比如“生成摘要”这个 Skill 的描述里可以写“如果用户只是询问具体信息,不要调用此 Skill”。这看起来是废话,但实测能显著降低误选率。
如果调整描述后还是选错,可以考虑在 Harness 层加一个“Skill 预筛选”步骤:先用一个小模型或关键词匹配把候选 Skill 缩小到三五个,再让 LLM 从中选。这样既降低了 LLM 的决策难度,也提高了选择准确率。
4.3 记忆丢失和上下文污染
记忆丢失的表现是 Agent 忘记了前面已经确认过的信息,比如用户已经说了“我要查的是 2023 年的数据”,Agent 后面又去查了 2024 年。上下文污染则是相反的问题:Agent 把前面某个失败尝试的错误信息当成了有效信息,导致后续决策跑偏。
这两个问题的根源都在记忆管理模块。我的解决方案是结构化记忆:把记忆分成“事实”“偏好”“中间结果”三类,分别存储和召回。事实是用户明确提供的信息,比如“2023 年”;偏好是用户的长期习惯,比如“喜欢表格形式的输出”;中间结果是 Agent 执行过程中产生的临时数据,比如“文档 A 的摘要”。
召回时按优先级取:先取事实,再取偏好,最后取中间结果。中间结果只在当前任务内有效,任务结束后清空。这样既能保证关键信息不丢,又能避免过期信息污染上下文。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| Agent 反复调用同一 Skill | 目标漂移或结果未达预期 | 查看推理日志中的子目标 | 加目标检查、结果去重、步骤上限 |
| Skill 选择错误 | 描述不清或候选过多 | 让 LLM 解释选择理由 | 补充适用/不适用场景,加预筛选 |
| 记忆丢失 | 上下文压缩过度 | 检查压缩后的摘要 | 结构化记忆,事实优先保留 |
| 上下文污染 | 中间结果未清理 | 检查记忆中的过期数据 | 任务结束后清空中间结果 |
| Tool 调用超时 | 下游服务慢或网络抖动 | 查看 Tool 耗时日志 | 配置超时和指数退避重试 |
| 输出格式错乱 | LLM 未按 schema 输出 | 检查原始输出和解析逻辑 | 加格式校验和自动修正 |
| 用户满意度低 | 过程不透明或追问过多 | 收集用户反馈 | 增加进度提示,减少不必要追问 |
4.5 几个反直觉的实操心得
第一个心得是不要追求 100% 自动化。有些团队想把所有决策都交给 Agent,结果做出来的东西又慢又不稳。我的做法是在关键节点保留人工确认,比如“即将发送邮件,请确认内容”,这样既降低了风险,也让用户更有掌控感。
第二个心得是日志比模型更重要。我见过太多团队花大量时间调 Prompt,却不好好写日志,出问题只能靠猜。我的项目里日志是最高优先级的模块,每一步的输入输出、耗时、token 消耗、LLM 原始输出全部记录,排查问题时直接看日志就能定位到具体环节。
第三个心得是小模型在 Agent 里被严重低估。很多环节不需要大模型,比如意图分类、结果校验、摘要生成,用一个小模型甚至规则引擎就能搞定,成本低一个数量级,速度还快。我的项目里大模型只用在“规划”和“最终生成”两个环节,其他环节全是小模型和规则。
第四个心得是Skill 的数量不是越多越好。早期我恨不得把每个功能都做成 Skill,结果 Agent 选择困难,用户也搞不清它能干嘛。后来砍到十个核心 Skill,每个都打磨得很扎实,整体体验反而提升了。Skill 的价值在于“组合”,而不是“数量”。
5. 从工具到伙伴,下一步往哪走
5.1 当前方案的边界与不足
我现在这套 Harness + Skill + Tool 的架构,在中等复杂度任务上表现不错,但遇到两类任务还是会吃力。一类是长周期任务,比如“跟踪这个竞品三个月,每周给我一份变化报告”,这种任务需要跨会话的记忆和定时触发,当前的 Harness 还没有很好的支持。另一类是多 Agent 协作,比如一个 Agent 负责查资料,另一个负责写报告,两者需要共享中间结果和协调进度,目前还是单 Agent 串行执行。
这两个方向我都在尝试,长周期任务打算引入一个轻量的任务队列和定时调度器,多 Agent 协作则考虑用消息总线做通信,Harness 层增加一个“协作协议”模块。不过这些还在实验阶段,等跑通了再单独写一篇总结。
5.2 给正在做 Agent 项目的同行几句实在话
如果你刚开始做 Agent,我的建议是先别碰多 Agent。单 Agent 的 Harness 都没跑稳,多 Agent 只会让问题指数级放大。先把单 Agent 的记忆、调度、异常处理做扎实,再考虑扩展。
另外,别被热词带偏。Harness、Skill、Tool 这些词本身不重要,重要的是你清楚每一层解决什么问题、边界在哪。我见过有人把简单的 if-else 逻辑包装成“Skill 引擎”,除了增加维护成本没有任何收益。
最后,用户反馈比任何评测指标都重要。论文里的指标是参考,真实用户愿不愿意用、用完会不会再来,才是检验 Agent 好坏的唯一标准。我每周都会看用户的实际对话记录,从中找改进点,这比调 Prompt 有效得多。
这个项目后续我还会继续迭代,重点放在长周期任务和多 Agent 协作上。等有新的踩坑经验,再整理成第二篇。如果你也在做类似的东西,欢迎交流,尤其是 Harness 层的设计思路,这块目前公开的资料还是太少,大家都是摸着石头过河。