基于 LangChain 的海量 API 动态检索与调用架构

1. 核心痛点与解决思路

问题:当系统接入成百上千个 API(如 ERP、CRM、SaaS 接口)时,直接将其全部塞入 LLM 的上下文(Context Window)会导致:

  1. 上下文溢出:超过 Token 限制。
  2. 注意力分散:LLM 在海量选项中无法精准选择,幻觉率急剧上升。
  3. 成本高昂:每次请求都携带数万 Token 的无用信息。

解决方案 :采用 RAG for Tools(工具检索增强) 模式。

  • 离线阶段:解析 API 文档,建立向量索引。
  • 在线阶段:根据用户意图检索 Top-N 相关 API -> 动态绑定到 LLM -> 执行多步推理。

2. 整体架构设计

graph TD subgraph "准备阶段 (Offline)" Swagger[Swagger/OpenAPI 文档] --> Parser[自定义解析器 & 清洗] Parser -->|生成 Pydantic Schema| Tools[结构化工具定义] Tools -->|Embedding 描述信息| VectorDB[(向量数据库 / FAISS)] end subgraph "运行阶段 (Runtime)" User[用户指令: '给 Alice 发红包'] --> Retriever[检索层] VectorDB -->|检索 Top-5 相关工具| SelectedTools[动态工具集] SelectedTools --> Binder[动态绑定层 (LLM.bind_tools)] Binder --> Agent[智能体执行循环 (Agent Loop)] Agent -->|思考 1| Step1[调用 search_user API] Step1 -->|返回 ID| Agent Agent -->|思考 2| Step2[调用 send_bonus API] Step2 -->|返回结果| Final[最终响应] end

3. 关键实现步骤

步骤一:Swagger 解析与结构化 (The Parser)

目标 :将冗余的 Swagger JSON 转换为 LLM 易懂、且具备严格类型检查的 StructuredTool

  • 核心动作
    1. Schema 瘦身:只保留 Endpoint、Method、以及关键参数描述。剔除无用的 HTTP 状态码和复杂的嵌套 Response 定义。
    2. Pydantic 建模 :利用 Python 的 pydantic 库动态创建参数模型,确保 LLM 传参符合类型规范(如 Integer 不传 String)。
    3. Enum 注入:将枚举值明确写入描述,防止 LLM 编造参数。

步骤二:工具向量化 (The Indexing)

目标:让系统能够根据自然语言理解 API 的功能。

  • 核心动作
    1. 提取 API 的 description(功能描述)作为向量化的文本内容。
    2. tool_name 和 Swagger 的 tags(分类)存为 Metadata,以便后续过滤。
    3. 存入 FAISS、Pinecone 或 Milvus 等向量库。

步骤三:动态检索与绑定 (Retrieval & Binding)

目标:在运行时(Runtime)动态构建 Prompt。

  • 逻辑流程
    User Query -> Vector Search -> Get Top-K Tools -> LLM.bind_tools(Top-K)

4. 核心代码实现 (Python + LangChain v0.3)

以下代码展示了从"解析"到"检索"再到"Agent执行"的完整闭环。

python 复制代码
import requests
from langchain_core.tools import StructuredTool
from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from pydantic import create_model

# ==========================================
# 1. 模拟 Swagger 解析器 (解析 + Pydantic 建模)
# ==========================================
def generate_tools_from_meta(api_definitions):
    tools = []
    for api in api_definitions:
        # 动态创建参数模型 (关键:保证精准度)
        param_model = create_model(f"{api['name']}_args", **api['parameters'])
        
        # 定义执行函数
        def _func(**kwargs):
            # 实际场景中这里是 requests.get/post
            return f"调用 {api['name']} 成功,参数: {kwargs}"
        
        # 封装为 LangChain Tool
        tools.append(StructuredTool.from_function(
            func=_func,
            name=api['name'],
            description=api['description'], # 向量检索的核心依据
            args_schema=param_model
        ))
    return tools

# 模拟 API 定义 (实际应从 Swagger JSON 读取)
mock_apis = [
    {"name": "search_user", "description": "根据姓名查找用户ID和信息", "parameters": {"name": (str, ...)}},
    {"name": "get_user_balance", "description": "查询用户的钱包余额", "parameters": {"user_id": (str, ...)}},
    {"name": "send_bonus", "description": "给指定用户ID发放奖金", "parameters": {"user_id": (str, ...), "amount": (int, ...)}},
    {"name": "get_weather", "description": "查询天气", "parameters": {"city": (str, ...)}},
    # ... 假设这里还有 900 个其他 API ...
]

