☰
AI Agent开发实战:RAG、Skills、MCP与LangChain协同架构
2026/9/25 3:12:13 网站建设 项目流程

1. 这不是“速成课”,而是一份AI Agent开发者的上岗说明书

你点开这个标题,大概率是刚被“Agent”这个词刷屏——朋友圈在聊、技术群在推、招聘JD里写着“熟悉LangChain/RAG/MCP者优先”,连产品经理都在会议里甩出“我们要上Agentic Workflow”。但翻完一堆教程,发现要么是调用一个API就喊“搞定!”,要么是直接扔给你300行看不懂的LangGraph代码,中间那层“人怎么想、系统怎么拆、问题怎么解”的真实逻辑,全被省略了。这就像教人修车,只告诉你“拧紧螺丝”,却不讲扭矩值为什么是25N·m、为什么必须按对角顺序、为什么垫片方向错了会漏油。本篇不讲虚的,它是我带过7个AI工程落地项目后,把所有踩过的坑、删掉的冗余、验证过的最小可行路径,全部摊开重写的一份实操手册。核心关键词就五个:AI Agent、RAG、Skills、MCP、LangChain——它们不是并列关系,而是分层协作的齿轮组。Agent是总控大脑,RAG是它的长期记忆库,Skills是它能调用的手和脚(比如查天气、读PDF、发邮件),MCP是让不同工具之间说同一种语言的翻译协议,LangChain则是把这四块拼图焊在一起的焊接台。DeepSeek这类模型属于底层引擎,它不决定“做什么”,只负责“怎么做得更准”;而Agent架构决定的是“先查资料、再对比方案、最后生成报告”这一整套决策链路。如果你的目标是能独立设计一个能自动整理会议纪要+提取待办+同步到飞书的日程助理,而不是只会跑通一个hello world demo,那这篇就是为你写的。它不承诺“三天学会”,但保证你每一步操作背后都有明确意图、可验证结果、可复盘依据。

2. 为什么必须放弃“从零开始学Python”的老路?Agent开发的本质是系统工程思维

2.1 把Agent当成“人”来设计,而不是当“函数”来调用

绝大多数新手教程失败的根本原因,在于一开始就陷入技术细节:先装Python,再pip install langchain,然后抄一段retriever代码……这相当于想造一辆能自动驾驶的车,却先去研究火花塞间隙。真正的起点,是你得先定义清楚这个Agent要解决什么具体问题。比如“帮销售团队自动分析竞品发布会视频”,这个需求拆解下来,至少包含四个不可跳过的环节:

  • 理解层:把视频转成文字(ASR),再用LLM提取关键信息(如新品参数、定价策略);
  • 记忆层:把历史竞品资料存进向量库,支持语义搜索(这就是RAG);
  • 执行层:调用企业微信API发送摘要,或用Playwright自动登录CRM填入新线索;
  • 协调层:当用户问“对比A和B两款产品”,Agent必须知道先查A的资料、再查B的资料、最后做表格对比——这个决策顺序不能硬编码,得靠Agent框架动态规划。

LangChain不是万能胶,它是帮你把这四层粘合起来的胶水;MCP不是魔法协议,它是让“查CRM”和“读PDF”这两个完全不同的工具,能互相听懂对方在说什么的翻译官。我见过太多人卡在第一步:花两周配好向量库,却没想明白“用户到底会问什么问题”。比如销售最常问的是“竞品X最近三个月降价几次?每次降多少?”,这就决定了你的RAG知识库必须按时间维度切块,而不是简单按文档切块。切块策略错了,检索准确率再高也没用——因为召回的永远是“最新发布会全文”,而不是“降价记录段落”。

2.2 RAG不是“加个向量库就完事”,它是知识管理的重新设计

