文章目录
第一部分:基础认知 —— Agent 到底是什么? 1.1 从 LLM 到 Agent 1.2 核心概念澄清:Agent、Skill、Tool、MCP 第二部分:深入 Function Calling 的底层机制 2.1 Function Calling 本质 2.2 LangChain 如何统一不同模型的差异 工具定义的统一:用 `@tool` 装饰器 响应解析的统一:全部转为 `AIMessage` 2.3 LangGraph 的角色:流程编排,不是解析器 💡 白话理解 🧱 代码上怎么体现 🚀 复杂任务才显出真价值 第三部分:传统 Function Calling vs. MCP 架构 3.1 传统方式:工具写死在 Agent 里 3.2 现代方式:用 MCP 实现工具的解耦与标准化 3.3 对比总结 第四部分:Agent 的架构设计:从 MVP 到完整四层 4.1 完整的四层架构 4.1.1 什么是 ReAct 循环? 4.1.2 四层架构的技术栈 4.1.3 Skill 到底属于哪一层? 4.1.4 人格提示词的两种加载方式 4.2 从 MVP 开始:两层架构 MVP 是什么? 最简代码示例(两层架构) 第五部分:Skill(技能)的加载与决策 5.1 Skill 的加载:渐进式披露 渐进式披露的核心设计思想 5.2 Skill 的决策:LLM 如何判断? 第六部分:Skill 与 MCP 的协同工作流(完整示例) 6.1 场景设定 6.2 准备工作:定义工具和 Skill 6.3 核心:用 LangGraph 组装 Agent 6.4 完整的执行流程 6.5 关键要点 第七部分:Agent 的自进化 —— 实用简化方案 7.1 进化思路 7.2 简化版设计 核心规则 所需工具 7.3 完整端到端实现 7.4 执行流程与进化演示 7.5 与完整方案的对比
第一部分:基础认知 —— Agent 到底是什么?
1.1 从 LLM 到 Agent
单纯的 LLM 就像一个博学的书呆子,只能一问一答,没有状态,不能执行动作。Agent = LLM + 工具 + 记忆 + 规划能力,它能自主拆解任务、调用工具、记住上下文,并完成真实世界的操作。
1.2 核心概念澄清:Agent、Skill、Tool、MCP
为了避免混淆,我们只使用以下四个明确定义的术语:
- Agent (智能体)
:负责规划、决策、调度的“大脑”。 - Skill (技能)
:特指知识型的 .md文件。它是教 Agent “怎么做”的操作手册(流程、规范、最佳实践),LLM需要阅读并理解它。 - Tool (工具)
:特指可执行的函数/方法。它是 Agent 完成具体动作的“手脚”(如查天气、发邮件、操作数据库),可以通过本地函数或 MCP Server 暴露。 - MCP (模型上下文协议)
:连接 Agent 和外部工具的标准化协议。它为 Agent 提供了“即插即用”调用各种工具的能力。同时,它也可以作为“传输管道”,被用来读取 Skill 文件。
协同公式:用户任务 → Agent 决策 → (需要流程指导时)通过 skill_load 工具加载 Skill 手册 → (需要执行动作时)通过 MCP 调用 Tool → 执行并反馈。
第二部分:深入 Function Calling 的底层机制
2.1 Function Calling 本质
它不是 LLM 暴露给外部的“接口”,而是 LLM 被训练出的结构化响应能力。 当你把 tools 参数(包含工具名称、描述和参数的 JSON Schema)传给 API 后,LLM 会返回一个特殊的 JSON 对象,例如:
{"tool_name": "get_weather","arguments": {"city": "北京"}}
你的代码拿到这个 JSON,去执行真正的函数,再把结果返回给 LLM 生成最终回复。
代码流程(以 OpenAI GPT-4o 为例):
- 定义工具,传给 API
:
tools = [{"type": "function","function": {"name": "get_weather","description": "获取指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string"}},"required": ["city"]}}}]response = openai.chat.completions.create(model="gpt-4o",messages=[{"role": "user", "content": "东京天气怎么样?"}],tools=tools)
- LLM 返回调用指令(不执行函数)
:
msg = response.choices[0].message# msg.tool_calls 中包含 function.name 和 arguments(JSON 字符串)
- 你的代码执行真正的函数
:
import jsonargs = json.loads(msg.tool_calls[0].function.arguments)result = get_weather(args["city"]) # 你写的真实函数
- 将结果返回给 LLM 生成最终回复
:
final = openai.chat.completions.create(model="gpt-4o",messages=[{"role": "user", "content": "东京天气怎么样?"},msg,{"role": "tool", "tool_call_id": msg.tool_calls[0].id, "content": result}])
本质:LLM 只输出 JSON 指令,真正执行全靠你的代码。LangChain 等框架将这个循环自动化了。
2.2 LangChain 如何统一不同模型的差异
LangChain 通过 模型适配器(Model Integration) 将不同厂商的差异封装起来,开发者只需用一套标准格式定义工具,剩下的转换全自动完成。
工具定义的统一:用 @tool 装饰器
你只需写一个 Python 函数并加上装饰器,LangChain 会自动为不同模型生成对应的 Schema:
from langchain_core.tools import tool@tooldef get_weather(city: str) -> str:"""获取指定城市的天气信息"""return f"{city}:晴,25°C"
当你选择不同模型时,LangChain 内部会将其转换为:
| OpenAI | {"type": "function", "function": {"name": "get_weather", "description": "...", "parameters": {...}}} |
| Anthropic | {"name": "get_weather", "description": "...", "input_schema": {...}} |
| Google Gemini | {"name": "get_weather", "description": "...", "parameters": {...}}tools.functionDeclarations) |
响应解析的统一:全部转为 AIMessage
无论哪个模型返回的原始响应,LangChain 都会将其解析为统一的 AIMessage 对象:
from langchain_core.messages import AIMessage# 所有模型的响应最终都变成这种标准格式ai_msg = AIMessage(content="", # 如果调用了工具,content 可为空tool_calls=[{"name": "get_weather","args": {"city": "北京"},"id": "call_xxx"}])
开发者只要检查 ai_msg.tool_calls 即可,无需关心底层是 OpenAI 的 message.tool_calls 还是 Anthropic 的 content[0].tool_use。
一句话总结:你用一套
@tool定义,LangChain 负责“翻译”成各模型的“方言”,并把各模型的“方言回复”统一解析成标准的AIMessage。
2.3 LangGraph 的角色:流程编排,不是解析器
💡 白话理解
把 Agent 想象成一场舞台剧:
- 演员
= LLM(负责思考和说话) - 道具组
= LangChain(负责递工具、翻译响应) - 导演
= LangGraph(指挥流程:谁上场、什么时候切换)
LangGraph 是导演,它不管台词怎么念(那是 LangChain 的事),只管“现在轮到谁上场”和“下一步去哪”。invoke 就是导演喊的那声“开机!”,整场戏自动按排练跑完。
🧱 代码上怎么体现
简单任务(一问一调一回):你原本需要手动写 while 循环来调度 LLM 和工具:
response = llm.invoke(user_input)while response.has_tool_calls:result = execute_tool(...)response = llm.invoke(result)
用 LangGraph 后,你把“角色”和“切换规则”定义成图,然后一声 invoke,它自动循环:
from langgraph.graph import StateGraph, ENDworkflow = StateGraph(dict)workflow.add_node("agent", call_llm)workflow.add_node("tools", call_tool)workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", "end": END})workflow.add_edge("tools", "agent")app = workflow.compile()result = app.invoke({"input": "查天气"})
你不再需要自己写 while,LangGraph 在后台自动管理循环和跳转。
🚀 复杂任务才显出真价值
当任务涉及多步判断、人工确认时,手动 while + if-else 会变得混乱且难以维护。场景:“查北京天气,如果下雨就给老板发邮件提醒(发前需要用户确认)”
手动实现需要状态变量和一堆分支:
step = "call_weather"while True:if step == "call_weather": ...elif step == "check_rain": ...elif step == "send_email": ...
每增加一个步骤,就要多一个 elif,逻辑纠缠。
LangGraph 实现:把每个步骤定义成独立节点,边定义路由,流程如蓝图般清晰:
workflow.add_node("weather", call_weather)workflow.add_node("after_weather", after_weather)workflow.add_node("wait_human", wait_for_human)workflow.add_node("send_email", send_email)workflow.add_conditional_edges("after_weather", lambda s: "wait_human" if "雨" in s else END)workflow.add_conditional_edges("wait_human", lambda s: "send_email" if user_says_yes else END)workflow.add_edge("send_email", END)
修改流程只需增删节点或改连线,不影响现有节点逻辑。
核心差别:手动
while让流程和业务逻辑耦合,LangGraph 把流程变成可独立维护的“蓝图”,复杂任务下优势巨大。
第三部分:传统 Function Calling vs. MCP 架构
3.1 传统方式:工具写死在 Agent 里
- 工具定义
:在代码中硬编码 JSON Schema。 - 调用逻辑
:手动解析 LLM 的 tool_calls并if-else判断执行哪个函数。 - 缺点
:工具和 Agent 强耦合,新增工具要改代码、重启服务,无法复用。
代码示例(OpenAI 原生调用,工具与 Agent 紧耦合):
import openai, json# 1. 硬编码工具定义tools = [{"type": "function","function": {"name": "get_weather","description": "获取指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string"}},"required": ["city"]}}}]# 2. Agent 调用response = openai.ChatCompletion.create(model="gpt-4o",messages=[{"role": "user", "content": "北京天气?"}],tools=tools)# 3. 手动解析与执行msg = response.choices[0].messageif msg.tool_calls:tool_call = msg.tool_calls[0]args = json.loads(tool_call.function.arguments)if tool_call.function.name == "get_weather":result = get_weather(args["city"]) # 本地函数# ... 更多工具需要更多 if-else
⚠️ 想加一个“发邮件”工具?你得修改
tools列表,增加elif分支,然后重启服务。
3.2 现代方式:用 MCP 实现工具的解耦与标准化
- 工具定义
:在独立的 MCP Server 中实现,通过 list_tools()动态获取。 - Agent
:作为 MCP Client 连接 Server,自动加载工具清单,统一用 function calling 格式发给 LLM。 - 优点
:工具热插拔、多 Agent 复用、统一安全治理。
代码示例(MCP Server + Agent 分离):
① MCP Server(tool_server.py)—— 工具定义与 Agent 解耦:
from fastapi import FastAPIfrom fastapi_mcp import FastApiMCPapp = FastAPI()@app.get("/weather/{city}", operation_id="get_weather")async def get_weather(city: str):return {"city": city, "weather": "晴", "temperature": "25°C"}mcp = FastApiMCP(app, name="工具箱", base_url="http://localhost:8000")mcp.mount() # 所有端点自动暴露为 MCP 工具
② Agent 侧(agent.py)—— 动态发现与调用:
from langchain_mcp_adapters.client import MCPClientfrom langchain.agents import create_openai_tools_agentasync def main():client = MCPClient(server_url="http://localhost:8000/mcp")tools = await client.load_tools() # 动态获取,无需硬编码agent = create_openai_tools_agent("gpt-4o", tools, system_prompt="你是助手")# ... 运行 agent
✅ 新增工具?只需在 MCP Server 上加一个端点,Agent 零改动即可自动发现。
3.3 对比总结
| 工具定义位置 | ||
| 新增工具 | ||
| 复用性 | ||
| 安全治理 |
第四部分:Agent 的架构设计:从 MVP 到完整四层
4.1 完整的四层架构
1. 大脑层 (LLM + 人格)2. 记忆与知识层 (短期/长期记忆、RAG, Skill)3. 规划与调度层 (ReAct 循环、任务编排)4. 工具/身体层 (MCP 工具、本地函数)
这是从真实产品中抽象出的通用模型,得到了社区和工业界的普遍认可。几乎所有 Agent 都可以映射到这四层,实际落地时会根据场景“剪裁、合并或强化”。
4.1.1 什么是 ReAct 循环?
ReAct = Reasoning + Acting(推理 + 行动),是目前 Agent 最主流的推理模式。
其核心是让 LLM 在解决问题时,严格按照 “思考 → 行动 → 观察 → 再思考…” 的循环进行,直到任务完成。
- Thought (思考)
:当前目标是什么?需要什么信息?应该用哪个工具? - Action (行动)
:调用具体工具(如查天气、计算器)。 - Observation (观察)
:工具返回了什么结果? - Final Answer (最终答案)
:根据观察结果,生成给用户的回复。
当你用 LangChain 或 LangGraph 创建 Agent 并开启 verbose=True,后台输出的正是这个循环过程。
4.1.2 四层架构的技术栈
| 1. 大脑层 | LLM APIsystem_prompt(MVP)或 persona.md 动态加载(正式) | |
| 2. 记忆与知识层 | 短期记忆 | |
| 3. 规划与调度层 | Agent 框架 | |
| 4. 工具/身体层 | 工具定义@tool 函数工具调用: Function Calling集成: langchain-mcp-adapters |
4.1.3 Skill 到底属于哪一层?
Skill (.md 文件) 属于第二层:记忆与知识层。它为 LLM 提供“流程性知识”,与 RAG 文档的“陈述性知识”互补。而用来加载 Skill 的 skill_load 工具本身属于第四层。
4.1.4 人格提示词的两种加载方式
| 硬加载 | system_prompt = "你是一个..." | |
| 动态加载 | persona.md 文件,像 Skill 一样在启动时通过 load_skill("persona") 加载 |
推荐做法:把人格当作一个特殊的 Skill,统一走渐进式披露通道。
- 架构统一
:人格和普通 Skill 用同一套机制管理。 - 灵活修改
:改人格只需编辑 .md文件,不用动代码。 - 可进化
:甚至可以让 Agent 自己更新人格(如 skill_update("persona", ...))。
在 MVP 示例(4.2 节)中我们用了硬加载,那是为了最快跑通。正式搭建时建议改用动态加载。
4.2 从 MVP 开始:两层架构
MVP 是什么?
MVP = Minimum Viable Product(最小可行产品)。用最少的代码、最简单的架构,先把核心功能跑通,验证想法可行后再逐步加东西。
最简代码示例(两层架构)
一个能查天气的 Agent,只用 Python + OpenAI API,不依赖任何框架:
import openai, json# ========== 第一层:工具(本地函数) ==========def get_weather(city: str) -> str:"""查询天气的工具函数"""weather_data = {"北京": "晴,25°C", "东京": "多云,18°C"}return weather_data.get(city, "未找到该城市天气")# 手动定义工具 schema(LLM 看的"菜单")tools = [{"type": "function","function": {"name": "get_weather","description": "查询指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string", "description": "城市名称"}},"required": ["city"]}}}]# ========== 第二层:大脑 + 规划 ==========def agent(user_input: str) -> str:# 1. 加载人格 + 发起第一次 LLM 调用messages = [{"role": "system", "content": "你是一个乐于助人的助手。"},{"role": "user", "content": user_input}]response = openai.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)msg = response.choices[0].message# 2. ReAct 循环:检查 LLM 是否想调用工具while msg.tool_calls:for tool_call in msg.tool_calls:args = json.loads(tool_call.function.arguments)result = get_weather(**args)messages.append(msg)messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": result})response = openai.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)msg = response.choices[0].messagereturn msg.contentprint(agent("北京今天天气怎么样?"))# 输出:北京今天天气晴朗,气温25°C。
这个不到 40 行的代码包含了 Agent 的所有核心要素:
| 大脑 (LLM) | openai.chat.completions.create() |
| 人格 | system: "你是一个乐于助人的助手。" |
| 工具 | get_weather()tools schema |
| 规划 (ReAct 循环) | while msg.tool_calls: |
| 记忆(短期) | messages |
何时升级?
工具数量 > 5 且需复用 → 引入 MCP 工具层。 多步推理、条件分支 → 引入 LangGraph 规划层。 跨会话记忆 → 引入 记忆层。
第五部分:Skill(技能)的加载与决策
5.1 Skill 的加载:渐进式披露
Skill 是知识型文件(.md),内容可能很长。为解决上下文窗口问题,必须采用 渐进式披露,按需分层加载:
| L1 元数据 | |||
| L2 核心指令 | SKILL.md 正文 | ||
| L3 资源 |
渐进式披露的核心设计思想
不是提前把所有技能内容塞给 LLM,而是把“查询技能目录”和“加载指定技能”这两个能力本身,作为工具(Tool)暴露给 LLM。 LLM 就像拿着一张菜单(list_skills 返回的技能名称+简介),想吃哪道菜再叫服务员去后厨拿完整的菜谱(load_skill 返回 .md 全文)。
启动时:LLM 只知道有 list_skills 工具,不知道任何技能内容(0 token)用户提问后:LLM 主动调用 list_skills → 拿到目录(L1,~100 tokens/个)决定使用后:LLM 调用 load_skill → 拿到完整手册(L2,只加载一个)
整个过程完全由 LLM 自主决策,加载技能本身也是 function calling 的一种。这就是渐进式披露能与 MCP 工具无缝融合的根本原因。
5.2 Skill 的决策:LLM 如何判断?
- 语义匹配(主要方式)
:LLM 根据用户意图和 Skill 元数据描述的匹配度自主决策。 - 关键词/意图路由(辅助方式)
:通过简单规则快速匹配。 - 向量检索(大规模场景)
:当 Skills 极多时,先用向量搜索筛选相关子集,再交给 LLM。
第六部分:Skill 与 MCP 的协同工作流(完整示例)
这个部分用一个完整的"发邮件"场景,展示知识(Skill)和行动(Tool)如何配合,以及代码层面如何实现。
6.1 场景设定
一个 MCP Server,暴露了业务工具: validate_email、compose_email、send_email几个 Skill 文件,存放在 ./skills/目录下一个 LangGraph Agent,负责调度一切
6.2 准备工作:定义工具和 Skill
① MCP Server 上的业务工具(tool_server.py):
from fastapi import FastAPIfrom fastapi_mcp import FastApiMCPapp = FastAPI()@app.post("/validate_email", operation_id="validate_email")async def validate_email(address: str) -> dict:"""校验收件人邮箱格式是否正确"""if "@" in address and "." in address:return {"valid": True, "address": address}return {"valid": False, "reason": "邮箱格式不正确"}@app.post("/compose_email", operation_id="compose_email")async def compose_email(to: str, subject: str, body: str) -> dict:"""生成邮件草稿并返回预览"""draft = f"收件人: {to}\n主题: {subject}\n正文: {body}"return {"draft": draft, "status": "ready_for_review"}@app.post("/send_email", operation_id="send_email")async def send_email(to: str, subject: str, body: str) -> dict:"""实际发送邮件"""return {"status": "sent", "message": f"邮件已成功发送至 {to}"}mcp = FastApiMCP(app, name="邮件工具箱", base_url="http://localhost:8000")mcp.mount()
② Skill 文件(./skills/send_email/SKILL.md):
# 发送邮件标准流程1. 使用 `validate_email` 校验收件人地址格式。2. 如果格式错误,提示用户修正后重新开始。3. 使用 `compose_email` 生成邮件草稿。4. 将草稿展示给用户确认(等待用户回复"确认"或"取消")。5. 用户确认后,使用 `send_email` 发送邮件。6. 发送成功后,向用户报告结果。
③ Skill 管理工具(本地 @tool 函数):
import osfrom langchain_core.tools import toolSKILLS_DIR = "./skills"@tooldef list_skills() -> str:"""列出所有可用技能的元数据(名称 + 简短描述)"""skills = []for skill_name in os.listdir(SKILLS_DIR):path = os.path.join(SKILLS_DIR, skill_name, "SKILL.md")if os.path.isfile(path):with open(path, "r", encoding="utf-8") as f:first_line = f.readline().strip("# ").strip()skills.append(f"- {skill_name}: {first_line}")return "\n".join(skills) if skills else "暂无可用技能。"@tooldef load_skill(skill_name: str) -> str:"""加载指定技能的完整 SKILL.md 内容"""path = os.path.join(SKILLS_DIR, skill_name, "SKILL.md")if not os.path.isfile(path):return f"技能 '{skill_name}' 不存在。"with open(path, "r", encoding="utf-8") as f:return f.read()
6.3 核心:用 LangGraph 组装 Agent
将上述所有工具(MCP 业务工具 + Skill 管理工具)统一注入 Agent:
import asynciofrom langgraph.prebuilt import create_react_agentfrom langchain_mcp_adapters.client import MCPClientfrom langchain_openai import ChatOpenAIasync def main():# ====== 第一步:加载所有工具 ======# 1. 从 MCP Server 动态加载业务工具mcp_client = MCPClient(server_url="http://localhost:8000/mcp")mcp_tools = await mcp_client.load_tools()# 2. 合并本地 Skill 管理工具all_tools = mcp_tools + [list_skills, load_skill]# ====== 第二步:创建 Agent(注入人格提示词) ======system_prompt = """你是一个专业的邮件助手。你有以下能力:1. 调用 `list_skills` 查看可用的操作手册。2. 当用户的任务需要标准流程指导时,先调用 `load_skill` 获取详细步骤,再按步骤执行。3. 严格按照加载的 Skill 文件中的流程操作,不要跳过任何步骤。4. 在发送邮件前,必须先展示草稿并等待用户确认。"""llm = ChatOpenAI(model="gpt-4o", temperature=0)agent = create_react_agent(llm, all_tools, system_prompt=system_prompt)# ====== 第三步:运行 ======result = await agent.ainvoke({"messages": [{"role": "user", "content": "帮我给 john@example.com 发一封邮件,主题'会议纪要',正文'明天下午三点开会'。"}]})print(result["messages"][-1].content)if __name__ == "__main__":asyncio.run(main())
6.4 完整的执行流程
| 1 | |||
| 2 | list_skills() | @tool | send_email: 发送邮件标准流程(L1 元数据) |
| 3 | load_skill("send_email") | @tool | |
| 4 | |||
| 5 | validate_email(...) | {"valid": true} | |
| 6 | |||
| 7 | compose_email(...) | ||
| 8 | |||
| 9 | |||
| 10 | send_email(...) | ||
| 11 |
6.5 关键要点
- 工具列表注入 LLM
:启动时通过 MCPClient.load_tools()动态获取 MCP 工具,与本地@tool函数合并,统一传给create_react_agent()。 - 渐进式披露
: list_skills只返回元数据(L1),LLM 决定使用后才调用load_skill获取完整内容(L2)。 - Skill 与 MCP 的分工
: - Skill (
.md文件):知识层,告诉 LLM “怎么做”。 - Skill 管理工具 (
list_skills/load_skill):工具层,负责"搬运知识"。 - MCP 业务工具 (
validate_email等):工具层,负责"执行动作"。 - LLM 的角色
:阅读 Skill 手册后,自主规划并按顺序调用 MCP 工具完成任务。
第七部分:Agent 的自进化 —— 实用简化方案
7.1 进化思路
完整自进化系统(如 Hermes)非常复杂,包含多条件触发、多层记忆、自动修复等机制。但对于一般项目,我们只需要一个最小可行自进化:通过简单的规则让 Agent 自主创建和更新 Skill,实现经验沉淀。
7.2 简化版设计
核心规则
skill_create 生成新技能 | skills/xxx.md | |
skill_update 修改相关技能 | SKILL.md | |
list_skills 检查有无新技能 |
所需工具
skill_create:创建新技能文件。 skill_update:更新已有技能文件。 list_skills/ load_skill:渐进式加载技能知识。
7.3 完整端到端实现
这里给出一个融合了业务工具、技能管理和自进化的完整可运行示例。Agent 执行退款任务时会自动沉淀技能。
① MCP Server (tool_server.py)
from fastapi import FastAPIfrom fastapi_mcp import FastApiMCPapp = FastAPI()@app.post("/validate_email", operation_id="validate_email")async def validate_email(address: str) -> dict:if "@" in address and "." in address:return {"valid": True, "address": address}return {"valid": False, "reason": "邮箱格式不正确"}@app.post("/compose_email", operation_id="compose_email")async def compose_email(to: str, subject: str, body: str) -> dict:draft = f"收件人: {to}\n主题: {subject}\n正文: {body}"return {"draft": draft, "status": "ready_for_review"}@app.post("/send_email", operation_id="send_email")async def send_email(to: str, subject: str, body: str) -> dict:return {"status": "sent", "message": f"邮件已发送至 {to}"}@app.get("/query_order", operation_id="query_order")async def query_order(order_id: str) -> dict:return {"order_id": order_id, "status": "已完成", "refundable": True}@app.post("/refund_create", operation_id="refund_create")async def refund_create(order_id: str) -> dict:return {"refund_id": "R123", "status": "退款已发起"}mcp = FastApiMCP(app, name="业务工具箱", base_url="http://localhost:8000")mcp.mount()
② 本地 Skill 管理工具 (skill_tools.py)
import osfrom langchain_core.tools import toolSKILLS_DIR = "./skills"os.makedirs(SKILLS_DIR, exist_ok=True)@tooldef list_skills() -> str:skills = []for skill_name in os.listdir(SKILLS_DIR):path = os.path.join(SKILLS_DIR, skill_name, "SKILL.md")if os.path.isfile(path):with open(path, "r", encoding="utf-8") as f:first_line = f.readline().strip("# ").strip()skills.append(f"- {skill_name}: {first_line}")return "\n".join(skills) if skills else "暂无可用技能。"@tooldef load_skill(skill_name: str) -> str:path = os.path.join(SKILLS_DIR, skill_name, "SKILL.md")if not os.path.isfile(path):return f"技能 '{skill_name}' 不存在。"with open(path, "r", encoding="utf-8") as f:return f.read()@tooldef skill_create(name: str, description: str, content: str) -> str:path = os.path.join(SKILLS_DIR, name, "SKILL.md")os.makedirs(os.path.dirname(path), exist_ok=True)with open(path, "w", encoding="utf-8") as f:f.write(f"# {description}\n\n{content}")return f"技能 '{name}' 已创建。"@tooldef skill_update(skill_name: str, new_content: str) -> str:path = os.path.join(SKILLS_DIR, skill_name, "SKILL.md")if not os.path.isfile(path):return f"技能 '{skill_name}' 不存在,请先用 skill_create 创建。"with open(path, "w", encoding="utf-8") as f:f.write(new_content)return f"技能 '{skill_name}' 已更新。"
③ 带自进化的 Agent 主程序 (agent.py)
import asynciofrom langgraph.prebuilt import create_react_agentfrom langchain_mcp_adapters.client import MCPClientfrom langchain_openai import ChatOpenAIfrom skill_tools import list_skills, load_skill, skill_create, skill_updateasync def main():# 加载工具mcp_client = MCPClient(server_url="http://localhost:8000/mcp")mcp_tools = await mcp_client.load_tools()all_tools = mcp_tools + [list_skills, load_skill, skill_create, skill_update]# 系统提示词(含自进化指令)system_prompt = """你是一个智能助手,拥有自我进化能力。## 核心行为准则1. 当需要执行标准化流程时,先调用 `list_skills` 查看是否有现成技能。2. 若有相关技能,调用 `load_skill` 获取详细步骤并严格遵循。3. 若无现成技能,使用通用推理完成任务。## 自我进化规则(重要!)1. **创建新技能**:完成一个任务后,若满足以下任一条件,请调用 `skill_create` 沉淀经验:- 任务中调用了 **3个以上不同的工具**- 这是一个你之前没做过的新流程- 用户要求记住这个做法2. **更新现有技能**:若用户明确指出你的做法有误或不够好,请调用 `skill_update` 修正对应技能。3. 技能名称用英文小写+下划线,内容简洁可操作,Markdown 格式。"""llm = ChatOpenAI(model="gpt-4o", temperature=0)agent = create_react_agent(llm, all_tools, system_prompt=system_prompt)# 运行:处理退款(新任务,将触发自进化)result = await agent.ainvoke({"messages": [{"role": "user", "content": "请帮我把订单 #1234 退款,并邮件通知客户 john@example.com。"}]})print("Agent 回复:", result["messages"][-1].content)if __name__ == "__main__":asyncio.run(main())
7.4 执行流程与进化演示
list_skills() | |||
query_order("1234") | |||
refund_create("1234") | |||
validate_email("john@example.com") | |||
send_email(...) | |||
| 触发进化 | |||
skill_create("handle_refund", "退款处理标准流程", "1. 用 query_order 查订单\n2. 若可退款,用 refund_create 发起退款\n3. 用 validate_email 校验邮箱\n4. 用 send_email 通知客户") | ./skills/handle_refund/SKILL.md | ||
第二次使用(进化已生效):
用户: 帮我把订单 #5678 也退款,通知 alice@example.com。Agent: 调用 list_skills → 发现 handle_refund 技能 → 调用 load_skill → 按步骤自动执行所有操作。
7.5 与完整方案的对比
.md 文件 + list_skills 发现 | |
skill_update | |
这个方案零额外框架、零额外服务,只靠 Prompt 规则和已有的 skill_create/skill_update 就能跑起来,适合日常项目使用。
夜雨聆风