all_tools = generate_tools_from_meta(mock_apis)
tool_map = {t.name: t for t in all_tools} # 方便后续通过名字找回对象

# ==========================================
# 2. 建立向量索引 (Indexing)
# ==========================================
docs = [
    Document(page_content=t.description, metadata={"tool_name": t.name}) 
    for t in all_tools
]
embeddings = OpenAIEmbeddings()
vector_store = FAISS.from_documents(docs, embeddings)
retriever = vector_store.as_retriever(search_kwargs={"k": 3}) # 每次只取 Top 3

# ==========================================
# 3. 运行时逻辑:检索 + Agent 执行
# ==========================================
def run_agent_with_retrieval(user_query):
    print(f"--- 用户指令: {user_query} ---")
    
    # 3.1 检索阶段
    retrieved_docs = retriever.invoke(user_query)
    selected_tools = [tool_map[d.metadata["tool_name"]] for d in retrieved_docs]
    print(f"   [检索命中]: {[t.name for t in selected_tools]}")
    
    # 3.2 绑定阶段
    llm = ChatOpenAI(model="gpt-4o", temperature=0)
    
    # 3.3 构建 Agent (处理多步依赖)
    # ReAct / Tool Calling Prompt
    prompt = ChatPromptTemplate.from_messages([
        ("system", "你是一个助手,请使用提供的工具解决问题。如果需要,你可以分多步执行。"),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"), # 记忆中间步骤 (如 Step1 查到的 ID)
    ])
    
    agent = create_tool_calling_agent(llm, selected_tools, prompt)
    agent_executor = AgentExecutor(agent=agent, tools=selected_tools, verbose=True)
    
    # 3.4 执行
    agent_executor.invoke({"input": user_query})

# ==========================================
# 4. 测试用例
# ==========================================
# 场景:多步调用 (先查人,再发钱)
# 预期检索:search_user, send_bonus
run_agent_with_retrieval("给 Alice 发 100 块钱奖金") 

5. 专家级优化建议 (Best Practices)

为了在生产环境中达到 99% 的可用性,建议实施以下策略:

A. 提升检索精准度

  1. 混合检索 (Hybrid Search):结合关键词匹配(BM25)和向量检索。防止因专有名词(如 API 里的特殊字段名)被向量化模糊掉。
  2. 元数据过滤 (Metadata Filtering)
    • 利用 Swagger 的 Tags
    • 先用一个轻量级 LLM 判断领域(如 "这是财务相关的问题"),然后在向量检索时加 Filter filter={"category": "Finance"}

B. 提升参数填充成功率

  1. Pydantic 校验回环
    • 当 LLM 传错参(如类型错误)时,Python 端捕获 ValidationError
    • 不要直接崩溃,而是将错误信息("Error: 'amount' must be an integer")作为 Observation 返回给 Agent。
    • Agent 看到错误后会进行 Self-Correction (自我修正) 并重新发起调用。
  2. Schema 描述优化
    • description 中给出 Example。例如:"用于查询用户,输入参数示例:user_id='u123'"。

C. 处理上下文依赖

  1. 对话历史注入:检索工具时,不要只用当前的 Query。如果用户上一句说了 "我是管理员",下一句说 "列出所有票据",检索器需要知道 "管理员" 这个上下文才能匹配到高权限 API。建议总结历史对话后再进行检索。


《在线详细方案:langchain加载api (tool)》

这是一个非常经典且高效的架构设计,通常被称为 "基于检索的动态工具绑定" (Retrieval-Augmented Tool Selection)

在 2024-2025 年的 LangChain 最佳实践中,我们不再一次性把所有工具塞给 Agent,而是利用 LCEL (LangChain Expression Language) 在运行时动态地将相关的工具"挂载"到 LLM 上。

下面是实现这一方案的完整步骤和代码示例。

核心原理

  1. 索引阶段 (Indexing):把你成百上千个工具的"功能描述(Description)"转换成向量,存入向量数据库(VectorDB)。
  2. 检索阶段 (Retrieval):当用户提问时,先用问题去向量库搜索 Top-N 个最相关的工具。
  3. 执行阶段 (Execution) :将检索到的这 N 个工具,通过 .bind_tools() 动态绑定到 LLM 的当前上下文中,进行调用。