RAG(Retrieval-Augmented Generation)常被简化为“检索+大模型生成”,但实际落地时,90%的问题出在“检索”之前。我们曾为一家医疗器械公司搭建产品问答系统,初期用默认的text-splitter按512字符切块,结果用户问“支架植入后抗凝药怎么调整?”,系统返回了《心血管介入手术指南》全文——因为所有段落都含“支架”“抗凝”关键词,但真正答案藏在第17页的“术后用药管理”小节里。后来我们做了三件事才解决:

  1. 结构化切块:用PDF解析工具识别标题层级,强制按“章节→小节→段落”三级切分,确保“抗凝药调整”这个子主题独立成块;
  2. 元数据注入:给每个块打上{"doc_type":"指南","section":"术后管理","update_date":"2024-03"}标签,检索时加filter条件;
  3. 混合检索:不只靠向量相似度,还叠加关键词匹配(如必须含“华法林”“INR”等术语)和BM25打分。

这说明RAG的核心不是算法,而是对业务知识的理解深度。你得像档案管理员一样思考:这份资料谁会用?在什么场景下用?最可能怎么问?——这些决定了切块方式、元数据字段、检索策略。网上那些“RAG实战”教程,90%只教你怎么装ChromaDB,却从不告诉你为什么要把PDF转成Markdown再切块,而不是直接丢进PDFLoader。因为PDF里的页眉页脚、表格线、页码全是噪声,会严重污染向量表示。实测下来,用PyMuPDF解析后再用unstructured清理格式,比直接用PyPDF2准确率高37%。

2.3 Skills不是“写几个function”,而是定义Agent的能力边界

Skills(技能)常被误解为“能调用API就行”,但真正决定Agent鲁棒性的,是Skill的输入校验、错误兜底、状态反馈机制。举个真实案例:我们给HR系统做的简历筛选Agent,最初写的fetch_candidate_info(candidate_id)Skill,只处理正常返回,结果某天招聘系统维护,接口返回503。Agent直接卡死,后续流程全停。后来重构为:

def fetch_candidate_info(candidate_id: str) -> dict: try: # 带超时和重试 response = requests.get(f"/api/candidate/{candidate_id}", timeout=10) response.raise_for_status() return response.json() except requests.exceptions.Timeout: return {"error": "timeout", "retry_after": 30} # 告诉Agent等30秒再试 except requests.exceptions.HTTPError as e: if e.response.status_code == 404: return {"error": "not_found", "candidate_id": candidate_id} else: return {"error": "server_error", "status_code": e.response.status_code}

关键变化有三点:

  • 明确错误分类:不是笼统的except Exception,而是区分超时、404、5xx,因为Agent需要不同应对策略(重试/跳过/告警);
  • 返回结构化错误:用字典而非字符串,让Agent能解析error类型并触发对应分支;
  • 提供恢复线索:retry_after告诉Agent何时重试,candidate_id保留上下文避免丢失任务。

这背后是Skills设计的黄金法则:每个Skill必须能回答三个问题——成功时返回什么?失败时返回什么?失败后Agent该怎么办?网上教程教你怎么写def search_web(query),却从不提query为空时该返回空列表还是抛异常,更不会告诉你如何让Agent在连续三次搜索失败后,自动切换到本地知识库兜底。这才是Skills的真正难点。

3. MCP:被严重低估的“Agent世界的HTTP协议”

3.1 MCP不是新技术,而是解决“工具方言混乱”的务实方案

MCP(Model Context Protocol)常被包装成“下一代Agent协议”,但剥开概念,它本质是给工具调用定一套通用语法。想象一下:你家有小米空调、华为音箱、海尔冰箱,如果每个品牌都用自己App控制,你就得装三个App、记三套指令。MCP就是让所有设备都支持“统一遥控器”——它定义了一套标准消息格式(JSON Schema),规定“调温度”必须包含{"device": "aircon", "action": "set_temperature", "value": 26},而不是小米的{"cmd": "temp", "para": "26"}、华为的{"intent": "adjust_temp", "target": 26}。

在Agent开发中,MCP解决的是同样问题:当你想让Agent既能查飞书日历、又能读Notion数据库、还能调用内部CRM,每个系统API都长得不一样。LangChain的Tool抽象层试图统一,但实际中仍需为每个工具写Adapter。MCP则前进一步:它要求工具开发者按标准实现/mcp/tools端点,返回所有可用功能的Schema描述;Agent只需一次发现,就能自动生成调用代码。我们实测过:接入一个支持MCP的浏览器扩展(如Workbuddy),Agent无需写一行新代码,就能自动获得“截当前页”“提取网页文本”“保存到Notion”三项能力——因为扩展已按MCP规范暴露了这些功能的输入输出定义。

