1. 项目概述:为什么“Agent的五大组成部分”不是概念游戏,而是工程落地的生死线
你有没有遇到过这样的情况:花两周时间搭好一个基于LLM的自动客服流程,测试时对答如流,一上线就频繁给出错误答案、重复提问、甚至把用户订单号当成退货理由?或者在开发一个会议纪要自动生成Agent时,它能完美提取发言内容,却总在最后一步——生成行动项列表时漏掉关键责任人?这些不是模型能力不足,而是系统设计缺了一块关键拼图:没有形成从感知到执行再到反思的完整闭环。我带团队做过27个不同行业的Agent项目,从金融风控辅助到工厂设备巡检报告生成,凡是稳定运行超6个月的,无一例外都严格遵循了“五大组成部分”的结构逻辑。这五个部分不是教科书里的理论分层,而是像汽车的发动机、变速箱、刹车、仪表盘和驾驶员反馈系统一样,缺一不可。比如“自我纠正”这个环节,很多人以为就是加个rethink prompt,实测发现,没配好“记忆管理”模块,Agent连自己上一轮错在哪都记不住;没设计好“工具调用协议”,它想纠错却根本调不动数据库校验接口。标题里说的“从LLM到自我纠正的完整闭环”,本质是把大模型从一个单向输出的“答题机器”,升级成一个具备目标感、容错力和进化能力的“数字员工”。它解决的不是“能不能做”,而是“敢不敢交出去用”。适合谁看?如果你正在用LangChain写chain、用Dify配workflow、用CrewAI搭crew,却总觉得效果飘忽、调试费劲、上线后问题不断——这篇就是为你写的。它不讲LLM原理,不比框架优劣,只拆解那五个真正决定成败的工程模块,以及每个模块里我踩过坑、验证过的硬核细节。
2. 核心架构拆解:五大组件不是并列关系,而是有严格依赖顺序的流水线
2.1 组件定位与依赖关系:为什么顺序错了,整个Agent就会“失智”
很多初学者把五大组件理解成五个可插拔的模块,像搭积木一样随便组合。这是最大的认知陷阱。实际上,它们构成一条强依赖的单向数据流水线,前一个组件的输出,是后一个组件的唯一输入源。打个比方:这就像一条精密的制药生产线——原料预处理(感知)→ 配方混合(规划)→ 压片封装(执行)→ 质检包装(验证)→ 批次复盘(自我纠正)。如果质检环节(验证)的数据没回传给复盘系统(自我纠正),下一批药片照样会带着同样缺陷出厂。我们来逐层拆解这个依赖链:
感知(Perception)是起点,但绝非简单输入:它不只是接收用户一句话或一段PDF文本。真正的感知模块必须完成三件事:上下文锚定(识别当前对话属于“售后投诉”还是“新功能咨询”场景)、意图粗筛(过滤掉“今天天气怎么样”这类无关闲聊)、信息可信度标注(对用户上传的Excel文件,标记“原始数据未清洗”“日期格式混乱”等风险标签)。我见过太多项目在这里翻车:一个电商Agent直接把用户截图里的手写价格当真,因为感知层没做OCR置信度校验。
规划(Planning)是大脑,但必须受约束:它接收感知层标注后的结构化输入,生成执行步骤。关键点在于“约束”——不是让LLM天马行空想方案,而是用工具描述模板(Tool Description Schema)和执行路径白名单(Execution Path Whitelist)把它框住。比如在财务报销Agent中,规划层永远不能生成“直接转账给申请人”这一步,必须强制走“提交至部门经理审批→财务复核→出纳执行”三段式路径。这个白名单不是写死的代码,而是用JSON Schema定义的动态规则,由运维后台实时更新。
执行(Action)是手脚,但需要“肌肉记忆”:它把规划层的指令翻译成具体API调用、数据库查询或文件操作。难点在于状态一致性保障。举个真实案例:某物流Agent规划“查运单→发短信通知→更新系统状态”,执行到第二步时短信网关超时,第三步却因重试机制被执行了两次,导致系统状态错乱。解决方案是在执行模块内置幂等性令牌(Idempotency Token)——每次执行前生成唯一token并存入Redis,API端校验token存在即拒绝重复执行。
验证(Verification)是质检员,但必须“懂业务”:它不检查“语法是否正确”,而检查“结果是否符合业务逻辑”。比如生成合同条款时,验证模块要调用规则引擎检查“违约金比例是否超过法定上限20%”“签字页是否缺失法人章位置”。我们用Drools规则库+轻量级Python脚本组合实现,避免把复杂业务规则硬塞进LLM提示词。
自我纠正(Self-Correction)是教练,但需要“训练日志”:这是最常被误解的环节。它不是让LLM自己改错,而是基于验证失败日志、用户显式反馈(如“这个答案不对”)、执行异常记录(如API返回503)这三类数据,触发策略重加载。比如当连续3次验证发现“地址解析错误”,系统自动将地址解析工具切换为高德地图API替代原生正则匹配,并在日志中标记“正则解析器降级”。
提示:五大组件的依赖是刚性的,但实现可以是弹性的。感知层可以用多模态模型处理图片,也可以用规则引擎做关键词匹配;执行层可以调用云API,也可以本地运行Python脚本。关键在于数据流必须严格遵循“感知→规划→执行→验证→自我纠正”这一环,任何跳过或反向调用都会导致系统不可控。
2.2 为什么“自我纠正”不是锦上添花,而是系统存活的底线
搜索热词里反复出现“agent安全”“自主容错控制”,这背后是血泪教训。去年我们交付的一个医疗问诊Agent,在上线第47天突然开始把“高血压”误判为“高血糖”,原因是上游医院HIS系统升级后,检验报告字段名从blood_sugar变成了glucose_level,而我们的数据映射规则没同步更新。如果没有自我纠正模块,这个问题会持续数周,直到人工巡检发现。而实际发生的是:验证层检测到诊断结论与历史用药记录严重冲突(如给糖尿病患者开降压药),触发自我纠正流程,系统自动比对近7天所有字段变更日志,定位到HIS接口变更,30分钟内推送新映射规则到生产环境。这就是自我纠正的真实价值——它让Agent从“脆弱的智能”变成“有韧性的系统”。它的核心不是更聪明,而是更诚实:当不确定时,宁可报错也不胡说;当出错时,能追溯根因而非掩盖症状。我们给自我纠正模块设了三条铁律:第一,所有纠正动作必须留痕,包括触发条件、决策依据、执行结果;第二,任何自动纠正必须经过灰度发布,首期只影响1%流量;第三,纠正失败时自动降级到人工审核队列。这三条规则,是我们所有Agent项目的上线准入红线。
3. 核心组件深度实现:每个模块的“最小可行代码”与避坑指南
3.1 感知模块:如何让Agent一眼看懂用户到底想要什么
感知模块的常见误区是把它做成一个“万能输入解析器”,试图用一个大模型处理所有输入类型。实测下来,这种设计调试困难、延迟高、成本爆炸。我们采用分层感知架构:底层用轻量级专用模型做粗筛,上层用大模型做精炼。以处理用户上传的PDF采购合同为例:
# 感知模块主流程(伪代码) def perceive(input_data): # Step 1: 元数据感知(毫秒级) file_type = detect_file_type(input_data) # PDF/DOCX/IMAGE page_count = get_page_count(input_data) text_ratio = calculate_text_density(input_data) # 纯文本?扫描件? # Step 2: 结构化感知(专用小模型) if file_type == "PDF" and text_ratio > 0.8: # 调用PDF文本提取微服务(基于pdfplumber优化版) raw_text = extract_pdf_text(input_data) # 用BERT微调模型识别文档类型 doc_type = classify_doc_type(raw_text) # 合同/发票/报告 else: # 调用OCR微服务(PaddleOCR轻量版) ocr_result = run_ocr(input_data) doc_type = "SCANNED_DOCUMENT" # Step 3: 语义感知(LLM精炼) # 构建结构化提示词,强制输出JSON prompt = f""" 你是一个专业文档分析师。请分析以下文档片段: [文档类型:{doc_type}] [原始文本:{raw_text[:2000]}] 请严格按JSON格式输出: {{ "intent": "用户核心诉求,如'确认付款账号'、'核对交货日期'", "key_entities": ["甲方名称", "乙方名称", "金额", "日期"], "confidence_score": 0.0-1.0, "risk_flags": ["签名页缺失", "金额单位不一致"] }} """ structured_output = llm_call(prompt) return structured_output避坑指南:
- 不要在感知层做决策:见过太多项目在感知阶段就让LLM判断“这是否是有效投诉”,结果因prompt微调导致误判率飙升。感知只负责“描述事实”,决策交给规划层。
- 必须做输入质量熔断:当
text_ratio < 0.3(纯图片文档)且OCR置信度<0.6时,直接返回“请上传清晰文本版合同”,绝不让低质量输入污染后续流程。 - 风险标签要可操作:
"risk_flags"里的每一项,都必须对应下游模块的具体处理逻辑。比如"签名页缺失"会触发规划层生成“要求用户补传签名页”的指令。
3.2 规划模块:如何让LLM的“想象力”不跑偏
规划模块的核心矛盾是:既要发挥LLM的推理能力,又要严防它“自由发挥”。我们的解法是三明治提示工程(Sandwich Prompting):把LLM夹在结构化输入和强制输出之间。
# 规划模块提示词模板(已脱敏) SYSTEM_PROMPT = """ 你是一个严谨的业务流程规划师。你的任务是根据用户需求和可用工具,生成可执行的步骤序列。 【约束规则】 1. 只能使用以下工具(ID必须完全匹配): - TOOL_001: 查询供应商数据库(参数:supplier_name) - TOOL_002: 生成标准合同模板(参数:contract_type, parties) - TOOL_003: 发送邮件通知(参数:recipient, subject, body) 2. 步骤数不得超过5步 3. 每步必须包含:tool_id, parameters, expected_output_format 4. 如果需求超出工具能力,输出{"error": "NOT_SUPPORTED"} 【输出格式】严格JSON数组,示例: [ {"step": 1, "tool_id": "TOOL_001", "parameters": {"supplier_name": "ABC公司"}, "expected_output_format": "JSON对象,含contact_person, phone"}, {"step": 2, "tool_id": "TOOL_002", "parameters": {"contract_type": "采购", "parties": ["我方", "ABC公司"]}, "expected_output_format": "PDF文件URL"} ] """ USER_PROMPT = f""" 用户需求:{perceived_intent} 可用工具:{available_tools_list} 历史交互:{last_3_turns} """关键技巧:
- 工具描述必须带业务语义:
TOOL_001的描述不是“调用供应商API”,而是“查询供应商数据库,返回联系人及电话,用于后续合同签署”。这能让LLM理解工具的业务目的。 - 预期输出格式要具体:写
"expected_output_format": "JSON对象"不如写"expected_output_format": "{"contact_person": "张三", "phone": "138****1234"}",大幅降低LLM幻觉概率。 - 加入历史交互压缩:不是简单拼接前三轮对话,而是用10字内摘要:“用户投诉发货延迟→提供物流单号→确认仓库缺货”。这比原始对话节省80% token,且保留关键线索。
3.3 执行模块:如何让Agent的手脚不抖、不慢、不错
执行模块的痛点是“看着简单,实则处处是坑”。我们总结出执行层的三不原则:不裸奔(不直连外部服务)、不裸调(不暴露密钥)、不裸等(不无限重试)。
# 执行模块安全调用示例 class SafeExecutor: def __init__(self): self.rate_limiter = RedisRateLimiter( key="executor:api_calls", max_calls=100, window_seconds=60 ) self.circuit_breaker = CircuitBreaker( failure_threshold=5, recovery_timeout=300 # 5分钟自动恢复 ) def execute_tool(self, tool_id, params): # 步骤1:熔断检查 if not self.circuit_breaker.allow_request(): raise ServiceUnavailable("工具服务暂不可用") # 步骤2:限流检查 if not self.rate_limiter.acquire(): raise RateLimitExceeded("调用频率超限") # 步骤3:安全调用(密钥从Vault获取,非硬编码) api_key = vault_client.get_secret(f"tool/{tool_id}/api_key") headers = {"Authorization": f"Bearer {api_key}"} # 步骤4:带幂等性的HTTP调用 idempotency_token = str(uuid4()) response = requests.post( url=f"https://api.example.com/{tool_id}", json=params, headers={**headers, "Idempotency-Key": idempotency_token}, timeout=(3, 10) # 连接3秒,读取10秒 ) # 步骤5:结果标准化 if response.status_code == 200: return {"status": "success", "data": response.json()} elif response.status_code in [400, 404]: return {"status": "failed", "error": "业务错误", "details": response.json()} else: self.circuit_breaker.record_failure() raise ExternalServiceError(f"HTTP {response.status_code}")血泪经验:
- 超时设置必须分层:连接超时(3秒)要远小于读取超时(10秒),防止网络卡顿拖垮整个Agent。
- 幂等性Key必须全局唯一:我们用
{tool_id}_{user_id}_{timestamp}_{random}生成,确保同一用户对同一工具的重复请求不会被误执行。 - 失败分类要精准:400/404是业务错误(如参数无效),应记录日志但不触发熔断;500/503是服务错误,必须熔断并告警。混为一谈会导致系统雪崩。
3.4 验证模块:如何让Agent自己当质检员
验证模块最容易陷入两个极端:要么过度依赖LLM做“主观判断”,要么用硬编码规则覆盖不全。我们的方案是双轨验证机制:规则引擎做确定性检查,LLM做模糊性判断。
# 验证模块双轨制 def verify_execution_result(tool_id, result, original_intent): # 轨道1:规则引擎(确定性) rule_violations = [] if tool_id == "TOOL_001": # 供应商查询 if not result.get("contact_person"): rule_violations.append("缺失联系人姓名") if not re.match(r"^1[3-9]\d{9}$", result.get("phone", "")): rule_violations.append("联系电话格式错误") # 轨道2:LLM模糊验证(需谨慎使用) if len(rule_violations) == 0 and tool_id == "TOOL_002": # 合同生成 # 仅当规则检查通过时,才用LLM做语义验证 llm_prompt = f""" 你是一个资深法务。请判断以下合同条款是否符合中国《民法典》规定: [原始需求:{original_intent}] [生成条款:{result.get('contract_terms', '')[:500]}] 请只回答YES或NO,不要解释。 """ llm_judgment = llm_call(llm_prompt).strip().upper() if llm_judgment != "YES": rule_violations.append("法务合规性存疑(LLM判定)") return { "is_valid": len(rule_violations) == 0, "violations": rule_violations, "verification_method": "rules" if len(rule_violations) > 0 else "llm" } # 验证结果处理 verification_result = verify_execution_result(tool_id, result, intent) if not verification_result["is_valid"]: # 记录到自我纠正日志 correction_log = { "timestamp": datetime.now().isoformat(), "trigger": "verification_failure", "component": "verification", "failure_reason": "; ".join(verification_result["violations"]), "tool_id": tool_id, "intent": intent } save_to_correction_log(correction_log)关键实践:
- LLM验证必须设闸门:只在规则引擎无法覆盖的模糊领域(如法律条款、创意文案)启用,且必须配置fallback机制(如LLM判定NO时,自动转人工审核)。
- 违规描述要可追溯:
"联系电话格式错误"比"数据校验失败"有用100倍,它直接指向修复动作。 - 验证结果必须结构化存储:我们用Elasticsearch索引验证日志,支持按
tool_id、violation_type、time_range多维检索,这是自我纠正的数据基础。
3.5 自我纠正模块:如何让Agent从错误中真正学会成长
自我纠正模块常被做成“LLM重试”,这是最危险的简化。真正的自我纠正,是基于证据的策略迭代。我们构建了三层纠正机制:
| 纠正层级 | 触发条件 | 响应动作 | 响应时间 | 数据来源 |
|---|---|---|---|---|
| 即时纠正 | 单次验证失败 | 重试相同工具(最多2次) | <1秒 | 当前执行日志 |
| 策略纠正 | 同类错误≥3次/小时 | 切换备用工具或调整参数 | <30秒 | 近1小时错误日志 |
| 系统纠正 | 同类错误≥10次/天 | 更新工具描述、修改规划约束、推送新规则 | <5分钟 | 近24小时全量日志 |
# 自我纠正主流程 def self_correct(trigger_event): # Step 1: 错误聚类(关键!) error_cluster = cluster_errors( trigger_event, time_window="1h", # 近1小时 similarity_threshold=0.85 # 用Sentence-BERT计算语义相似度 ) # Step 2: 决策树判断纠正层级 if len(error_cluster) == 1: # 即时纠正:重试 return retry_last_action() elif len(error_cluster) >= 3 and is_same_tool(error_cluster): # 策略纠正:切换工具 return switch_to_backup_tool(error_cluster[0]["tool_id"]) elif len(error_cluster) >= 10 and is_same_intent(error_cluster): # 系统纠正:深度分析 root_cause = analyze_root_cause(error_cluster) if root_cause == "API_SCHEMA_CHANGED": return update_api_schema(root_cause["details"]) elif root_cause == "PROMPT_AMBIGUITY": return refine_prompt(root_cause["ambiguous_phrases"]) # Step 3: 执行纠正并记录 correction_result = execute_correction_plan() log_correction_action({ "trigger": trigger_event, "plan": correction_result["plan"], "outcome": correction_result["outcome"], "timestamp": datetime.now().isoformat() }) return correction_result # 真实案例:API_SCHEMA_CHANGED纠正 def update_api_schema(change_details): # 自动解析OpenAPI Spec变更 old_spec = get_current_openapi_spec("supplier_api") new_spec = fetch_latest_openapi_spec("supplier_api") diff = compare_openapi_specs(old_spec, new_spec) # 生成修正动作 actions = [] for field_change in diff["field_renames"]: # 更新工具描述中的参数名 update_tool_description( tool_id="TOOL_001", param_mapping={field_change["old_name"]: field_change["new_name"]} ) # 更新执行模块的参数转换逻辑 add_param_transform_rule( tool_id="TOOL_001", transform_code=f"params['{field_change['new_name']}'] = params.pop('{field_change['old_name']}')" ) return {"type": "schema_update", "actions": actions}核心心得:
- 错误聚类是灵魂:不用语义相似度,同类错误会被分散在不同时间点,无法触发策略纠正。我们用Sentence-BERT微调模型计算错误描述向量,相似度>0.85即归为一类。
- 纠正动作必须可逆:每次系统纠正都生成回滚脚本,如
update_api_schema会同时生成revert_api_schema,确保万一出错能秒级恢复。 - 人工审核是最后一道闸:所有系统纠正动作,必须经值班工程师二次确认(我们用企业微信机器人推送待办),杜绝“AI改AI”的失控循环。
4. 实战部署与监控:让五大组件在生产环境稳如磐石
4.1 生产环境架构:为什么K8s不是选择,而是必需
在单机或VM上跑Agent,就像用自行车拉集装箱——不是不能动,而是随时会散架。我们所有Agent项目强制采用Kubernetes微服务架构,五大组件各自独立部署,通过Service Mesh通信。这不是为了炫技,而是解决三个致命问题:
- 资源隔离:感知模块(OCR/ASR)吃GPU,规划模块(LLM)吃CPU,执行模块(数据库)吃IO,混部必然互相抢占。
- 弹性伸缩:促销季客服Agent的感知请求量暴增5倍,只需给感知服务Pod扩容,其他组件不受影响。
- 故障域隔离:某次执行模块的数据库连接池耗尽,只影响执行层,感知、规划仍可正常工作,用户看到的是“正在处理中”,而非“服务不可用”。
# 核心服务部署示意(简化版) --- # 感知服务(GPU节点) apiVersion: apps/v1 kind: Deployment metadata: name: perception-service spec: replicas: 3 template: spec: nodeSelector: cloud.google.com/gke-accelerator: nvidia-tesla-t4 containers: - name: perception image: perception-service:v2.1 resources: limits: nvidia.com/gpu: 1 --- # 规划服务(CPU密集型) apiVersion: apps/v1 kind: Deployment metadata: name: planning-service spec: replicas: 5 template: spec: containers: - name: planning image: planning-service:v3.0 resources: limits: cpu: "4" memory: "8Gi" --- # 服务网格入口(Istio) apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: agent-gateway spec: hosts: - "agent-api.example.com" http: - match: - uri: prefix: /perceive route: - destination: host: perception-service - match: - uri: prefix: /plan route: - destination: host: planning-service运维铁律:
- 组件间通信必须加密:所有Service Mesh流量强制mTLS,杜绝内部窃听。
- 健康检查必须业务化:不只是
/healthz返回200,规划服务的健康检查要调用一次/plan?test=true,验证LLM调用链路完整。 - 日志必须全链路追踪:每个请求生成唯一
trace_id,贯穿五大组件日志,用Jaeger可视化调用链。曾靠此快速定位到“自我纠正模块的Redis连接池泄漏”问题。
4.2 关键监控指标:盯住这5个数字,胜过看100个图表
监控不是堆指标,而是盯住决定系统生死的北极星指标。我们为五大组件定义了5个核心监控项,全部接入Grafana看板,告警阈值经3年生产环境锤炼:
| 组件 | 监控指标 | 健康阈值 | 异常含义 | 应对动作 |
|---|---|---|---|---|
| 感知 | perception_latency_p95 | <800ms | OCR/ASR模型响应慢 | 切换至CPU版轻量模型 |
| 规划 | plan_success_rate | >99.5% | LLM规划失败率超标 | 降级至规则引擎兜底 |
| 执行 | action_idempotency_rate | >99.9% | 幂等性失效(重复执行) | 紧急回滚执行模块版本 |
| 验证 | verification_bypass_rate | <0.1% | 验证模块被绕过 | 立即阻断所有执行请求 |
| 自我纠正 | correction_effectiveness | >95% | 纠正后仍失败 | 启动人工介入流程 |
真实告警案例:
- 某日
plan_success_rate跌至98.2%,排查发现是LLM服务提供商临时升级了模型,导致部分工具描述解析失败。我们立即触发“策略纠正”,将规划模块切换至本地缓存的旧版工具描述库,15分钟内恢复。 action_idempotency_rate突降至99.2%,追踪发现是Redis集群某节点网络分区,幂等性Token校验失效。自动执行预案:暂停该Redis节点流量,切至备用集群。
注意:所有监控指标必须关联到具体的纠正动作。没有应对预案的监控,只是制造焦虑的噪音。
4.3 安全加固:Agent不是玩具,而是生产系统
搜索热词里高频出现“agent安全”,这绝非空谈。我们把安全嵌入五大组件的DNA:
- 感知层:文件上传强制沙箱扫描(ClamAV + 自研YARA规则),PDF解析禁用JavaScript执行,图片OCR前做EXIF元数据清洗。
- 规划层:LLM提示词注入防护(用
jinja2模板引擎替代字符串拼接),工具ID白名单校验(收到TOOL_999直接拒收)。 - 执行层:所有外部API调用走API网关,网关实施OAuth2.0鉴权+IP白名单+QPS限制;数据库操作强制参数化查询,杜绝SQL注入。
- 验证层:敏感信息脱敏(身份证号→
***1234,银行卡号→**** **** **** 5678),验证结果日志不存原始数据。 - 自我纠正层:纠正动作执行前,必须通过安全策略引擎(OPA)校验,例如“更新API Schema”动作需满足:
user_role == "admin" AND change_impact == "low"。
最严苛的安全实践:
- 零信任网络:组件间通信不依赖IP白名单,而是每个Pod启动时向Vault申请短期证书,Service Mesh用证书双向认证。
- 数据最小化:自我纠正日志只存
tool_id、error_type、timestamp,绝不存原始输入、中间结果、用户ID等PII信息。 - 红蓝对抗:每季度邀请第三方安全团队进行Agent专项渗透测试,重点攻击“规划层提示词注入”和“执行层API越权调用”。
5. 常见问题与实战排障:那些文档里不会写的血泪教训
5.1 “Agent总是重复执行同一个动作”——90%的根源在这里
现象:用户说“查一下订单123”,Agent连续5次调用订单查询API,返回5份相同结果。
排查路径:
- 先看验证层:检查
verification_bypass_rate是否飙升。如果是,说明验证模块没生效,执行结果未经校验就直接返回。 - 再查规划层:抓取规划模块输出,看是否生成了
{"step": 1, "tool_id": "ORDER_QUERY", ...}后,又生成了{"step": 2, "tool_id": "ORDER_QUERY", ...}。这通常是因为LLM没理解“查询一次即可”,规划约束太松。 - 终极根因:执行模块的幂等性失效。我们曾遇到Redis集群脑裂,导致幂等性Token校验失败,同一请求被多次执行。
速效方案:
- 立即检查
action_idempotency_rate监控; - 在执行模块加日志埋点:“执行前校验Token [X],结果 [Y]”;
- 临时方案:在规划层加硬约束
"max_executions_per_tool": 1。
实操心得:遇到重复执行,80%概率是幂等性问题。别急着调LLM,先查Redis和执行模块日志。
5.2 “Agent在某个步骤突然卡死,既不报错也不返回”——超时陷阱
现象:用户发起请求后,30秒无响应,Nginx返回504 Gateway Timeout。
典型场景与解法:
场景1:LLM调用超时
表现:规划层日志停在llm_call started,无后续。
解法:检查LLM服务监控,确认llm_response_time_p95是否飙升;临时将规划层超时从30秒降到10秒,失败后降级至规则引擎。场景2:外部API无响应
表现:执行层日志停在requests.post(...),无返回。
解法:执行模块必须设timeout=(3,10),连接3秒,读取10秒;增加requests.adapters.HTTPAdapter(max_retries=1)防重试风暴。场景3:验证逻辑死循环
表现:验证层日志显示verify_execution_result started,无结束。
解法:验证模块所有外部调用(如规则引擎、LLM)必须设超时;内部循环必须加计数器,超100次强制退出。
避坑口诀:
“所有外部调用必设超时,所有循环必设计数器,所有等待必有兜底。”
5.3 “自我纠正越纠越错”——纠正逻辑的负反馈陷阱
现象:系统自动纠正后,错误率反而从5%升到20%。
三大负反馈根源:
- 错误聚类不准:把“用户输错订单号”和“订单号字段名变更”聚为一类,导致纠正动作错误(如把字段名改回去)。
- 纠正动作副作用:为解决“合同生成慢”,将LLM切换至轻量模型,结果导致“法务条款合规性”下降。
- 缺乏人工审核:系统自动执行高危纠正(如修改数据库Schema),未经过人工确认。
破局方案:
- 聚类增强:在错误描述中强制加入上下文标签,如
[CONTEXT: user_input_mismatch]vs[CONTEXT: api_schema_change],提升聚类精度。 - 灰度发布:所有系统纠正动作,首期只对1%流量生效,观察
correction_effectiveness达标(>95%)后再全量。 - 双签机制:涉及Schema变更、Prompt重写等高危动作,必须经研发+产品双人企业微信审批。
真实教训:
我们曾因聚类不准,把“用户上传模糊图片”和“OCR模型精度下降”归为一类,系统自动降级OCR模型,结果所有图片解析质量暴跌。此后,我们在错误日志中强制加入input_quality_score(图片清晰度、音频信噪比等量化指标),彻底解决聚类污染。
5.4 “Agent在不同场景下表现差异巨大”——上下文管理失效
现象:同一个Agent,处理“订单查询”很准,处理“退货申请”就频繁出错。
根因分析表:
| 问题环节 | 表现 | 检查点 | 解决方案 |
|---|---|---|---|
| 感知层 | 对退货场景的意图识别准确率低 | 检查classify_doc_type模型是否用退货单样本微调 | 用退货单PDF/图片重训感知模型 |
| 规划层 | 退货流程步骤缺失 | 检查工具白名单是否包含RETURN_PROCESS_TOOL | 动态加载场景化工具集 |
| 执行层 | 退货API返回格式与订单API不同 | 检查执行模块是否做response_normalization | 统一各API响应格式为{"status":"success","data":{}} |
| 验证层 | 退货结果无业务规则校验 | 检查验证规则库是否含退货条款 | 补充退货时效、库存扣减等规则 |
关键实践:
我们为每个业务场景(订单、退货、投诉、咨询)维护独立的场景配置包(Scene Package),包含:感知模型权重、规划工具白名单、执行适配器、验证规则集。Agent启动时,根据用户输入自动加载对应包,而非用一套通用逻辑硬扛所有场景。
5.5 “LLM输出格式总不按要求,JSON总少个逗号”——提示工程的终极解法
现象:规划模块要求LLM输出JSON数组,但LLM常返回`{"step