实现代码 (基于 LangChain v0.2/v0.3 + FAISS)

假设我们模拟有 100 个工具,但每次只让 LLM 看到最相关的 3 个。

1. 环境准备

你需要安装以下库:

bash 复制代码
pip install langchain langchain-openai langchain-community faiss-cpu numpy
2. 定义工具 (模拟海量工具)

这里我们定义一些假工具。在真实场景中,这些工具可能是从 OpenAPI 文档自动生成的。

python 复制代码
from langchain_core.tools import tool
from langchain_core.pydantic_v1 import BaseModel, Field

# 模拟:定义几个不同领域的工具
# 关键点:docstring (文档字符串) 非常重要,它是向量检索的依据!

@tool
def check_order_status(order_id: str):
    """用于查询用户的电商订单状态、发货情况和物流信息。"""
    return f"订单 {order_id} 正在配送中。"

@tool
def refund_order(order_id: str):
    """用于处理用户的退款请求,取消订单并退回款项。"""
    return f"订单 {order_id} 退款流程已启动。"

@tool
def get_weather(city: str):
    """获取指定城市的实时天气情况,温度和湿度。"""
    return f"{city} 天气晴朗,25度。"

@tool
def search_employee_info(name: str):
    """查询公司内部员工的联系方式、部门和职位信息。"""
    return f"员工 {name} 属于研发部。"

# 假设我们有一个包含 100 个工具的列表
# 在实际项目中,你可能有 all_tools = [tool1, tool2, ..., tool1000]
all_tools = [check_order_status, refund_order, get_weather, search_employee_info]
3. 建立工具索引 (Vector Store)

我们需要将工具的描述向量化。

python 复制代码
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document

# 1. 将工具转换为 Document 对象
# 向量搜索主要匹配的是 tool.description
tool_documents = []
tool_map = {} # 用于根据名字快速找回工具对象

for t in all_tools:
    doc = Document(
        page_content=t.description, # 搜索的内容:工具描述
        metadata={"tool_name": t.name} # 元数据:存工具名
    )
    tool_documents.append(doc)
    tool_map[t.name] = t

# 2. 创建向量数据库
embeddings = OpenAIEmbeddings()
vector_store = FAISS.from_documents(tool_documents, embeddings)
retriever = vector_store.as_retriever(search_kwargs={"k": 2}) # 设定只检索 Top 2 个相关工具

print("--- 工具索引建立完成 ---")
4. 构建动态工具绑定链 (The Magic Part)

这是最关键的一步。我们使用 LCEL 来构建一个处理流程:
用户问题 -> 检索相关工具 -> 绑定到 LLM -> LLM 决策

python 复制代码
from operator import itemgetter
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableLambda

llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 辅助函数:根据检索到的 Document 找回真实的 Tool 对象
def get_tools(query):
    # 1. 执行向量搜索
    docs = retriever.invoke(query)
    # 2. 根据名字找回工具函数
    retrieved_tools = [tool_map[d.metadata["tool_name"]] for d in docs]
    print(f"\n[检索日志] 用户问: '{query}' -> 检索到的工具: {[t.name for t in retrieved_tools]}")
    return retrieved_tools

# 核心逻辑:动态绑定工具
# 我们不能直接用 .bind_tools(),因为工具列表是动态的
# 我们需要一个 prompt | llm 的链,在运行时注入工具
def prompt_processing_node(inputs):
    query = inputs["query"]
    # 1. 动态检索工具
    selected_tools = get_tools(query)
    # 2. 动态绑定到 LLM
    llm_with_tools = llm.bind_tools(selected_tools)
    return {"llm": llm_with_tools, "tools": selected_tools}

# 定义 Prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手。请根据提供的工具回答用户问题。"),
    ("human", "{query}"),
])

# 定义链 (LCEL)
# 这里的逻辑是:先检索工具,然后把绑定了工具的 LLM 传给下一步执行
chain = (
    RunnablePassthrough.assign(context=lambda x: prompt_processing_node(x))
    | (lambda x: x["context"]["llm"].invoke(prompt.format(query=x["query"])))
)
5. 测试运行
python 复制代码
# 测试 1: 询问订单 (应该检索到 check_order_status 和 refund_order)
response1 = chain.invoke({"query": "帮我查一下订单 12345 到哪了"})
print(f"LLM 决定调用: {response1.tool_calls}")