3.2 如何判断一个工具是否真支持MCP?看这三个硬指标

很多教程说“启用MCP连接”就万事大吉,但实际落地必须验证。我们总结出判断MCP支持度的三要素:

  1. Discovery Endpoint:工具必须提供GET /mcp/server或GET /.well-known/mcp端点,返回JSON格式的服务器元数据(含版本、支持的capabilities);
  2. Tool Schema:调用GET /mcp/tools必须返回符合 OpenAPI 3.0 规范的工具描述,包含parameters(输入字段)、responses(输出结构)、examples(调用示例);
  3. Runtime Validation:Agent发起调用时,工具端必须校验请求JSON是否符合Schema,并返回清晰的validation_errors字段(而非笼统的400 Bad Request)。

曾有个客户采购的“智能客服插件”宣称支持MCP,但/mcp/tools返回的是HTML页面——这根本不是MCP,只是挂了个名。我们用curl测试:

curl -H "Accept: application/json" http://localhost:8000/mcp/tools | jq '.tools[0].parameters'

如果返回null或报错,立刻放弃。真正合规的MCP工具,parameters字段会精确到每个字段的type、required、description,比如:

{ "name": "search_knowledge_base", "description": "在企业知识库中搜索相关文档", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "搜索关键词"}, "max_results": {"type": "integer", "default": 5, "minimum": 1, "maximum": 20} }, "required": ["query"] } }

这个Schema让Agent能自动生成表单、做前端校验、甚至生成自然语言提示词(如“请用户提供搜索关键词”)。没有Schema,一切自动化都是空中楼阁。

3.3 在LangChain中集成MCP:不是替换,而是增强

很多人以为用MCP就得抛弃LangChain,这是巨大误区。LangChain的Tool类和MCP是互补关系:LangChain负责Agent的决策流(Chain、AgentExecutor),MCP负责工具的标准化接入。我们的标准集成方式是:

  1. 用requests调用MCP工具的/mcp/tools端点,动态生成LangChainTool对象;
  2. 将MCP工具的parametersSchema转换为LangChain的args_schema(Pydantic Model);
  3. 在_run()方法中,将LangChain传入的参数序列化为MCP标准JSON,再POST到工具端点。

关键代码片段:

from langchain.tools import BaseTool from pydantic import BaseModel, Field import requests class MCPTool(BaseTool): mcp_url: str # 工具MCP端点,如http://localhost:8000/mcp tool_name: str def _run(self, **kwargs) -> str: # 1. 构建MCP标准请求体 payload = { "tool": self.tool_name, "params": kwargs, "context": {"session_id": self.session_id} # 传递上下文 } # 2. 发送请求 resp = requests.post(f"{self.mcp_url}/mcp/tool", json=payload, timeout=30) resp.raise_for_status() result = resp.json() # 3. 提取结果,兼容MCP标准响应格式 return result.get("result", str(result)) # 动态注册MCP工具 def register_mcp_tool(mcp_url: str, tool_name: str) -> MCPTool: # 从/mcp/tools获取Schema,生成args_schema... schema = get_mcp_schema(mcp_url, tool_name) # 实现略 return MCPTool( name=tool_name, description=schema["description"], mcp_url=mcp_url, tool_name=tool_name, args_schema=create_pydantic_model(schema["parameters"]) # 自动构建 )

这样做的好处是:既享受LangChain成熟的Agent编排能力(如ReAct、Plan-and-Execute),又获得MCP带来的工具即插即用优势。我们上线的销售助手,初始只集成了CRM和飞书,两周后接入新的BI查询工具,只需register_mcp_tool("http://bi-mcp:8000", "query_bi")一行代码,Agent立刻获得新能力,无需改任何决策逻辑。

4. LangChain不是学习门槛,而是降低复杂度的杠杆

4.1 别被“LangChain=复杂框架”吓退,它本质是DSL(领域特定语言)

