原型 demo 三天能跑通,生产稳定却花了三个月。这不是我一个人的经历——在 Agent 从「能跑」到「敢上线」之间,隔着工程化的一整套功课。这篇文章把代价最贵的 7 个坑写下来,每个坑都附一句可执行的建议。
1. 一上来就上编排框架
最初我直接选了编排框架,想把「规划—执行—反思」画成一张漂亮的流程图。结果是:图的复杂度先于产品失控,调试要依赖可视化面板,出问题时根本说不清是哪一层出的错。后来我把流程重写成显式的循环,几十行代码,逻辑一目了然。
# 显式循环:控制流写在自己能看见的地方
while not done and step < MAX_STEPS:
plan = llm.plan(state) # 生成下一步计划
tool = llm.choose_tool(plan)
result = run_tool(tool, plan.args)
state = update_state(state, result)
done = llm.should_stop(state) # 明确的中止条件
建议:第一版用显式循环,等确认了「这个 Agent 真的有循环、分支、多 Agent 协作需求」,再引入框架。框架解决的是编排复杂度,不是产品问题。
2. 结构化输出是刚需,别信自由文本
让模型「用自然语言回一句结果」,后果是解析逻辑写了一堆正则,模型一换版本就碎。工具名、参数、状态判断、最终答案——全部走结构化输出加 schema 校验,校验失败就让模型带着错误信息重试。
from pydantic import BaseModel, ValidationError
class ToolCall(BaseModel):
name: str
arguments: dict[str, str]
def parse_tool_call(llm, raw: str, max_retries: 2) -> ToolCall:
for attempt in range(max_retries + 1):
try:
return ToolCall.model_validate_json(raw)
except ValidationError as e:
raw = llm.chat(
f"上一次输出不是合法 JSON:{e.errors()[:2]},请只返回一个 JSON 对象"
)
raise OutputParseError("结构化输出连续失败,进入兜底")
建议:输出 schema 用类型定义写死;校验失败进重试循环,最多 N 次;仍失败就走兜底——明确报错给用户,绝不静默吞掉。
3. 上下文是预算,不是垃圾桶
把全部历史一股脑塞进上下文,token 成本线性涨,模型注意力还被无关内容稀释——长对话后期质量肉眼可见地下滑。我后来做了三层控制:会话早期细节即时压缩成摘要;超过窗口上限的旧消息先出摘要再丢弃原文;每次调用前按「最近消息 + 摘要 + 本轮检索结果」重新拼装上下文。
建议:给上下文设硬预算(比如输出前最多 8k token),超了就压缩。压缩策略要写清楚「保留什么、丢弃什么」,否则压缩本身会变成另一个黑盒。
4. 工具调用失败要幂等重试
支付、下单这类工具一旦「超时重发」就可能重复扣款;查询类工具又允许安全重试。我最初所有工具统一重试 3 次,上线第二周就出了线上事故。后来为每个工具声明幂等性,重试前生成幂等键,非幂等工具宁可失败也不重发。
# 只对瞬时错误重试,非幂等工具宁可失败也不重发
async def run_tool_with_retry(tool, args, *, max_retries=3):
for attempt in range(max_retries + 1):
try:
return await tool(**args)
except TransientError:
if attempt == max_retries:
raise
await asyncio.sleep(2 ** attempt) # 1s / 2s / 4s 退避
建议:给工具加幂等声明 + 幂等键;区分瞬时错误与业务错误,只对瞬时错误重试。事故复盘时,重试策略永远排在第一位。
5. 可观测性从第一天就建
「模型为什么这么回」是 Agent 项目最难回答的问题。等出问题再补日志,等于没有日志。我的做法:每个会话一个 trace_id,贯穿「输入 → 规划 → 工具调用 → 输出」每一步;模型输入输出、工具入参出参、token 用量全量落盘(脱敏后);配一个简单的看板盯三个指标:工具失败率、平均延迟、单次请求成本。
建议:第一天就加 trace_id 和全链路日志,比任何事后优化都值。可观测性的目标不是「有日志」,而是「任何一次异常都能被还原」。
6. 没有评测就没有迭代
改一个 prompt 觉得变好了,过两周发现另一个场景崩了——因为没有回归集。后来我建了一个 60 条的评测集,按场景分类:正常路径、边界输入、恶意输入、超长对话。每次改 prompt、换模型、调工具描述,都全量跑一遍,对比通过率与关键指标。
建议:评测集从第一天就攒,边用边录「真实用户问题 + 期望行为」。这是 Agent 项目最值钱的数据资产,比任何 prompt 模板都值钱。
7. 成本与延迟是架构问题
上线后账单吓人:平均每个问题 5 次模型调用、2 万 token。优化路径有三条:入口路由(简单问题走小模型或预设模板);相似问题直接复用缓存结果;把「需要推理」和「只需要检索」的请求分流到不同链路。
建议:把成本模型写进架构评审——每次请求花多少 token、多少毫秒,和功能一起过。延迟与成本的优化,永远比「再换一个大模型」更有效。
上线前 checklist
- 所有输出都有 schema 校验与兜底路径
- 每个工具声明了幂等性与超时策略
- 每个会话有 trace_id,异常可完整还原
- 评测回归集已跑通,关键指标有基线
- 上下文有预算上限与压缩策略
- 单次请求成本与延迟有预算,超限有告警
Demo 展示能力,生产暴露问题。这 7 个坑没有一个属于「模型不够聪明」,全部是工程问题——而这恰恰是好消息:工程问题,是可以用工程方法解决的。