Agent-Reach:从工具调用到A2A,打造可靠触达的AI Agent
1. Agent-Reach 想解决的是最后一公里而不是最前一公里先说一个我见过太多次的现场。团队花了三周时间把一个大模型 Agent 的对话体验调得非常顺滑演示的时候能聊需求、能分析数据、能给出看起来很靠谱的方案会议室里一片叫好。然后产品同学问了一句那它能帮我把 Jira 上那个单子状态改掉吗 全场安静。这就是大多数 Agent 项目真正卡住的地方——不是不会说话而是接不上真实世界。我把这类问题统称为 Agent 的触达问题模型再聪明它的能力边界也只到生成一段文本为止剩下的事情比如读一个内部接口、写一条数据库记录、调用一个需要鉴权的服务、把结果同步给另一个系统全都需要工程侧把它接起来。Agent-Reach 就是围绕这件事做的一个工程实践项目它的核心主张很朴素Agent 的价值等于它能触达的系统数量乘以触达的可靠性而不是等于它的模型参数量。如果你正在做 AI Agent 开发或者刚刚开始学 agent 开发、想搞明白 agent 架构到底由哪些部件拼起来那这篇内容应该对你有用。我不打算把它写成一个 API 手册而是按照我实际落地时的顺序来讲先讲清 Agent-Reach 的定位再拆它的骨架然后给出一个能跑起来的最小版本接着重点讲我踩过的坑最后聊多 agent 协作和 A2A 协议这一层。看完整篇你应该能自己动手搭一个真的能办事的 agent而不是一个只能陪聊的壳子。1.1 一个很典型的失败现场Demo 里很聪明上线就废我接手过一个内部工具 Agent 的烂摊子。它在测试环境表现很好回答问题的准确率看起来有八成以上但只要一接真实数据就开始出现各种诡异行为有时候把工具调用的参数拼错有时候反复调用同一个接口十几次有时候明明拿到了结果却说我没有找到相关信息。日志里最常出现的一句话是agent execution terminated due to error但错误堆栈指向的地方跟真正的问题八竿子打不着。排查了整整两天最后发现根因有三个而且没有一个跟模型能力有关。第一工具描述写得含糊模型只能靠猜参数格式第二上下文中塞了太多无关的历史对话把真正重要的工具返回结果挤到了很后面模型注意力被稀释第三工具调用没有做超时和重试边界一个偶发的网络抖动直接让整条链路崩了。这三件事指向同一个结论Agent 的稳定性问题绝大多数发生在模型之外。Agent-Reach 的设计出发点就在这里——把模型决策和系统触达这两件事彻底分开让前者保持灵活让后者做到确定。1.2 Reach 的三层含义外部触达、上下文触达、协作触达Reach这个词在这个项目里有三层含义一开始我只想到了第一层后来发现另外两层同样关键。外部触达是最直观的让 agent 能调用外部工具、接口、数据库、文件系统。这一层解决的是手的问题。你需要定义工具契约、处理鉴权、做参数校验、管好超时和重试。上下文触达是第二层也是最容易被忽略的agent 能不能看见它需要的信息。不是把所有信息都堆进 prompt 就叫触达而是要在正确的时间把正确的片段送进上下文窗口。这一层解决的是眼睛的问题涉及检索、摘要、压缩、优先级排序。协作触达是第三层多个 agent 之间怎么互相找到、互相调用、互相传递结果。这一层解决的是组织的问题绕不开多 agent 协作和 A2A 这类协议。我见过不少人只做了第一层就宣称我的 agent 能干活了结果一上规模就崩。原因很简单手有了眼睛没睁开还没法跟别人配合。1.3 Agent-Reach 和现成 agent 框架的关系先把话说明白Agent-Reach 不是一个要取代 LangGraph、AutoGen、Spring AI 这类 agent 框架的新轮子。它更像是这些框架之上的一层触达治理实践。框架负责的是编排、状态机、消息传递这些结构性的事Agent-Reach 关心的是编排过程中的可靠性细节——工具怎么注册、上下文预算怎么分配、失败怎么降级、结果怎么校验。这个定位很重要因为它决定了你的学习路径。如果你连 agent 的基本循环思考、行动、观察、再思考都还没搞清楚先去补框架的基础如果你已经能跑通一个 demo但一上真实业务就各种花式报错那你需要的恰恰是这一层的东西。提示判断自己处在哪个阶段有个简单标准——如果你的 agent 挂掉的时候你第一反应是换个更强的模型试试那说明你还在第一阶段如果你的第一反应是让我看看是哪次工具调用出了岔子恭喜你已经开始做工程了。2. 把 Agent-Reach 拆开看从任务入口到结果回收的完整链路聊完定位我们把它拆开。一个能稳定触达外部系统的 agent我认为最少要有四个部件入口层负责理解意图和路由执行层负责真正干活记忆层负责信息的存取与淘汰观测层负责让你在出事的时候能查。缺任何一个系统都会在某个阶段变成黑盒。我用一个真实的任务来串这条链路用户说帮我把上周那个还没关的工单更新一下附上今天的排查结论。这句话里藏着好几个难点——上周那个需要检索和消歧更新需要写权限排查结论需要从别处取。下面逐层说。2.1 入口层路由识别节点到底在识别什么很多教程会把入口层写得非常轻描淡写好像只要一个分类 prompt 就够了。实际做下来路由识别节点是整个系统里最影响体验的一环因为它决定了后面所有环节的走向。我把它拆成三个连续的小动作。第一步是意图归类判断这句话是要查询、要写入、要执行多步任务还是只是闲聊。第二步是实体抽取与消歧把上周那个还没关的工单这种指代解析成具体的 ID——这一步通常要配合检索因为指代信息不在当前对话里。第三步是能力匹配看现有工具集里有没有能完成这个意图的工具如果没有是降级回复还是尝试任务分解。这里有个很容易犯的错把三个动作塞进一次模型调用。看起来省了一次请求实际上会让模型在三件事之间互相干扰尤其是当意图不明确的时候模型倾向于和稀泥产出一个模糊的分类结果后面的执行层就只能靠猜。我的做法是拆成两次调用先做意图归类选项少、约束强、准确率高再做实体消歧可以带上检索结果作为上下文。# 意图归类选项少、约束强用低温度保证稳定性 INTENT_PROMPT 你是一个意图分类器。只输出下面四个标签中的一个不要解释 - QUERY: 只读操作查询信息 - WRITE: 会修改外部系统状态的操作 - MULTI_STEP: 需要多个工具配合完成 - CHITCHAT: 闲聊或与系统能力无关 用户输入{user_input} 标签注意最后一行的标签这个小技巧能显著提升分类模型输出的稳定性因为它相当于强制模型紧接着输出答案而不是先来一段好的让我分析一下……。这类 prompt 层面的经验文档里通常不会写但实测下来差别很大。2.2 执行层harness 和 agent 到底谁在干活这是被问得最多的一个概念问题我用自己的话解释一下。Agent 是决策者harness 是执行者所在的脚手架。Agent 这一侧包含的是模型本身加上它的策略——比如系统提示词、工具选择逻辑、终止条件判断。它做的事是决定下一步干什么。Harness 这一侧是外部的运行时它负责把工具真正调起来、把结果序列化回模型能读的格式、管理上下文窗口、处理超时重试、记录每一步的状态。换句话说agent 负责想,harness 负责跑。区分这两个概念的实际意义在于绝大部分 bug 应该去 harness 里找而不是去调 prompt。我前面提到的那个agent execution terminated due to error听起来像是模型的问题实际上是 harness 里没处理工具返回的非预期格式直接把异常抛到了最外层。一个最小可用的执行循环大概长这样def run_agent_loop(task, tools, max_steps8): messages [{role: system, content: SYSTEM_PROMPT}, {role: user, content: task}] for step in range(max_steps): resp llm.chat(messages, toolstools.schemas()) if resp.finish_reason tool_call: call resp.tool_call try: result tools.invoke(call.name, call.args, timeout15) payload {ok: True, data: truncate(result, 4000)} except Exception as e: payload {ok: False, error: str(e)[:500]} messages.append(resp.as_message()) messages.append({role: tool, content: json.dumps(payload)}) else: return resp.content return 达到最大步数仍未完成请缩小任务范围后重试。这段代码里有三个细节值得展开。timeout15是硬性边界没有它一次卡住的调用能拖垮整条链路。truncate(result, 4000)是上下文预算控制工具返回的原始数据经常有几万字符不截断的话第二次循环就把窗口撑爆了。异常被转成{ok: False, ...}而不是直接抛出是为了让模型知道这次失败了它可以换个方式重试而不是整个流程中断。2.3 记忆层短期、工作、长期三种记忆的存储与淘汰Agent 记忆这个词被讲得很玄落到工程上其实就三件事存什么、存多久、什么时候取。我把记忆分成三层。短期记忆是当前这轮对话的原始消息存在内存里按 token 预算做滑动窗口。工作记忆是当前任务执行过程中产生的中间结果比如某个接口返回的关键字段存在任务的上下文对象里任务结束就销毁。长期记忆是跨会话需要保留的信息比如用户的偏好、历史决策存在外部存储里按需检索。关键在淘汰策略。我见过最危险的做法是把所有历史消息一股脑塞进上下文指望长上下文模型能自己搞定。实测下来上下文越长模型对中间部分的注意力越弱而工具返回的关键结果往往就落在中间。我的做法是给三类信息设不同的保留优先级记忆类型存储位置保留策略典型预算系统提示词内存永不淘汰固定尽量精简最近对话轮次内存保留最近 3 到 5 轮原文约 30%工具返回结果任务上下文只保留结构化摘要原始数据落盘约 40%历史对话摘要内存 外部存储每 N 轮压缩一次约 20%检索召回片段按需注入只注入当前这一步需要的约 10%这张表是我调整过很多次之后的稳定版本。注意工具返回结果这一行——我保留了结构化摘要但原始数据写到磁盘上并给模型一个引用路径。这样模型知道有这么个东西存在需要细节的时候可以通过另一个工具去读而不是一开始就把几万字符全塞进窗口。2.4 观测层可回放比可观测更重要可观测这个词现在很流行但我认为对 agent 来说可回放才是刚需。因为 agent 的行为有随机性同一个输入两次跑出来的路径可能完全不同你光看指标和日志很难复现问题。Agent-Reach 在这一层的做法是记录完整的执行轨迹每一步的输入上下文哈希、模型的原始输出、工具调用的名字和参数、工具返回的原始内容、耗时、token 消耗。把这些按 step 编号串起来存成一份可以重放的 JSON。出问题的时候我把这份轨迹喂给一个复盘 prompt让模型帮我分析哪一步开始偏了。{ trace_id: t-20240513-0042, task: 更新工单 8813 的排查结论, steps: [ {step: 0, type: route, intent: WRITE, latency_ms: 620}, {step: 1, type: tool_call, name: search_ticket, args: {query: 上周 未关闭}, ok: true, latency_ms: 340}, {step: 2, type: tool_call, name: get_ticket_detail, args: {id: 8813}, ok: true, latency_ms: 210}, {step: 3, type: tool_call, name: update_ticket, args: {id: 8813, field: conclusion}, ok: false, error: 403 insufficient_scope, latency_ms: 180} ] }有了这份轨迹上面那个 403 的问题根本不用猜——写权限没开。如果没有这一步记录你面对的只有一句更新失败了然后就得开始漫无目的地试。3. 搭一个能跑通的最小版本从零到第一次成功触达前面讲的都是设计层面的东西这一节我们动手。我给的目标很明确让 agent 完成一次真实的、可验证的外部触达——读一个接口处理返回再把结果写回去。跑通这一条链路之后后面所有的扩展都是在这条链路上做加法。3.1 技术选型我为什么选这套组合选型这件事没有标准答案但有几个判断维度我想强调一下。语言层面我选 Python理由不是AI 生态好这种泛泛的说法而是具体的工具调用的参数校验、JSON 序列化、异步并发这几个在 Python 里都有非常成熟的库能省掉大量胶水代码。如果你团队本身是 Java 技术栈用 Spring AI 那一套也完全没问题逻辑是相通的只是注解和配置的写法不同。模型层面我建议开发和调试阶段用能力强的模型稳定运行后再考虑降级。原因很实际调试阶段你需要快速定位是模型没理解意图还是工具设计有问题用一个能力弱的模型你很难分清这两者。等链路稳定了再把那些约束强、选项少的节点比如意图分类换成便宜的小模型。存储层面短期和working记忆放内存就行别过早引入 Redis长期记忆和 trace 用文件系统起步量大了再换。我见过一个团队在只有几十个用户的时候就上了分布式向量数据库结果 90% 的时间花在运维上而不是打磨 agent 本身。依赖清单很精简pip install fastapi uvicorn pydantic httpx # 按需选择模型 SDK例如 openai、anthropic 或国内厂商的官方 SDK pip install python-dotenv tenacitytenacity这个库值得单独说一句它做重试和退避非常省心比手写for i in range(3)靠谱得多尤其是需要指数退避加抖动的时候。3.2 目录结构为后面三个月的自己着想我见过太多 agent 项目把所有逻辑塞在一个main.py里两周之后连作者自己都不敢改。我建议一开始就分好目录不用很复杂但边界要清楚agent_reach/ core/ loop.py # 执行循环harness 的核心 context.py # 上下文预算管理与压缩 memory.py # 三层记忆的读写接口 trace.py # 轨迹记录与回放 tools/ registry.py # 工具注册与 schema 生成 impl/ # 各工具的具体实现 routes/ router.py # 意图归类与实体消歧 config/ settings.py # 环境变量与参数集中管理 tests/ cases/ # 可回放的测试用例这个结构的关键在于core/loop.py和tools/registry.py是解耦的。执行循环只认工具 schema不关心工具怎么实现工具实现只管自己的业务逻辑不关心谁调用它。这个边界一旦划清你后面加工具就是纯粹的新增不会牵动核心代码。3.3 工具注册从 echo 到真实接口的正确过渡工具注册这一步我要给一个非常具体的建议先用 echo 工具跑通全链路再换成真实接口。所谓 echo 工具就是无论传什么参数都原样返回的工具。它的作用是把链路问题和业务问题隔离开。当你的 agent 用 echo 工具都跑不通的时候你百分之百知道问题在编排层等 echo 通了换真实接口出问题你就能确定问题在接口对接层。这个排查思路帮我省过无数次时间。from pydantic import BaseModel, Field class EchoArgs(BaseModel): text: str Field(..., description原样返回的文本内容) def echo(text: str) - str: return text TOOL_SPEC { name: echo, description: 把输入文本原样返回。仅用于验证链路连通性。, args_model: EchoArgs, handler: echo, }注意description里那句仅用于验证链路连通性。这句话很重要它告诉模型这个工具的适用边界避免它在正式任务里乱用。工具描述写得越精确模型的误用率越低——这一点我在下一节还会展开。换成真实接口的时候参数模型要用Field把每个字段的格式、取值范围、是否必填都写清楚。我踩过的一个坑是某个日期字段只写了date: str模型有时候传2024-05-13有时候传2024/05/13有时候干脆传上周一。后来我把描述改成ISO 8601 格式的日期形如 2024-05-13错误率立刻降下来了。3.4 怎么判断它真的通了三个验收标准跑通不等于可靠。我给自己定了三个验收标准全部满足才算这条链路能上。第一连续 20 次相同输入的结果一致性。允许措辞不同但调用的工具序列和最终状态变更必须一致。如果有超过 15% 的偏差说明路由或工具描述有问题。第二故障注入测试。人为让接口返回 500、返回超时、返回格式错误的 JSON观察 agent 是否能给出有意义的降级回复而不是抛出异常或者陷入死循环。这一步能提前发现 80% 的线上问题。第三权限边界验证。用一个没有写权限的账号跑写入任务看系统是否正确拒绝并且给出清晰提示。权限问题在生产环境里出现的频率远超你的想象。这三条做完你才算有了一个可以往上面加东西的地基。4. 我踩过的坑Agent-Reach 落地过程中最费时间的六件事这一节是我最想写的部分。前面讲的是应该怎么做这里讲的是为什么很多人做不到——因为坑都藏在细节里而且报错信息通常不会告诉你真正的原因。4.1 工具描述写得太好模型反而乱用刚做的时候我以为工具描述越详细越好于是给每个工具写了三五百字的说明把所有能想到的场景都塞进去。结果模型的行为变得很奇怪一个本该用查询工具的任务它调了更新工具理由是描述里提到了该工具也可以用于获取信息。问题的本质是工具描述是给模型做选择的依据不是给人类看的文档。描述越长、覆盖场景越多工具之间的边界就越模糊模型的误用率越高。我后来定了一条规则每个工具的description只写三件事——这个工具做什么、什么时候用、什么时候不要用。超过 200 字符就要反思是不是话太多了。还有个小细节工具名前缀会影响模型的选择。我带过的一个项目里两个工具分别叫search和search_v2模型经常随机选一个。后来改成search_ticket和search_user误用直接消失了。名字本身携带的信息量比你想象的大。4.2 上下文爆炸长上下文不等于无限上下文长上下文模型出来之后很多人包括我第一反应是再也不用做上下文管理了。这个想法代价很大。我做过一个对比实验同一个任务一份是完整的两万 token 上下文一份是压缩到四千 token、只保留关键信息。结果是压缩版的任务成功率反而更高而且延迟和成本都低了一个数量级。原因在于模型在长上下文里对中间位置的注意力明显弱于开头和结尾而工具返回的关键结果经常就在中间。所以我的做法是给上下文做预算分配而不是能塞多少塞多少。具体的压缩策略有三类结构化成表格把冗长的工具返回提取成字段表、摘要化让模型把长文本压成三五句话但保留数字和 ID、外置化原始数据落盘上下文里只留引用和一小段摘录。这三招配合使用能把大部分任务的上下文控制在五千 token 以内。注意压缩的时候千万别把 ID、金额、时间戳这类硬信息摘要掉。我吃过一次亏摘要把工单号8813压成了某工单后面所有调用全部失败。规则很简单数字和标识符原样保留只压缩叙述性文本。4.3 死循环与agent execution terminated的根因定位这个报错我遇到过至少五种不同的根因列出来供对照现象根因修复方式反复调用同一工具参数完全相同工具返回结果被截断模型看不到关键字段以为没成功调整截断策略保证关键字段完整每次调用参数微小变化永不收敛缺少最大步数限制或步数上限设得过大设置 6 到 10 步上限超限返回可读提示工具 A 调工具 BB 又调 A依赖关系设计成了环路依赖图必须是有向无环单次调用卡住直到超时缺少单步超时或超时时间设得过长单步 15 秒整体 60 秒模型输出格式不符合预期解析层过于严格没有容错解析失败时把错误反馈给模型重试一次排查这类问题的关键工具是完整轨迹。我前面强调可回放就是因为在处理死循环的时候你唯一需要的信息是第几步开始偏离了预期。有了轨迹通常是看一眼就能定位没有轨迹就只能靠猜和加日志。另外提一句最大步数这个参数千万别设成 20 或 30。我见过有人为了给模型足够的发挥空间设成 50结果是一个小问题烧掉了几十万 token。合理的做法是给不同类型的任务设不同的上限单工具任务 3 步多步任务 8 步探索性任务最多 12 步。4.4 记忆污染为什么你的 agent 记住了错误的东西长期记忆是个双刃剑。它能显著提升体验也能把一次偶然的错误固化成永久的偏见。我遇到过一个案例用户第一次使用时随口说了句我们公司不用邮件沟通系统把这个当作长期偏好存了下来。之后所有涉及通知的任务agent 都主动避开邮件渠道哪怕用户明确要求发邮件。这就是典型的记忆污染——把一次性的上下文信息当成了稳定的用户偏好。我的解法是给长期记忆加一道准入判断只有满足下面三个条件之一的信息才允许写入长期记忆。一是用户在两次以上不同会话中重复表达过二是用户显式要求记住三是系统通过结构化字段比如设置页面的选项获取的。其他所有信息一律放在工作记忆里任务结束就丢。同时要给记忆加过期时间和可撤销机制。存进去的东西如果三个月没被再次访问就标记为待清理用户说不是这样的的时候要能立刻把对应的记忆条目删掉。这两条在实现上都不难但如果没有一开始就设计后面补起来会很痛苦。4.5 权限边界为什么写入类工具必须走二次确认这是我认为整个系统里最重要的一条安全规则凡是会改变外部系统状态的工具都不能由模型单方面决定执行。我采用的是提议加确认的模式。模型可以生成写入调用的提案包含目标对象、要改的字段、改前改后的值但真正执行前必须经过一道确认。确认方式有两种一种是人工确认适合高风险操作一种是规则确认适合格式校验、范围校验这类可以自动化的检查。规则确认里我会强制检查几项目标 ID 是否存在于本次会话的上下文中防止模型编造 ID、变更范围是否在允许的字段白名单内、变更前后的值差异是否在合理区间比如金额变动超过 50% 直接拒绝。这三项检查实现起来都很简单但能挡掉大部分误操作。还有一点容易被忽略不要把高权限的凭证放在 agent 能直接访问的环境里。工具实现应该走一个受控的代理层代理层负责把 agent 的意图翻译成具体的 API 调用并且记录审计日志。这样即使模型被诱导做了不该做的事代理层也能拦下来。4.6 Agent 的测试为什么不能只做单元测试传统软件的测试思路是给定输入、断言输出。Agent 这套东西没法这么测因为输出是自然语言路径是随机的。我的测试体系分四层。契约测试测工具本身这部分就是常规单元测试跟 agent 无关。路由测试测意图分类用一批标注好的样本看准确率和混淆矩阵。轨迹测试测完整链路做法是把历史成功轨迹存下来作为基准新的运行如果步骤数量或工具序列偏离太多就报警。对抗测试专门测边界情况包括模糊指代、矛盾指令、超长输入、恶意注入这几类。对抗测试里我特别重视矛盾指令这一类。比如用户说查一下工单 8813不用改了如果 agent 还是去调用更新工具说明它对否定表达的理解有严重问题。这类问题在真实使用中出现的频率比你想的高。5. 多 Agent 协作与 A2AReach 的横向扩展单个 agent 的能力有天花板当任务复杂度上去之后自然会想到拆成多个 agent。这一步走得好系统会变得清晰走得不好你会得到一个更难调试的分布式混沌。5.1 什么时候该拆多 Agent三个判断信号不是所有场景都需要多 agent。我见过很多项目为了看起来先进而拆结果是把一个可控的问题变成了三个不可控的问题。我总结了三个真正该拆的信号。第一个信号是工具集规模超过 20 个。工具越多模型选择越困难误用率上升。这时候按领域拆分成多个 agent每个 agent 只面对 5 到 8 个工具选择准确率会明显回升。第二个信号是任务步骤之间存在明确的阶段划分而且不同阶段需要不同的上下文。比如调研、分析、产出报告这三个阶段如果硬塞进一个 agent上下文里会同时存在三个阶段的无关信息。拆开之后每个 agent 的上下文都更干净。第三个信号是不同子任务需要不同的权限级别。读任务和写任务混在一起你就没法做精细的权限控制。拆开之后读 agent 拿只读凭证写 agent 拿写入凭证安全边界一下清晰了。反过来说如果只是任务步骤多一点但都共享同一套工具和上下文那就别拆老老实实做单 agent 多步执行。5.2 Agent Card 与协议版本差异配置项为什么对不上多 agent 之间要互相认识就需要一套描述自己的格式这就是 Agent Card 的作用。它本质上是一份能力声明我是谁、我能做什么、我的接口在哪、我需要什么鉴权、我支持哪些交互模式。这里有个很实际的坑不同版本的协议对 Agent Card 的字段定义不完全一样直接照搬会踩空。我遇到过的典型情况是某个字段在新版本里是数组结构旧版本里是单个对象如果一方按旧格式发、另一方按新格式解析就会出现明明配置了却读不到的现象。我的建议是先把双方用到的字段列成一张表逐项对一遍类型和必填性再动手写代码。这听起来很笨但能省掉一整天对着日志发呆的时间。字段对齐的时候要特别关注三处能力声明是单值还是列表、鉴权信息是内联还是引用、错误码的取值集合是否一致。提示做跨版本对接的时候先在两边各写一个最小的 echo agent用最简的 card 互相调通再逐步往里面加字段。一上来就用完整的 card 对接出问题的时候你分不清是协议问题还是业务问题。5.3 协作中的失败传播与降级策略多 agent 系统最危险的地方是失败会传染。一个 agent 返回了半成品结果下游 agent 拿不到完整信息但它不知道上游出问题了于是继续往下做最后产出一个看起来完整、实际上基于错误的结论。这种问题比直接报错难查十倍。我对付这个的办法是在结果里强制带状态标记。每个 agent 返回的内容都必须包含三个字段完成度完成、部分完成、失败、置信度高、中、低、缺失项列表。下游 agent 拿到结果先看状态如果完成度是部分完成并且缺失项包含关键字段就直接降级处理而不是硬着头皮往下走。降级策略我设了三档。第一档是补全缺失的信息如果可以通过其他途径拿到就先补全再继续。第二档是缩范围把任务范围缩小到已有信息能支撑的部分明确告诉用户哪部分没做完。第三档是中断并求助如果缺口太大直接停下来让用户决策而不是生成一个不可靠的结果。三档之间的切换条件要写死在代码里不能交给模型判断。因为模型天生倾向于想办法完成任务它在信息不足的时候会编这是它的特性不是 bug。你的任务是在它编之前把它拦下来。6. 学 Agent 这条路怎么走我建议的能力清单最后聊点学习的。agent 这个方向现在信息量非常大各种名词、框架、协议铺天盖地很多人卡在不知道怎么下手。我把自己的学习路径整理出来按阶段说。6.1 先把名词厘清这一步千万别跳过我见过太多人因为概念混淆走了弯路。这里把最容易搞混的几组说清楚。Agent 和 skill 的区别agent 是一个能自主决策、能循环执行的实体skill 是它掌握的一项具体能力通常对应一个封装好的工具或一段固定的处理流程。一个 agent 可以拥有多个 skill但拥有 skill 不代表就是 agent——如果没有决策和循环那只是一个被调用的函数。Harness 和 agent 的区别前面讲过agent 负责决定做什么harness 负责真正把事情跑起来。你可以把 harness 理解成agent 的操作系统和运行环境。LLM 和 embedding 的区别LLM 负责生成和推理embedding 负责把文本变成向量以便计算相似度。两者解决的是完全不同的问题但在 agent 系统里经常配合使用——embedding 用来做检索LLM 用来做理解和生成。Agent 和框架的区别框架是搭建 agent 的工具箱agent 是你用工具箱做出来的东西。别把学框架当成学 agent。6.2 分阶段的学习路线第一阶段一到两周跑通一个最小的 agent 循环能调用两三个工具能处理工具调用失败。目标不是做出什么有用的东西而是把所有环节都亲手摸一遍。这个阶段我强烈建议不要用框架手写循环哪怕只有一百行代码。第二阶段两到四周加上上下文管理和记忆。这时候你会开始遇到上下文不够用的问题逼着自己去学压缩和检索。同时开始做轨迹记录养成出事看轨迹的习惯。第三阶段一个月以上接触多 agent 协作和协议对接。这个阶段的前提是你已经有一个稳定的单 agent 系统否则你会在分布式调试里迷失。第四阶段工程化。测试体系、权限管控、监控告警、成本优化这些都是让 agent 从 demo 变成产品的必经之路。6.3 面试里那些绕不开的问题如果你在看机会agent 方向的面试问题大致集中在几个方向。一类是概念辨析比如让你说清楚 agent 和 skill、harness 和 agent 的区别这类问题考察的是你有没有真正动过手。一类是故障排查比如agent 陷入死循环你怎么定位这类问题看的是排查思路而不是答案本身回答的时候一定要把看轨迹、分步骤缩小范围这个过程讲出来。还有一类是设计题比如给你一个工具超过 30 个的场景你怎么设计 agent 结构这类问题没有标准答案但如果你能说出按领域拆、按权限拆、控制单 agent 的工具数量这几个原则基本就够了。我个人在面试别人时的判断标准很朴素看对方描述问题时有没有具体到某一步工具调用。能讲到这个粒度的通常是真的做过只会讲架构图的大概率只是读过文章。7. 最后分享几个我用了很久的小习惯写到这里也差不多了分享几个实际工作中养成的小习惯都是踩坑之后总结出来的。第一个习惯是给每个新工具写一个反面用例。也就是记录这个工具在什么情况下不该被调用。工具描述里写什么时候不要用比写什么时候用更能减少误用。我的工具描述模板里永远有这一行。第二个习惯是把每次线上问题的轨迹存进一个专门的目录按月归档。半年下来你就有了一个真实问题的语料库做对抗测试的时候直接拿来用比凭空构造用例有效得多。我现在这个库里有两百多条覆盖了各种奇怪的边界情况。第三个习惯是在 agent 的最终回复里加一句我做了什么。不是给模型看的是给用户看的。让用户知道这次任务调用了哪些工具、改了哪些数据既提升了可信度也在出事的时候能快速定位。这一句的成本几乎为零但用户反馈一直很好。Agent 这个方向变化很快新框架、新协议、新模型层出不穷但底层那些东西——上下文预算怎么分、失败怎么降级、权限怎么管、问题怎么查——变化其实很慢。把这一层打扎实了上层换什么你都能接得住。这也是我做 Agent-Reach 这个项目最深的体会触达的可靠性才是 agent 真正值钱的部分。