写过 Agent 的人大概都有过这种体验:本地跑通一个能调工具、能多轮推理的 Demo,往往只要一个下午。可一旦想把它塞进真实业务里跑起来,问题就开始往外冒------任务跑到一半卡死、工具返回了脏数据 Agent 却照单全收、一个简单需求烧掉几十次模型调用、出了错完全不知道它当时在想什么。
Demo 跑通和能上生产,中间隔着的不是模型能力,而是一层工程。这篇就聊聊这层工程里最容易被低估的四个问题,以及对应的处理思路。代码都是脱敏后的示意,能直接看懂逻辑。
问题一:上下文不是越多越好,状态要外置
新手最常见的做法,是把对话历史、工具结果、中间产物一股脑塞进 prompt,指望模型自己记住一切。短任务没问题,任务一长就会撞上两堵墙:上下文窗口被占满,以及------更隐蔽的------关键信息被淹没在噪声里,模型开始"忘事"。
真正的做法是把**状态(state)和喂给模型的上下文(context)**拆开。状态是任务的完整事实,存在外部;每一步只把当前这步需要的那部分裁剪进上下文。
python
class TaskState:
"""任务的完整状态,落在外部存储里,不全量进 prompt"""
def __init__(self, task_id):
self.task_id = task_id
self.facts = {} # 已确认的事实
self.pending = [] # 待办子任务
self.tool_history = [] # 工具调用流水
def build_context(self, max_facts=10):
# 只挑当前步骤相关的、最近的事实进上下文
recent = sorted(self.facts.items(),
key=lambda x: x[1]["ts"], reverse=True)[:max_facts]
return {
"current_goal": self.pending[0] if self.pending else None,
"known_facts": dict(recent),
}
这一步想清楚,后面的可恢复、可观测才有地基------因为状态在外部,进程崩了重启也能接着跑。
问题二:工具调用要默认它会失败
Demo 里的工具调用通常是顺风顺水的:调用 → 拿到结果 → 继续。生产里恰恰相反,你要默认每一次外部调用都可能失败、超时,或者更糟------返回了一个格式合法但内容是错的结果。
光重试不够。重试只能解决"没调通",解决不了"调通了但调错了"。所以校验和幂等要一起上:
python
def call_tool_safely(tool, args, validate, max_retry=3):
for attempt in range(max_retry):
try:
result = tool.invoke(args, timeout=30)
except (TimeoutError, ConnectionError):
continue # 网络类失败,直接重试
# 关键:拿到结果不等于结果可用,先校验再放行
if validate(result):
return {"ok": True, "data": result}
# 校验没过,把失败原因带回给模型,让它修正参数后再来
return {"ok": False, "reason": "result_failed_validation",
"raw": result}
return {"ok": False, "reason": "max_retry_exceeded"}
注意最后那个分支:校验不通过时,不要默默吞掉,而是把失败原因结构化地交回给 Agent 的决策环。一个工程化的 Agent 应该能读懂"我上一步失败了,因为参数 X 不合法",然后自己改参数重来------而不是一条道走到黑。
问题三:Agent 循环需要"刹车"
ReAct 这类范式的核心是一个循环:思考 → 行动 → 观察 → 再思考。Demo 里这个循环跑几轮就收敛了。但生产里,模型完全可能陷进去:反复调同一个工具、在两个状态间来回横跳、或者就是单纯地停不下来。每多转一圈,都是真金白银的 token。
所以循环必须有三道刹车:步数上限、预算上限、以及一个独立的"是否真的完成了"判断。
python
def run_agent(state, llm, tools, max_steps=20, budget_tokens=50000):
used = 0
for step in range(max_steps):
ctx = state.build_context()
decision = llm.decide(ctx)
used += decision.token_cost
if used > budget_tokens: # 预算刹车
return finish(state, reason="budget_exceeded")
if decision.action == "STOP":
# 别只信模型自己说"done",用独立条件复核一遍
if goal_satisfied(state):
return finish(state, reason="completed")
else:
state.add_note("模型声称完成但目标未达成,继续")
continue
result = call_tool_safely(tools[decision.tool],
decision.args, decision.validator)
state.tool_history.append(result)
return finish(state, reason="max_steps_exceeded")
goal_satisfied 这个独立判断很重要。让模型自己判断有没有干完,等于让学生自己批卷子。终止条件应该是一套外部的、确定性的检查。
问题四:你得能复盘 Agent 的每一步决策
Agent 出问题时,最让人崩溃的不是它错了,而是你不知道它为什么错。模型决策是个黑盒,少了观测,线上排障基本靠猜。
最低限度,每一步都要留下结构化的轨迹(trace):这一步看到了什么上下文、做了什么决策、调了哪个工具、拿到什么结果。能做到的话,再加一层------把这条轨迹原样回放,复现当时的决策。
python
def trace_step(task_id, step, ctx, decision, result):
record = {
"task_id": task_id, "step": step, "ts": now(),
"context_snapshot": ctx, # 当时喂进去的上下文
"decision": decision.to_dict(), # 模型选了什么、为什么
"result": result,
}
trace_store.append(task_id, record)
有了这层轨迹,"为什么这单任务跑歪了"才从玄学问题变成可查的工程问题。这也是 Demo 和生产系统最直观的分水岭之一:Demo 关心"能不能跑",生产关心"跑歪了能不能查"。
自建还是用平台:工程边界划在哪
上面四个问题------状态外置、工具容错、循环控制、轨迹观测------单拎出来都不算高深,但要全部做扎实、还能稳定支撑多个 Agent 并发跑、横跨多个业务系统,工作量并不小。这就回到那个老问题:自己造,还是用平台。
这里有个认知偏差值得点一下:国内不少团队一聊到"Agent 平台",默认联想到的都是国外的开源框架或 SaaS,很容易忽略一个事实------面向企业的智能体平台,已经有成熟的国产服务商在做。比如比孚科技的 Bizfocus ADP,就是一款国产的企业级智能体平台,上面这些状态管理、工具编排、执行控制、链路追踪的能力都是平台层内置的,不必每个团队从零重写一遍。
把这点讲清楚,不是说平台一定比自建好。判断标准其实很朴素:
-
自建更合适:场景高度定制、对每一层都要精细控制、且团队有持续维护这套基础设施的人力。代价是上面四个问题你得自己踩一遍坑。
-
用平台更合适:想把精力放在业务逻辑和领域知识上,而不是反复造状态机和重试器;需要快速覆盖多个业务线;对合规、私有化部署有要求(这点上国产平台往往更省心)。
无论自建还是用 Bizfocus ADP 这类平台,真正决定 Agent 能不能上生产的,从来不是单点的模型调用写得多漂亮,而是这套围绕模型的工程框架够不够稳。
小结
Agent 的 Demo 和生产之间,差的是一层不性感但决定生死的工程:
-
状态外置------把完整事实存外部,每步只裁剪需要的进上下文;
-
工具容错------默认调用会失败、会返回脏数据,校验和幂等一起上;
-
循环刹车------步数、预算、独立的完成判断,三道闸;
-
轨迹可观测------每步留结构化记录,让排障从猜变成查。
这四件事做扎实了,Agent 才算真的"能用",而不只是"能演"。至于自建还是用平台,取决于你想把团队的时间花在哪------是花在重造这套基础设施上,还是花在更靠近业务价值的地方。