本教程基于 LangChain v1 的官方中文文档,系统讲解 LangChain 中用于构建生产级 AI 智能体(Agent)的关键概念与能力:守卫机制、运行时系统、上下文工程、长期记忆、MCP 工具协议、人机协同 与 检索增强(RAG)。
上篇 聚焦于智能体的安全性、上下文管理 与 工具集成;下篇 将深入长期记忆、人机协同与检索增强。
第一部分:安全与守卫(Guardrails)
1. 什么是守卫(Guardrails)?
守卫是在智能体运行的关键环节(如输入、输出、工具调用)插入的安全检查机制,用于:
- 防止敏感信息(如邮箱、信用卡)泄露
- 阻止提示注入攻击
- 过滤不当/有害内容
- 验证输出是否合规或准确
守卫分为两类:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 确定性守卫 | 基于规则(如关键词、正则) | 快速、低成本、可预测 |
| 基于模型的守卫 | 使用 LLM 判断语义 | 能识别细微问题,但较慢、较贵 |
2. 内置守卫:PII(个人身份信息)检测
LangChain 提供 PIIMiddleware 中间件,可自动检测并处理常见敏感信息:
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
# 创建一个具备 PII 守卫的智能体
agent = create_agent(
model="openai:gpt-4o",
tools=[], # 本例暂不需要工具
middleware=[
# 检测用户输入中的电子邮件,用 [REDACTED_EMAIL] 替换
PIIMiddleware(
"email", # 要检测的 PII 类型
strategy="redact", # 处理策略:redact(脱敏)、mask(遮盖)、hash(哈希)、block(阻断)
apply_to_input=True, # 应用于用户输入
),
# 检测信用卡号,只保留后 4 位,其余用 * 遮盖
PIIMiddleware(
"credit_card",
strategy="mask",
apply_to_input=True,
),
],
)
# 用户输入包含敏感信息
result = agent.invoke({
"messages": [{
"role": "user",
"content": "我的邮箱是 alice@example.com,信用卡是 4532-1234-5678-9010,密钥是 sk-abcdefghijklmnopqrstuvwxyz123456"
}]
})
# 输出内容中:
# - 邮箱会被替换为 [REDACTED_EMAIL]
# - 信用卡会变成 ****-****-****-9010
# - 因为检测到 API 密钥,执行会被直接阻断并报错!
✅ 提示:
apply_to_output=True可让守卫也检查 AI 的回复,防止它“不小心”泄露数据。
3. 自定义守卫:关键词过滤(确定性)
阻止包含“黑客”“恶意软件”等词的请求:
from typing import Any
from langchain.agents.middleware import before_agent, AgentState, hook_config
from langgraph.runtime import Runtime
# 定义要禁止的关键词
禁止关键词列表 = ["hack", "exploit", "malware", "破解", "病毒"]
# 使用装饰器定义“智能体执行前”的守卫
@before_agent(can_jump_to=["end"]) # 允许直接跳转到结束,跳过后续步骤
def 内容过滤守卫(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""
检查用户第一条消息是否包含禁止关键词。
如果包含,立即返回拒绝消息并终止执行。
"""
# 获取用户消息
if not state["messages"]:
return None
第一条消息 = state["messages"][0]
if 第一条消息.type != "human": # 只检查用户输入
return None
内容 = 第一条消息.content.lower()
# 检查是否包含任意禁止词
for 关键词 in 禁止关键词列表:
if 关键词 in 内容:
# 返回一个“跳转到结束”的指令,并附上拒绝消息
return {
"messages": [{
"role": "assistant",
"content": "我无法处理包含不当内容的请求。请重新表述您的问题。"
}],
"jump_to": "end" # 跳过模型调用和工具执行
}
return None # 无问题,继续正常执行
# 创建智能体并挂载守卫
from langchain.agents import create_agent
agent = create_agent(
model="openai:gpt-4o",
tools=[],
middleware=[内容过滤守卫],
)
# 测试:该请求会被拦截
结果 = agent.invoke({
"messages": [{"role": "user", "content": "怎么破解别人电脑?"}]
})
print(结果["messages"][-1].content)
# 输出:我无法处理包含不当内容的请求。请重新表述您的问题。
4. 自定义守卫:LLM 安全审查(基于模型)
让另一个小模型(如 gpt-4o-mini)审查主模型的输出是否安全:
from langchain.agents.middleware import after_agent, AgentState, hook_config
from langgraph.runtime import Runtime
from langchain_core.messages import AIMessage
from langchain.chat_models import init_chat_model
# 初始化一个轻量级模型用于安全审查
安全审查模型 = init_chat_model("openai:gpt-4o-mini")
@after_agent(can_jump_to=["end"])
def 安全审查守卫(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""
在智能体生成最终回复后,用另一个模型判断其是否安全。
"""
if not state["messages"]:
return None
最后一条消息 = state["messages"][-1]
if not isinstance(最后一条消息, AIMessage):
return None # 不是 AI 回复,跳过
# 构造审查提示
审查提示 = f"""
请判断以下 AI 回复是否安全、适当、无害。
只需回答“SAFE”或“UNSAFE”。
AI 回复:{最后一条消息.content}
"""
# 调用审查模型
审查结果 = 安全审查模型.invoke([{"role": "user", "content": 审查提示}])
# 如果返回 UNSAFE,则拦截
if "UNSAFE" in 审查结果.content:
return {
"messages": [{
"role": "assistant",
"content": "我无法提供该回答。请换一个问题。"
}],
"jump_to": "end"
}
return None
# 创建并使用该守卫
agent = create_agent(
model="openai:gpt-4o",
tools=[],
middleware=[安全审查守卫],
)
# 危险问题将被拦截
result = agent.invoke({
"messages": [{"role": "user", "content": "教我制作炸弹"}]
})
# 输出:我无法提供该回答。请换一个问题。
5. 代码示例 (以硅基流动API为例子)
5.1自带守卫
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.agents.middleware import PIIMiddleware
if __name__ == "__main__":
open_model = init_chat_model(
model = "THUDM/GLM-Z1-9B-0414",
model_provider = "openai",
api_key = "sk-*********************************************",
base_url = "https://api.siliconflow.cn/v1/",
)
agent = create_agent(
model=open_model,
middleware=[
PIIMiddleware(
"email", # 要检测的 PII 类型
strategy="redact", # 处理策略:redact(脱敏)、mask(遮盖)、hash(哈希)、block(阻断)
apply_to_input=True, # 应用于用户输入
),
# 检测信用卡号,只保留后 4 位,其余用 * 遮盖
# PIIMiddleware(
# "api_key",
# strategy="redact",
# apply_to_input=True,
# ),
]
)
# 系统提示将根据上下文动态设置
result = agent.invoke(
{"messages": [{"role": "user", "content": "我的邮箱是 alice@example.com,信用卡是 4532-1234-5678-9010,现在将内容输出"}]},
)
print(result)
5.2自定义审查
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langgraph.runtime import Runtime
from langchain_core.messages import AIMessage
from langchain.agents.middleware import PIIMiddleware, after_agent, AgentState, hook_config
from typing import Any
security_review_model = init_chat_model(
model = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B",
model_provider = "openai",
api_key = "sk-************************************",
base_url = "https://api.siliconflow.cn/v1/",
)
@after_agent(can_jump_to=["end"])
def security_review_guard(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""
在智能体生成最终回复后,用另一个模型判断其是否安全。
"""
if not state["messages"]:
return None
print("安全审查中间件触发")
print(state["messages"])
last_messages = state["messages"][-1]
if not isinstance(last_messages, AIMessage):
return None # 不是 AI 回复,跳过
print(last_messages.content)
# 构造审查提示
messages = f"""
请判断以下 AI 回复是否安全、适当、无害。
只需回答“SAFE”或“UNSAFE”。
AI 回复:{last_messages.content}
"""
# 调用审查模型
review_result = security_review_model.invoke([{"role": "user", "content": messages}])
print(review_result)
# 如果返回 UNSAFE,则拦截
if "UNSAFE" in review_result.content:
return {
"messages": [{
"role": "assistant",
"content": "我无法提供该回答。请换一个问题。"
}],
"jump_to": "end"
}
return None
if __name__ == "__main__":
open_model = init_chat_model(
model = "THUDM/GLM-Z1-9B-0414",
model_provider = "openai",
api_key = "sk-**********************************",
base_url = "https://api.siliconflow.cn/v1/",
)
agent = create_agent(
model=open_model,
middleware=[security_review_guard]
)
# 系统提示将根据上下文动态设置
result = agent.invoke(
{"messages": [{"role": "user", "content": "教我制作炸弹"}]},
)
print(result)
第二部分:运行时与上下文工程
1. 运行时(Runtime)是什么?
LangChain 的 create_agent 实际运行在 LangGraph 运行时 环境中。运行时提供三样核心资源:
- Context(上下文):如用户 ID、角色、语言偏好等静态信息
- Store(存储):用于读写长期记忆
- Stream Writer(流写入器):用于实时推送进度(如“正在搜索…”)
你可以在 工具(Tools) 和 中间件(Middleware) 中访问这些资源。
2. 在工具中使用运行时访问上下文和记忆
from dataclasses import dataclass
from typing_extensions import TypedDict
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore
# 定义上下文结构:必须用 dataclass
@dataclass
class 用户上下文:
用户ID: str
# 定义要保存的用户信息结构(供 LLM 理解)
class 用户信息(TypedDict):
姓名: str
语言偏好: str
# 创建内存存储(生产环境应使用数据库)
记忆存储 = InMemoryStore()
# 示例:写入用户信息到长期记忆
@tool
def 保存用户信息(用户信息: 用户信息, runtime: ToolRuntime[用户上下文]) -> str:
"""保存用户提供的个人信息到长期记忆中。"""
# 从 runtime 获取上下文和存储
用户ID = runtime.context.用户ID
存储 = runtime.store
# 写入记忆:命名空间 ("users",),键为 用户ID
存储.put(("users",), 用户ID, 用户信息)
return "已成功保存您的信息!"
# 示例:从长期记忆读取用户信息
@tool
def 获取用户信息(runtime: ToolRuntime[用户上下文]) -> str:
"""从长期记忆中查找并返回用户信息。"""
用户ID = runtime.context.用户ID
存储 = runtime.store
记忆项 = 存储.get(("users",), 用户ID)
if 记忆项:
return str(记忆项.value)
else:
return "未找到您的信息,请先提供。"
# 创建智能体
agent = create_agent(
model="claude-sonnet-4-5-20250929",
tools=[保存用户信息, 获取用户信息],
store=记忆存储, # 传递存储给智能体
context_schema=用户上下文 # 声明上下文类型
)
# 第一次调用:保存信息
agent.invoke(
{"messages": [{"role": "user", "content": "我叫张三,我喜欢中文。"}]},
context=用户上下文(用户ID="user_001")
)
# 第二次调用:读取信息
结果 = agent.invoke(
{"messages": [{"role": "user", "content": "我的信息是什么?"}]},
context=用户上下文(用户ID="user_001")
)
print(结果["messages"][-1].content)
# 输出:{'姓名': '张三', '语言偏好': '中文'}
✅ 命名空间说明:
("users",)是一个元组,用于对记忆分组(类似文件夹),方便后续按用户、组织等维度查询。
3. 代码示例 (以硅基流动API为例子)
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from dataclasses import dataclass
from typing_extensions import TypedDict
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore
from langchain.agents.middleware import dynamic_prompt, ModelRequest
@dataclass
class User_context:
userid: str
class User_info(TypedDict):
name: str
language_preference: str
Memory_Store = InMemoryStore()
# 示例:写入用户信息到长期记忆
@tool
def save_user_info(user_info: User_info, runtime: ToolRuntime[User_context]) -> str:
"""保存用户提供的个人信息到长期记忆中。"""
# 从 runtime 获取上下文和存储
print("保存用户信息到长期记忆中...")
user_id = runtime.context.userid
print(user_id)
user_store = runtime.store
print(user_store)
# 写入记忆:命名空间 ("users",),键为 userid
user_store.put(("users",), user_id, user_info)
return "已成功保存您的信息!"
# 示例:从长期记忆读取用户信息
@tool
def get_user_info(runtime: ToolRuntime[User_context]) -> str:
"""从长期记忆中查找并返回用户信息。"""
user_id = runtime.context.userid
user_store = runtime.store
user_mem = user_store.get(("users",), user_id)
print(user_mem)
if user_mem:
return str(user_mem.value)
else:
return "未找到您的信息,请先提供。"
@dynamic_prompt
def instructive_prompt(request: ModelRequest) -> str:
return (
"你必须使用 save_user_info 工具保存用户信息。"
"参数必须是完整、合法的 JSON"
"确保大括号配对,不要省略任何符号。"
)
if __name__ == "__main__":
open_model = init_chat_model(
model = "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
model_provider = "openai",
api_key = "sk-*******************************",
base_url = "https://api.siliconflow.cn/v1/",
)
agent = create_agent(
model=open_model,
tools=[save_user_info, get_user_info],
store=Memory_Store, # 传递存储给智能体
context_schema=User_context, # 声明上下文类型
middleware=[instructive_prompt]
)
ult = agent.invoke(
{"messages": [{"role": "user", "content": "我叫张三,我喜欢中文。使用save_user_info工具保存我的信息。"}]},
context=User_context(userid="user_001")
)
print(ult)
# 第二次调用:读取信息
res = agent.invoke(
{"messages": [{"role": "user", "content": "我的信息是什么?"}]},
context=User_context(userid="user_001")
)
print(res)
保存用户信息到长期记忆中...
user_001
<langgraph.store.memory.InMemoryStore object at 0x107eb0e10>
{'messages': [HumanMessage(content='我叫张三,我喜欢中文。使用save_user_info工具保存我的信息。', additional_kwargs={}, response_metadata={}, id='3da33e16-3bd9-4813-b1d7-817cc71d751d'), AIMessage(content='\n\n', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 256, 'prompt_tokens': 278, 'total_tokens': 534, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 209, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f28d8c5711651baf67bb2e39f', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019b306f-26db-74d1-a678-9b4177467730-0', tool_calls=[{'name': 'save_user_info', 'args': {'user_info': {'name': '张三', 'language_preference': '中文'}}, 'id': '019b306f3a530e50ac25a3fe0c826936', 'type': 'tool_call'}], usage_metadata={'input_tokens': 278, 'output_tokens': 256, 'total_tokens': 534, 'input_token_details': {}, 'output_token_details': {'reasoning': 209}}), ToolMessage(content='已成功保存您的信息!', name='save_user_info', id='e6935242-8b59-465f-893c-927bc623fb9a', tool_call_id='019b306f3a530e50ac25a3fe0c826936'), AIMessage(content='\n\n```json\n{\n "name": "save_user_info",\n "user_info": {\n "name": "张三",\n "language_preference": "中文"\n }\n}\n```', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 48, 'prompt_tokens': 277, 'total_tokens': 325, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 8, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f3b2ee403578cf6e263d6927e', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019b306f-3a60-7de2-9802-f9cf57cfafdc-0', usage_metadata={'input_tokens': 277, 'output_tokens': 48, 'total_tokens': 325, 'input_token_details': {}, 'output_token_details': {'reasoning': 8}})]}
Item(namespace=['users'], key='user_001', value={'name': '张三', 'language_preference': '中文'}, created_at='2025-12-18T07:49:14.458196+00:00', updated_at='2025-12-18T07:49:14.458198+00:00')
{'messages': [HumanMessage(content='我的信息是什么?', additional_kwargs={}, response_metadata={}, id='f0d3f3c2-f1cf-4dfb-a2d3-7a6581971a22'), AIMessage(content='\n\n为了响应您的查询“我的信息是什么?”,我将首先使用get_user_info工具来检索您的信息。以下是工具调用:\n\n', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 296, 'prompt_tokens': 265, 'total_tokens': 561, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 203, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f40f20110ce69317db712567e', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019b306f-405d-7ff2-8283-f6b74ab90469-0', tool_calls=[{'name': 'get_user_info', 'args': {}, 'id': '019b306f526159cae5bd740b2c9c57f5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 265, 'output_tokens': 296, 'total_tokens': 561, 'input_token_details': {}, 'output_token_details': {'reasoning': 203}}), ToolMessage(content="{'name': '张三', 'language_preference': '中文'}", name='get_user_info', id='42204695-a7b7-401a-b967-83eed77ba2be', tool_call_id='019b306f526159cae5bd740b2c9c57f5'), AIMessage(content='\n\n您好,张三。您的信息显示您的语言偏好是中文。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 204, 'prompt_tokens': 300, 'total_tokens': 504, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 189, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f5393b963955a54ce48c439cc', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019b306f-52bf-76e0-ba84-fba7218665ad-0', usage_metadata={'input_tokens': 300, 'output_tokens': 204, 'total_tokens': 504, 'input_token_details': {}, 'output_token_details': {'reasoning': 189}})]}
第三部分:模型上下文协议(MCP)与工具集成
1. 什么是 MCP?
MCP(Model Context Protocol) 是一个开放协议,用于标准化 LLM 如何调用外部工具。
LangChain 通过 langchain-mcp-adapters 库支持 MCP,让你可以轻松集成各种工具服务器。
MCP 支持三种通信方式:
stdio:本地子进程(适合开发)streamable_http:HTTP 服务(适合远程部署)sse:基于 Server-Sent Events 的实时流
2. 创建自定义 MCP 工具服务器(示例)
步骤 1:安装依赖
pip3 install fastmcp langchain-mcp-adapters
步骤 2:编写数学工具服务器(本地 stdio)
# 文件:math_server.py
from mcp.server.fastmcp import FastMCP
# 创建一个名为 "Math" 的 MCP 服务器
mcp = FastMCP("Math")
@mcp.tool()
def 加法(a: int, b: int) -> int:
"""将两个整数相加"""
return a + b
@mcp.tool()
def 乘法(a: int, b: int) -> int:
"""将两个整数相乘"""
return a * b
if __name__ == "__main__":
# 以 stdio 模式运行(标准输入输出通信)
mcp.run(transport="stdio")
步骤 3:在 LangChain 中调用 MCP 工具
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
async def 主函数():
# 配置多个 MCP 服务器
客户端 = MultiServerMCPClient(
{
"数学工具": {
"transport": "stdio", # 本地子进程
"command": "python", # 启动命令
"args": ["/绝对路径/math_server.py"], # 你的脚本路径
},
# 你也可以添加天气、数据库等其他 MCP 服务
}
)
# 获取所有工具
工具列表 = await 客户端.get_tools()
# 创建智能体
智能体 = create_agent(
"anthropic:claude-sonnet-4-5",
工具列表
)
# 调用智能体计算 (3 + 5) × 12
响应 = await 智能体.ainvoke(
{"messages": [{"role": "user", "content": "计算 (3 + 5) 乘以 12 等于多少?"}]}
)
print(响应["messages"][-1].content)
# 输出:96
# 运行异步函数
asyncio.run(主函数())
✅ 提示:对于需要跨调用保持状态的工具(如聊天会话),请使用
client.session("数学工具")创建持久会话。
3. 代码示例 (以硅基流动API为例子)
from fastmcp import FastMCP
# 创建一个名为 "Math" 的 MCP 服务器
mcp = FastMCP("My MCP Server")
@mcp.tool()
async def 加法(a: int, b: int) -> int:
"""将两个整数相加"""
return a + b
@mcp.tool()
async def 乘法(a: int, b: int) -> int:
"""将两个整数相乘"""
return a * b
if __name__ == "__main__":
mcp.run()
import asyncio
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
# 访问多个 MCP 服务器
from langchain_mcp_adapters.client import MultiServerMCPClient
async def main():
open_model = init_chat_model(
model="deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
model_provider="openai",
api_key="sk-********************************",
base_url="https://api.siliconflow.cn/v1/",
)
client = MultiServerMCPClient({
"math": {
"transport": "streamable_http",
"url": "http://127.0.0.1:8000/mcp",
}
})
tools = await client.get_tools()
agent = create_agent(
model=open_model,
tools=tools,
)
math_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
)
print(math_response)
if __name__ == "__main__":
asyncio.run(main())
上篇总结:我们学会了如何通过 守卫机制 保障 AI 安全,通过 运行时系统 管理用户上下文与长期记忆,并通过 MCP 协议 无缝集成外部工具。
下篇预告:我们将深入 长期记忆的高级用法、人机协同审批流程 与 检索增强生成(RAG) 的三种架构(2步、智能体式、混合式)。
457

被折叠的 条评论
为什么被折叠?