# 测试 2: 询问天气 (应该检索到 get_weather)
response2 = chain.invoke({"query": "北京今天下雨吗"})
print(f"LLM 决定调用: {response2.tool_calls}")

进阶优化方案

上面的代码解决了核心问题,但在生产环境中,你还需要考虑以下几点:

1. 多轮对话中的工具一致性 (Tool Persistence)

问题 :如果在第一轮对话中 LLM 决定调用 Order_Tool,用户接着问"那退款呢?",此时如果单纯依靠第二句的语义检索,可能检索不到上下文相关的工具。
解法

  • 混合检索:检索查询时,不仅使用当前的 User Query,还要结合最近的 Chat History Summary。
  • 粘性会话 :如果上一轮用过 Order_Tool,在下一轮检索时,强制保留该工具在 Candidate List 中,或者给该工具加权。
2. 元数据过滤 (Metadata Filtering)

场景 :你有 5000 个 API,分为 HR、财务、IT 三大类。
解法:在向量检索前,先让 LLM 做一次粗分类(Classification)。

  • 用户问:"我要请假"。
  • 分类器 Agent:判断属于 Domain: HR
  • 检索器:vector_store.as_retriever(search_kwargs={"filter": {"domain": "HR"}})
  • 这样可以极大提高检索准确度。
3. 应对"参数修正"

向量检索只能帮你找到"函数名"。但如果用户问"查询 A 产品的库存",LLM 可能不知道 A 产品的 ID。
解法

  • 不要只检索 Tool。
  • 配合 RAG (知识库):先检索文档(比如产品代码表),再检索工具。

总结

实现"成百上千 API 调用"的核心在于:不要把 Context 当作数据库

  1. 离线:将 API 文档 Embed 到向量库。
  2. 在线User Query -> Vector Search -> Top-K Tools -> LLM.bind_tools(Top-K)

这种方式可以将 Token 消耗降低 90% 以上,同时让 LLM 的注意力更集中,减少幻觉。



《离线详细方案:langchain加载swagger的api 》

将 Swagger/OpenAPI 文档整合到 LangChain 并实现精准多步骤 的 API 调用,核心难点在于:原始的 Swagger 文件太啰嗦(Token 消耗大)且格式不适合 LLM 直接理解

要实现"精准调用",不能直接把几十 KB 的 Swagger JSON 扔给 LLM,而是要经过 "清洗 + 结构化转换" 的过程。

以下是业界标准的 三步走方案


第一步:Swagger 解析与清洗 (The Parser)

Swagger 文档通常包含大量对 LLM 无用的信息(如几十种 HTTP 状态码定义、复杂的嵌套 Schema)。你需要把它们转换成 LLM 能看懂的 Tool Definition

你可以使用 langchain-community 提供的工具,或者自己写一个轻量级解析器。

方案 A:使用 LangChain 内置加载器 (适合接口少于 20 个)

如果是小型服务,直接用 OpenAPIToolkit

python 复制代码
from langchain_community.agent_toolkits.openapi import planner
from langchain_community.utilities import OpenAPISpec
from langchain_openai import ChatOpenAI

# 1. 加载 Swagger YAML/JSON
# 这一步会自动把 API 转换成 LLM 可读的文本描述
spec = OpenAPISpec.from_file("openapi.yaml")

# 2. 创建一个能够规划调用顺序的 Agent
llm = ChatOpenAI(model="gpt-4o", temperature=0)
agent = planner.create_openapi_agent(llm, spec, verbose=True)

# 3. 调用
agent.invoke("查找用户 Alice 的 ID,并修改她的邮箱为 alice@test.com")
  • 缺点:如果接口超过 20 个,Prompt 会过长,导致 LLM 变笨。
方案 B:自定义解析为结构化工具 (适合接口多,推荐)

这是实现精准调用的关键。我们需要把 Swagger 的 Endpoint 转换成 OpenAI 格式的 JSON Schema。

可以使用第三方库 openapi-parser 或简单的脚本:

python 复制代码
import requests
import json
from langchain_core.tools import StructuredTool
from pydantic import create_model

