LangChain 接入 MCP 实战指南:解锁模型上下文管理新范式

在构建基于大语言模型(LLM)的复杂应用时,一个核心挑战是如何高效、精准地管理提供给模型的上下文信息。传统的上下文拼接方式往往导致令牌(Token)浪费、信息冗余,甚至因超出模型窗口限制而丢失关键信息。为了解决这一痛点,MCP(Model Context Protocol) 应运而生,而 LangChain 作为流行的LLM应用框架,其与MCP的集成则为开发者提供了一套优雅的解决方案。本文将带你从零开始,深入理解MCP,并一步步完成LangChain的接入实践。

什么是 MCP(Model Context Protocol)?

MCP 是一个开放的协议,旨在标准化大语言模型与外部上下文源(如数据库、知识库、API)之间的交互方式。其核心思想是将“上下文管理”从应用逻辑中解耦出来,交由专门的“上下文服务器”处理。

MCP 的核心优势:

  1. 上下文优化:智能地选择、总结和注入最相关的上下文片段,而非简单拼接全文,极大节省Token并提升相关性。
  2. 协议标准化:提供统一的接口,使得任何兼容MCP的上下文源(如公司Wiki、产品文档、代码库)都能被LLM应用轻松使用。
  3. 可扩展性:可以轻松接入新的数据源,而无需修改核心应用代码。
  4. 性能提升:通过减少不必要上下文的传输和处理,降低延迟和成本。

一个典型的MCP架构包括:

  • MCP 客户端:你的LLM应用(如基于LangChain构建的应用)。
  • MCP 服务器:托管和管理上下文数据的服务。
  • MCP 协议:定义客户端与服务器之间请求(如搜索、获取片段)和响应的规范。

LangChain 框架简介

LangChain 是一个用于开发由语言模型驱动的应用程序的框架。它通过“链”(Chains)、“代理”(Agents)、“记忆”(Memory)和“检索”(Retrieval)等抽象概念,简化了与LLM交互、连接外部数据源和构建复杂工作流的流程。

将LangChain与MCP结合,意味着你可以利用LangChain强大的编排能力,同时享受MCP带来的专业化、智能化的上下文管理。

实战:将 MCP 服务器接入 LangChain

下面我们通过一个具体示例,展示如何将一个提供“公司产品文档”的MCP服务器接入到LangChain的检索链中。

步骤 1:环境准备与安装

首先,确保已安装Python(建议3.8+)和必要的库。

pip install langchain langchain-community requests
# 假设我们使用OpenAI的模型
pip install openai

步骤 2:了解你的 MCP 服务器

假设我们有一个运行在 http://localhost:8080 的MCP服务器,它提供了以下端点:

  • GET /search?query=关键词&limit=5:搜索相关文档片段。
  • GET /contexts:列出所有可用的上下文源(如user_guide, api_docs)。

服务器返回的搜索结果为JSON格式:

{
  "results": [
    {
      "id": "doc_123",
      "text": "产品的核心功能是...",
      "source": "user_guide",
      "score": 0.95
    }
  ]
}

步骤 3:创建自定义的 LangChain Retriever

LangChain 的 Retriever 接口是连接外部数据源的关键。我们需要创建一个兼容MCP协议的Retriever。

from typing import List, Optional
from langchain.schema import BaseRetriever, Document
from langchain.callbacks.manager import CallbackManagerForRetrieverRun
import requests

class MCPRetriever(BaseRetriever):
    """一个从MCP服务器检索上下文的Retriever。"""
    
    base_url: str = "http://localhost:8080"
    """MCP服务器的基础URL。"""
    
    default_source: Optional[str] = None
    """默认检索的上下文源。"""
    
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        # 构造搜索请求URL
        url = f"{self.base_url}/search"
        params = {"query": query, "limit": 4}
        if self.default_source:
            params["source"] = self.default_source
            
        try:
            response = requests.get(url, params=params)
            response.raise_for_status() # 检查HTTP错误
            data = response.json()
        except requests.exceptions.RequestException as e:
            # 在实际应用中应有更完善的错误处理
            print(f"请求MCP服务器失败: {e}")
            return []
            
        # 将MCP返回的结果转换为LangChain的Document对象
        documents = []
        for item in data.get("results", []):
            # 将元数据存入Document的`metadata`字段
            doc = Document(
                page_content=item["text"],
                metadata={
                    "source": item.get("source"),
                    "id": item.get("id"),
                    "relevance_score": item.get("score")
                }
            )
            documents.append(doc)
            
        return documents

步骤 4:在 LangChain Chain 中使用 MCP Retriever

现在,我们可以将这个Retriever集成到一个简单的问答链中。

