生产级AI Agent落地三要素:可观测性、状态韧性与工具契约
1. 这不是理论课,是300多次上线后撕下来的实战清单
“AI Agents in Production: What Actually Works”——这个标题里最重的词不是“AI”,也不是“Agents”,而是 in Production 。我干这行十一年,前五年写算法、调模型、跑benchmark,后六年天天守着监控看CPU飙升、日志报错、用户投诉说“机器人又卡住了”。所谓300+部署,不是PPT里的数字:是给银行做信贷审批Agent时被风控系统拦在API网关外的凌晨三点;是给连锁药店部署库存调度Agent后,发现它把“板蓝根颗粒”和“板蓝根片”当成两种完全不相关的SKU,导致补货单发错仓库的第七次回滚;是给教育平台上线作文批改Agent,结果它把学生写的“老师像蜡烛一样燃烧自己”判为“逻辑矛盾”——因为训练数据里没喂过比喻句的推理链。
这些不是失败案例,是 生产环境的默认状态 。你读到的所有LLM架构图、ReAct流程图、Tool-Use论文,在真正接入CRM、ERP、支付网关、短信通道、内部权限系统的那一刻,都会被现实按在地上摩擦。本文不讲LangChain怎么链、LlamaIndex怎么索、RAG怎么召回——那些文档里全有。我要拆的是:当Agent要替你接客服电话、审合同条款、填报销单、调度物流车、生成周报PPT时, 哪几块骨头必须提前敲碎、哪几根筋必须重新接、哪几处血肉必须现场缝合 。核心关键词就三个: 可观测性、状态韧性、工具契约 。它们不是技术选型里的加分项,而是Agent能活过第一个工作日的生死线。适合三类人:正在把Demo往生产推的工程师、评估Agent落地风险的技术负责人、以及被老板问“为什么ChatGPT能写诗但我们的Agent连请假单都填不对”的产品经理。别指望看完就能抄作业——这300次部署教会我的第一件事是: 没有标准答案,只有适配你系统毛细血管的解法 。
2. 生产级Agent的底层设计逻辑:从“能跑”到“敢用”的四道断崖
2.1 为什么90%的Agent Demo在生产环境里活不过48小时?
我统计过前50次失败部署,根本原因高度集中: 所有Demo都假设世界是静止的,而生产系统是流动的血液 。举个最典型的例子:一个“自动处理客户退换货”的Agent,本地测试时用Mock API返回{"status": "success", "tracking_id": "SF123456789"},一切丝滑。上线后第一单,快递公司接口返回了新字段{"status": "success", "tracking_id": "SF123456789", "carrier_code": "SF", "estimated_delivery": "2024-06-15T09:00:00Z"}。Agent的JSON Schema校验直接崩掉,因为它的Parser只认两个字段。这不是代码bug,是 契约幻觉 ——以为工具接口永远不变,就像以为Excel表格永远有A列姓名、B列电话、C列邮箱,直到财务部突然加了D列“纳税识别号(非必填)”并把B列电话改成“手机号/固话(二选一)”。
再比如“会议纪要生成Agent”,Demo里用固定Prompt:“请提取发言者、议题、结论、待办事项”。真实场景中,销售总监的语音转文字结果里混着“呃…那个…我们先看下Q3的pipeline”,而法务总监的录音里全是“根据《民法典》第584条…”。Agent若没预设 领域分层解析器 (Sales语境用意图识别模型,Legal语境切法律条文引用模块),就会把“呃…”当待办事项,“第584条”当结论。这暴露了第二个断崖: 语义理解必须绑定业务上下文,而非通用LLM能力 。
第三个断崖更隐蔽: 状态管理的物理边界被忽略 。很多Agent用内存变量存“当前处理到第几个客户”,结果服务重启后,队列里100个待处理工单全变成“从头开始”。或者用Redis存session,但没设TTL,三个月后Redis内存爆满,整个Agent集群雪崩。这背后是认知偏差:把Agent当成无状态函数,而它实际是 有状态的微型服务 ,必须像管理数据库连接池一样管理它的生命周期。
提示:别信“Agent即微服务”的宣传话术。微服务可以独立扩缩容、有明确SLA、有熔断降级;Agent是微服务里最不守规矩的那个实习生——它会随机调用17个不同协议的API、把错误日志写进5个不同路径、在凌晨两点触发3个不同部门的告警。你的架构必须先承认它的“野性”,再用护栏约束它。
2.2 四道必须跨过的断崖:可观测性、状态韧性、工具契约、人机协同
2.2.1 断崖一:可观测性——不是加个Prometheus就行
生产环境里,你不能只问“Agent跑没跑”,而要问“它在哪个环节喘气、哪口气没喘上来、为什么呛着了”。我们给Agent装了三层观测网:
-
第一层:结构化日志埋点 。不是print(f"Calling {tool_name}"),而是统一Log Schema:
{ "event_type": "tool_call_start", "agent_id": "refund_v2", "step_id": "verify_inventory_001", "tool_name": "warehouse_api_check_stock", "input_hash": "a1b2c3d4", "timestamp": "2024-06-10T08:23:45.123Z" }关键在
input_hash——对工具输入做SHA256哈希,同一输入必然产生同hash。当某次调用超时,你能在日志里搜input_hash=a1b2c3d4,立刻定位到所有相关事件(开始、结束、重试、失败),而不是在百万行日志里猜“是不是刚才那个单子”。 -
第二层:决策链路快照 。每次LLM生成Action,我们强制保存完整推理过程:
[THOUGHT] 用户要退2024年5月买的耳机,需先查库存是否充足,再查订单是否超7天。 [OBSERVATION] 库存API返回:{"sku": "EAR-PRO-2024", "available": 12, "reserved": 3} [ACTION] call_tool("order_api_get_detail", {"order_id": "ORD-7890"})这些快照存入专用时序数据库(我们用TimescaleDB),支持按
agent_id + timestamp范围查询。当用户投诉“为什么说我订单已超期”,你直接查快照,看到Agent读到的订单创建时间是2024-05-01,而实际系统里是2024-04-30(时区转换bug),问题秒定位。 -
第三层:业务指标熔断 。可观测性最终要驱动行动。我们定义了Agent专属SLO:
tool_call_success_rate_5m > 99.5%(5分钟内工具调用成功率)avg_thinking_time_ms < 1200(平均思考耗时,超2秒触发降级)human_handoff_rate_1h < 5%(每小时人工接管率)
这些指标不走Prometheus,而是直连告警系统。当human_handoff_rate_1h连续3次超阈值,自动触发:① 切换至简化版Prompt(去掉复杂推理链)② 向运营群发消息“退款Agent疑似遇到新退货政策,请核查”③ 将后续100个请求路由至人工坐席。
2.2.2 断崖二:状态韧性——让Agent像蒲公英一样活着
状态韧性不是“高可用”,而是“可复活”。我们见过太多Agent因状态丢失导致业务中断:
-
场景 :物流调度Agent为100辆车规划路线,计算到第73辆时服务器宕机。重启后,它不记得前72辆已分配,重新开始计算,导致同一辆车被派两次单。
-
解法 : 状态分层持久化 。
- 瞬时状态 (<1秒):存在内存,如当前token流、临时变量。
- 事务状态 (<5分钟):存在Redis,带TTL=300s,Key格式
agent:dispatch_v3:session:{session_id},内容为JSON:{ "completed_trucks": ["TRK-001", "TRK-002"], "current_truck_index": 73, "route_plan_draft": "[...]" } - 业务状态 (长期):存在PostgreSQL,表
agent_dispatch_sessions,字段含session_id,status(running/completed/failed),last_updated_at,business_context(JSONB存订单ID、司机ID等)。
Agent启动时,优先查PostgreSQL找status='running'的session,恢复后继续;若Redis里有更细粒度进度,则合并使用。
-
更狠的招 : 状态快照+差异回放 。每处理10个订单,Agent自动生成快照:
SNAPSHOT_20240610_082345: - processed_orders: [ORD-001, ORD-002, ..., ORD-010] - inventory_state_hash: f3a7b2c1... - tool_call_history_hash: d4e5f6a7...快照存S3,同时记录“从SNAPSHOT_N到SNAPSHOT_N+1”的增量操作日志(如“ORD-011调用warehouse_api减库存2件”)。宕机后,Agent加载最新快照,再重放增量日志——比全量恢复快17倍,且避免状态漂移。
2.2.3 断崖三:工具契约——把API当祖宗供着
生产环境里,工具(Tool)不是LLM的玩具,是它的命脉。我们强制所有Tool实现三重契约:
-
输入契约 :每个Tool必须提供
validate_input()方法,用Pydantic V2定义严格Schema。例如退款Tool:class RefundInput(BaseModel): order_id: str = Field(..., pattern=r"^ORD-\d{4}$") # 强制ORD-开头+4位数字 amount: float = Field(..., ge=0.01, le=10000.0) # 金额0.01~10000 reason: Literal["quality_issue", "wrong_item", "no_longer_needed"] # 枚举值Agent调用前先校验,不合法直接抛
InputValidationError,不进网络层。 -
输出契约 :Tool返回必须是
ToolResult对象,含status(success/error)、data(业务数据)、raw_response(原始HTTP响应)。Agent绝不直接解析response.json(),而是调用result.parse_data(),该方法内置:- 字段缺失兜底(
data.get("tracking_id", "N/A")) - 类型强转(
int(data.get("quantity")) or 0) - 新旧字段兼容(若API新增
carrier_code,旧版Agent仍能取data["tracking_id"])
- 字段缺失兜底(
-
行为契约 :Tool必须声明
idempotency_key(幂等键)和timeout_seconds。例如支付回调Tool,idempotency_key="payment_callback_{order_id}_{timestamp}",确保同一笔支付重复通知只处理一次;timeout_seconds=8,超时立即熔断,不拖垮Agent。
注意:我们禁用所有“动态Tool注册”。每个Tool必须在Agent启动时静态加载、校验契约、写入元数据表。曾有团队用
eval()动态加载Tool,结果线上被注入恶意代码——生产环境里, 确定性比灵活性重要一万倍 。
2.2.4 断崖四:人机协同——Agent不是替代人,是给人递扳手
最成功的Agent部署,都把“人工介入点”设计成呼吸孔。我们定义三种协同模式:
-
预设拦截点 (Pre-defined Handoff):在关键决策前主动暂停。例如信贷审批Agent,当
risk_score > 0.85且income_verification_status == "pending"时,不自动拒贷,而是生成:{ "handoff_reason": "high_risk_pending_income_verify", "suggested_action": "人工核查流水截图", "required_fields": ["bank_statement_url", "id_card_front_url"] }运营后台收到后,自动弹窗提醒坐席,并预填所需材料字段。
-
异常学习闭环 (Exception Learning Loop):当Agent因未知错误失败(如新出现的错误码
ERR-4091),它不重试,而是:① 记录完整上下文到exception_queue② 返回用户:“正在紧急处理,请稍候” ③ 触发异步任务:将错误样本送入标注平台,30分钟内由资深员工标注正确动作 ④ 模型微调Pipeline自动拉起,2小时内更新Agent的错误处理策略。 -
渐进式接管 (Gradual Takeover):新Agent上线不一刀切换。我们用“影子模式”(Shadow Mode):Agent同步运行但不执行动作,只输出预测结果。对比真实人工操作,计算
action_match_rate。当连续7天>95%,才开启“半接管”(Agent执行,人工复核);>99%后全接管。期间所有不匹配case自动归档,成为下一轮训练数据。
3. 核心实操环节:从零搭建一个抗压Agent的七步法
3.1 第一步:定义你的“最小可行痛苦点”(MVPP)
别一上来就想做“全能Agent”。先问: 哪个业务环节的重复劳动最让你半夜惊醒? 我们帮某电商做的首个Agent,目标不是“自动处理所有售后”,而是解决“每天2000+张‘发错货’工单里,83%要人工查物流轨迹再截图回复”。这就是MVPP: 高频、规则明确、结果可验证、失败影响可控 。
- 高频:日均2000单,人力成本≈3人天/天
- 规则明确:只要物流单号+收件人手机号,就能调用快递API查最新节点
- 结果可验证:API返回“签收”或“派件中”,与用户描述“没收到”冲突即需人工介入
- 失败可控:查错10单,损失是10张截图,不是10单退款
MVPP帮你聚焦:只开发 track_package 一个Tool,只训练LLM识别“发错货”意图,只设计“查轨迹→比对时间→生成回复”三步链路。上线3天,人工处理量从2000降到320,ROI立现。
3.2 第二步:构建工具层——用“契约工厂”代替手动编码
我们不用LangChain的 Tool 类,而是自研 ContractedTool 基类,所有生产Tool必须继承它:
class ContractedTool(ABC):
@abstractmethod
def validate_input(self, input_dict: dict) -> ValidationResult:
pass
@abstractmethod
def execute(self, validated_input: BaseModel) -> ToolResult:
pass
@property
@abstractmethod
def idempotency_key(self) -> str:
pass
@property
@abstractmethod
def timeout_seconds(self) -> int:
pass
# 实际退款Tool
class RefundTool(ContractedTool):
def validate_input(self, input_dict: dict) -> ValidationResult:
try:
return ValidationResult(valid=True, data=RefundInput(**input_dict))
except ValidationError as e:
return ValidationResult(valid=False, error=str(e))
def execute(self, validated_input: RefundInput) -> ToolResult:
# 调用真实退款API,带重试、熔断、日志
response = self._call_payment_api(validated_input)
return ToolResult(
status="success" if response.status_code == 200 else "error",
data=response.json(),
raw_response=response.text
)
@property
def idempotency_key(self) -> str:
return f"refund_{validated_input.order_id}_{validated_input.amount}"
@property
def timeout_seconds(self) -> int:
return 12
为什么不用现成框架? LangChain的Tool缺乏输入强校验、无幂等键、超时控制松散。我们300次部署里,27%的故障源于Tool层失控——而这套契约工厂,让Tool故障率下降到0.3%。
3.3 第三步:设计可观测性骨架——日志、快照、指标三位一体
可观测性不是事后分析,是事前防御。我们Agent启动时自动初始化:
- 日志管道 :用
structlog封装,所有日志打上agent_id,session_id,step_id,input_hash。 - 快照引擎 :每完成一个业务单元(如处理完一张工单),调用
snapshot_engine.save(session_id, step_data),存入TimescaleDB。 - 指标收集器 :用
aioprometheus暴露agent_thinking_time_seconds(直方图)、tool_call_total(计数器)、human_handoff_total(计数器)。
关键技巧: 给每个日志事件加“因果链ID” 。例如:
- 用户消息ID:
msg_abc123 - Agent生成的第一个Thought ID:
tht_def456(父ID=msg_abc123) - 调用库存Tool的ID:
tool_inv789(父ID=tht_def456) - Tool返回的Observation ID:
obs_xyz012(父ID=tool_inv789)
这样在Kibana里,输入msg_abc123,就能展开整条链路,看到“哪里卡住、哪里出错、哪里慢”。
3.4 第四步:状态管理——用“三明治存储”应对各种崩溃
状态存储不是选Redis还是PostgreSQL,而是 分层用 :
| 层级 | 存储介质 | 生命周期 | 典型数据 | 恢复策略 |
|---|---|---|---|---|
| 瞬时层 | 内存 | <1秒 | 当前token流、临时变量 | 崩溃即丢,无恢复 |
| 事务层 | Redis | ≤5分钟 | session进度、未完成步骤 | 启动时查Redis,有则恢复 |
| 业务层 | PostgreSQL | 长期 | session元数据、最终结果、审计日志 | 启动时查PG, status=running 则重建Redis状态 |
实操细节 :
- Redis Key命名:
agent:{agent_name}:session:{session_id},TTL=300s - PostgreSQL表
agent_sessions:CREATE TABLE agent_sessions ( id SERIAL PRIMARY KEY, session_id VARCHAR(64) NOT NULL, agent_name VARCHAR(64) NOT NULL, status VARCHAR(20) CHECK (status IN ('running','completed','failed')), created_at TIMESTAMPTZ DEFAULT NOW(), last_updated_at TIMESTAMPTZ DEFAULT NOW(), business_context JSONB, -- 存订单ID、用户ID等 final_result JSONB ); - 恢复逻辑伪代码:
def restore_state(session_id): # 1. 查PG找running session pg_session = db.query("SELECT * FROM agent_sessions WHERE session_id=%s AND status='running'", session_id) if not pg_session: return None # 2. 查Redis获取详细进度 redis_state = redis.get(f"agent:refund_v3:session:{session_id}") if redis_state: return merge_pg_and_redis(pg_session, redis_state) else: # Redis过期,仅用PG基础信息恢复 return init_from_pg(pg_session)
3.5 第五步:人机协同接口——让运营人员看得懂、管得住
Agent再智能,也得有人兜底。我们给运营后台做了三件事:
-
实时决策看板 :显示当前所有运行中的Agent session,每行含:
Session ID(可点击查看详情)Status(running/paused/failed)Last Action(如“已调用物流API,等待响应”)Human Handoff Reason(如“物流API超时,需人工查单”)Actions(按钮:Resume / Force Complete / Re-run with Debug)
-
一键接管工具 :当坐席点击“Force Complete”,后台:
① 终止Agent进程
② 读取当前session的business_context(如订单ID)
③ 自动跳转到CRM系统对应订单页
④ 预填备注:“Agent在[步骤X]失败,建议操作:[具体指引]” -
异常标注平台 :对失败case,坐席只需:
- 选择错误类型(API超时/字段缺失/逻辑错误)
- 填写正确动作(应调用XX工具/应返回XX文案)
- 提交后,该样本自动进入训练队列,2小时内更新Agent。
实操心得:我们曾给某银行做反欺诈Agent,初期坐席抱怨“看不懂Agent在想什么”。后来我们在看板里加了一栏“Agent Confidence Score”(基于LLM输出logprobs计算),当分数<0.6时自动标红,并提示“低置信度,建议人工复核”。坐席反馈:“现在知道什么时候该信它,什么时候该骂它”。
3.6 第六步:灰度发布——用“影子-半控-全控”三阶段保命
任何Agent上线,必须走三阶段:
-
影子模式(Shadow Mode) :Agent同步运行,但不执行任何动作,只输出预测结果。对比真实人工操作,计算:
action_match_rate(动作类型匹配率)field_accuracy(关键字段准确率,如退款金额、物流单号)time_to_decision(决策耗时,对比人工平均耗时)
准入门槛 :连续3天action_match_rate > 90%且field_accuracy > 95%。
-
半控模式(Assisted Mode) :Agent执行动作,但:
- 所有执行前,向坐席弹窗:“Agent将执行[动作],确认?”
- 坐席可点击“Override”手动修改参数(如把退款金额从199改为299)
- 每次Override自动记录为训练样本
准入门槛 :半控模式下,override_rate < 5%且user_satisfaction_score > 4.2/5(通过短信问卷收集)。
-
全控模式(Full Mode) :Agent自主执行,仅当触发SLO熔断(如
human_handoff_rate_1h > 8%)时,自动降级回半控。
关键参数 :我们规定影子模式至少跑7天,半控模式至少14天。曾有团队为赶工期跳过影子模式,结果上线首日,Agent把“用户要求补发”误判为“用户要求退款”,批量触发退款——而影子模式本可提前发现这个意图识别漏洞。
3.7 第七步:故障排查手册——300次踩坑总结的TOP5问题速查
| 问题现象 | 根本原因 | 排查步骤 | 解决方案 | 预防措施 |
|---|---|---|---|---|
| Agent频繁超时,CPU持续100% | LLM推理卡在长文本生成,未设max_tokens | ① 查 agent_thinking_time_seconds 直方图,看99分位是否突增 ② 查日志,找 [THOUGHT] 后无 [ACTION] 的记录 ③ 抽样失败请求,看输入文本长度 |
在LLM调用时强制 max_tokens=512 ,超长文本先摘要再处理 |
所有输入文本加长度检查,>2000字符自动触发摘要Tool |
| 工具调用成功率骤降至30% | 对方API变更(如新增鉴权Header),但Tool未更新 | ① 查 tool_call_total{status="error"} 指标 ② 查错误日志,找 HTTP 401 或 403 ③ 比对最近一次API文档更新时间 |
临时在Tool中硬编码新Header,2小时内发版 | 建立API契约监控:每日抓取对方OpenAPI Spec,比对变更自动告警 |
| Agent状态丢失,同一任务重复执行 | Redis实例故障,TTL未生效 | ① 查 redis_memory_used_bytes 是否突增 ② 查 redis_expired_keys_total 是否归零 ③ 查Agent日志,找 session not found in redis |
立即从PostgreSQL恢复session,手动标记已处理任务 | Redis配置 maxmemory-policy allkeys-lru ,加哨兵监控 keyspace_misses |
| 用户投诉“Agent答非所问” | Prompt中示例(few-shot)过时,LLM学偏了 | ① 抽样投诉case,看 [THOUGHT] 是否合理 ② 查 prompt_version 字段,确认是否用旧Prompt ③ 用新Prompt重跑历史case,对比结果 |
紧急回滚Prompt版本,24小时内用新数据微调 | Prompt版本化管理,每次更新需A/B测试, accuracy_delta > 0.5% 才发布 |
| 人工接管率飙升至15% | 新业务规则上线(如“618大促期间免运费”),Agent未学习 | ① 查 human_handoff_total{reason="new_policy_unknown"} ② 查运营知识库,确认新规则发布时间 ③ 查Agent训练数据截止时间 |
紧急注入新规则到RAG知识库,重启Agent | 建立“业务规则变更”订阅机制,规则上线自动触发Agent知识更新Pipeline |
4. 真实部署案例复盘:从0到支撑日均50万单的售后Agent
4.1 项目背景:某头部电商平台的售后困局
这家平台日均售后单超50万,其中:
- 62%为“发错货/少发货”(需查物流+联系仓库)
- 23%为“商品质量问题”(需审核图片+判定责任)
- 15%为“用户主观原因”(如“不喜欢”“尺寸不合适”)
痛点:
- 人工坐席平均处理时长8.2分钟/单,峰值时段排队超2000人
- 35%的“发错货”单,坐席要手动查3个系统(订单、物流、仓库)才能回复
- 新员工培训周期长达6周,离职率32%
目标:6个月内,将“发错货”单的自动化率提升至85%,平均处理时长压至90秒内。
4.2 架构设计:不炫技,只求稳
我们放弃所有花哨架构,采用极简设计:
- Agent Core :Python + FastAPI,单体服务(拒绝微服务化,减少网络开销)
- LLM :Qwen2-7B-Instruct(国产,私有化部署,推理延迟<1.2s)
- 工具层 :
logistics_api:查快递轨迹(对接中通、顺丰、京东)warehouse_api:查库存状态(对接自研WMS)image_analyzer:审核用户上传图片(YOLOv8+CLIP微调)
- 状态存储 :Redis(事务层)+ PostgreSQL(业务层)
- 可观测性 :TimescaleDB(快照)+ Prometheus(指标)+ Loki(日志)
关键取舍 :
- 不用RAG:售后规则全部固化在Prompt中(共127条规则,版本化管理),因RAG引入延迟且不可控。
- 不用Memory:所有上下文通过
session_id关联,避免LLM记忆污染。 - 不用Orchestration框架:所有流程用Python
if/elif/else硬编码,因售后链路固定(查单→比对→生成回复→记录结果)。
4.3 实施过程:七步法落地细节
- MVPP定义 :只做“发错货”单,目标:自动回复物流信息,无需人工查单。
- 工具契约 :
logistics_apiTool强制校验:- 输入:
tracking_number必须符合各快递正则(中通^7[0-9]{12}$,顺丰^SF[0-9]{10}$) - 输出:必须含
latest_status(枚举值)、delivery_time(ISO格式)
- 输入:
- 可观测性 :在Loki中建Dashboard,实时看:
logistics_api_call_total{status="success"}vs{status="error"}agent_thinking_time_seconds_bucket{le="1.0"}(90%请求<1秒)
- 状态管理 :每单生成唯一
session_id=ord_{order_id}_{timestamp},Redis存{"step":"query_logistics", "attempts":2},PG存{"order_id":"ORD-123", "status":"processing"}。 - 人机协同 :当
logistics_api返回status="unknown",自动弹窗:“物流单号无效,请人工核实”,并预填订单页链接。 - 灰度发布 :
- 影子模式7天:
action_match_rate=94.2%,field_accuracy=98.7% - 半控模式14天:
override_rate=3.1%,主要因新快递公司“极兔”单号格式未覆盖 - 全控模式上线首日:处理12.7万单,人工接管率4.8%,平均耗时87秒
- 影子模式7天:
4.4 效果与教训
成果 :
- 6个月后,“发错货”单自动化率达89.3%(超目标)
- 人工坐席日均处理量从200单升至350单(因省去查单时间)
- 新员工培训周期缩短至2周(只需学异常处理)
- 客服满意度(CSAT)从76%升至89%
血泪教训 :
- 教训1:别信“标准单号格式” 。上线第3天,发现德邦快递新增“DB”开头单号,但正则只写了
^DB[0-9]{10}$,实际有^DB[0-9]{12}$。我们连夜更新正则,但更狠的是:在Tool里加了fallback_parser,当标准解析失败,用模糊匹配(提取连续10-12位数字)再试一次。 - 教训2:图片审核不能只靠AI 。早期
image_analyzer把“包装破损”误判为“商品完好”,因训练数据全是高清图,而用户拍的多是昏暗环境。解决方案:加预处理Pipeline——先用OpenCV增强对比度,再送模型;同时,当置信度<0.7,强制人工审核。 - 教训3:监控要盯“人”的指标 。我们最初只看
tool_call_success_rate,直到发现坐席在高峰期故意点“Override”来抢工单(因绩效按处理量算)。后来加了override_reason字段,当reason="performance_incentive"占比超10%,自动触发HR介入。
5. 常见问题与避坑指南:来自300次部署的硬核回答
5.1 “我们该用LangChain还是LlamaIndex?”——这是个伪命题
问这个问题的人,通常还没想清楚: 你要解决什么问题,而不是用什么工具 。
- LangChain适合快速搭Demo:它把Tool、Memory、Chain封装好,你5分钟能跑通“查天气+写邮件”。但生产环境里,它的抽象层成了黑盒——当
tool_call失败,你得扒源码看它怎么重试、怎么熔断、怎么传Header。 - LlamaIndex强在RAG,但售后场景里,90%的规则是确定性的(如“发错货=查物流+比对收件人”),不需要检索。强行上RAG,反而增加延迟和不确定性。
我们的做法 :
- 工具层:自研
ContractedTool,不依赖任何框架 - 编排层:用Python原生
if/elif/else,逻辑清晰可debug - RAG层:仅当需要动态知识(如“最新售后政策”)时,用轻量级
ChromaDB+sentence-transformers,不碰LlamaIndex
实操心得:某团队用LangChain搭Agent,上线后发现
ConversationTokenBufferMemory在长对话中内存泄漏。他们花了3天读源码,最后发现是max_token_limit没设对。而我们的原生实现,内存占用恒定在12MB以内——因为根本没用Memory,所有状态存Redis。
5.2 “LLM选开源还是闭源?Qwen、GLM、Claude哪个好?”
更多推荐

所有评论(0)