Agent-Reach:从工具调用到多Agent协作的触达半径工程
Agent-Reach 这个词第一次跳进我视野的时候我脑子里冒出来的不是某个框架的名字而是一个特别具体的问题我手里那个聊天聊得挺顺的 Agent为什么一让它真正去办事就掉链子让它查工单它编了一个工单号让它改配置它把参数名写错让它跨系统对账它跑到第三步就开始凭空想象第四步的结果。后来我把这类问题归了一个类叫触达问题——不是模型不够聪明而是它够不着。Agent-Reach 说的就是这件事一个 Agent 到底能够到多远、够到多准、够到之后还能不能记住自己干了什么。这篇文章不打算吹概念我想把这套东西拆成能落地的零件从名词对齐一路讲到工程细节、多 Agent 协作、评测埋点和踩坑复盘适合刚开始做 agent 开发的人也适合已经跑通过 Demo、但线上总出幺蛾子的同行。1. 从聊天框到能真正办事Agent-Reach 要跨的三道坎1.1 我理解的触达半径我习惯把 Agent 的能力用一个词概括叫触达半径。它不是单一维度而是三层叠在一起的最里面一层是信息触达也就是这个 Agent 能读到哪些数据——本地文件、知识库、数据库、某个内部接口返回的 JSON中间一层是操作触达它能对真实世界做出哪些改变——发消息、建单、改状态、下单、提交表单最外面一层是协作触达它能调用多少其他 Agent 或者被别人调用。这三层半径决定了同一句指令在不同系统里能走多远。你对着一个只会聊天的模型说帮我把上周的异常订单捞出来标记后推给客服组它顶多给你一段看起来很专业的 SQL 草稿而一个触达半径够大的 Agent会自己去查订单库、跑筛选条件、调用标记接口、再把结果发到客服的工作队列里最后回你一句处理了 37 单其中 4 单因为缺少收货人手机号被跳过。差别不在文笔在半径。我见过太多团队把精力全砸在换个更强的模型上结果触达半径一点没变。原因很简单模型负责决策半径由接线决定。接线这件事不性感但它才是瓶颈。1.2 三道坎看得见、动得了、记得住把触达半径拆开之后会发现问题自然分成三道坎而且它们的排查顺序不能颠倒。第一道坎是看得见。Agent 需要感知当前环境有哪些工具可用、每个工具要什么参数、当前对话进行到哪一步、上一次调用是成功还是失败。很多模型幻觉其实是感知缺失导致的——它不知道有个查询接口于是自己编了一个结果。这类问题只要把工具清单和运行状态如实喂进去八成能缓解。第二道坎是动得了。知道了要调用什么还得真的把调用发出去、把返回值解析回来、把失败的情况分类处理。这里的坑最密集超时、限流、鉴权过期、返回结构和文档不一致、分页没处理导致只拿到前 20 条。绝大多数线上事故都发生在这一层而不是决策层。第三道坎是记得住。一次任务往往要跨十几轮调用中间产生的中间结果、用户临时补充的约束、失败的尝试都需要被记下来。记不住的 Agent 会反复踩同一个坑第一步因为参数缺失失败走到第五步又用同样的缺失参数再试一遍。提示排查 Agent 问题时先确认它看到的工具清单是否完整再确认调用链路是否真的执行最后才怀疑模型决策能力。顺序反了会浪费大量时间。1.3 模型变强不等于触达变远这里有个反直觉的结论我是在真实项目里被教育出来的把底座模型换成更强的版本触达能力往往只涨一点点甚至不涨。原因是触达的瓶颈通常在外部系统的接口质量上。工具描述含糊、参数命名反直觉、错误信息只有一句 500、返回结构随版本漂移——这些问题对任何模型都一样致命。我做过一次粗糙的对照同一个任务集同一个执行框架只换模型。结果是在工具描述写得清楚、错误信息结构化的那批工具上两个模型的成功率都接近 90%而在描述模糊、报错只有状态码的那批工具上两个模型的成功率都掉到 40% 上下差距不到 5 个百分点。这个结果对我的冲击挺大——与其纠结选哪个模型不如花一个下午把工具描述重写一遍。2. 名词先对齐Agent、Harness、Skill、Java Agent、Embedding 各管一段2.1 Agent 与 Harness驾驶舱和底盘的分工这两个词被混用得太厉害了。我的划分方式很土但很准Agent 是决策的那部分Harness 是承载决策的那套装置。Agent 关心的是我现在该做什么——给定目标、历史、可用工具选出下一步动作。Harness 关心的是这个动作怎么被安全、可观测、可中断地执行——它负责拼装上下文、注入工具清单、解析模型输出、执行调用、把结果塞回历史、处理超时、控制循环次数、记录日志、在超过预算时刹车。用一个类比Agent 是驾驶员Harness 是底盘加仪表盘加刹车。你换驾驶员车的行为会变但底盘漏油换十个驾驶员也没用。我在项目里见过最典型的错误就是把 Harness 该干的活交给模型比如让模型自己去数已经调用几次工具了有没有超预算。模型数不准而且它数错了你也不知道。这类状态必须由 Harness 用代码维护。一个合格的 Harness 至少要管这些事上下文装配系统提示、工具 schema、历史消息、当前观察值按什么顺序拼、超长了怎么裁输出解析从自由文本里稳定抠出工具名和参数失败时的兜底策略执行调度串行还是并行、并发上限、超时时间、取消信号循环控制最大轮次、重复动作检测、预算上限状态记录每一步的输入输出、耗时、token 消耗全部留痕安全闸门哪些动作需要人工确认、哪些参数需要脱敏这六件事里任何一件交给模型自己感觉线上都会出问题。2.2 Skill 是能力封装不是缩小版 AgentSkill 和 Agent 的区别我通常用一句话解释Skill 是动词的封装Agent 是目标的封装。一个 Skill 描述怎么把一件事做对比如如何把会议纪要转成待办并写入项目管理系统它内部可以有固定步骤、固定模板、固定校验规则但不负责决定现在要不要做这件事。Agent 恰恰相反它要判断的是当前该不该调用这个 Skill、用哪个参数调用、调用失败之后换什么策略。所以这两者的组合方式很清晰Agent 做路由和决策Skill 做执行和质量兜底。什么时候该把一段逻辑抽成 Skill我的判断标准是三条里中两条这套步骤被反复用到而且每次步骤顺序基本一样这套步骤有明确的成功判据能自动校验这套步骤错了代价比较大需要固定校验而不是让模型临场发挥比如生成周报适合做成 Skill步骤固定、有模板、能校验字段完整性。而判断这批数据异常的原因不适合做成 Skill因为根因千变万化那属于 Agent 的决策范畴。注意Skill 里不要塞进需要自主判断的分支。一旦 Skill 内部也要调模型做决策你就得到了一个套娃调试成本会翻倍。2.3 两个都叫 agent 的东西LLM Agent 和 Java Agent这个歧义值得单独说一段因为搜索agent 开发的时候你会同时撞上两个完全不相干的领域。LLM Agent是我们这篇文章讨论的对象以大语言模型为决策核心通过工具调用与外部世界交互的程序。Java Agentjavaagent是另一回事它是 JVM 层面的一种字节码增强机制通过 premain 或者 attach 的方式在类加载前后修改字节码常被用于监控埋点、链路追踪、性能诊断。它和语言模型没有任何关系只是名字撞了。我之所以强调这一点是因为带新人时经常遇到他们拿着一篇 Java Agent 的文章问我这个和我们的 Agent 框架怎么结合。答案是基本不结合除非你想用 javaagent 给自己的 Agent 服务做无侵入埋点——那倒是个不错的组合用 javaagent 采集方法级耗时再和 Agent 的调用日志对齐能很快定位到是哪个下游接口拖慢了整条链路。类似的还有几个容易混的词Embedding是把文本映射成向量的技术用于语义检索它本身不做决策RAG是检索 生成的组合套路属于信息触达层的手段具身智能 Agent指的是有物理身体、能在真实环境里感知和行动的系统和纯软件 Agent 在感知和执行上有本质差异。2.4 Embedding 和 RAG 在触达链路里的位置很多人把 RAG 当成 Agent 的替代品这是个定位错误。RAG 解决的是让模型读到它没见过的资料只覆盖了前面说的信息触达层它不解决操作触达也不解决多轮状态维护。在一个完整的触达链路里Embedding 通常出现在两个位置一个是工具检索当工具数量上百之后把所有工具的 schema 全塞进上下文会撑爆窗口这时先用向量检索筛出最相关的十来个工具再给模型看另一个是记忆检索从长期记忆库里捞出和当前任务相关的片段。我实测下来工具数量在 30 个以内时全量塞进上下文反而更稳因为模型能看到全貌不容易漏掉;超过 50 个之后检索式注入的收益才明显起来。这个分界线不是理论值取决于你的工具描述长度和上下文窗口建议自己压测一次。3. 记忆与上下文决定 Agent-Reach 能走多远3.1 记忆分三层别用一套存储硬扛Agent 记忆是个被讲烂的话题但真正落地时最常见的错误是只用一种存储方式。我的做法是分三层各自解决不同问题层级存放内容典型载体生命周期常见误用工作记忆当前任务的中间结果、刚拿到的观察值进程内存、上下文变量单次任务塞进向量库导致检索噪声会话记忆本轮对话的目标、约束、已确认信息会话表、KV 存储数小时到数天无限增长从不裁剪长期记忆跨会话沉淀的偏好、经验、事实向量库 结构化表长期只写不读检索无过滤这三层的读写策略完全不同。工作记忆要快、要精确检索就是字典查键别搞语义搜索会话记忆要能压缩长对话必须做摘要长期记忆才需要向量检索但也必须带元数据过滤否则你会在三个月后捞出一堆过期事实。3.2 长上下文 CoT 的账要算清楚长上下文窗口现在动辄上百万 token很容易让人产生不用做记忆管理的错觉。我踩过的坑是窗口够长但模型在超长上下文里的注意力会稀释。中间位置的信息最容易被忽略这在很多评测里都有体现。所以即使窗口够大我依然坚持做三件事一是关键信息前置把当前目标和硬约束放在上下文开头和结尾二是中间结果摘要化工具返回的长 JSON 不原样塞先抽字段再放进去三是显式引用让模型在推理时引用编号比如根据观察值 3方便你自己核对它到底看没看见。CoT 也是同理。让模型把推理过程写出来对复杂任务的正确率确实有提升但代价是 token 消耗成倍上涨而且推理链越长中间出错的机会越多。我的折中方案是只在决策分叉点要求显式推理在简单步骤上直接出动作。具体实现就是在提示里区分两类情况让模型只在需要选择策略时展开思考。3.3 记忆的写入策略比检索策略重要大多数人把精力花在检索上——调权重、换模型、加 rerank。但我实际观察下来记忆系统的质量上限由写入决定。原因很直白检索只能捞出已经写进去的东西。如果写入时把整段对话原封不动丢进去检索出来的就是一坨噪声如果写入时做了结构化抽取——用户偏好报表用周粒度约束不接收周末推送——检索质量立刻不一样。我现在用的写入规则是三条只写稳定信息。一次性的中间状态不写长期记忆写进去就是污染。带来源和时间戳。每条记忆挂上它来自哪次会话、什么时候产生的检索时按时间衰减。允许更新和失效。用户偏好周粒度报表这条记忆在用户改了口径之后必须能被覆盖而不是变成两条互相矛盾的记录。第三条最容易被忽略但它是长期记忆能不能用的分水岭。我见过一个客服 Agent 因为记忆从不更新三个月后还在按用户早期的错误描述回答问题。3.4 上下文压缩的三种做法与副作用任务跑长了必须压缩我试过三种做法各有代价。滑动窗口最简单只保留最近 N 轮。缺点是早期确立的目标和约束会被丢掉Agent 跑到后面开始跑题。补救方式是把目标和约束单独抽出来放在窗口之外的固定位置。滚动摘要是让模型定期把历史总结成一段话。好处是信息密度高坏处是摘要本身会失真尤其是数字和 ID摘要一次就可能把一个订单号写成另一个。我的做法是数字和标识符绝不进摘要它们单独存成结构化字段摘要里只用占位引用。关键帧保留是我目前最常用的在历史里标记若干关键帧——任务开始、拿到关键结果、发生失败重试——这几轮完整保留中间的过程性轮次压缩成一句话。这样既省 token又保证了关键信息不被摘要污染。三种方式可以混用我的默认组合是关键帧完整保留 其余轮次滚动摘要 数字字段结构化单独存。4. 工具通道让 Agent 的手真的够得着4.1 工具描述写不好模型再强也调不对工具描述是 Agent 世界里的 API 文档而它的读者是模型。我总结了几条改写原则都是在真实失败案例上磨出来的。第一名字要动词开头且唯一。query_order比orderService好get_order_list和list_orders同时存在就是灾难模型会随机挑一个。第二描述里写什么时候用不只写是什么。只写查询订单不够要写当用户提供订单号或需要按时间范围统计订单时使用不要用它查询退款记录那应该用 query_refund。第三参数要给例子。日期格式写清楚是2024-03-01还是时间戳枚举值列全。我遇到过因为日期格式没写模型一半次数传了中文格式接口直接报错。第四把副作用写在显眼位置。会改数据的工具必须明确标注否则模型会在只是看看的场景下调用它。4.2 参数校验、错误回传与可恢复的报错工具调用失败时返回什么直接决定 Agent 能不能自我修复。我坚持一个原则错误信息要能指导下一步动作。一个反例是返回{code: 500, msg: internal error}。模型看到这个只能瞎猜常见行为是原样重试重试三次后放弃并给用户一句系统繁忙。一个正例是返回结构化错误{error_type: invalid_param, field: start_date, expected: YYYY-MM-DD, hint: 请将日期改为 2024-03-01 格式后重试}。模型拿到这个下一次调用基本就能修正。我把错误类型固定成几类每类对应不同处理策略错误类型含义Agent 应该怎么做invalid_param参数格式或取值不对按 hint 修正参数后重试not_found目标不存在不要重试换查询条件或告知用户rate_limited触发限流退避等待后重试降低并发auth_expired凭证失效停止重试触发续期流程upstream_error下游异常有限次重试失败后降级或转人工ambiguous匹配到多条向用户澄清不要自己挑一条这张表的价值在于它把重试从一种本能变成了一种有条件的策略。不该重试的时候重试是 Agent 浪费预算的头号原因。4.3 超时、重试与幂等超时设置要分层单次工具调用超时、单轮 Agent 步进超时、整个任务超时。三层的值依次放大但都要有。我吃过没有任务级超时的亏——一个 Agent 在某个循环里反复尝试跑了四十分钟才被人工发现。重试必须配幂等。查询类接口天然幂等重试随意写操作就要小心。我的做法是给每个写操作生成一个幂等键通常由任务 ID 步骤序号组成随请求带上服务端识别到重复键就返回首次结果而不是再执行一遍。如果服务端不支持幂等键那就只能靠本地记录这一步已经发出去了并且在重试前先查询状态确认。提示不要把所有失败都交给自动重试。写操作的重试策略应该是先查状态再决定要不要重发而不是直接重发。4.4 浏览器通道最像人、也最容易碎的一条路当目标系统没有 API 时浏览器自动化就成了唯一通道。它让 Agent 的触达半径瞬间变大但代价是稳定性。我在这条路上踩的坑可以写一整篇。核心问题是选择器脆弱。基于 DOM 路径的选择器页面一改版就全废。我的替代方案优先级是优先用可见文本定位比如按按钮文字找其次用语义属性如 aria-label再次用稳定的业务属性如>def run_agent(task, tools, max_steps20, token_budget200_000): memory init_memory(task) # 工作 会话 长期三层 used_tokens 0 seen_actions [] # 用于检测重复动作 for step in range(max_steps): ctx build_context(task, memory, tools) if used_tokens token_budget: return finish(budget_exceeded, memory.summary()) raw llm(ctx) # 返回文本或结构化动作 used_tokens count_tokens(ctx, raw) action parse_action(raw) if action.type final: return finish(ok, action.content) # 重复动作检测连续三次同样的调用直接打断 key (action.tool, stable_json(action.args)) seen_actions.append(key) if seen_actions[-3:].count(key) 3: memory.add_note(检测到重复动作已中断请更换策略) continue tool tools.get(action.tool) if tool is None: memory.add_observation(f工具 {action.tool} 不存在可用工具见清单) continue err validate_args(tool.schema, action.args) if err: memory.add_observation(err.to_hint()) # 结构化提示指导修正 continue if tool.requires_confirmation: ok request_human_approval(tool, action.args) if not ok: memory.add_observation(用户拒绝了该操作) continue result call_with_timeout(tool, action.args, timeouttool.timeout) memory.add_observation(summarize(result)) # 长结果先摘要 memory.add_structured(result.numeric_fields) # 数字与 ID 单独存 return finish(max_steps_exceeded, memory.summary())这段代码里有几个细节值得强调。seen_actions的重复检测是最廉价的止损手段之一成本几行代码能挡掉大量原地打转的情况。summarize(result)在放回上下文之前先压缩避免了长响应把窗口撑爆。add_structured把数字和 ID 单独存是为了防止后面做摘要时把这些关键值改错。5. 多 Agent 与 A2A把触达半径横向摊开5.1 什么时候才值得拆多 Agent多 Agent 是这两年最容易被滥用的设计。我的判断标准很单一当不同子任务需要的工具集、提示词、权限边界差异足够大并且把它们塞进一个 Agent 会互相干扰时才拆。举个具体例子。数据分析 Agent 和 工单处理 Agent 值得拆前者需要大量查询工具和计算能力不需要写权限后者需要写权限但只需要少量工具。混在一起会导致提示词长度暴涨而且写权限暴露给了不需要它的场景。反例是检索 Agent 总结 Agent。这类拆分基本没收益只是把一次调用变成两次多了一轮信息损耗。我早期特别爱这么拆后来发现总结质量反而不如单 Agent 直接做。5.2 Agent Card 里到底写了什么Agent 之间要能互相发现和调用就需要一份描述自己的名片。在 A2A 这类协作规范里这张名片通常叫 Agent Card它挂在约定路径下别人先读卡、再决定怎么调用。一张实用的卡至少要包含这些内容{ name: order-analyst, description: 订单异常分析与标记支持按时间范围和状态筛选, version: 1.2.0, capabilities: [query, annotate], skills: [ { id: find_abnormal_orders, description: 按时间范围找出异常订单, input_schema: {start_date: string, end_date: string}, output_schema: {orders: array, count: integer} } ], auth: {scheme: bearer}, limits: {qps: 5, timeout_seconds: 30} }这张卡里我特别看重三个字段。capabilities明确了它能做什么类型的操作调用方一眼就知道它有没有写权限。limits里的 QPS 和超时是我的经验之谈——没有这两个字段调用方会默认无限并发直接把下游打挂。version用于灰度协作方可以按版本路由流量。Card 最常见的两个问题是字段写得像宣传文案强大的分析能力调用方读完还是不知道该怎么传参以及能力声明和实际行为不一致卡上写着只读实际会改数据。后者在跨团队协作里是会出事故的。5.3 路由识别节点编排里最容易被轻视的一环不管你是用图编排还是用中心调度中间都会有一个路由识别节点判断当前请求该交给哪个 Agent 或哪个 Skill。这一环经常被写成一句简单的提示词让模型选然后线上就开始出各种误路由。我后来把路由拆成两步先规则后模型。规则层处理高确定性的情况比如请求里出现了明确的订单号就走订单链路出现了明确的报表周期就走报表链路——这些用正则或关键词就能判准确率接近百分之百还省一次模型调用。规则层兜不住的长尾再交给模型并且给模型的候选列表要带每个候选的适用场景描述而不是只给名字。另外路由要给不确定留出口。我会允许路由节点返回unclear然后触发一次澄清反问而不是硬选一个。硬选的代价通常比多问一句大得多。5.4 协作最常失控的三件事第一件是循环调用。A 调 BB 在某个分支又调回 A如果没有调用深度限制两个 Agent 能互相刷到超时。我的做法是给每次协作请求带一个调用链标记链路上出现过自己就拒绝再次进入。第二件是上下文丢失。A 把自己的完整上下文丢给 BB 只返回结论A 就不知道 B 中间做了什么一旦结论不对无法追责。解决办法是要求 B 返回结构化结果包含结论、依据引用、置信度和耗时让 A 有能力判断要不要采信。第三件是权限放大。A 有写权限它调用的 B 如果权限校验缺失就相当于 A 把自己的权限借给了 B。这在设计上是灾难。正确做法是每次协作调用都做权限校验按调用方的身份而不是被调用方的能力来授权。6. 评测、观测与安全让触达可度量、可刹车6.1 别只跑通 Demo先攒失败样本集Demo 跑通那一刻是最危险的时刻因为你会误以为已经做完了。我现在的习惯是Demo 一通就开始攒失败样本故意传错参数、故意让下游超时、故意返回空结果、故意返回多条匹配。每一个失败样本都对应一条期望行为——应该报错并提示格式、应该退避重试两次后转人工。这套样本集的价值在于它是你后续所有改动的回归测试。换个提示词、加个工具、调个阈值跑一遍样本集就知道有没有退化。没有这套东西你的每次优化都是盲改。我用的评分维度固定在四个任务成功率端到端有没有完成、步骤效率用了多少轮、多少 token、安全性有没有越权、有没有该确认没确认、可解释性能不能从日志还原它为什么这么做。四个维度里安全性是一票否决项。6.2 埋点埋在哪几个位置才算够埋点太少等于没埋太多又没人看。我固定在六个位置每轮上下文的 token 数与摘要耗时每次工具调用的名称、参数摘要、耗时、结果状态每次参数校验失败的具体字段和原因每次重试的次数与最终结果每次路由决策的选择与置信信息任务结束时的总轮次、总 token、终止原因这六个点合起来能回答绝大多数为什么这次没办好的问题。我特别看重第五点因为误路由往往是最隐蔽的问题——任务没报错但结果就是不对劲查日志才发现一开始就走错了链路。6.3 权限边界与人工确认门Agent 的权限应该按最小必要原则给而且要和它的角色绑定。查询类 Agent 不给写权限执行类 Agent 的写权限限定在明确的资源范围里。人工确认门设在哪我的经验是设在这三类动作上不可逆的删除或覆盖、对外发送的消息、涉及金额或额度的操作。这三类一旦出错修复成本远高于多问一句的成本。确认门本身也要设计好不能只弹一个是否继续。有效的确认提示应该包含要做什么、影响什么范围、不可逆的点在哪、预计影响多少条记录。我见过因为确认提示只有一句确认执行吗用户连点三次确认最后删错了数据的事故。注意确认门不要设得太密。如果每一步都要确认用户会形成无脑点确认的习惯那道门就形同虚设了。6.4 面试里被追问最多的几个问题聊到这顺便说说这类岗位面试里反复出现的几个追问方向也算是自查清单。问得最多的是你的 Agent 怎么防止死循环。合格答案必须提到最大轮次、重复动作检测、任务级超时这三层只答一层会被继续追问。其次是多轮对话里状态怎么保持。这题考的是你有没有真的做过带记忆的系统答用向量库存历史基本会被追问到细节上下文超长了怎么办、记忆冲突了怎么办、检索出来的过期事实怎么处理。还有工具调用失败了你怎么办。这题的分水岭在于有没有区分错误类型答重试三次是初级答案答按错误类型分策略写操作先查状态再决定是否重发才是做过的人的答案。最后一个高频问题是你如何评估你的 Agent。如果你只能回答跑了一些例子感觉还行那基本就到这里了能说出失败样本集、四个评分维度、回归测试流程的才会被认可是真做过。7. 学习路线与我的踩坑复盘7.1 分阶段路线如果要我给一条从零到能上线的路线我会这么排。第一阶段只做一件事单工具单轮。写一个 Agent只给它一个查询工具让它在给定问题下正确调用并返回结果。这个阶段的目的是把工具描述、参数解析、返回值处理这三件事做扎实。别急着加记忆加记忆会掩盖问题。第二阶段做多工具多轮。给五到十个工具加入循环控制和失败处理重点练上下文装配和错误回传。这个阶段你会大量遇到模型选了错误的工具那就是工具描述该改了。第三阶段加记忆与长任务。引入三层记忆做上下文压缩跑需要十几轮才能完成的任务。这个阶段暴露的都是状态管理问题。第四阶段做多 Agent 与评测。拆出第二个 Agent加上协作协议和失败样本集跑回归。这个阶段过后你才算有了一个能上线的雏形。每一阶段都别跳。我见过直接上第四阶段然后全线崩掉的最后回头补第一阶段的工具描述反而绕了远路。7.2 那些我交过学费的坑下面这张表里的每一条都是我在真实项目里吃过亏的不是从文档里抄的。坑现象我的修正让模型自己数轮次无限循环跑到超时才停轮次和预算由 Harness 用代码维护工具名语义重叠随机挑一个行为不稳定名字动词开头、语义唯一重叠的合并长 JSON 原样入上下文窗口爆掉关键字段被忽略先抽字段再入上下文数字单独存写操作无幂等键重试导致重复下单任务 ID 步骤号构成幂等键记忆只写不更新三个月后还在用旧偏好记忆带时间戳支持覆盖和失效确认提示太笼统用户无脑确认删错数据提示必须含范围、影响量、不可逆点路由靠模型硬选任务不报错但结果不对先规则后模型允许返回不确定并反问只看成功率不看步骤效率成本失控把轮次和 token 纳入评测维度这张表里我最想强调让模型自己数轮次这条。它看起来是个小问题但它是很多 Agent 跑飞的根本原因——你无法用一个本身就不擅长计数的组件来管理计数。7.3 最后说点实际的我在实际项目里最深的体会是Agent 的能力上限往往不由模型决定而由你愿意在接线上花多少功夫决定。工具描述重写一遍、错误信息结构化一下、加个重复动作检测、把数字从摘要里摘出来单独存——这四件事加起来可能只要两天但对成功率的提升远超过换一个更大的模型。另外一个小技巧分享给正在做多 Agent 的人先别急着拆先用一个 Agent 加命名空间把工具分组。也就是在单 Agent 内部按业务域给工具加前缀、分组注入看看效果。只有当分组之后提示词仍然过长、或者权限边界实在没法统一时再拆成多个 Agent。我发现很多被拆掉的多 Agent 架构用分组就能解决而拆分带来的循环调用、上下文丢失、权限放大这三个问题远比省下的那点提示词长度难处理。最后提一句长期维护的事Agent 系统要有可回放能力。把每次任务的完整输入、上下文装配结果、每步决策和工具返回都存下来出事时能原样重放一遍。这东西在开发期看起来多余但在线上出问题时它是唯一能让你在半小时内定位原因的手段。我现在的习惯是任何一个准备上线的 Agent先确认它的回放链路通了再谈功能。