LangChain常被吐槽“API太绕”,但真相是:它用Python语法封装了Agent开发的通用模式。比如“先检索再生成”这个动作,手写要:

  1. 初始化向量库客户端;
  2. 调用similarity_search获取top-k文档;
  3. 拼接文档内容到prompt模板;
  4. 调用LLM API;
  5. 解析返回的JSON。

LangChain用RetrievalQA一行搞定:

from langchain.chains import RetrievalQA from langchain.llms import OpenAI qa_chain = RetrievalQA.from_chain_type( llm=OpenAI(temperature=0), chain_type="stuff", # 拼接所有文档 retriever=vectorstore.as_retriever(), return_source_documents=True ) result = qa_chain({"query": "竞品X的电池续航是多少?"})

这里的chain_type="stuff"就是DSL:它隐含了“把所有检索结果拼成一段文本喂给LLM”的逻辑。LangChain的价值,正在于把重复模式提炼成可配置的组件。我们统计过,一个典型Agent项目中,70%的代码是胶水逻辑(参数传递、错误处理、日志记录),LangChain把这些封装成Runnable、BaseTool、CallbackHandler等抽象,让你专注业务逻辑。新手最大的误区,是试图“读懂所有源码”,而应该学会“用对组件”。就像开车不用懂发动机原理,但得知道油门、刹车、档位怎么配合。

4.2 LangChain与LangGraph:不是替代,而是分工进化

LangGraph常被宣传为“LangChain的升级版”,但实际是定位差异。LangChain适合线性流程(如RAG问答、工具链调用),LangGraph专攻循环、条件、并行等复杂编排。我们做过对比测试:

  • 场景1:会议纪要生成(固定流程:语音转文字→提取要点→生成摘要→发邮件)→ LangChain的SequentialChain足够,代码量少30%;
  • 场景2:多轮销售谈判辅助(用户提问→查产品资料→若价格敏感则查促销政策→若竞品对比则启动RAG→生成话术)→ LangGraph的StateGraph必需,因为需要根据中间结果动态跳转。

LangGraph的核心创新是State(状态机):每个节点输出必须是dict,且键名需预定义(如{"messages": [...], "next_action": "search_rag"}),这强制你思考“当前步骤完成后,系统应该记住什么、下一步可能是什么”。而LangChain的Chain是黑盒,输出格式自由,调试时很难追溯中间状态。所以选型原则很明确:流程确定、分支简单 → LangChain;流程动态、需状态追踪 → LangGraph。我们现在的项目,90%用LangChain搭骨架,只有核心决策模块用LangGraph嵌入——二者通过RunnableLambda无缝集成。

4.3 企业级项目避坑:别在LangChain上过度定制

企业项目最常犯的错误,是过早优化LangChain。比如:

  • 为提升速度,自己重写VectorStoreRetriever,结果发现瓶颈其实在LLM API延迟,而非检索本身;
  • 为“更可控”,弃用AgentExecutor,手写状态管理,结果调试时发现Agent在第三步卡死,却找不到日志线索;
  • 为“更安全”,在LLMChain里加层层输入过滤,结果误杀合法的中文标点,导致RAG检索失败。

我们的经验是:先用LangChain官方组件跑通全流程,再用性能分析工具(如cProfile)定位真实瓶颈。实测数据:在一个日均1000次调用的客服Agent中,95%的延迟来自LLM响应(平均2.3s),向量检索仅占0.12s,网络IO占0.08s。这意味着优化检索算法毫无意义,而应该:

  • 用缓存(Redis)缓存高频问题答案;
  • 对LLM请求做并发池(asyncio.Semaphore限流);
  • 用流式响应(streaming)让用户感知“已在处理”。

LangChain的CallbackHandler是调试神器:

class DebugCallback(BaseCallbackHandler): def on_chain_start(self, serialized, inputs, **kwargs): print(f"Chain {serialized['name']} started with {inputs}") def on_llm_end(self, response, **kwargs): print(f"LLM returned {len(response.generations)} candidates") agent_executor = AgentExecutor( agent=agent, tools=tools, callbacks=[DebugCallback()] # 所有关键节点自动打印 )

有了它,你不再需要在30个文件里加print(),就能看到Agent每一步在想什么、调了什么工具、返回了什么。这才是企业级开发该有的可观测性。

5. 企业级实战:从需求到上线的完整闭环(以“智能周报生成器”为例)

5.1 需求拆解:把模糊目标变成可验证的验收清单

客户提出“希望AI自动写周报”,这太模糊。我们用“5W2H”法拆解:

  • What:生成包含“本周完成事项”“下周计划”“风险与阻滞”三部分的Markdown周报;
  • Who:面向研发组长,需汇总组内5个成员的Git提交、Jira任务、会议纪要;
  • Where:输出到飞书文档,同时邮件抄送CTO;
  • When:每周五下午5点自动触发;
  • Why:减少组长手动整理时间(原需2小时/周);
  • How:从GitLab API拉代码提交,从Jira API拉任务状态,从飞书云文档拉会议纪要;
  • How Much:准确率≥90%(人工抽检10份,错误≤1处)。

验收清单由此生成:

模块验收项测试方法
数据采集能正确识别“已完成”Jira任务(状态=Done且resolution=Fixed)查看Jira API返回的raw JSON,验证filter逻辑
RAG知识库会议纪要中提到的“Q3上线计划”能被准确召回用retriever.get_relevant_documents("Q3上线计划")检查返回内容
报告生成“风险与阻滞”部分必须包含未关闭的Blocker级Jira任务检查LLM输出中是否含"risk": [{"jira_id": "PROJ-123", "summary": "数据库迁移延迟"}]
自动化每周五17:00准时执行,失败时钉钉告警查看Celery任务日志,模拟网络故障测试告警

这个清单让开发过程不再凭感觉,每个功能点都有明确出口。

5.2 技术栈选型:为什么选LangChain+MCP+ChromaDB,而不是LlamaIndex+FastAPI?

选型不是比参数,而是比与业务的契合度。我们对比过主流方案:

维度LangChain+MCP方案LlamaIndex方案自研FastAPI方案
工具接入速度支持MCP的工具1行代码接入,非MCP工具用BaseTool封装(平均2小时/工具)需为每个工具写QueryEngine适配器(平均8小时/工具)每个工具需独立开发REST接口(平均40小时/工具)
RAG可调试性RetrievalQA内置return_source_documents,可直接查看检索结果Response对象需手动解析source_nodes,调试成本高完全自定义,但需额外开发调试端点
企业安全合规所有组件可私有部署,无外部依赖部分插件依赖Cloud服务(如Pinecone)完全可控,但开发周期长
团队技能匹配Python工程师1周可上手核心API需深入理解Indexing/Querying概念需全栈能力(前端+后端+运维)

最终选择LangChain+MCP,因为客户团队有Python背景但无AI专项经验,且要求2周内交付MVP。我们用ChromaDB(轻量级向量库)替代Pinecone,因为:

  • 不需要GPU,纯CPU即可运行;
  • 数据存在本地SQLite,符合客户“数据不出内网”要求;
  • chroma_client.create_collection(metadata={"hnsw:space": "cosine"})一行代码就能指定相似度算法,比ElasticSearch配置简单10倍。

提示:ChromaDB的hnsw:space参数决定向量距离计算方式,cosine适合文本语义相似度,l2适合数值型特征。别盲目用默认值,实测在中文文档检索中,cosine比l2准确率高22%。

5.3 关键实现:让Agent真正“理解”周报的业务逻辑

最难的部分不是技术,而是把业务规则翻译成Agent能执行的指令。比如“本周完成事项”需满足:

  • 来源:GitLab的merged_at在本周内的MR,Jira的resolutiondate在本周内的Done任务;
  • 过滤:排除[CI]、[DOC]等自动化提交;
  • 聚合:同一Jira任务关联多个Git提交,只计1次;
  • 表述:用“完成XX模块开发”代替“提交commit abc123”。

我们用LangChain的CustomTool封装业务逻辑:

from langchain.tools import Tool def get_weekly_summary() -> str: """返回结构化周报数据,供LLM渲染""" # 1. 获取本周日期范围 now = datetime.now() start_of_week = (now - timedelta(days=now.weekday())).replace(hour=0, minute=0, second=0) end_of_week = start_of_week + timedelta(days=6, hours=23, minutes=59, seconds=59) # 2. 聚合GitLab数据(伪代码) gitlab_mrs = gitlab_api.search_mrs( state="merged", merged_after=start_of_week.isoformat(), merged_before=end_of_week.isoformat() ) # 过滤自动化提交,聚合Jira ID jira_ids = set() for mr in gitlab_mrs: if not re.match(r"^\[CI\]|^\[DOC\]", mr.title): jira_ids.update(extract_jira_ids(mr.description)) # 3. 获取Jira任务详情 jira_tasks = [] for jira_id in jira_ids: task = jira_api.get_issue(jira_id) if task.resolutiondate and start_of_week <= task.resolutiondate <= end_of_week: jira_tasks.append({ "key": jira_id, "summary": task.summary, "assignee": task.assignee.displayName }) return json.dumps({ "completed": jira_tasks, "planned": get_next_week_tasks(), # 类似逻辑 "risks": get_blocked_tasks() # 类似逻辑 }, ensure_ascii=False) weekly_tool = Tool( name="get_weekly_summary", description="获取本周研发工作汇总数据,包括已完成任务、下周计划、风险项", func=get_weekly_summary )

这个Tool的关键在于:它返回的是结构化JSON,而非自然语言。LLM的Prompt明确要求:“你收到的数据是JSON,请严格按字段生成Markdown,不要添加额外解释”。这避免了LLM“自由发挥”导致格式错乱。我们测试过,用自然语言描述(如“本周完成了3个任务”)作为输入,LLM有时会杜撰不存在的任务;而结构化输入+强约束Prompt,准确率稳定在98.7%。

5.4 上线与监控:生产环境的“心跳检测”怎么做?

上线不是终点,而是观测的开始。我们为周报Agent部署了三层监控:

  1. 基础设施层:Prometheus抓取ChromaDB内存占用、LLM API调用延迟、任务队列长度;
  2. 业务逻辑层:在Agent关键节点埋点,记录retriever.invoke()返回的文档数、llm.invoke()的token消耗、tool.run()的成功率;
  3. 用户体验层:在飞书文档末尾自动添加<!-- Generated by AI-Agent v1.2 on 2024-06-15T17:00:00Z -->,方便人工追溯。

最实用的监控是“失败归因分析”。当周报生成失败时,系统自动触发诊断:

  • 检查GitLab API是否返回401(Token过期);
  • 检查Jira返回的任务数是否为0(可能Jira状态字段变更);
  • 检查RAG检索是否返回空(知识库是否更新);
  • 检查LLM是否返回<|endoftext|>(提示词被截断)。

诊断结果生成Markdown报告,自动发到运维群:

[ERROR] 周报生成失败 (2024-06-15 17:03:22) - 根本原因:Jira API返回500,错误信息:"Field 'resolutiondate' not found" - 临时修复:修改Jira查询参数,用'statuscategorychangedate'替代 - 长期方案:与Jira管理员确认字段变更,更新SDK

这套机制让我们把平均故障恢复时间(MTTR)从8小时降到22分钟。记住:Agent不是越“智能”越好,而是越“可诊断”越可靠。

6. 常见问题与排查技巧实录:那些教程绝不会告诉你的细节

6.1 RAG检索不准?先检查这三件事,90%的问题当场解决

RAG效果差,新手第一反应是换模型或调参数,但实际80%的问题出在数据预处理。我们整理了高频问题速查表:

现象可能原因排查命令/方法解决方案
检索返回无关文档PDF解析失败,大量空白页或乱码pdfinfo your_file.pdf查页数;`pdftotext -layout your_file.pdf -head -n 20` 查前20行文本
同一问题多次检索结果不同向量库未持久化,重启后数据丢失chroma_client.get_collection("docs").count()返回0在ChromaDB初始化时指定persist_directory="/path/to/db",并调用client.persist()
中文检索效果差分词器未适配中文,按字切分而非词切分from langchain.text_splitter import RecursiveCharacterTextSplitter; splitter = RecursiveCharacterTextSplitter(); print(splitter.split_text("人工智能"))改用ChineseRecursiveTextSplitter(需安装jieba),或设置separators=["\n\n", "\n", "。", "!", "?", ";", ",", ""]

特别提醒:RecursiveCharacterTextSplitter的chunk_size不是越大越好。我们实测,对技术文档,chunk_size=512比1024准确率高15%,因为大块文本包含过多上下文噪声,反而稀释关键信息。而对法律条文,chunk_size=1024更优——因为条款完整性更重要。

6.2 Agent无限循环?用这个“熔断机制”一键止损

Agent卡在循环里(如反复调用同一个Tool),通常是因为LLM返回的action_input格式错误,导致Tool返回异常,Agent又尝试重试。我们的熔断方案:

  1. 在AgentExecutor中设置max_iterations=15(默认是15,但很多教程没提);
  2. 自定义CallbackHandler,记录每次Action:
class LoopDetector(BaseCallbackHandler): def __init__(self): self.action_history = [] def on_agent_action(self, action, **kwargs): # 记录最近3次Action self.action_history.append(action.tool) if len(self.action_history) > 3: self.action_history.pop(0) # 连续3次相同Action,触发熔断 if len(set(self.action_history)) == 1 and len(self.action_history) == 3: raise RuntimeError("Agent stuck in action loop: " + action.tool) agent_executor = AgentExecutor( agent=agent, tools=tools, callbacks=[LoopDetector()], max_iterations=15 # 双保险 )

这个机制上线后,循环故障率从12%降到0.3%。关键是:熔断后不是报错退出,而是返回友好提示:“系统检测到操作重复,已为您生成备选方案”,并调用备用Skill(如直接返回知识库摘要)。

6.3 MCP工具调用失败?90%是HTTP头或认证问题

MCP调试最头疼的不是代码,而是网络细节。我们踩过的坑:

  • 问题:requests.post(url, json=payload)返回401,但Postman能成功;

  • 原因:MCP工具要求Content-Type: application/json,而requests默认不设,某些服务端严格校验;

  • 解法:显式设置头headers={"Content-Type": "application/json"};

  • 问题:本地测试OK,部署到K8s后MCP调用超时;

  • 原因:K8s Service DNS解析慢,http://mcp-service:8000在Pod内解析失败;

  • 解法:用http://mcp-service.default.svc.cluster.local:8000绝对域名,或在Deployment中加dnsPolicy: ClusterFirstWithHostNet;

  • 问题:MCP工具返回{"error": "invalid_request"},但Payload肉眼检查无误;

  • 原因:Payload中含中文,requests默认用utf-8编码,但某些Java服务端期望gbk;

  • 解法:json.dumps(payload, ensure_ascii=False).encode('gbk'),并设headers={"Content-Type": "application/json;charset=gbk"}。

注意:MCP规范要求工具端必须支持UTF-8,但现实中有不少遗留系统不遵守。遇到这种情况,宁可改服务端,也不要让Agent做编码转换——因为Agent的职责是决策,不是字符集适配。

6.4 LangChain内存暴涨?关闭这个日志开关立竿见影

LangChain默认开启详细日志,尤其verbose=True时,会把整个Prompt、所有检索文档、LLM原始响应全打到stdout。一个10MB的PDF切块后,日志可能达2GB/天,直接撑爆磁盘。解决方案:

import logging # 关闭LangChain内部日志 logging.getLogger("langchain").setLevel(logging.WARNING) # 或更彻底:只记录ERROR for name in ["langchain.chains", "langchain.agents", "langchain.retrievers"]: logging.getLogger(name).setLevel(logging.ERROR)

同时,在AgentExecutor中禁用verbose:

agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=False, # 关键! handle_parsing_errors=True # 防止格式错误崩溃 )

实测效果:日志体积从1.2GB/天降到8MB/天,磁盘IO压力下降97%。记住:生产环境的第一原则是“可观测但不拖慢”,日志不是越多越好,而是刚好够诊断。

我在实际项目中发现,最有效的学习方式不是从LangChain文档首页开始,而是打开GitHub的langchain/langchain仓库,直接搜RetrievalQA,看它的__init__.py和runnable.py——你会发现所谓“复杂API”,本质就是几行llm.invoke()和retriever.get_relevant_documents()的组合。Agent开发没有玄学,它只是把人类解决问题的步骤,用代码固化下来

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询