# 假设这是解析 Swagger 后得到的简化结构(你需要写一段代码遍历 Swagger JSON)
# 目标是将 Swagger 的 /users/{id} 变成如下结构
api_definitions = [
    {
        "name": "get_user_by_id",
        "description": "通过 ID 获取用户详细信息",
        "path": "/users/{id}",
        "method": "GET",
        "parameters": {
            "id": (str, ...) # Pydantic 类型定义
        }
    },
    {
        "name": "update_user_email",
        "description": "更新用户的电子邮箱",
        "path": "/users/{id}/email",
        "method": "POST",
        "parameters": {
            "id": (str, ...),
            "email": (str, ...)
        }
    }
]

# 动态生成 LangChain 工具
def generate_tools_from_spec(api_defs, base_url):
    tools = []
    for api in api_defs:
        # 1. 动态创建 Pydantic 模型 (用于参数验证,保证精准!)
        param_model = create_model(f"{api['name']}_args", **api['parameters'])
        
        # 2. 定义实际执行函数
        def _func(**kwargs):
            # 这里实现通用的 HTTP 请求逻辑
            path = api['path'].format(**kwargs) # 替换路径参数
            method = api['method']
            url = f"{base_url}{path}"
            # 过滤掉路径参数,剩下的作为 body 或 query
            payload = {k: v for k, v in kwargs.items() if f"{{{k}}}" not in api['path']}
            
            if method == "GET":
                resp = requests.get(url, params=payload)
            else:
                resp = requests.post(url, json=payload)
            return resp.json()
        
        # 3. 封装成 LangChain Tool
        tools.append(StructuredTool.from_function(
            func=_func,
            name=api['name'],
            description=api['description'],
            args_schema=param_model # 注入 Pydantic 模型,LLM 靠这个精准提取参数
        ))
    return tools

# 生成工具列表
my_tools = generate_tools_from_spec(api_definitions, "https://api.example.com")

第二步:整合向量检索 (The Retrieval)

接上一个问题的思路,现在你有了 my_tools 列表(可能包含 500 个 API)。现在结合 Swagger 的特性做检索优化。

Swagger 的 tags(标签)是天然的分类器。

python 复制代码
# 在存入向量库时,带上 Swagger 的 Tag
docs = []
for tool in my_tools:
    doc = Document(
        page_content=tool.description, # "通过 ID 获取用户..."
        metadata={
            "tool_name": tool.name,
            "category": "UserManagement" # 来自 Swagger tags
        }
    )
# ... 建立 FAISS 索引 (代码同上一条回复) ...

第三步:处理多步关联调用 (The Chain/Agent)

这是你提到的"调用多个 API"的核心场景。例如:

"给 Alice 发个红包"

  1. 先调 search_user 拿 Alice 的 user_id
  2. 再调 send_bonus(user_id, amount)

普通的 chain.invoke 很难处理这种参数依赖 。你需要使用 LangGraph (LangChain 的新一代架构) 或者 ReAct Agent

使用 ReAct Agent 实现自动多步调用

LLM 会根据第一步的结果,自己决定第二步调什么。

python 复制代码
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# 1. 假设这是通过向量检索出来的 Top 5 工具
# 在实际运行时,这里是动态的
runtime_tools = [tool_search_user, tool_send_bonus, tool_get_balance] 

llm = ChatOpenAI(model="gpt-4o")

# 2. 定义 Prompt,告诉 LLM 它可以使用工具
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个助手。如果需要,你可以分多步使用工具解决问题。"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"), # 重要:这里存储了中间步骤的输入输出
])

# 3. 创建 Agent
agent = create_tool_calling_agent(llm, runtime_tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=runtime_tools, verbose=True)

# 4. 执行多步任务
# Log 会显示:
# Action 1: search_user(name="Alice") -> 返回 {"id": "u123", ...}
# Action 2: send_bonus(user_id="u123", amount=100) -> 返回 "Success"
agent_executor.invoke({"input": "给 Alice 发 100 块钱红包"})

专家级技巧:如何提高"精准度"?

