1. 从“能跑通”到“能交付”AI Agent 工程师的分水岭我见过太多团队在 Agent 项目上栽跟头不是因为模型选错了也不是因为框架不够先进而是卡在一个更朴素的问题上Demo 跑得挺漂亮一上生产就散架。你让 Agent 查个天气、写封邮件它干得利索你让它处理一个真实的客户工单、完成一次跨系统的数据核对、生成一份可以直接发给客户的报告它就开始胡言乱语、丢步骤、编数据。这个落差就是“调用模型”和“交付结果”之间的距离。先把概念理清楚因为热词里问得最多的就是这几个东西的区别。LLM大语言模型是一个函数输入文本、输出文本它本身没有记忆、没有手脚、没有目标感。AI 模型是个更宽泛的说法图像模型、语音模型、推荐模型都算LLM 只是其中一类。而AI Agent是在 LLM 外面套了一整套“感知—决策—行动—校验”的循环结构它能调用工具、能读写外部状态、能根据执行结果调整下一步、能在失败时重试或求助。打个比方LLM 是一个博学但只会动嘴的顾问Agent 是给这个顾问配了手、配了眼睛、配了记事本、还配了一个会盯着他干活的监工。至于 DeepSeek它属于 LLM 这一层是一个具体的模型产品你可以把它当作 Agent 的“大脑”来用但它本身不是 Agent。这个区分为什么重要因为它直接决定了你的工作重心。如果你以为自己在做 Agent实际上只是在做“调模型”那你所有的精力都会花在 prompt 调优上而真正让结果可交付的那些工程问题——状态管理、错误恢复、工具契约、输出校验——你一个都没碰。上线之后你会发现prompt 调到再完美也挡不住工具返回一个空值、挡不住网络超时、挡不住模型在第三步突然忘了第一步的约束。我个人的判断标准很简单一个 Agent 系统是否合格不看它在顺境下表现多好看它在逆境下能不能兜住。工具挂了怎么办模型输出格式错了怎么办用户中途改了需求怎么办外部数据源返回了意料之外的字段怎么办这些问题的答案才是 Agent 工程师真正的价值所在。下面我会把这几年在 Agent 交付上踩过的坑、总结的方法按“结果交付”这条主线拆开讲。2. 结果交付的第一道坎把“意图”翻译成“可执行的契约”2.1 为什么大多数 Agent 失败在第一步很多人做 Agent 的起点是写一个漂亮的 system prompt把角色、能力、约束一股脑塞进去然后指望模型“理解”用户想要什么。这个做法在单轮任务里勉强能用一旦任务有多步、有分支、有外部依赖就会暴露出一个根本问题自然语言意图和可执行动作之间缺少一层显式的契约。什么叫契约就是你把“用户想要什么”翻译成“系统要做什么”时必须明确下来的那些东西输入是什么格式、输出必须满足什么结构、中间可以调用哪些工具、每个工具的前置条件和后置效果是什么、失败时允许的重试策略是什么。这些东西如果只存在于 prompt 的模糊描述里模型每次执行都会给你一点“惊喜”。我踩过最典型的一个坑做一个合同信息抽取的 Agentprompt 里写了“请提取合同中的甲方、乙方、金额、签署日期”。测试集上准确率 90% 多一上真实数据就崩。原因是真实合同里“金额”可能写成“合同总价”“价款”“人民币大写”甚至分散在多个条款里模型有时候返回一个数字有时候返回一段描述有时候干脆把“金额”和“税率”搞混。下游系统拿到这种参差不齐的输出直接报错。2.2 用结构化输出把“模糊”变成“确定”解决这个问题的核心手段是强制结构化输出。不管你用哪个框架最终都要让模型的输出落到一个明确的 schema 上。我的做法是分三层第一层定义输出 schema。用 JSON Schema 或者 Pydantic 模型把每个字段的类型、必填性、取值范围写死。比如金额字段必须是number类型日期必须是YYYY-MM-DD格式枚举字段只能取预设的几个值。第二层在 prompt 里嵌入 schema 并给出正反例。不要只说“返回 JSON”要把完整的 schema 贴进去再给一两个正确示例和一个错误示例明确告诉模型“字段缺失时填 null不要编造”。第三层在代码侧做校验和修复。模型返回后先用 schema 校验一遍不通过就触发一次“修复调用”——把错误信息和原始输出一起喂回去让模型修正。修复最多重试两次两次还不行就降级到人工兜底。from pydantic import BaseModel, Field, ValidationError from typing import Optional import json class ContractInfo(BaseModel): party_a: str Field(description甲方全称) party_b: str Field(description乙方全称) amount: Optional[float] Field(defaultNone, description合同金额单位元) sign_date: Optional[str] Field(defaultNone, patternr\d{4}-\d{2}-\d{2}) def extract_with_repair(raw_text: str, max_retry: int 2) - ContractInfo: prompt build_prompt(raw_text, ContractInfo.model_json_schema()) for attempt in range(max_retry 1): output call_llm(prompt) try: data json.loads(output) return ContractInfo(**data) except (json.JSONDecodeError, ValidationError) as e: if attempt max_retry: raise prompt build_repair_prompt(raw_text, output, str(e)) raise RuntimeError(unreachable)这段代码看起来简单但它把“模型可能出错”这件事变成了系统设计的一部分而不是靠运气。交付结果的前提是承认模型不可靠然后用工程手段把不可靠性关进笼子里。2.3 工具契约比 prompt 更重要的东西Agent 和普通 LLM 应用最大的区别是它会调用工具。而工具调用的质量几乎完全取决于你给工具定义的契约是否清晰。我见过太多人写工具描述时只写一句“查询用户信息”然后模型就懵了传什么参数返回什么结构查不到怎么办一个好的工具契约应该包含功能一句话说明、参数名和类型、每个参数的含义和示例值、返回值的结构、可能的错误码、以及调用时的注意事项。这些信息要同时出现在两个地方——给模型看的 tool description 里和给代码用的函数签名里。两者必须一致否则模型以为传的是user_id代码里接的是uid这种低级错误在联调阶段能耗掉你一整天。提示工具描述里的示例值非常关键。模型对示例的敏感度远高于对抽象描述的理解。与其写“用户ID字符串类型”不如写“用户ID例如 u_10086”。一个具体的例子能消除大量歧义。3. 状态与记忆Agent 交付结果时最容易丢的东西3.1 多步任务里模型为什么会“失忆”Agent 处理一个多步任务时每一步的输入输出都在变化。如果每一轮都把完整历史塞给模型token 会爆炸如果只塞最近几轮模型就会丢掉早期的重要约束。这就是所谓的“上下文漂移”——走到第五步的时候模型已经忘了第一步用户说的“预算不能超过 5000”。我做过一个采购比价的 Agent任务流程是解析需求 → 搜索供应商 → 抓取报价 → 比价 → 生成推荐。测试时发现到了比价环节模型经常推荐一个超出预算的选项。排查后发现预算约束是在第一轮用户输入里给的但比价环节的 prompt 里只带了最近两轮的对话预算信息早就被挤出去了。3.2 显式状态机比“让模型自己记”靠谱得多解决办法不是加大上下文窗口而是把关键状态从对话历史里抽出来显式管理。我的做法是维护一个结构化的 state 对象里面存放任务的所有关键信息用户约束、已完成步骤、中间结果、待办事项。每一轮调用模型时把 state 序列化后作为独立字段传入而不是混在对话历史里。class AgentState(BaseModel): user_constraints: dict Field(default_factorydict) completed_steps: list[str] Field(default_factorylist) intermediate_results: dict Field(default_factorydict) pending_step: Optional[str] None retry_count: int 0 def build_step_prompt(state: AgentState, current_input: str) - str: return f 当前任务状态 - 用户约束{json.dumps(state.user_constraints, ensure_asciiFalse)} - 已完成步骤{state.completed_steps} - 中间结果{json.dumps(state.intermediate_results, ensure_asciiFalse)} - 当前待执行{state.pending_step} 本轮输入{current_input} 请基于以上状态执行当前步骤并返回结构化结果。 这样做的好处是状态是代码可控的不会因为对话轮次增加而丢失。而且当任务失败需要重试时你可以精确地回滚到某一步而不是从头再来。Agent 的可靠性很大程度上取决于你对状态的控制粒度。3.3 长期记忆和短期记忆要分开处理短期记忆就是当前任务的 state任务结束就丢弃。长期记忆是跨任务的知识比如用户偏好、历史交互摘要、领域知识库。这两者的存储和检索策略完全不同。短期记忆要求强一致、低延迟放内存或 Redis 就行长期记忆要求可检索、可更新通常用向量库加结构化存储。我见过有人把两者混在一起结果就是每次调用都要检索一大堆无关的历史既慢又不准。分开处理按需注入是记忆管理的基本原则。具体来说短期 state 每轮必带长期记忆只在相关步骤按需检索 top-k 条注入。4. 错误恢复决定 Agent 能不能上生产的关键能力4.1 把失败当成常态来设计Demo 阶段大家默认一切顺利生产阶段一切都不顺利。工具会超时、API 会限流、模型会返回垃圾、外部数据会缺字段。如果你的 Agent 没有为这些情况设计恢复路径那它就是一个随时会炸的定时炸弹。我的经验是在写第一行 Agent 代码之前先列一张失败清单这个任务可能在哪一步失败每步失败的原因有哪些每种原因对应的恢复策略是什么这张清单比任何架构图都重要。失败类型典型场景恢复策略工具超时外部 API 响应慢指数退避重试最多 3 次超限则降级工具返回空查询无结果换关键词重试或标记该步为“无数据”继续模型输出格式错JSON 解析失败触发修复调用带错误信息重试模型输出内容错编造数据交叉校验与已知事实比对不一致则重试任务目标漂移多步后偏离原意每步执行前用 state 校验目标一致性资源耗尽token 超限压缩历史只保留关键 state4.2 重试不是万能药要区分“可重试”和“不可重试”很多人一遇到失败就无脑重试结果浪费了大量 token 和时间问题依然存在。重试的前提是失败原因是暂时性的。网络超时可以重试参数格式错重试一百次也没用得先修参数。我的做法是在错误处理层加一个分类器暂时性错误超时、限流、临时不可用走重试永久性错误参数非法、权限不足、数据不存在走修复或降级未知错误走兜底。class ErrorCategory(Enum): TRANSIENT transient PERMANENT permanent UNKNOWN unknown def classify_error(exc: Exception) - ErrorCategory: if isinstance(exc, (TimeoutError, RateLimitError)): return ErrorCategory.TRANSIENT if isinstance(exc, (ValueError, PermissionError, KeyError)): return ErrorCategory.PERMANENT return ErrorCategory.UNKNOWN def handle_failure(exc, state, step): category classify_error(exc) if category ErrorCategory.TRANSIENT and state.retry_count 3: state.retry_count 1 return RetryAction(backoff2 ** state.retry_count) if category ErrorCategory.PERMANENT: return RepairAction(hintstr(exc)) return FallbackAction(reasonunknown_error)4.3 降级路径要提前设计好不是所有失败都能恢复。当重试和修复都失败时Agent 需要一个体面的降级方案。降级不是“报错退出”而是“用次优方案完成任务并明确告知用户哪些部分没做到”。比如比价 Agent 抓不到某个供应商的报价降级方案是基于已有数据给出推荐并标注“供应商 X 报价未获取建议人工确认”。用户能接受不完美的结果但不能接受一个假装完美的错误结果。5. 从“单 Agent”到“多 Agent 协作”的取舍5.1 什么时候该拆什么时候不该拆热词里“多智能体”出现频率很高但我要泼一盆冷水大多数任务不需要多 Agent。多 Agent 带来的协调成本、通信开销、状态同步复杂度往往超过它带来的收益。我见过一个团队把简单的文档摘要任务拆成“读取 Agent 摘要 Agent 校验 Agent”结果延迟翻了三倍错误率反而上升因为三个 Agent 之间的信息传递又引入了新的失真点。判断标准很简单如果子任务之间是串行且强依赖的用单 Agent 加状态机就够了如果子任务可以并行、且各自需要不同的工具集和 prompt 策略才考虑拆。比如一个“市场调研”任务需要同时查竞品、查舆情、查财报这三块互不依赖拆成并行 Agent 就合理。5.2 多 Agent 协作的核心是“接口”而不是“智能”一旦决定拆重点就不是让每个 Agent 多聪明而是让它们之间的接口多清晰。每个 Agent 的输入输出必须是结构化的、可校验的。A Agent 的输出直接作为 B Agent 的输入时中间要有一层 schema 校验防止 A 的格式变化把 B 搞崩。我通常会在 Agent 之间加一个“消息总线”层所有跨 Agent 的通信都走统一的消息格式带上来源、目标、类型、payload、时间戳。这样任何一个 Agent 出问题都能快速定位是哪个环节产生的脏数据。5.3 别让 Agent 之间“自由对话”有些框架鼓励 Agent 之间用自然语言自由交流看起来很酷实际上很难调试。两个 Agent 聊着聊着就跑偏了你根本不知道哪句话导致了错误决策。我的建议是Agent 之间的通信尽量结构化只在确实需要协商的场景才用自然语言并且限制轮次。能用一个字段传的信息不要用一段话。6. 交付结果的最后一公里可观测性与评估6.1 没有 trace就没有交付Agent 系统最让人头疼的是“它为什么这么干”。用户投诉结果不对你打开日志一看只有最终输出中间的决策过程一片空白。这时候你根本无从排查。可观测性不是可选项是交付的必需品。我的做法是给每一步都打 trace输入是什么、模型返回什么、调用了哪个工具、工具返回什么、state 怎么变的、耗时多少、token 消耗多少。这些 trace 汇总起来你就能完整回放一次任务执行。市面上有现成的 tracing 工具但核心是你要在代码里埋点而不是指望框架自动帮你搞定一切。dataclass class StepTrace: step_name: str input_snapshot: dict model_output: str tool_calls: list[dict] state_before: dict state_after: dict latency_ms: int token_usage: dict error: Optional[str] None def traced_step(step_fn): def wrapper(state, *args, **kwargs): trace StepTrace(step_namestep_fn.__name__, ...) try: result step_fn(state, *args, **kwargs) trace.state_after state.model_dump() return result except Exception as e: trace.error str(e) raise finally: persist_trace(trace) return wrapper6.2 评估不能只看最终结果很多人评估 Agent 只看“最终答案对不对”这远远不够。一个任务最终成功了但中间调了 20 次工具、重试了 8 次、花了 5 分钟这种成功在生产环境是不可接受的。评估要分层最终结果准确率、步骤成功率、平均重试次数、平均延迟、token 成本。这几个指标要一起看才能判断一个 Agent 是否真的可交付。我通常会建一个回归测试集覆盖正常场景、边界场景、异常场景。每次改动 prompt 或工具后跑一遍回归看各项指标有没有退化。没有回归测试的 Agent 开发就是在裸奔。6.3 人工兜底不是失败是设计的一部分最后说一个心态问题。很多工程师觉得 Agent 需要人工兜底是“没做好”其实恰恰相反。在当前的模型能力下完全无人值守的 Agent 只适用于极窄的场景。大多数生产级 Agent 都是“人机协作”模式Agent 处理 80% 的常规情况剩下 20% 的疑难或高风险情况转人工。关键是要把转人工的触发条件设计好——置信度低于阈值、涉及高风险操作、连续失败超过次数这些都应该自动升级。把人工兜底设计好比追求 100% 自动化更现实也更能交付真正有价值的结果。用户要的不是“全自动”是“问题被解决”。7. 一些关于工具选型和团队协作的实在话7.1 框架选型别被“全家桶”绑架现在 Agent 框架层出不穷Python 有 LangChain、LlamaIndexJava 有 Spring AI还有各种低代码平台。我的建议是先用最薄的抽象把核心循环跑通再按需引入框架。很多框架为了通用性做了大量封装出问题时你根本不知道是哪一层在捣鬼。我见过团队用某个框架的 AgentExecutor结果工具调用的参数被框架悄悄改了一层排查了两天才发现。如果你用 Java 技术栈Spring AI 的抽象相对克制适合企业级集成如果用 PythonLangGraph 的状态机模型比早期的 Chain 更可控。但无论用哪个核心逻辑要能脱离框架独立测试这是底线。7.2 团队里谁该做 Agent 工程师热词里有人问“AI Agent 工程师”和“算法工程师”“运维工程师”的区别。我的观察是Agent 工程师更像是一个“全栈问题解决者”要懂模型的能力边界要会写工程代码要理解业务场景要能做数据校验要会设计评估方案。它不需要你训模型但需要你知道模型什么时候会骗你它不需要你搭集群但需要你知道工具超时该怎么退避。如果你是从后端转过来的你的工程能力是优势补一补 prompt 工程和评估方法就能上手如果你是从算法转过来的你对模型的理解是优势补一补状态管理和错误处理就能落地。这个岗位的核心竞争力是“把不确定的东西变得可交付”的能力。7.3 一个练手项目的建议如果你想练手别一上来就做“全能助手”。选一个边界清晰、结果可验证的小任务比如“根据给定的商品链接抓取价格和库存生成一份比价表”。这个任务包含了 Agent 的核心要素工具调用、结构化输出、错误处理、结果校验。把它做到能在真实网站上稳定跑 100 次不出错比做十个花哨的 Demo 都有价值。我在带新人的时候第一周就让他们做这个做完基本就理解了 Agent 交付的全部痛点。工具会挂、页面会变、数据会缺、格式会乱这些真实世界的脏东西才是 Agent 工程师的日常。把这些处理好了你交付的就不是一个“能聊天的模型”而是一个“能干活的结果”。