1. 从“会用”到“用好”:AI大模型应用的能力分水岭
很多人用AI大模型的路径都差不多:打开对话框,输入问题,等它吐出一段文字,复制粘贴,完事。这个阶段我称之为“问答模式”,本质上就是把大模型当成一个更聪明的搜索引擎在用。但如果你仔细观察身边那些真正把AI用出生产力的人,会发现他们的操作方式完全不同——他们不是在“问”AI,而是在“指挥”AI干活。这个区别,就是普通用户和高效使用者的分水岭。
我自己在这个转变上踩过不少坑。早期我也是把大模型当聊天机器人用,写文案、查资料、翻译文档,效率确实有提升,但总觉得差点意思。直到我开始接触Agent Skills、MCP协议、LangChain这些概念,才意识到问题的核心:大模型本身只是一个“大脑”,它没有手没有脚,不能主动去读取你的文件、不能调用你的数据库、不能操作你的浏览器。你要让它真正“下地干活”,就必须给它配上工具和技能。
这一篇要聊的,就是怎么把AI大模型从“嘴炮王者”变成“实干家”。核心围绕四个东西展开:Agent Skills(智能体技能)、SKILL.md(技能描述文件)、MCP(模型上下文协议)、LangChain(智能体编排框架)。这四个概念不是孤立的,它们之间有清晰的层次关系。我会从整体设计思路讲起,然后逐个拆解核心细节,再给出一套可复现的实操流程,最后把我遇到过的问题和排查经验整理出来。无论你是刚接触AI应用开发的新手,还是已经在用LangChain搭东西的老手,应该都能从中找到对自己有用的部分。
提示:这篇文章不涉及任何需要特殊网络环境才能访问的内容,所有工具和框架均为公开可获取的开源项目或正规商业产品。
2. 整体设计思路:为什么需要“技能”和“协议”
2.1 大模型的能力边界在哪里
先搞清楚一个基本问题:大模型能做什么,不能做什么。大模型的核心能力是理解和生成自然语言,它能读懂你的意图,能写出通顺的文字,能进行逻辑推理。但它有几个硬伤:第一,它不能主动获取实时信息,训练数据截止之后就一无所知;第二,它不能操作外部系统,不能帮你发邮件、查数据库、点按钮;第三,它没有持久记忆,每次对话都是从零开始。
这三个硬伤决定了,如果你只用一个裸的大模型,它能帮你做的事情非常有限。你问它“今天天气怎么样”,它只能根据训练数据编一个答案,因为它没有实时数据。你让它“帮我把这份Excel里的数据整理一下”,它做不到,因为它碰不到你的文件系统。
所以,要让大模型真正有用,就必须解决这三个问题:给它接上实时数据源、给它操作外部系统的能力、给它记住上下文和历史。这就是Agent Skills和MCP要解决的核心问题。
2.2 Agent Skills的本质:给大模型装“操作手册”
Agent Skills这个概念,说白了就是给大模型写一份“操作手册”,告诉它在什么场景下应该做什么、怎么做。你可以把它理解成给一个新员工写的SOP(标准作业程序)。新员工能力再强,如果不告诉他公司的流程、工具怎么用、遇到问题找谁,他也干不了活。
一个Skill通常包含几个要素:触发条件(什么情况下用这个技能)、执行步骤(具体怎么做)、所需工具(需要调用哪些外部能力)、输出格式(结果应该长什么样)。比如你做一个“日报生成”的Skill,触发条件是“用户要求生成日报”,执行步骤是“读取当日工作记录→提取关键事项→按模板组织→输出”,所需工具是“文件读取API”,输出格式是“Markdown格式的日报”。
这里的关键在于,Skill不是代码,而是描述。它用自然语言告诉大模型“你应该怎么做”,大模型根据这个描述来规划自己的行动。这跟传统的编程思路完全不同——传统编程是你写死每一步逻辑,Skill是你给大模型一个框架,让它自己填充细节。
2.3 SKILL.md的角色:技能的标准载体
SKILL.md就是承载Skill描述的文件格式。为什么用Markdown?因为大模型对Markdown的理解能力最强,结构清晰、层次分明,而且人类也能直接阅读和编辑。一个典型的SKILL.md文件长这样:
# 日报生成技能 ## 触发条件 当用户说“生成今天的日报”或“帮我写日报”时触发。 ## 执行步骤 1. 调用文件读取工具,获取 `~/worklog/today.md` 的内容 2. 提取其中的关键事项,按“完成/进行中/待办”分类 3. 按照公司日报模板组织内容 4. 输出Markdown格式的日报 ## 所需工具 - 文件读取工具(read_file) - 日期获取工具(get_current_date) ## 输出格式 参考 `~/templates/daily_report.md` 的格式这个文件的好处是,它既是给大模型看的指令,也是给人看的文档。你团队里任何人拿到这个文件,都能理解这个技能是干什么的、怎么工作的。而且修改起来极其方便,改几个字就能调整大模型的行为,不需要重新编译部署。
2.4 MCP协议:统一工具调用的“插座标准”
MCP的全称是Model Context Protocol,翻译过来叫“模型上下文协议”。你可以把它理解成AI世界的USB接口标准。在没有MCP之前,每个大模型平台调用外部工具的方式都不一样,OpenAI有一套Function Calling的格式,Anthropic有自己的一套,开源模型又各有各的做法。你想让大模型调用一个数据库,得针对每个平台写不同的适配代码,烦不胜烦。
MCP的出现就是为了解决这个问题。它定义了一套标准的协议,规定了大模型怎么发现工具、怎么调用工具、怎么接收结果。任何工具只要实现了MCP协议,就能被任何支持MCP的大模型调用。这就像USB接口统一了外设连接标准一样,你不需要关心鼠标是罗技的还是雷蛇的,插上就能用。
MCP的工作模式是这样的:你运行一个MCP Server,它暴露一组工具(比如“查询数据库”“发送邮件”“读取文件”)。大模型通过MCP Client连接到这个Server,发现有哪些工具可用,然后根据任务需要调用相应的工具。整个过程是标准化的,跟具体的大模型平台无关。
2.5 LangChain的定位:智能体的“总调度”
LangChain是一个用来构建大模型应用的框架。如果说MCP是插座标准,Agent Skills是操作手册,那LangChain就是整个系统的总调度。它负责把大模型、工具、记忆、技能这些东西串起来,形成一个完整的智能体。
LangChain的核心抽象包括:Model(大模型)、Tool(工具)、Agent(智能体)、Memory(记忆)、Chain(链)。你用LangChain搭一个智能体,大概的流程是:定义一个Agent,给它配一个大模型,挂上几个Tool,设置好Memory,然后Agent就会根据用户的输入,自动决定调用哪个工具、怎么处理结果、怎么组织回复。
LangChain的好处是它把很多底层细节封装好了,你不需要从零实现工具调用、上下文管理、错误处理这些东西。但坏处是抽象层次比较高,出了问题排查起来比较麻烦。我后面会专门讲这块的坑。
2.6 四者的协作关系
把这四个东西串起来看:LangChain是骨架,负责整体调度;MCP是神经,负责连接外部工具;Agent Skills是肌肉记忆,告诉智能体在特定场景下怎么做;SKILL.md是载体,把技能描述固化下来。一个完整的智能体应用,大概是这样的结构:
- LangChain Agent作为主控
- 通过MCP Client连接多个MCP Server
- 每个MCP Server提供一组工具
- Agent根据SKILL.md中的描述决定调用哪些工具
- 执行结果返回给Agent,Agent组织成最终回复
这个架构的好处是高度模块化。你想加一个新能力,只需要写一个MCP Server暴露新工具,再写一个SKILL.md描述怎么用这个工具,不需要改动核心逻辑。这种可扩展性,是构建复杂AI应用的基础。
3. 核心细节解析与实操要点
3.1 怎么写一份大模型能看懂的SKILL.md
写SKILL.md看着简单,实际上很考验功力。我见过太多人写的Skill,大模型根本执行不了,问题出在描述太模糊。比如“处理用户请求”这种描述,大模型看了等于没看,它不知道具体要做什么。
一份好的SKILL.md,核心原则是“具体到不需要思考”。你要假设读这个文件的是一个能力很强但完全不了解你业务的新人,他需要知道:什么情况下触发、第一步做什么、第二步做什么、每一步用什么工具、遇到异常怎么办、最终输出什么格式。
我总结了一个模板结构,实测下来大模型执行准确率最高:
# 技能名称 ## 何时使用 [具体的触发条件,越具体越好] ## 前置检查 [执行前需要确认的事项] ## 执行步骤 1. [第一步,包含调用的工具和参数] 2. [第二步,说明如何处理上一步的结果] 3. [第三步,说明如何组织输出] ## 异常处理 - 如果[某步骤]失败,则[备选方案] - 如果[某数据]缺失,则[默认行为] ## 输出要求 [格式、长度、语气等要求]这里有个关键技巧:步骤描述要用“动词开头”,比如“调用”“提取”“判断”“输出”,而不是“对……进行处理”这种被动描述。大模型对动词开头的指令执行得更准确。
另一个技巧是,在SKILL.md里可以嵌入示例。比如你写一个“代码审查”的技能,可以在文件末尾附上一段示例输入和期望输出。大模型看到示例后,执行准确率会明显提升。这跟Few-shot Prompting是一个道理。
3.2 MCP Server的搭建与工具暴露
搭建一个MCP Server,本质上就是把你已有的能力包装成标准接口。假设你有一个内部数据库,你想让大模型能查询它,你需要写一个MCP Server,暴露一个“query_database”工具。
MCP Server的实现方式取决于你用什么语言。Python的话,官方有mcp库可以用。一个最简单的MCP Server大概长这样:
from mcp.server import Server from mcp.types import Tool, TextContent server = Server("my-database-server") @server.list_tools() async def list_tools(): return [ Tool( name="query_database", description="查询内部数据库,输入SQL语句,返回查询结果", inputSchema={ "type": "object", "properties": { "sql": {"type": "string", "description": "要执行的SQL查询语句"} }, "required": ["sql"] } ) ] @server.call_tool() async def call_tool(name: str, arguments: dict): if name == "query_database": result = execute_sql(arguments["sql"]) return [TextContent(type="text", text=str(result))]这段代码的关键在于description字段。大模型就是靠这个描述来判断什么时候该调用这个工具的。所以描述要写得清楚:这个工具是干什么的、输入是什么、输出是什么。不要写“查询数据库”就完了,要写“查询内部销售数据库,支持标准SQL语句,返回JSON格式的查询结果”。
工具的参数定义也很重要。inputSchema用的是JSON Schema格式,你要把每个参数的类型、描述、是否必填都写清楚。大模型会根据这个Schema来构造调用参数,Schema写得越清楚,大模型传参的准确率越高。
3.3 LangChain Agent的配置与调优
用LangChain搭Agent,核心是选对Agent类型和配好工具。LangChain提供了几种Agent类型,常用的有zero-shot-react-description、conversational-react-description、openai-functions等。选哪种取决于你的场景:如果只是简单的工具调用,用openai-functions最省事;如果需要多轮对话带记忆,用conversational-react-description;如果需要复杂的推理链,可以考虑plan-and-execute。
配置Agent的时候,有几个参数特别关键:
max_iterations:最大迭代次数,控制Agent最多执行多少步。设太小了任务完不成,设太大了可能陷入死循环。一般设10-15比较合适。max_execution_time:最大执行时间,防止Agent卡死。根据任务复杂度设,一般60-120秒。early_stopping_method:提前停止策略,当Agent认为任务完成时怎么处理。一般用force,强制返回当前结果。handle_parsing_errors:解析错误处理,当大模型输出的格式不对时怎么办。建议设为True,让LangChain自动重试。
工具配置方面,每个工具都要有清晰的name和description。name要简短且唯一,description要详细说明工具的用途和输入输出。我见过有人把工具名写成tool1、tool2,大模型根本不知道什么时候该用哪个。工具名应该像search_web、read_file、send_email这样,一看就知道是干什么的。
3.4 工具调用的参数传递与结果处理
工具调用最容易出问题的地方是参数传递。大模型根据工具的Schema生成参数,但有时候会生成格式不对的参数。比如Schema要求整数,它传了个字符串;Schema要求数组,它传了个对象。这种问题在LangChain里通常表现为ValidationError。
处理这类问题,我的经验是在工具函数入口加一层参数校验和转换。比如:
def safe_int(value): if isinstance(value, int): return value if isinstance(value, str): return int(value.strip()) raise ValueError(f"无法转换为整数: {value}")然后在工具函数里用这个safe_int来处理参数。这样即使大模型传参格式不太对,也能自动纠正。
结果处理方面,工具返回的结果要尽量结构化。不要返回一大段自然语言,而是返回JSON格式的数据。大模型对结构化数据的处理能力更强,而且后续步骤如果需要提取字段,结构化数据更方便。如果工具返回的是自然语言,建议在返回前做一次结构化转换。
3.5 多工具协作的编排策略
当Agent挂载了多个工具时,怎么编排它们的调用顺序是个关键问题。LangChain的ReAct模式是让大模型自己决定调用顺序,但实际用下来,大模型有时候会选错工具或者顺序搞反。
我的做法是在SKILL.md里明确写出工具调用的顺序。比如一个“竞品分析”的技能,我会写清楚:第一步用search_web搜索竞品信息,第二步用read_file读取内部产品文档,第三步用compare_data对比数据,第四步用generate_report生成报告。这样大模型就不会乱来了。
另一个技巧是给工具设置优先级。在工具描述里加上“优先使用此工具”或“仅当其他工具不可用时使用此工具”这样的提示。大模型对这类提示的遵循度还不错。
如果工具之间有依赖关系,比如工具B的输入是工具A的输出,那就在SKILL.md里明确写出来:“将第一步的输出作为第二步的输入”。不要让大模型自己去推断依赖关系,它有时候会搞错。
4. 实操过程与核心环节实现
4.1 环境准备与依赖安装
先说一下我的环境:Python 3.11,macOS,16GB内存。这个配置跑LangChain和MCP Server完全够用。如果你用Windows,建议用WSL2,因为有些MCP Server在Windows原生环境下会有路径问题。
安装依赖:
pip install langchain langchain-openai mcp python-dotenv如果你要用Anthropic的模型,再加一个:
pip install langchain-anthropic环境变量配置,创建一个.env文件:
OPENAI_API_KEY=你的key ANTHROPIC_API_KEY=你的key这里注意,不要把key硬编码在代码里,用python-dotenv加载。我见过有人把key提交到GitHub上,结果被人盗刷了几百美元。
4.2 搭建第一个MCP Server
我们来搭一个实用的MCP Server:文件操作服务。它暴露三个工具:读取文件、写入文件、列出目录。
import os from mcp.server import Server from mcp.types import Tool, TextContent server = Server("file-server") @server.list_tools() async def list_tools(): return [ Tool( name="read_file", description="读取指定路径的文件内容,返回文本", inputSchema={ "type": "object", "properties": { "path": {"type": "string", "description": "文件的绝对路径"} }, "required": ["path"] } ), Tool( name="write_file", description="将内容写入指定路径的文件,如果文件不存在则创建", inputSchema={ "type": "object", "properties": { "path": {"type": "string", "description": "文件的绝对路径"}, "content": {"type": "string", "description": "要写入的内容"} }, "required": ["path", "content"] } ), Tool( name="list_directory", description="列出指定目录下的所有文件和子目录", inputSchema={ "type": "object", "properties": { "path": {"type": "string", "description": "目录的绝对路径"} }, "required": ["path"] } ) ] @server.call_tool() async def call_tool(name: str, arguments: dict): if name == "read_file": with open(arguments["path"], "r", encoding="utf-8") as f: content = f.read() return [TextContent(type="text", text=content)] elif name == "write_file": with open(arguments["path"], "w", encoding="utf-8") as f: f.write(arguments["content"]) return [TextContent(type="text", text=f"已写入 {arguments['path']}")] elif name == "list_directory": entries = os.listdir(arguments["path"]) return [TextContent(type="text", text="\n".join(entries))]这个Server跑起来后,大模型就能通过MCP协议调用这三个工具了。注意read_file和write_file的路径参数,我要求传绝对路径,避免相对路径带来的歧义。
4.3 编写SKILL.md并接入LangChain
现在写一个“代码审查”的SKILL.md:
# 代码审查技能 ## 何时使用 当用户要求审查代码、检查代码质量、或提供代码改进建议时使用。 ## 前置检查 - 确认用户提供了代码文件路径或代码内容 - 确认审查的语言类型(Python/JavaScript/Java等) ## 执行步骤 1. 如果用户提供的是文件路径,调用 `read_file` 读取文件内容 2. 分析代码,检查以下方面: - 语法错误和潜在bug - 代码风格一致性 - 性能问题 - 安全隐患 3. 按严重程度分类问题:严重/警告/建议 4. 对每个问题给出具体的修改建议和示例代码 5. 输出审查报告 ## 异常处理 - 如果文件不存在,提示用户检查路径 - 如果代码语言无法识别,默认按Python处理 ## 输出要求 - 使用Markdown格式 - 每个问题包含:位置、问题描述、修改建议 - 最后给出总体评分(1-10分)然后在LangChain里加载这个Skill:
from langchain.agents import initialize_agent, AgentType from langchain_openai import ChatOpenAI from langchain.tools import Tool from mcp_client import MCPClient # 连接MCP Server mcp_client = MCPClient("http://localhost:8000") # 把MCP工具包装成LangChain Tool tools = [] for mcp_tool in mcp_client.list_tools(): tools.append(Tool( name=mcp_tool.name, description=mcp_tool.description, func=lambda input, t=mcp_tool: mcp_client.call_tool(t.name, input) )) # 加载SKILL.md作为系统提示 with open("skills/code_review.md", "r") as f: skill_prompt = f.read() # 初始化Agent llm = ChatOpenAI(model="gpt-4", temperature=0) agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True, max_iterations=10, early_stopping_method="force", agent_kwargs={"system_message": skill_prompt} ) # 执行任务 result = agent.run("请审查 ~/projects/demo.py 这个文件") print(result)这段代码的关键在于agent_kwargs={"system_message": skill_prompt},把SKILL.md的内容作为系统提示注入。这样Agent在执行任务时,就会按照SKILL.md中描述的步骤来操作。
4.4 完整案例:自动生成周报
我们来走一个完整的案例:自动生成周报。这个案例会用到文件读取、日期获取、文本生成三个能力。
首先,MCP Server需要增加一个获取日期的工具:
from datetime import datetime @server.list_tools() async def list_tools(): return [ # ... 之前的工具 ... Tool( name="get_current_date", description="获取当前日期,返回格式为YYYY-MM-DD", inputSchema={"type": "object", "properties": {}} ) ] @server.call_tool() async def call_tool(name: str, arguments: dict): # ... 之前的处理 ... if name == "get_current_date": return [TextContent(type="text", text=datetime.now().strftime("%Y-%m-%d"))]然后写周报生成的SKILL.md:
# 周报生成技能 ## 何时使用 当用户说“生成本周周报”或“帮我写周报”时触发。 ## 执行步骤 1. 调用 `get_current_date` 获取当前日期 2. 计算本周的日期范围(周一到周五) 3. 调用 `read_file` 读取 `~/worklog/` 目录下本周的所有日志文件 4. 从日志中提取关键事项,按以下分类整理: - 已完成事项 - 进行中事项 - 遇到的问题 - 下周计划 5. 按照周报模板组织内容 6. 调用 `write_file` 将周报保存到 `~/reports/weekly_YYYY-MM-DD.md` ## 输出要求 - 使用Markdown格式 - 每个事项用一句话概括 - 问题部分要包含解决方案或需要的支持执行这个Agent:
result = agent.run("帮我生成本周周报")Agent会自动执行:获取日期→计算周范围→读取日志→提取事项→生成周报→保存文件。整个过程不需要人工干预。
4.5 参数计算与选择过程
在配置Agent的时候,有几个参数需要根据实际情况计算。我拿max_iterations举例说明怎么定这个值。
假设你的任务平均需要调用3个工具,每个工具调用算一步,加上大模型思考和最终输出,大概需要5-7步。那么max_iterations至少设10,留一些余量。但如果设太大,比如50,当Agent陷入循环时,它会一直调用工具直到达到上限,浪费时间和token。
我的经验公式是:max_iterations = 预期工具调用数 × 2 + 3。比如预期调用3个工具,那就是3×2+3=9,取整设10。
max_execution_time的计算类似。每个工具调用平均耗时2-5秒,大模型思考耗时3-10秒。如果预期7步,那就是7×(5+10)=105秒,设120秒比较安全。
这些参数不是固定的,需要根据实际运行情况调整。我建议先用保守值跑几次,观察实际耗时和步数,再优化。
5. 常见问题与排查技巧实录
5.1 工具调用失败排查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent不调用任何工具 | 工具描述不清晰 | 检查工具description是否说明了用途 | 重写description,明确工具功能和适用场景 |
| 调用工具时报ValidationError | 参数类型不匹配 | 打印大模型生成的参数 | 在工具函数入口加类型转换 |
| 工具调用后Agent不继续 | 返回值格式不对 | 检查工具返回的内容 | 确保返回TextContent类型 |
| Agent陷入循环调用 | 任务目标不明确 | 查看Agent的思考过程 | 在SKILL.md中明确终止条件 |
| MCP连接失败 | Server未启动或端口错误 | 检查Server日志和端口 | 确认Server运行且端口正确 |
| 工具调用超时 | 工具执行时间过长 | 检查工具函数执行时间 | 增加超时设置或优化工具性能 |
5.2 大模型“不听话”的几种典型情况
第一种:大模型不按SKILL.md的步骤走。这种情况通常是因为SKILL.md的描述不够具体,或者步骤之间有歧义。解决办法是把步骤拆得更细,每一步只做一件事,并且用明确的动词开头。
第二种:大模型选错工具。比如该用read_file的时候用了search_web。这通常是因为两个工具的description太相似。解决办法是在description里明确区分使用场景,比如read_file写“读取本地文件”,search_web写“搜索互联网信息”。
第三种:大模型生成的参数格式不对。比如Schema要求数组,它生成了字符串。解决办法是在工具函数里做参数兼容处理,同时可以在SKILL.md里加一个参数示例,告诉大模型正确的格式。
第四种:大模型提前终止任务。比如任务还没完成就返回了结果。这通常是因为early_stopping_method设成了force,而大模型误判任务已完成。解决办法是在SKILL.md里明确写出完成条件,比如“必须生成报告文件后才能结束”。
5.3 性能优化的几个实用技巧
第一个技巧:缓存工具调用结果。如果某个工具调用结果在短时间内不会变化,可以加一层缓存。比如读取文件内容,如果文件没修改,第二次读取可以直接用缓存。LangChain有内置的Cache机制,配置一下就行。
第二个技巧:并行调用无依赖的工具。如果两个工具之间没有依赖关系,可以让它们并行执行。LangChain的AgentExecutor支持并行工具调用,在SKILL.md里标注哪些步骤可以并行即可。
第三个技巧:限制上下文长度。Agent执行过程中会产生大量中间结果,如果不加限制,上下文会越来越长,导致token消耗剧增。可以在Agent配置里设置max_tokens,或者在SKILL.md里要求大模型对中间结果做摘要。
第四个技巧:用更小的模型做工具调用决策。工具调用决策不需要太强的推理能力,用GPT-3.5或者更小的模型就够了。只在最终生成内容时用GPT-4。这样能大幅降低成本。
5.4 安全与权限控制
让大模型操作外部系统,安全是个必须考虑的问题。我踩过的坑包括:大模型误删了文件、大模型把敏感数据写到了日志里、大模型调用了不该调用的接口。
我的做法是加三层防护:第一层,在MCP Server层面做权限控制,敏感操作需要额外的token验证;第二层,在工具函数里做输入校验,比如文件路径必须在指定目录下,SQL语句必须是SELECT开头;第三层,在SKILL.md里写明禁止操作,比如“禁止删除文件”“禁止修改生产数据库”。
另外,所有工具调用都要记日志。日志内容包括:调用时间、工具名、参数、返回值、执行结果。出了问题可以追溯。
5.5 调试与日志记录
LangChain的verbose=True会打印Agent的思考过程,调试的时候很有用。但生产环境建议关掉,或者把日志写到文件里。
我习惯在MCP Server里也加日志:
import logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('mcp_server.log'), logging.StreamHandler() ] ) @server.call_tool() async def call_tool(name: str, arguments: dict): logging.info(f"调用工具: {name}, 参数: {arguments}") try: result = await execute_tool(name, arguments) logging.info(f"工具 {name} 执行成功") return result except Exception as e: logging.error(f"工具 {name} 执行失败: {str(e)}") raise这样出问题的时候,翻日志就能定位到是哪一步出了错。
5.6 版本兼容性避坑
LangChain的版本更新非常快,不同版本之间的API差异很大。我遇到过升级LangChain后,原来的Agent代码跑不起来的情况。建议在requirements.txt里锁定版本:
langchain==0.1.0 langchain-openai==0.0.5 mcp==0.1.0MCP协议本身也在演进,不同版本的MCP Server和Client之间可能有兼容性问题。建议Server和Client用同一个版本。
另外,大模型的API也在变。OpenAI的Function Calling格式改过好几次,Anthropic的Tool Use格式也调整过。如果你的代码依赖特定的API格式,升级前一定要看Changelog。
6. 从单技能到技能库:规模化扩展的思路
当你掌握了单个Skill的开发和接入之后,下一步自然是构建一个技能库。我目前的技能库里有二十多个Skill,覆盖了日常工作的方方面面:代码审查、文档生成、数据分析、竞品调研、会议纪要整理等等。
技能库的管理有几个要点。第一是命名规范,我用领域-功能.md的格式,比如dev-code_review.md、doc-meeting_notes.md,这样一眼就能看出技能属于哪个领域。第二是版本控制,每个Skill的修改都走Git,方便回溯和协作。第三是定期清理,有些Skill用了几次发现效果不好,或者场景已经不需要了,就及时删掉,避免技能库臃肿。
技能之间的组合也很重要。比如“竞品调研”这个任务,可以拆成“搜索竞品信息”“读取内部文档”“对比分析”“生成报告”四个子技能,每个子技能独立维护,需要的时候组合起来用。这种模块化的思路,让技能库的扩展变得非常灵活。
我现在遇到一个新任务,第一反应不是“怎么写代码实现”,而是“有没有现成的Skill可以用,没有的话怎么组合出来”。这种思维方式的转变,才是高效使用AI大模型的核心。
最后分享一个我最近在用的技巧:把常用的SKILL.md放在一个目录里,用LangChain的DirectoryLoader自动加载。这样新增技能只需要往目录里扔一个md文件,不需要改代码。配合MCP的动态工具发现,整个系统就是自扩展的。新工具接入后,写一个对应的SKILL.md描述怎么用,Agent就能自动学会这个新能力。这套机制跑通之后,我的AI助手是真的能“下地干活”了,不只是聊天。