LangChain 工程化实战:从核心概念到生产级架构
LangChain 工程化实战:从核心概念到生产级架构
适用版本:LangChain 1.0+(兼容 0.3.x 核心 API)
技术栈:Python 3.10+ / LangChain / LangGraph / Redis Stack
定位:团队内部技术分享,聚焦工程决策与架构设计
目录
- LangChain 架构全景
- 环境工程与多模型接入
- Model I/O:标准化模型交互
- Prompt Engineering 工程化
- Output Parser 结构化输出
- LCEL 链式调用与工作流编排
- Memory 记忆系统
- Tools / Function Calling
- 向量化与 RAG 检索增强生成
- MCP 模型上下文协议
- Agent 智能体
1. LangChain 架构全景
1.1 本质定义
LangChain 是一套将大语言模型与外部世界连接起来的工程化框架。核心思想:以 LLM 为推理引擎,通过标准化的组件接口,将模型与数据源、工具、记忆、检索策略等外部能力组合成可编排的工作流。
1.2 生态系统分层架构(v1.0)
┌─────────────────────────────────────────────────────────┐
│ 部署层 (Deployment) │
│ LangGraph Cloud(云端部署)| LangSmith(可观测性平台) │
├─────────────────────────────────────────────────────────┤
│ 组件层 (Components) │
│ langchain-community / langchain-openai / ... │
│ 第三方集成:模型、向量库、工具、文档加载器 │
├─────────────────────────────────────────────────────────┤
│ 架构层 (Architecture) │
│ langchain-core(基础抽象 + LCEL) │
│ LangGraph(图状态机,支持循环/条件/并行工作流) │
└─────────────────────────────────────────────────────────┘
核心模块划分(v1.0 模块化拆分后):
| 包名 | 职责 |
|---|---|
langchain-core |
基础抽象(Runnable、Message、Prompt等)+ LCEL 表达式语言 |
langchain-community |
第三方集成(模型、工具、向量库等社区维护包) |
langchain-openai |
OpenAI 专用适配器(ChatOpenAI、Embeddings等) |
langgraph |
图状态机,协调多 Chain/Agent/Tool,支持循环调用 |
langsmith |
可观测性:调试、评测、Prompt 管理、性能监控 |
1.3 六大核心组件
组件间耦合度极低,无固定调用顺序,可自由组合:
| 组件 | 职责 |
|---|---|
| Models | 统一模型调用接口(LLM / Chat / Embedding) |
| Prompts | 提示词模板管理,支持多角色、多变量 |
| Chains | 通过 LCEL 将多个组件串联为数据处理管道 |
| Memory | 对话上下文持久化,支持内存/Redis/Postgres等后端 |
| Retrieval | RAG 检索:文档加载 → 分割 → 向量化 → 相似性检索 |
| Agents | 基于 ReAct 模式的自主决策引擎,动态调用工具 |
1.4 已知工程痛点
在工程落地中需正视以下问题:
- 文档滞后:迭代频率极高(约 2-3 天一版本),文档与代码脱节是常态,应以 API 源码为准
- 抽象层过深:为兼容所有模型和数据库,封装层数多,调用栈深,调试成本高。不是运行慢,是认知负担重
- 版本不兼容:小版本升级可能导致 API 变更,需锁定版本并关注 Breaking Changes
- Agent 模块持续重构:v0.3 的
AgentExecutor在 v1.0 中已逐步向 LangGraph 迁移
2. 环境工程与多模型接入
2.1 依赖安装
# 配置清华源(可选)
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# 核心依赖
pip install langchain # 主框架
pip install langchain-openai # OpenAI 兼容接口
pip install openai # OpenAI SDK
pip install python-dotenv # 环境变量管理
pip install langchain-core # 基础抽象层
2.2 大模型服务平台选型
| 平台 | 特点 | 接入方式 |
|---|---|---|
| 阿里云百炼 | 通义千问系列,国内访问稳定 | OpenAI 兼容协议 |
| DeepSeek | 高性价比,支持深度推理模式 | OpenAI 兼容协议 |
| OpenRouter | 多模型聚合网关,一站式切换 | OpenAI 兼容协议 |
| 硅基流动 | 国内开源模型聚合 | OpenAI 兼容协议 |
| Ollama | 本地部署,数据不出域 | 本地 HTTP 接口 |
核心原则:所有调用均基于 OpenAI 协议标准,通过 base_url + api_key 切换不同平台,实现多模型热切换。
2.3 模型初始化(v1.0 主流方式)
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv(encoding='utf-8')
# 统一入口:init_chat_model(推荐,自动推断 provider)
model = init_chat_model(
model="qwen-plus",
model_provider="openai",
api_key=os.getenv("QWEN_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# DeepSeek 接入(无需 base_url,langchain-deepseek 内置)
from langchain_deepseek import ChatDeepSeek
ds_model = ChatDeepSeek(
model="deepseek-chat", # V3.2 非思考模式
# model="deepseek-reasoner", # V3.2 深度思考模式
temperature=0,
api_key=os.getenv("DEEPSEEK_API_KEY"),
)
2.4 企业级封装模式
from langchain_openai import ChatOpenAI
from langchain_core.exceptions import LangChainException
import logging, os
from dotenv import load_dotenv
load_dotenv(encoding='utf-8')
logger = logging.getLogger(__name__)
def init_llm_client() -> ChatOpenAI:
api_key = os.getenv("QWEN_API_KEY")
if not api_key:
raise ValueError("环境变量 QWEN_API_KEY 未配置")
return ChatOpenAI(
model="deepseek-v3.2",
api_key=api_key,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
temperature=0.7,
max_tokens=2048
)
def main():
try:
llm = init_llm_client()
# 流式输出(生产环境推荐)
for chunk in llm.stream("介绍 LangChain,300 字以内"):
print(chunk.content, end="")
except ValueError as e:
logger.error(f"配置错误:{e}")
except LangChainException as e:
logger.error(f"模型调用失败:{e}")
if __name__ == "__main__":
main()
2.5 Ollama 本地部署
适用于数据敏感场景,模型完全本地化运行:
# 安装并拉取模型
ollama pull qwen:4b
ollama pull deepseek-r1:14b
# 验证服务(默认端口 11434)
netstat -ano | findstr 11434
# LangChain 整合 Ollama
# pip install -qU langchain-ollama
from langchain_ollama import ChatOllama
model = ChatOllama(model="qwen:4b", base_url="http://localhost:11434")
print(model.invoke("你好").content)
3. Model I/O:标准化模型交互
3.1 核心设计
Model I/O 是 LangChain 与模型交互的标准化层,覆盖完整的数据流:
输入格式化(Format) → 模型推理(Predict) → 输出解析(Parse)
PromptTemplate LLM/ChatModel OutputParser
3.2 消息类型体系
LangChain 定义了四种核心消息角色,与 OpenAI 协议对齐:
| 消息类型 | type | 用途 |
|---|---|---|
SystemMessage |
"system" |
设定 AI 行为边界、角色定位(并非所有模型都支持) |
HumanMessage |
"user" / "human" |
用户原始输入 |
AIMessage |
"ai" |
模型输出,可包含文本或工具调用请求 |
ToolMessage |
"tool" |
工具/函数调用的返回结果 |
所有消息均包含 type、content、response_metadata 等基础属性。
3.3 模型调用方式
| 方法 | 模式 | 适用场景 |
|---|---|---|
invoke() |
同步单次 | 常规问答 |
ainvoke() |
异步单次 | 高并发 Web 服务(FastAPI) |
stream() |
同步流式 | 实时打字机效果,逐步返回 |
astream() |
异步流式 | 高并发 + 流式 |
batch() |
同步批量 | 批量处理,提升吞吐量 |
abatch() |
异步批量 | 高并发批量任务 |
3.4 openai.OpenAI vs langchain_openai.ChatOpenAI
| 维度 | openai.OpenAI |
ChatOpenAI |
|---|---|---|
| 定位 | 官方原生 SDK,底层工具 | LangChain 高层适配器 |
| 生态 | 独立,无框架依赖 | 融入 LangChain 生态(Chain/Agent/Memory) |
| 适用 | 简单调用,轻快纯粹 | 复杂工作流,需与其他组件协作 |
| 结论 | 简单调用选 OpenAI,复杂工作流选 ChatOpenAI |
4. Prompt Engineering 工程化
4.1 PromptTemplate 文本模板
最基础的模板,通过变量占位符动态生成提示词:
from langchain_core.prompts import PromptTemplate
# 方式一:from_template(推荐)
prompt = PromptTemplate.from_template(
"你是一个{role},请用{style}风格回答:{question}"
)
# 方式二:构造方法(支持 partial_variables 预设固定变量)
prompt = PromptTemplate(
template="系统版本:{version}\n用户问题:{question}",
input_variables=["question"],
partial_variables={"version": "v2.1"} # 预设系统参数
)
# 格式化方法对比
prompt.format(role="专家", style="简洁", question="什么是RAG") # → 字符串
prompt.invoke({...}) # → PromptValue 对象(可 .to_string() / .to_messages())
prompt.partial({...}) # → 新的 PromptTemplate(可继续填充)
4.2 ChatPromptTemplate 对话模板
专为多角色、多轮对话设计,参数为 (role, content) 元组列表:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 基础用法
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个{role},请简短回答"),
("human", "请回答:{question}")
])
# 带历史消息占位符(Memory 场景必备)
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个专业的AI助手"),
MessagesPlaceholder("history"), # 动态插入历史消息
("human", "{question}")
])
参数类型支持:tuple(最常用)、dict、Message 对象、PromptTemplate 对象,可混合使用。
4.3 外部加载 Prompt
将 Prompt 保存为 JSON/YAML 文件,实现版本化管理:
from langchain_core.prompts import load_prompt
prompt = load_prompt("prompts/rag_query.yaml")
5. Output Parser 结构化输出
5.1 为什么需要 Output Parser
LLM 原始输出是字符串,但工程应用需要结构化数据(JSON、Pydantic 对象等)才能进行后续逻辑处理。Output Parser 作为模型与业务逻辑之间的中间层,负责:
- 格式转换:文本 → JSON / Pydantic / 指定结构
- 数据校验:确保输出符合预期 schema
- 格式引导:通过
get_format_instructions()自动生成格式说明注入 Prompt
5.2 核心方法
| 方法 | 作用 |
|---|---|
parse() / invoke() |
将模型输出格式化为指定结构 |
get_format_instructions() |
返回格式说明字符串,注入 Prompt 引导模型输出 |
5.3 常用解析器
StrOutputParser(最简)
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
result = parser.invoke(ai_message) # 提取 content 字段为字符串
JsonOutputParser
from langchain_core.output_parsers import JsonOutputParser
parser = JsonOutputParser()
# 方式一:手动在 Prompt 中指定 JSON 格式要求
# 方式二:利用 get_format_instructions() 自动注入格式说明
format_instructions = parser.get_format_instructions()
结构化输出(Pro 级)
结合 TypedDict 或 Pydantic 定义输出 schema,通过 with_structured_output() 强制模型按 schema 返回:
from pydantic import BaseModel, Field
class MovieReview(BaseModel):
title: str = Field(description="电影名称")
rating: float = Field(description="评分,0-10")
summary: str = Field(description="一句话总结")
# 绑定结构化输出(模型自动按 schema 返回)
structured_model = model.with_structured_output(MovieReview)
result = structured_model.invoke("评价一下《流浪地球2》")
# result 是 MovieReview 实例,可直接 .title / .rating 访问
6. LCEL 链式调用与工作流编排
6.1 Runnable 统一接口
设计动机:在没有统一接口时,各组件调用方式各异(.format()、.invoke()、.parse()、.run()),组合时需手动适配。Runnable 将所有可执行组件统一为相同的方法集:
# 所有 Runnable 组件共享统一接口
prompt.invoke({"topic": "AI"}) # 提示模板
model.invoke(prompt_value) # 语言模型
parser.invoke(ai_message) # 输出解析器
chain.invoke({"question": "你好"}) # 整个链
Runnable 核心方法:invoke(同步)、batch(批量)、stream(流式)、ainvoke(异步)。
6.2 LCEL 管道语法
LCEL(LangChain Expression Language)通过管道符 | 将多个 Runnable 像拼积木一样组合:
# 核心公式:Prompt | Model | Parser
chain = prompt | model | output_parser
result = chain.invoke({"topic": "编程"})
# Chain 本身也是 Runnable,可以继续被组合
6.3 链结构类型
| 链类型 | 类名 | 模式 | 场景 |
|---|---|---|---|
| 顺序链 | RunnableSequence |
A | B | C 串行执行 |
标准数据处理管道 |
| 分支链 | RunnableBranch |
if-elif-else 条件路由 | 按输入类型分发不同处理逻辑 |
| 串行链 | RunnableSerializable |
子链叠加,多步骤串行 | 多次 LLM 调用串联 |
| 并行链 | RunnableParallel |
多子链同时执行,汇总结果 | 多模型投票、多路推理、多模态 |
| 函数链 | RunnableLambda |
普通 Python 函数转 Runnable | 自定义数据转换逻辑注入管道 |
顺序链示例
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("用一句话解释什么是{concept}")
chain = prompt | model | StrOutputParser()
print(chain.invoke({"concept": "RAG"}))
并行链示例
from langchain_core.runnables import RunnableParallel
# 同时问两个模型,聚合结果
parallel_chain = RunnableParallel(
qwen_answer=chain_qwen,
deepseek_answer=chain_deepseek
)
results = parallel_chain.invoke({"question": "什么是向量数据库"})
函数链示例
from langchain_core.runnables import RunnableLambda
def format_output(text: str) -> dict:
return {"answer": text, "length": len(text)}
chain = prompt | model | StrOutputParser() | RunnableLambda(format_output)
7. Memory 记忆系统
7.1 问题背景
LLM 本身无状态,每次 invoke 都是独立请求。前一轮对话的信息在下一轮即被"遗忘"。Memory 的核心职责:在多次对话间维持上下文连贯性。
7.2 实现原理
记忆组件实现三个基本功能:读取历史、写入历史、存储历史。工作流:
- 链执行前:从 Memory 读取历史消息 → 与用户输入一起注入 Prompt → 传给 LLM
- 链执行后:将用户输入 + LLM 输出 → 写入 Memory
- 下次调用:重复上述过程
7.3 架构演进(0.3 → 1.0)
| 版本 | 核心类 | 状态 |
|---|---|---|
| v0.3 | ConversationChain + ConversationBufferMemory |
已废弃,灵活性不足,与 LCEL 不兼容 |
| v1.0 | RunnableWithMessageHistory + BaseChatMessageHistory |
推荐,模块化,支持 LCEL,长期支持 |
| 复杂场景 | LangGraph Checkpointer + 记忆中间件 |
面向复杂多轮 Agent 场景 |
7.4 内存版实现
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 会话存储(内存版,进程重启后丢失)
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder("history"),
("human", "{question}")
])
chain = RunnableWithMessageHistory(
prompt | llm,
get_session_history,
input_messages_key="question",
history_messages_key="history"
)
config = {"configurable": {"session_id": "user-001"}}
chain.invoke({"question": "我叫张三"}, config)
chain.invoke({"question": "你还记得我是谁吗"}, config) # 能记住
7.5 Redis 持久化版
from langchain_community.chat_message_histories import RedisChatMessageHistory
REDIS_URL = "redis://localhost:6379"
def get_session_history(session_id: str):
return RedisChatMessageHistory(
session_id=session_id,
url=REDIS_URL,
# ttl=3600 # 可选:设置过期时间
)
# 其余链构建逻辑与内存版完全一致
chain = RunnableWithMessageHistory(
prompt | llm, get_session_history,
input_messages_key="question",
history_messages_key="history"
)
RedisStack 部署(支持向量检索 + JSON + 全文搜索):
docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server
8. Tools / Function Calling
8.1 核心问题
LLM 本质是静态文本生成器,不具备:
- 访问数据库、调用 API 的能力
- 执行代码或文件操作的能力
- 实时访问互联网或动态数据的能力
Tool(Function Calling)机制让模型具备"调用外部函数"的能力。
8.2 工作原理
用户提问 → LLM 判断是否需要工具
↓ 需要
返回 function_call(函数名 + 参数)
↓
应用层执行函数,获取结果
↓
将工具结果 + 原始上下文 → 再次送入 LLM
↓
LLM 综合判断,生成最终回答
关键点:LLM 本身不执行函数,它只是"决定调用哪个函数、传什么参数",真正的执行在应用层。
8.3 自定义 Tool
from langchain_core.tools import tool
from pydantic import BaseModel, Field
# 方式一:@tool 装饰器(简单场景)
@tool
def add_number(a: int, b: int) -> int:
"""两个整数相加"""
return a + b
# 方式二:Pydantic Schema(生产推荐,参数描述更精确)
class AddInput(BaseModel):
a: int = Field(description="第 1 个加数")
b: int = Field(description="第 2 个加数")
@tool(args_schema=AddInput)
def add_number_pro(a: int, b: int) -> int:
"""执行两个整数的加法运算"""
return a + b
# 工具元信息自动提取
print(add_number.name) # 'add_number'
print(add_number.description) # '两个整数相加'
print(add_number.args) # {'a': {...}, 'b': {...}}
8.4 实战:天气查询 Tool + LLM 调用链
from langchain_core.tools import tool
from langchain_core.output_parsers import JsonOutputKeyToolsParser, StrOutputParser
from langchain_core.prompts import PromptTemplate
import httpx, json, os
@tool
def get_weather(loc: str):
"""
查询即时天气。参数 loc 为城市英文名(如 Beijing、Shanghai)。
返回 OpenWeather API 的 JSON 字符串结果。
"""
url = "https://api.openweathermap.org/data/2.5/weather"
params = {
"q": loc,
"appid": os.getenv("OPENWEATHER_API_KEY"),
"units": "metric",
"lang": "zh_cn"
}
resp = httpx.get(url, params=params, timeout=30)
return json.dumps(resp.json())
# 绑定工具到模型
llm_with_tools = llm.bind_tools([get_weather])
# 构建工具调用链:模型 → 解析工具调用参数 → 执行工具
parser = JsonOutputKeyToolsParser(key_name=get_weather.name, first_tool_only=True)
weather_chain = llm_with_tools | parser | get_weather
# 构建输出链:将 JSON 天气数据转为自然语言描述
output_prompt = PromptTemplate.from_template(
"将以下 JSON 天气数据转为简洁的中文天气描述:{weather_json}"
)
output_chain = output_prompt | llm | StrOutputParser()
# 完整链路
full_chain = weather_chain | (lambda x: {"weather_json": x}) | output_chain
result = full_chain.invoke("北京今天的天气如何?")
9. 向量化与 RAG 检索增强生成
9.1 Embedding 文本向量化
Embedding 将文本映射到高维向量空间,使语义相似的文本在空间中距离更近。
核心应用:语义搜索、聚类、推荐、异常检测、分类。
# 阿里云百炼 Embedding
import dashscope
from http import HTTPStatus
resp = dashscope.MultiModalEmbedding.call(
model="tongyi-embedding-vision-plus",
dashscope_api_key=os.getenv("QWEN_API_KEY"),
input=[{"text": "LangChain 工程化实战"}]
)
embedding_vector = resp.output["embeddings"][0]["embedding"]
语义相似度计算(余弦相似度):向量距离越小,语义越相关。
9.2 RAG 核心设计
问题根源:LLM 知识仅限于训练数据,对专有领域/实时数据一无所知,且会产生"幻觉"(已读乱回、似是而非)。
RAG 解决思路:先检索,后生成——类似考试时给你准备了小抄。
┌──────────── 索引阶段(Index)────────────┐
│ 文档加载 → 文本分割 → Embedding → 向量存储 │
└──────────────────────────────────────────┘
↓
┌──────────── 检索阶段(Retrieval)──────────┐
│ 用户提问 → Embedding → 相似性检索 → 取回文档片段 │
│ ↓ │
│ 将检索结果注入 Prompt → LLM 阅读理解 → 生成回答 │
└──────────────────────────────────────────┘
9.3 核心组件
文档加载器(Document Loaders)
支持 PDF、Word、TXT、CSV、网页等多种格式,统一输出为 Document 对象:
# Document 对象结构
Document(
page_content="文档正文内容(字符串)",
metadata={"source": "文件路径", "page": 1} # 元数据(字典)
)
所有加载器继承 BaseLoader,统一 load() 接口。
文本分割器(Text Splitters)
为什么分割:文档过大,超出 Token 限制;分割后检索精度更高。
最常用:RecursiveCharacterTextSplitter(递归字符切分):
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每个片段最大字符数
chunk_overlap=50, # 片段间重叠字符数(保持语义连贯)
separators=["\n\n", "\n", "。", ",", " "] # 递归分割符
)
# 分割文本
chunks = splitter.split_text(long_text)
# 分割 Document 对象
docs = splitter.split_documents(documents)
向量存储(Vector Stores)
| 向量库 | 特点 |
|---|---|
| RedisStack | 同时支持向量检索 + 全文搜索 + JSON,一库多用 |
| Chroma | 轻量级,适合原型开发 |
| FAISS | Meta 开源,纯内存向量检索,性能极高 |
| Milvus | 分布式,适合大规模生产环境 |
9.4 RAG 完整链路代码
# 1. 加载文档
from langchain_community.document_loaders import TextLoader
loader = TextLoader("knowledge_base.txt", encoding="utf-8")
docs = loader.load()
# 2. 分割文档
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 3. 向量化并存入 Redis
from langchain_community.vectorstores import Redis
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(
model="text-embedding-v3",
api_key=os.getenv("QWEN_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
vectorstore = Redis.from_documents(
chunks, embeddings,
redis_url="redis://localhost:6379",
index_name="rag_index"
)
# 4. 构建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 5. 构建 RAG 链
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
rag_prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答用户问题。如果上下文中没有相关信息,请明确告知。
上下文:{context}
用户问题:{question}
""")
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| rag_prompt
| llm
| StrOutputParser()
)
result = rag_chain.invoke("系统的错误码 5001 代表什么?")
10. MCP 模型上下文协议
10.1 为什么需要 MCP
痛点:每个外部服务(微信、数据库、天气 API)都有独特的接口规范,AI 需要为每个服务单独适配连接方式,维护成本随工具数量线性增长。
MCP(Model Context Protocol):AI 世界的"Type-C 协议"——统一标准接口,让 LLM 用一种协议连接所有工具和数据源。
类比理解:
- 后端同学熟悉的 gRPC:标准化微服务间通信
- MCP:标准化 LLM 与外部世界的通信
- 本质:Function Calling 的更高级抽象,Agent 架构的基础设施
10.2 架构设计
MCP 遵循 客户端-服务器架构:
┌────────────────────────────────────────────────┐
│ MCP Host(AI 应用,如聊天机器人) │
│ ┌────────────┐ ┌────────────┐ │
│ │ MCP Client │ │ MCP Client │ (1:1 连接) │
│ └─────┬──────┘ └─────┬──────┘ │
└────────┼──────────────────┼────────────────────┘
│ │
┌────▼────┐ ┌────▼────┐
│MCP Server│ │MCP Server│
│(天气服务)│ │(网页抓取)│
└────┬────┘ └────┬────┘
│ │
本地资源/API 远程资源/API
通信协议:
| 协议 | 方式 | 场景 |
|---|---|---|
| STDIO | 标准输入/输出流 | 本地轻量工具、命令行程序 |
| SSE | HTTP Server-Sent Events | 网络服务、远程服务器、长连接场景 |
10.3 MCP Server 实现
from mcp.server.fastmcp import FastMCP
import httpx, json, os
mcp = FastMCP("WeatherServer", host="0.0.0.0", port=8000)
@mcp.tool()
def get_weather(city: str) -> str:
"""查询指定城市的即时天气信息。参数 city 为城市英文名。"""
url = "https://api.openweathermap.org/data/2.5/weather"
params = {"q": city, "appid": os.getenv("OPENWEATHER_API_KEY"),
"units": "metric", "lang": "zh_cn"}
resp = httpx.get(url, params=params, timeout=10)
return json.dumps(resp.json(), ensure_ascii=False)
if __name__ == "__main__":
mcp.run(transport="sse") # 启动 SSE 服务
10.4 MCP 配置文件
{
"mcpServers": {
"weather": {
"url": "http://127.0.0.1:8000/sse",
"transport": "sse"
},
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"],
"transport": "stdio"
}
}
}
10.5 MCP Client + Agent 集成
import asyncio
from langchain.chat_models import init_chat_model
from langchain_classic.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_mcp_adapters.client import MultiServerMCPClient
async def run_mcp_agent():
# 加载 MCP 服务器配置
with open("mcp.json") as f:
servers_cfg = json.load(f).get("mcpServers", {})
# 初始化 MCP 客户端,获取所有工具
mcp_client = MultiServerMCPClient(servers_cfg)
tools = await mcp_client.get_tools()
# 构建 Agent
llm = init_chat_model(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com"
)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个使用工具完成任务的AI助手"),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 交互式对话
while True:
user_input = input("你: ").strip()
if user_input.lower() == "quit":
break
result = await executor.ainvoke({"input": user_input})
print(f"AI: {result['output']}")
asyncio.run(run_mcp_agent())
11. Agent 智能体
11.1 Tool vs Agent
| 维度 | Tool | Agent |
|---|---|---|
| 本质 | 能力封装(函数/工具) | 决策引擎 |
| 角色 | 被动等待调用 | 主动推理决策 |
| 类比 | 工具箱里的螺丝刀、锤子 | 知道何时用什么工具的工匠 |
| 决策能力 | 无 | 有(ReAct:推理 + 行动) |
Agent = LLM(推理大脑)+ Tools(能力集)+ 决策循环(ReAct)
11.2 ReAct 工作原理
用户输入 → LLM 理解任务
↓
推理:需要调用什么工具?
↓ 是 → 调用工具 → 获取结果 → 回到推理
↓ 否
综合所有信息,生成最终答案
核心循环:Think → Act → Observe → Think → ... → Final Answer
11.3 Agent 架构演进
| 版本 | 架构 | 说明 |
|---|---|---|
| v0.3 | Agent(决策)+ AgentExecutor(执行) |
Agent 决定操作,Executor 执行工具并循环 |
| v1.0 | LangGraph 状态机 | 图结构编排,支持条件分支、循环、并行,更灵活可控 |
11.4 v0.3 AgentExecutor 工作流
1. 输入解析:LLM 分析用户输入,理解任务目标
2. 推理规划:基于 ReAct 生成操作计划,决定是否/如何调用工具
3. 工具调用:执行工具,获取结果并反馈给 LLM
4. 迭代推理:LLM 根据工具结果更新推理,可能触发更多工具调用
5. 终止输出:循环直到任务完成,综合所有信息生成最终答案
from langchain_classic.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 定义工具集
tools = [get_weather, search_db, calculator]
# 构建 Agent
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个智能助手,善于使用工具解决问题"),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"), # Agent 推理过程占位
])
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 打印推理过程
handle_parsing_errors=True
)
result = executor.invoke({"input": "查一下北京天气,然后告诉我适合穿什么"})
11.5 技术栈关系总结
Tool Calling → 让 LLM 调用外部工具(能力扩展)
RAG → 让 LLM 获取外部知识上下文(知识增强)
MCP → 让 LLM 之间标准化通信(协议统一)
Agent → 让 LLM 自主推理决策(智能编排)
四者层层递进,共同构成现代 AI 应用的技术栈:
- Tool 是基础能力单元
- RAG 解决知识边界问题
- MCP 统一工具接入协议
- Agent 在最顶层做智能编排与决策
附录:版本兼容速查
| 特性 | v0.3 写法 | v1.0 写法 |
|---|---|---|
| 模型初始化 | ChatOpenAI(model=...) |
init_chat_model(model=..., model_provider="openai") |
| Agent | create_openai_tools_agent + AgentExecutor |
LangGraph 状态机 |
| Memory | ConversationBufferMemory |
RunnableWithMessageHistory |
| 消息类型 | FunctionMessage |
ToolMessage |
| 链构建 | LLMChain / SequentialChain |
LCEL(prompt | model | parser) |
浙公网安备 33010602011771号