LangChain 工程化实战:从核心概念到生产级架构

LangChain 工程化实战:从核心概念到生产级架构

适用版本:LangChain 1.0+(兼容 0.3.x 核心 API)
技术栈:Python 3.10+ / LangChain / LangGraph / Redis Stack
定位:团队内部技术分享,聚焦工程决策与架构设计


目录

  1. LangChain 架构全景
  2. 环境工程与多模型接入
  3. Model I/O:标准化模型交互
  4. Prompt Engineering 工程化
  5. Output Parser 结构化输出
  6. LCEL 链式调用与工作流编排
  7. Memory 记忆系统
  8. Tools / Function Calling
  9. 向量化与 RAG 检索增强生成
  10. MCP 模型上下文协议
  11. 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 已知工程痛点

在工程落地中需正视以下问题:

  1. 文档滞后:迭代频率极高(约 2-3 天一版本),文档与代码脱节是常态,应以 API 源码为准
  2. 抽象层过深:为兼容所有模型和数据库,封装层数多,调用栈深,调试成本高。不是运行慢,是认知负担重
  3. 版本不兼容:小版本升级可能导致 API 变更,需锁定版本并关注 Breaking Changes
  4. 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" 工具/函数调用的返回结果

所有消息均包含 typecontentresponse_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 级)

结合 TypedDictPydantic 定义输出 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 实现原理

记忆组件实现三个基本功能:读取历史、写入历史、存储历史。工作流:

  1. 链执行前:从 Memory 读取历史消息 → 与用户输入一起注入 Prompt → 传给 LLM
  2. 链执行后:将用户输入 + LLM 输出 → 写入 Memory
  3. 下次调用:重复上述过程

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
posted @ 2026-06-15 08:11  黄忠  阅读(31)  评论(0)    收藏  举报