不要倒着建 AI Agent:从 Demo 到生产系统的组件化教程

这篇教程解决什么问题

很多 AI Agent 原型看起来能跑,但一到生产就会出现一种危险状态:系统没有报错,输出也像是合理的,但实际已经偏离目标。原文把这种问题叫做 built backwards:先想“我要一个 Agent 做 X”,再加工具、写 prompt,然后相信模型推理会把上下文、状态、重试、工具失败和验证全部串起来。

这篇教程把原文观点改写成一个可落地的工程流程:先定义组件和责任,再组合出 Agent 行为。

适合读者:

不适合:

核心原则:模型只负责决策,不负责架构

原文最值得保留的一句话是:生产级 AI Agent 不是一个智能实体,而是一套系统。

把这句话落到工程上,就是:

如果一个 Agent 的答案是“这些都交给模型自己想”,它就是倒着建的。

最小可行架构

下面是一个比“LLM + prompt + tools”更适合生产演进的最小架构:

用户请求
  ↓
任务入口:校验输入、定义目标、生成 request_id
  ↓
上下文准备器:读取必要资料,裁剪历史,形成 context_packet
  ↓
决策层 LLM:只输出结构化 decision
  ↓
编排器:根据 decision 调工具、重试、超时、路由
  ↓
工具执行层:窄工具、明确输入输出、无隐藏副作用
  ↓
状态存储:记录当前事实、版本、过期时间、读写人
  ↓
验证与可观测:trace、指标、人工确认、回滚依据

这里的关键不是组件数量,而是每个责任都有归属。哪怕全部写在一个 Python 文件里,也比把所有职责塞进模型推理更容易调试。

反模式:倒着建 Agent 的 5 个信号

你可以先用这 5 个问题审查现有系统:

  1. 上下文是谁准备的?如果答案是“模型自己从历史里找”,风险高。
  2. 重试是谁控制的?如果答案是“框架内部自动重试”,但你看不到状态变化,风险高。
  3. 工具是否只做一件事?如果一个工具既查 API、又更新缓存、又写数据库,风险高。
  4. 状态是否有版本和过期规则?如果模型使用 20 分钟前的偏好还没人知道,风险高。
  5. 可观测性是否能判断“做得对不对”?如果只有日志,没有 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、是否记录审计日志。

验收标准

失败处理

案例 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"
}

验收标准

失败处理

案例 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

验收标准

失败处理

可复制的离线最小示例:让编排器负责重试和 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"
    }
  ]
}

这个例子的关键点:

一周落地路线

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 推到生产或内部稳定流程前,至少检查:

常见失败与处理

失败 1:模型输出看似合理,但事实错了

处理:检查 context packet,不要先调 prompt。多数情况下是输入上下文错误、过期或缺失。

失败 2:工具调用偶尔污染状态

处理:把工具拆成 read / validate / write 三类;写操作必须单独审计。

失败 3:框架自动重试导致重复副作用

处理:把重试挪到自己可见的编排层;对写操作使用幂等键。

失败 4:多 Agent 共享旧状态

处理:不要共享完整聊天历史;共享结构化状态卡,并给状态加版本。

失败 5:只有日志,没有判断正确性的证据

处理:补 trace 和 validation result。日志回答“发生了什么”,验证回答“发生得对不对”。

结论

不要把 Agent 当成一个会自己协调一切的智能体。生产级 Agent 更像一个普通分布式系统:模型只是其中一个组件。真正可靠的系统,是先把上下文、状态、工具、编排、验证和可观测性分清楚,再让模型在这个边界内做决策。

一句话记住:组件先行,行为随后;模型决策,不管架构。