from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate

# 1. 初始化MCP Retriever
mcp_retriever = MCPRetriever(default_source="user_guide")

# 2. 初始化LLM
llm = ChatOpenAI(model_name="gpt-4", temperature=0)

# 3. (可选)定义自定义提示模板,指导模型如何使用检索到的上下文
prompt_template = """请根据以下提供的上下文信息来回答问题。如果你无法从上下文中找到答案,请如实说明你不知道,不要编造信息。

上下文:
{context}

问题:{question}
请给出有帮助的答案:"""

PROMPT = PromptTemplate(
    template=prompt_template, input_variables=["context", "question"]
)

# 4. 创建检索式问答链
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff", # 简单地将所有检索到的文档“堆叠”到上下文中
    retriever=mcp_retriever,
    chain_type_kwargs={"prompt": PROMPT}, # 使用自定义提示
    return_source_documents=True # 返回用于生成答案的源文档,便于调试
)

# 5. 运行链
question = "产品如何实现数据备份?"
result = qa_chain({"query": question})
print(f"问题:{question}")
print(f"答案:{result['result']}")
print("\n--- 引用的源文档 ---")
for doc in result['source_documents']:
    print(f"来源:{doc.metadata.get('source')} | 片段:{doc.page_content[:100]}...")

实际应用场景与最佳实践

应用场景

  1. 智能客服助手:从海量产品手册、FAQ中动态检索最相关的几条信息作为上下文,生成精准回复。
  2. 代码辅助工具:连接项目代码库的MCP服务器,在程序员提问时,自动注入相关函数、类或API的文档。
  3. 企业内部知识问答:集成Confluence、Notion等作为MCP源,让LLM成为公司知识的统一接口。

最佳实践

  • 服务器端优化:MCP服务器应实现高效的语义搜索(如使用向量数据库)和内容摘要能力,返回高质量片段。
  • 客户端缓存:在MCPRetriever中实现简单的缓存机制,避免对相同查询的重复请求。
  • 元数据利用:充分利用Documentmetadata字段。在提示词中,可以指导模型关注高relevance_score的片段,或告知信息来源。
  • 错误处理与降级:生产环境中,必须考虑MCP服务器不可用的情况,并设计降级策略(如使用本地缓存或简化检索逻辑)。
  • 安全性:确保MCP服务器接口有适当的认证和授权,防止敏感信息泄露。

常见问题解答(FAQ)

Q1: MCP 和 LangChain 自带的 VectorStoreRetriever 有什么区别? A1: VectorStoreRetriever 通常用于检索已嵌入(Embedding)并存储在向量数据库中的文档。MCP Retriever 是一个更通用的接口,它可以对接任何遵循MCP协议的服务,这些服务内部可能使用向量检索、关键词检索甚至复杂的业务逻辑来提供上下文。MCP提供了更大的灵活性和解耦。

Q2: 如果我的 MCP 服务器需要认证怎么办? A2: 你可以在自定义的 MCPRetriever_get_relevant_documents 方法中,在 requests.get 调用里添加认证头(如 headers={'Authorization': 'Bearer YOUR_TOKEN'})。更安全的方式是从环境变量或配置文件中读取凭证。

Q3: 如何处理 MCP 返回的上下文过长,仍然超出模型令牌限制的情况? A3: 这是MCP服务器设计时应考虑的核心问题。好的MCP服务器应该能返回精炼的片段。在客户端,你可以通过设置 limit 参数控制返回片段数量,或者在LangChain链中使用 chain_type="map_reduce""refine" 来处理长文档,但这些方法成本更高。最佳实践是在MCP服务器端做好内容分块和摘要。

Q4: 我可以同时连接多个 MCP 服务器吗? A4: 当然可以。你可以创建多个不同的 MCPRetriever 实例,然后使用 LangChain 的 EnsembleRetrieverMultiQueryRetriever 将它们组合起来,从多个源进行综合检索。

总结

通过将 LangChain 与 MCP 协议结合,开发者能够构建出上下文感知能力更强、更高效、更易维护的LLM应用。本文带你走完了从理解概念到实现集成的完整路径:

  1. 理解MCP 作为上下文管理标准化协议的价值。
  2. 创建自定义Retriever,作为LangChain与MCP服务器之间的桥梁。
  3. 构建集成MCP的问答链,并看到实际运行效果。
  4. 探索了应用场景,并总结了生产环境的最佳实践。

这种架构分离了关注点,让你的AI应用能够灵活地接入不断增长的知识源,同时保持核心逻辑的简洁。现在,就尝试为你自己的项目接入MCP,体验智能化上下文管理带来的强大助力吧!