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_api Tool强制校验:
    • 输入: 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秒

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哪个好?”

更多推荐