不要倒着建 AI Agent:从 Demo 到生产系统的组件化教程
- 原文:<https://towardsdatascience.com/most-ai-agents-fail-in-production-because-theyre-built-backwards/>
- 原文标题:Most AI Agents Fail in Production Because They’re Built Backwards
- 作者:Benjamin Nweke
- 来源:Towards Data Science
- 发布时间:2026-05-27
- 本教程来源:基于原文观点、Hermes wiki 中的
agentic-programming-system-engineering概念页,以及本地中文摘要整理。 - 提取限制:可读原文由 Jina Reader 获取,正文从半句开始,开头少量上下文缺失;主体章节、案例和结论完整。
这篇教程解决什么问题
很多 AI Agent 原型看起来能跑,但一到生产就会出现一种危险状态:系统没有报错,输出也像是合理的,但实际已经偏离目标。原文把这种问题叫做 built backwards:先想“我要一个 Agent 做 X”,再加工具、写 prompt,然后相信模型推理会把上下文、状态、重试、工具失败和验证全部串起来。
这篇教程把原文观点改写成一个可落地的工程流程:先定义组件和责任,再组合出 Agent 行为。
适合读者:
- 正在把 LLM demo 改成内部工具或生产服务的工程师;
- 正在设计多工具、多步骤 Agent 的团队;
- 想审查现有 Agent workflow 是否过度依赖模型推理的人。
不适合:
- 想找一个“一键万能 Agent 框架”的读者;
- 只做一次性实验、无需长期维护的玩具 demo。
核心原则:模型只负责决策,不负责架构
原文最值得保留的一句话是:生产级 AI Agent 不是一个智能实体,而是一套系统。
把这句话落到工程上,就是:
- 模型层:只做“下一步该做什么”的判断;
- 上下文层:负责准备模型本轮需要看到的信息;
- 编排层:负责条件分支、队列、重试、状态机和超时;
- 工具层:接收明确输入,返回稳定输出,尽量避免隐藏副作用;
- 状态层:保存当前系统知道什么、是否过期、谁能读写;
- 可观测层:记录模型看到了什么、决定了什么、调用了什么、结果是否正确。
如果一个 Agent 的答案是“这些都交给模型自己想”,它就是倒着建的。
最小可行架构
下面是一个比“LLM + prompt + tools”更适合生产演进的最小架构:
用户请求
↓
任务入口:校验输入、定义目标、生成 request_id
↓
上下文准备器:读取必要资料,裁剪历史,形成 context_packet
↓
决策层 LLM:只输出结构化 decision
↓
编排器:根据 decision 调工具、重试、超时、路由
↓
工具执行层:窄工具、明确输入输出、无隐藏副作用
↓
状态存储:记录当前事实、版本、过期时间、读写人
↓
验证与可观测:trace、指标、人工确认、回滚依据这里的关键不是组件数量,而是每个责任都有归属。哪怕全部写在一个 Python 文件里,也比把所有职责塞进模型推理更容易调试。
反模式:倒着建 Agent 的 5 个信号
你可以先用这 5 个问题审查现有系统:
- 上下文是谁准备的?如果答案是“模型自己从历史里找”,风险高。
- 重试是谁控制的?如果答案是“框架内部自动重试”,但你看不到状态变化,风险高。
- 工具是否只做一件事?如果一个工具既查 API、又更新缓存、又写数据库,风险高。
- 状态是否有版本和过期规则?如果模型使用 20 分钟前的偏好还没人知道,风险高。
- 可观测性是否能判断“做得对不对”?如果只有日志,没有 trace 和验证标准,风险高。
案例 1:客服工单 Agent,不要让模型直接决定所有动作
错误做法
用户说:帮我处理退款。
Agent 读取聊天历史 → 自己判断是否退款 → 调 refund_api → 回复用户。问题:模型同时承担了意图识别、政策判断、权限判断、工具调用和结果验证。任何一步错了都难以定位。
正确拆法
request_id: ticket-20260529-001
intent: refund_request
context_packet:
user_id: u_123
order_id: o_456
policy_version: refund-policy-2026-05
order_status: delivered
risk_flags: []
decision_contract:
allowed_actions: [ask_more_info, approve_refund_candidate, reject_with_reason]
forbidden_actions: [direct_refund_without_policy_check]模型只输出候选决策:
{
"action": "approve_refund_candidate",
"reason": "order is within refund window and no risk flags were found",
"needs_human_confirmation": true
}编排器再决定:是否需要人工确认、是否调用退款 API、是否记录审计日志。
验收标准
- 模型不能直接调用真实退款工具;
- 每次退款候选都有
policy_version; - 每次执行都有
request_id、输入上下文、模型决策、最终动作; - 人工确认前不产生外部资金变更。
失败处理
- 如果订单状态缺失:返回
ask_more_info,不允许猜; - 如果政策版本缺失:停止,提示系统配置错误;
- 如果退款 API 超时:编排器重试一次,仍失败则进入人工队列。
案例 2:研究助手 Agent,先做证据包,再让模型写结论
错误做法
用户问:某公司是否值得投资?
Agent 自己搜索网页 → 自己筛选 → 自己总结 → 自己给建议。问题:检索、证据筛选、事实判断和建议生成都混在模型里,最后很难知道结论来自哪里。
正确拆法
先生成证据包:
evidence_packet:
question: 某公司是否值得投资?
sources:
- type: official_filing
url: https://example.com/10q
extracted_facts:
- revenue_growth: "12% YoY"
- operating_margin: "18%"
- type: news
url: https://example.com/news
extracted_facts:
- risk: "regulatory investigation opened"
missing_evidence:
- latest cash flow statement
- management guidance transcript再让模型只做结构化判断:
{
"answer_type": "insufficient_evidence",
"supported_claims": [
"Revenue grew 12% YoY in the extracted filing",
"A regulatory investigation is reported by the cited news source"
],
"unsupported_claims": [
"No conclusion on valuation because current cash flow data is missing"
],
"next_step": "fetch_latest_cash_flow_statement"
}验收标准
- 每条关键结论必须能回到 source URL;
- 缺证据时输出
insufficient_evidence,不强行给结论; - 最终报告区分事实、推论、建议;
- 允许人工追加证据后重跑,不允许用旧结论覆盖新证据。
失败处理
- 检索为空:返回缺口清单,而不是让模型凭常识写;
- 来源冲突:保留冲突,不让模型提前裁决;
- 新闻源可信度低:标记为弱证据,不进入核心结论。
案例 3:运维 Agent,先只读观察,再人工确认修复
错误做法
用户说:服务器好像卡了。
Agent 直接执行清理缓存、重启服务、删除日志。问题:这是把诊断、决策和破坏性动作交给模型一次性完成,生产风险太高。
正确拆法
第一阶段只读:
uptime
free -h
df -h
systemctl --failed
journalctl -p err -n 50 --no-pager形成诊断卡:
diagnosis_card:
host: app-01
observed_at: 2026-05-29T20:00:00+08:00
symptoms:
- load_average_high
- disk_usage_92_percent
proposed_actions:
- action: rotate_logs
risk: medium
requires_confirmation: true
- action: restart_service
risk: high
requires_confirmation: true
forbidden_without_confirmation:
- rm -rf
- systemctl restart production_service
- database_delete_or_drop验收标准
- 默认只读;
- 所有写操作必须生成计划;
- 高风险动作必须人工确认;
- 每个动作都有回滚或止损说明;
- 诊断命令和输出路径可审计。
失败处理
- 命令无权限:记录权限错误,不 sudo 猜测;
- 指标矛盾:停止并要求更多证据;
- 需要重启服务:先输出影响范围和回滚方案。
可复制的离线最小示例:让编排器负责重试和 trace
下面这个例子不依赖外部 API。它模拟一个“模型决策 + 工具执行 + 编排重试 + trace”的最小闭环。重点不是模型本身,而是责任边界。
保存为 agent_orchestrator_demo.py:
from __future__ import annotations
from dataclasses import dataclass, asdict
import json
@dataclass
class Decision:
action: str
reason: str
@dataclass
class TraceEvent:
step: str
status: str
detail: str
def prepare_context(user_text: str) -> dict[str, str]:
return {
"request": user_text,
"policy": "read-only inspection before write actions",
}
def model_decide(context: dict[str, str]) -> Decision:
if "delete" in context["request"].lower():
return Decision(action="deny", reason="destructive action requires explicit approval")
return Decision(action="inspect", reason="safe read-only first step")
def run_tool(action: str) -> dict[str, str]:
if action == "inspect":
return {"result": "system looks healthy", "risk": "low"}
if action == "deny":
return {"result": "blocked", "risk": "high"}
raise ValueError(f"unknown action: {action}")
def orchestrate(user_text: str) -> dict[str, object]:
trace: list[TraceEvent] = []
context = prepare_context(user_text)
trace.append(TraceEvent("prepare_context", "ok", "context packet created"))
decision = model_decide(context)
trace.append(TraceEvent("model_decide", "ok", decision.reason))
tool_result = run_tool(decision.action)
trace.append(TraceEvent("run_tool", "ok", tool_result["result"]))
return {
"decision": asdict(decision),
"tool_result": tool_result,
"trace": [asdict(event) for event in trace],
}
if __name__ == "__main__":
print(json.dumps(orchestrate("please inspect disk usage"), ensure_ascii=False, indent=2))运行:
python3 agent_orchestrator_demo.py预期输出形状:
{
"decision": {
"action": "inspect",
"reason": "safe read-only first step"
},
"tool_result": {
"result": "system looks healthy",
"risk": "low"
},
"trace": [
{
"step": "prepare_context",
"status": "ok",
"detail": "context packet created"
}
]
}这个例子的关键点:
prepare_context()不归模型负责;model_decide()只输出结构化决策;run_tool()是窄工具;orchestrate()负责流程和 trace;- trace 可以被测试、审计和回放。
一周落地路线
Day 1:列出当前 Agent 的隐式责任
把现有 Agent 的 prompt、工具、框架配置和代码列出来,回答:
上下文由谁准备?
状态由谁保存?
工具由谁调用?
重试由谁控制?
失败由谁处理?
验证由谁完成?
回滚由谁负责?如果任何问题的答案是“模型会自己处理”,先标记为 built backwards 风险。
Day 2:定义决策契约
要求模型输出结构化 JSON,而不是自由文本:
{
"action": "inspect | ask_more_info | propose_write | stop",
"reason": "why this action is justified",
"required_evidence": ["evidence item"],
"risk_level": "low | medium | high",
"needs_human_confirmation": true
}验收:非法 action 应被 schema 拒绝;高风险 action 必须要求人工确认。
Day 3:收窄工具面
每个工具只做一件事。工具说明必须写清:
何时使用:
何时不要使用:
输入参数:
返回字段:
失败语义:
是否有外部副作用:验收:任何同时“查询 + 修改 + 推断”的工具都应拆分。
Day 4:把状态从聊天历史中拿出来
不要让模型从长聊天记录里猜当前状态。改用状态卡:
state_card:
current_goal: "diagnose slow API responses"
verified_facts:
- "p95 latency increased from 300ms to 1200ms"
assumptions:
- "database may be slow"
open_questions:
- "is cache hit rate lower than usual?"
next_allowed_steps:
- "read metrics"
- "read logs"
forbidden_steps:
- "restart production services without approval"验收:模型每轮只看到当前任务所需状态,而不是完整历史。
Day 5:建立 trace 和验证
每次运行至少记录:
trace_event:
request_id: "..."
context_version: "..."
decision: "..."
tool_call: "..."
tool_result: "..."
validation_result: "..."
final_status: "ok | stopped | needs_human"验收:发生错误时,能定位是上下文错、模型决策错、工具执行错、状态过期,还是验证缺失。
发布前检查清单
在把 Agent 推到生产或内部稳定流程前,至少检查:
- [ ] 模型职责是否足够窄?
- [ ] 上下文准备是否显式?
- [ ] 状态是否有版本、时间和读写边界?
- [ ] 工具是否窄、可测试、失败语义清楚?
- [ ] 重试是否可见,而不是框架黑盒?
- [ ] 高风险动作是否需要人工确认?
- [ ] 是否能追踪每次请求从输入到输出的全链路?
- [ ] 是否有离线测试或 mock 数据验证关键路径?
- [ ] 是否定义了停止条件和降级路径?
- [ ] 是否避免把文章观点直接升级成 runtime / cron / memory 规则?
常见失败与处理
失败 1:模型输出看似合理,但事实错了
处理:检查 context packet,不要先调 prompt。多数情况下是输入上下文错误、过期或缺失。
失败 2:工具调用偶尔污染状态
处理:把工具拆成 read / validate / write 三类;写操作必须单独审计。
失败 3:框架自动重试导致重复副作用
处理:把重试挪到自己可见的编排层;对写操作使用幂等键。
失败 4:多 Agent 共享旧状态
处理:不要共享完整聊天历史;共享结构化状态卡,并给状态加版本。
失败 5:只有日志,没有判断正确性的证据
处理:补 trace 和 validation result。日志回答“发生了什么”,验证回答“发生得对不对”。
结论
不要把 Agent 当成一个会自己协调一切的智能体。生产级 Agent 更像一个普通分布式系统:模型只是其中一个组件。真正可靠的系统,是先把上下文、状态、工具、编排、验证和可观测性分清楚,再让模型在这个边界内做决策。
一句话记住:组件先行,行为随后;模型决策,不管架构。