当 API 很多且参数很像时,LLM 容易传错参。在解析 Swagger 时,必须做以下优化:

  1. Schema 瘦身 (Trim Schema)

    • Swagger 的 responses 通常包含巨大的 JSON 结构定义。不要把这些完全塞给 LLM。
    • LLM 只需要知道:"这个接口成功会返回 JSON,其中包含 user_id 字段"即可。
    • 技巧 :在 tool.description 里手动(或脚本自动)摘要返回值关键字段。
    • Bad Description: "Returns User Object."
    • Good Description: "Returns user details, looks like: {id: '...', email: '...'}. Use the 'id' field for other operations."
  2. 强制类型检查 (Enforce Pydantic)

    • 如果 Swagger 定义 ageinteger,但 LLM 传了字符串 "18",Pydantic 会直接报错。
    • 在 LangChain 中,捕获这个验证错误,并将错误信息(ValidationError: value is not a valid integer回传给 LLM
    • LLM 看到错误后,会进行自我修正(Self-Correction)并重试。
  3. 枚举值注入 (Enum Injection)

    • 如果 API 有参数 status 只能是 ["open", "closed"],务必在 Pydantic 或 Tool Description 中列出这些值。Swagger 的 enum 字段必须解析并透传给 Prompt。

总结架构图

graph LR Swagger[Swagger/OpenAPI JSON] --> Parser[自定义解析器] Parser -->|1. 清洗 & 扁平化| ToolDefs[结构化工具定义 (Pydantic)] ToolDefs -->|2. 存入| VectorDB[(向量数据库)] User[用户提问] --> Retriever[检索层] VectorDB -->|3. 检索 Top-N| SelectedTools[选中的工具集合] SelectedTools --> Agent[LangChain Agent Executor] Agent -->|4. 思考: 缺 ID?| Step1[调用 GetUser API] Step1 -->|返回 ID| Agent Agent -->|5. 思考: 有 ID 了| Step2[调用 Update API]


《用langchain整合higress3.x网关中的mcp服务》

答案是:完全可以

Higress 3.x(作为 AI 网关)的核心特性之一就是支持 MCP Server Hosting(MCP 服务托管)。它可以把后端的 REST API(通过 OpenAPI/Swagger)或者数据库直接转换成标准的 MCP 协议服务。

而 LangChain 提供了 langchain-mcp-adapters 适配器,能够作为 MCP Client 去连接 Higress 暴露出来的 SSE 端口。

以下是完整的对接方案。


1. 架构逻辑

在这个架构中,Higress 和 LangChain 的角色如下:

  • Higress (服务端) :作为 MCP Host。它负责把你的存量 API(如 HTTP 接口、Nacos 服务)包装成 MCP 协议,并暴露一个 SSE (Server-Sent Events) 端点。
  • LangChain (客户端) :作为 MCP Client。它通过 Python 代码连接 Higress 的 SSE 端点,自动发现工具(Tools),并将其挂载给 LLM。

LangChain 应用 Higress Gateway (3.x) OpenAPI 插件 暴露 1. 连接 & 握手 2. 转换工具 MCP Adapter LangChain Tools 智能体 MCP Server Plugin 存量 REST API SSE Endpoint\n/mcp/sse

2. 实现步骤

第一步:在 Higress 网关侧配置 MCP 服务

你需要先在 Higress 控制台将接口暴露为 MCP。

  1. 创建服务来源:在 Higress 中添加你的后端服务(可以是 Nacos、K8s Service 或 DNS 域名)。
  2. 导入路由与 API:确保你已经配置了指向该服务的路由,最好导入 OpenAPI (Swagger) 文档。
  3. 开启 MCP 插件
    • 进入路由配置,找到 MCP Server 插件(或 AI Proxy 插件中的 MCP 模块)。

    • 配置转换规则:选择 "OpenAPI to MCP"。

    • 获取端点 :配置完成后,Higress 会生成一个 MCP SSE 访问地址,通常格式如下:

      text 复制代码
      http://<你的网关域名>/mcp/<服务名称>/sse
    • (可选) 配置鉴权:如果网关开启了 Key Auth,记下 API Key。

第二步:在 LangChain 侧连接 MCP 服务

你需要安装 LangChain 的 MCP 适配器库。

安装依赖:

bash 复制代码
pip install langchain langchain-openai langchain-mcp-adapters mcp

编写代码:

Higress 暴露的是基于 HTTP SSE 的 MCP 服务,所以我们需要使用 MultiServerMCPClient 或者底层的 SSEClient

python 复制代码
import asyncio
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_mcp_adapters.client import MultiServerMCPClient

async def run_mcp_agent():
    # 1. 定义 Higress 的 MCP SSE 端点
    # 注意:Higress 3.x 暴露的通常是 SSE 协议的 URL
    higress_mcp_url = "http://localhost:8080/mcp/my-service/sse"  # 替换为你实际的 Higress 地址
    
    # 2. 创建 MCP 客户端并连接
    # MultiServerMCPClient 可以同时连接多个 MCP 服务
    async with MultiServerMCPClient() as client:
        print(f"正在连接 Higress MCP 服务: {higress_mcp_url} ...")
        
        # 建立 SSE 连接
        # 如果 Higress 设置了鉴权,可以在 headers 中传递 token
        await client.connect_sse(
            url=higress_mcp_url,
            headers={"Authorization": "Bearer YOUR_API_KEY"} # 如果没有鉴权可省略
        )
        
        # 3. 获取并转换工具
        # 这会自动拉取 Higress 转换好的所有 API 定义
        tools = client.get_tools()
        print(f"成功加载工具: {[t.name for t in tools]}")
        
        # 4. 初始化 LLM 和 Agent
        llm = ChatOpenAI(model="gpt-4o", temperature=0)
        
        prompt = ChatPromptTemplate.from_messages([
            ("system", "你是一个助手,请使用提供的工具回答问题。"),
            ("human", "{input}"),
            ("placeholder", "{agent_scratchpad}"),
        ])
        
        agent = create_tool_calling_agent(llm, tools, prompt)
        agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
        
        # 5. 执行调用
        response = await agent_executor.ainvoke({"input": "请帮我查询 ID 为 1001 的订单详情"})
        print("Agent 回复:", response["output"])

if __name__ == "__main__":
    asyncio.run(run_mcp_agent())

3. 方案关键点说明

  1. 传输协议 (Transport)

    • MCP 协议支持 stdio (本地进程) 和 sse (HTTP 流) 两种传输方式。
    • Higress 作为网关,是通过 SSE (Server-Sent Events) 暴露服务的。
    • 因此,LangChain 代码中必须使用支持 SSE 的连接方式 (connect_sse),而不能用 stdio
  2. 工具发现 (Discovery)

    • 代码中的 client.get_tools() 是动态的。
    • 当你在 Higress 网关上更新了 API 定义(例如增加了一个 Query 接口),LangChain 客户端重启或重连后,会自动获取到最新的工具列表,无需修改 Python 代码。
  3. 鉴权 (Authentication)

    • 网关侧 :建议在 Higress 给 MCP 路由配置 Key AuthBasic Auth 插件。
    • 代码侧 :在 connect_sse(headers=...) 中传入对应的认证头,保证安全性。
  4. 性能优化

    • 如果工具数量依然非常多(成百上千),仍然建议结合前文提到的 "工具检索(Tool Retrieval)" 方案。
    • LangChain 的 MCP Adapter 拿到的 tools 是一个列表,你可以先把这个列表存入向量库,再按需检索,而不是一次性全部喂给 create_tool_calling_agent

总结

Higress 3.x 原生支持将 API 转化为 MCP SSE 服务,LangChain 通过 langchain-mcp-adapters 可以完美对接。这是一种无侵入式的架构,后端服务完全不需要感知 MCP 的存在,全部由网关和 LangChain 完成协议转换和调用。

相关推荐
candyTong12 小时前
一觉醒来,大模型就帮我排查完页面性能问题
前端·javascript·架构
网安情报局12 小时前
除了 CDN,DDoS 攻击还有哪些更有效的防护方式?
网络
Promise微笑12 小时前
2026年国产替代油介损测试仪:油介损全场景解决方案与技术演进
大数据·网络·人工智能
深海鱼在掘金12 小时前
深入浅出 LangChain —— 第三章:模型抽象层
人工智能·langchain·agent
深海鱼在掘金13 小时前
深入浅出 LangChain —— 第二章:环境搭建与快速上手
人工智能·typescript·langchain
空中海14 小时前
Kubernetes 入门基础与核心架构
贪心算法·架构·kubernetes
AnalogElectronic15 小时前
linux 测试网络和端口是否连通的命令详解
linux·网络·php
米高梅狮子16 小时前
08.CronJob和Service
云原生·容器·架构·kubernetes·自动化
Rust研习社16 小时前
使用 Axum 构建高性能异步 Web 服务
开发语言·前端·网络·后端·http·rust
灰子学技术16 小时前
Envoy HTTP 流量层面的 Metric 指标分析
网络·网络协议·http