Agentic AI 的工程挑战——不只是 Demo 好看,生产里问题多
Agentic AI 的工程挑战——不只是 Demo 好看,生产里问题多
适读人群:AI 应用开发者、技术负责人 | 阅读时长:约 12 分钟 | 核心价值:Agent 落地的真实坑位,避免踩同样的雷
去年三月,我接了一个"AI 自动化客服 Agent"的项目。甲方的需求很诱人:用户在小程序里问问题,Agent 自动判断意图,调工具查库存、查订单、改地址,实在搞不定再转人工。
Demo 我做了两天,效果惊艳。产品经理看完直接拍桌子:就是这个!
然后我花了接下来三个月,把这个 Demo 改成了一个勉强能在生产上跑的东西。那三个月是我做 AI 开发以来最难熬的时间段,没有之一。
这篇文章我想认真说说 Agent 在生产里到底难在哪,以及我的判断:什么场景真的值得上 Agent,什么场景你上了只会给自己挖坑。
Agent 为什么 Demo 好看,生产挂掉
Demo 里的环境是被精心控制的。我给 LLM 喂的 10 个测试问题都是我自己设计的,工具调用链最长三步,出错了我直接跳过继续演示。
生产里没有这种好事。
不稳定性:LLM 的输出本质上是概率分布
我那个客服 Agent 的核心问题,是 LLM 每次决策都是一次采样。同一个用户问题,今天输出 JSON {"action": "query_order", "order_id": "12345"},明天可能给你来一句"好的,我来帮您查询订单,订单号是 12345"。
工具调用依赖结构化输出,但 LLM 给出结构化输出的稳定性远低于你想象。
我当时的数据:用 GPT-4o,在严格的 system prompt 约束下,工具调用格式出错率大概在 3-5%。听起来不高?一个用户操作流程涉及 5 次工具调用,出错率叠加接近 20%。每 5 个用户就有一个卡住。
解法不是没有——function calling、structured output、重试机制——但每一层都在加复杂度。
import anthropic
from tenacity import retry, stop_after_attempt, wait_exponential
import json
client = anthropic.Anthropic()
tools = [
{
"name": "query_order",
"description": "查询订单状态",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "订单号"}
},
"required": ["order_id"]
}
}
]
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=4))
def call_agent_with_retry(user_message: str) -> dict:
"""带重试的 Agent 调用,但注意:重试本身会带来额外成本"""
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": user_message}]
)
# 检查是否触发工具调用
for block in response.content:
if block.type == "tool_use":
return {"tool": block.name, "input": block.input}
# 没有工具调用,返回文本
return {"text": response.content[0].text}
# 问题:重试 3 次意味着最坏情况下花了 3 倍 token
result = call_agent_with_retry("帮我查一下订单 12345 的状态")成本失控:Token 消耗是指数级的
这是我踩得最狠的坑。
单轮对话的 token 成本,和多步 Agent 的 token 成本,根本不是同一个量级。
以我那个客服 Agent 为例:
单次用户请求的 token 消耗估算:
- System prompt(工具定义 + 约束规则):约 800 tokens
- 对话历史(传入上下文):约 500 tokens
- 第1步工具调用 + 结果:约 300 tokens
- 第2步工具调用 + 结果:约 300 tokens
- 最终回复生成:约 200 tokens
合计:约 2100 tokens/请求
对比简单 RAG 方案:约 600 tokens/请求
成本比:3.5x上线第一个月,API 账单是预期的 4 倍。甲方第一反应是"你是不是写了 Bug 在循环调用"。
不是 Bug,是 Agent 的本质。你要让 LLM 做多步决策,就必须在每一步都把上下文全部传入。上下文越来越长,成本越来越高。
更糟糕的是失控场景:Agent 陷入循环,反复调用同一个工具却得不到满意的结果。没有 token 上限保护的话,一个请求能烧掉几万 token。
class AgentWithCostControl:
def __init__(self, max_steps: int = 5, max_tokens_per_session: int = 10000):
self.max_steps = max_steps
self.max_tokens_per_session = max_tokens_per_session
self.total_tokens_used = 0
def run(self, user_message: str) -> str:
messages = [{"role": "user", "content": user_message}]
steps = 0
while steps < self.max_steps:
if self.total_tokens_used > self.max_tokens_per_session:
return "抱歉,本次对话超出处理限制,请联系人工客服。"
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=messages
)
# 记录 token 消耗
self.total_tokens_used += response.usage.input_tokens + response.usage.output_tokens
# 检查是否需要继续
if response.stop_reason == "end_turn":
return response.content[0].text
# 处理工具调用(省略具体实现)
steps += 1
return "处理步骤超限,请简化您的问题。"调试困难:你不知道哪一步出的问题
传统后端出了 Bug,看日志、加断点,链路清晰。
Agent 出了 Bug,你面对的是:
- 用户说"我查了半天没查到订单"
- LLM 的决策过程不透明
- 工具有没有被正确调用?调用参数对不对?返回结果被正确解析了吗?
- 还是 LLM 根本没调工具,直接编造了一个答案?
我后来专门做了一套 trace 中间件,把每一步的 input/output 都记下来,才勉强让调试变得可行:
import time
from dataclasses import dataclass, field
from typing import Any
@dataclass
class AgentStep:
step_id: int
action: str # "llm_call" | "tool_call" | "tool_result"
input_data: Any
output_data: Any
tokens_used: int
latency_ms: float
timestamp: float = field(default_factory=time.time)
class TracedAgent:
def __init__(self):
self.trace: list[AgentStep] = []
self.step_counter = 0
def log_step(self, action: str, input_data: Any, output_data: Any,
tokens: int = 0, latency: float = 0):
self.trace.append(AgentStep(
step_id=self.step_counter,
action=action,
input_data=input_data,
output_data=output_data,
tokens_used=tokens,
latency_ms=latency
))
self.step_counter += 1
def dump_trace(self) -> str:
"""出问题时 dump 完整链路"""
lines = []
for step in self.trace:
lines.append(f"[Step {step.step_id}] {step.action}")
lines.append(f" Input: {str(step.input_data)[:200]}")
lines.append(f" Output: {str(step.output_data)[:200]}")
lines.append(f" Tokens: {step.tokens_used}, Latency: {step.latency_ms:.0f}ms")
return "\n".join(lines)即便有了 trace,调试 Agent 的时间成本仍然是普通后端的 3-5 倍。因为很多 Bug 只在特定的用户输入下才复现,而 LLM 的行为不可完全复现。
用户体验差:等待时间无法接受
用户发一条消息,等待 5-8 秒才收到回复,这个体验在大多数场景下都是不可接受的。
但 Agent 的多步推理就是要时间。每一步工具调用都有网络延迟,LLM 推理本身也要时间。
我测过:
- 单步 LLM 回复:1.5-3 秒
- 3 步工具调用的 Agent:6-12 秒
- 5 步以上的复杂 Agent:15-30 秒
用户在等什么?他不知道。他只看到一个转圈的动画。
流式输出能改善感知延迟,但工具调用阶段没有内容输出,这段时间仍然是黑盒。
什么场景真的适合 Agent
说了这么多问题,Agent 就是个花架子吗?不是。但它适合的场景比大多数人想象的要窄。
适合 Agent 的场景特征:
1. 任务完成时间不敏感
内部自动化流程:数据清洗、报告生成、邮件处理。用户不在线等待,跑完发通知就行。哪怕花 5 分钟,也没关系。
2. 任务价值足以覆盖成本
一次 Agent 调用消耗 $0.05 的 API 费用,对应的是原来需要人工花 30 分钟处理的任务——这个经济账是成立的。
但如果只是替代一个 SQL 查询,成本完全不合算。
3. 决策树有限且可枚举
不要幻想"通用 Agent"。适合生产的 Agent 往往是"限定领域的 Agent":只能查订单、改地址、申请退款这三件事,其他的一律转人工。
工具数量越少,行为越可预测,出错率越低。
4. 有明确的人工兜底机制
Agent 出错不是"如果"而是"何时"。在 Agent 无法处理或置信度低的情况下,必须有平滑的人工接管路径。
这不是 Agent 的失败,是正确的系统设计。
我的判断:
2025 年,Agent 技术还没到"随手可用"的阶段。如果你的场景可以用 RAG + 简单的工具调用解决,不要强行引入多步 Agent。等模型更稳定、工具链更成熟、成本再降一半,是三年内大概率会发生的事。
现在上 Agent,你是在用工程努力弥补技术成熟度的不足。这不一定是错,但要清醒地知道代价。
