基于 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 完成协议转换和调用。

相关推荐
刺客xs2 小时前
TCP网络通信
网络·网络协议·tcp/ip
TG:@yunlaoda360 云老大2 小时前
华为云国际站代理商的CBR支持哪些云服务备份?
网络·人工智能·华为云
Gavin在路上2 小时前
企业架构之深度解析企业架构与流程架构的共生关系(3)
架构
Dillon Dong2 小时前
Linux后台抓包利器:`nohup`与`tcpdump`的完美结合
linux·网络·tcpdump
GIOTTO情2 小时前
Infoseek 危机公关系统技术实现深度解析:AI 驱动的全链路舆情处置架构与工程实践
人工智能·架构
爬山算法2 小时前
Netty(26)如何实现基于Netty的RPC框架?
网络·网络协议·rpc
玥轩_5213 小时前
OSPF路由协议单区域配置
服务器·网络·智能路由器·交换机·ospf·动态路由
专业开发者3 小时前
照明如何成为建筑网络的平台
网络·物联网
Joy T3 小时前
【快速入门】提示工程(PE,Prompt Engineering):大模型时代的自然语言编程范式
架构·llm·prompt·人机交互