1. 核心痛点与解决思路
问题:当系统接入成百上千个 API(如 ERP、CRM、SaaS 接口)时,直接将其全部塞入 LLM 的上下文(Context Window)会导致:
- 上下文溢出:超过 Token 限制。
- 注意力分散:LLM 在海量选项中无法精准选择,幻觉率急剧上升。
- 成本高昂:每次请求都携带数万 Token 的无用信息。
解决方案 :采用 RAG for Tools(工具检索增强) 模式。
- 离线阶段:解析 API 文档,建立向量索引。
- 在线阶段:根据用户意图检索 Top-N 相关 API -> 动态绑定到 LLM -> 执行多步推理。
2. 整体架构设计
3. 关键实现步骤
步骤一:Swagger 解析与结构化 (The Parser)
目标 :将冗余的 Swagger JSON 转换为 LLM 易懂、且具备严格类型检查的 StructuredTool。
- 核心动作 :
- Schema 瘦身:只保留 Endpoint、Method、以及关键参数描述。剔除无用的 HTTP 状态码和复杂的嵌套 Response 定义。
- Pydantic 建模 :利用 Python 的
pydantic库动态创建参数模型,确保 LLM 传参符合类型规范(如 Integer 不传 String)。 - Enum 注入:将枚举值明确写入描述,防止 LLM 编造参数。
步骤二:工具向量化 (The Indexing)
目标:让系统能够根据自然语言理解 API 的功能。
- 核心动作 :
- 提取 API 的
description(功能描述)作为向量化的文本内容。 - 将
tool_name和 Swagger 的tags(分类)存为 Metadata,以便后续过滤。 - 存入 FAISS、Pinecone 或 Milvus 等向量库。
- 提取 API 的
步骤三:动态检索与绑定 (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. 提升检索精准度
- 混合检索 (Hybrid Search):结合关键词匹配(BM25)和向量检索。防止因专有名词(如 API 里的特殊字段名)被向量化模糊掉。
- 元数据过滤 (Metadata Filtering) :
- 利用 Swagger 的
Tags。 - 先用一个轻量级 LLM 判断领域(如 "这是财务相关的问题"),然后在向量检索时加 Filter
filter={"category": "Finance"}。
- 利用 Swagger 的
B. 提升参数填充成功率
- Pydantic 校验回环 :
- 当 LLM 传错参(如类型错误)时,Python 端捕获
ValidationError。 - 不要直接崩溃,而是将错误信息("Error: 'amount' must be an integer")作为 Observation 返回给 Agent。
- Agent 看到错误后会进行 Self-Correction (自我修正) 并重新发起调用。
- 当 LLM 传错参(如类型错误)时,Python 端捕获
- Schema 描述优化 :
- 在
description中给出 Example。例如:"用于查询用户,输入参数示例:user_id='u123'"。
- 在
C. 处理上下文依赖
- 对话历史注入:检索工具时,不要只用当前的 Query。如果用户上一句说了 "我是管理员",下一句说 "列出所有票据",检索器需要知道 "管理员" 这个上下文才能匹配到高权限 API。建议总结历史对话后再进行检索。
《在线详细方案:langchain加载api (tool)》
这是一个非常经典且高效的架构设计,通常被称为 "基于检索的动态工具绑定" (Retrieval-Augmented Tool Selection)。
在 2024-2025 年的 LangChain 最佳实践中,我们不再一次性把所有工具塞给 Agent,而是利用 LCEL (LangChain Expression Language) 在运行时动态地将相关的工具"挂载"到 LLM 上。
下面是实现这一方案的完整步骤和代码示例。
核心原理
- 索引阶段 (Indexing):把你成百上千个工具的"功能描述(Description)"转换成向量,存入向量数据库(VectorDB)。
- 检索阶段 (Retrieval):当用户提问时,先用问题去向量库搜索 Top-N 个最相关的工具。
- 执行阶段 (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 当作数据库。
- 离线:将 API 文档 Embed 到向量库。
- 在线 :
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 发个红包"
- 先调
search_user拿 Alice 的user_id。- 再调
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 时,必须做以下优化:
-
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."
- Swagger 的
-
强制类型检查 (Enforce Pydantic):
- 如果 Swagger 定义
age是integer,但 LLM 传了字符串 "18",Pydantic 会直接报错。 - 在 LangChain 中,捕获这个验证错误,并将错误信息(
ValidationError: value is not a valid integer)回传给 LLM。 - LLM 看到错误后,会进行自我修正(Self-Correction)并重试。
- 如果 Swagger 定义
-
枚举值注入 (Enum Injection):
- 如果 API 有参数
status只能是["open", "closed"],务必在 Pydantic 或 Tool Description 中列出这些值。Swagger 的enum字段必须解析并透传给 Prompt。
- 如果 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。
- 创建服务来源:在 Higress 中添加你的后端服务(可以是 Nacos、K8s Service 或 DNS 域名)。
- 导入路由与 API:确保你已经配置了指向该服务的路由,最好导入 OpenAPI (Swagger) 文档。
- 开启 MCP 插件 :
-
进入路由配置,找到 MCP Server 插件(或 AI Proxy 插件中的 MCP 模块)。
-
配置转换规则:选择 "OpenAPI to MCP"。
-
获取端点 :配置完成后,Higress 会生成一个 MCP SSE 访问地址,通常格式如下:
texthttp://<你的网关域名>/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. 方案关键点说明
-
传输协议 (Transport):
- MCP 协议支持
stdio(本地进程) 和sse(HTTP 流) 两种传输方式。 - Higress 作为网关,是通过 SSE (Server-Sent Events) 暴露服务的。
- 因此,LangChain 代码中必须使用支持 SSE 的连接方式 (
connect_sse),而不能用stdio。
- MCP 协议支持
-
工具发现 (Discovery):
- 代码中的
client.get_tools()是动态的。 - 当你在 Higress 网关上更新了 API 定义(例如增加了一个 Query 接口),LangChain 客户端重启或重连后,会自动获取到最新的工具列表,无需修改 Python 代码。
- 代码中的
-
鉴权 (Authentication):
- 网关侧 :建议在 Higress 给 MCP 路由配置
Key Auth或Basic Auth插件。 - 代码侧 :在
connect_sse(headers=...)中传入对应的认证头,保证安全性。
- 网关侧 :建议在 Higress 给 MCP 路由配置
-
性能优化:
- 如果工具数量依然非常多(成百上千),仍然建议结合前文提到的 "工具检索(Tool Retrieval)" 方案。
- LangChain 的 MCP Adapter 拿到的
tools是一个列表,你可以先把这个列表存入向量库,再按需检索,而不是一次性全部喂给create_tool_calling_agent。
总结
Higress 3.x 原生支持将 API 转化为 MCP SSE 服务,LangChain 通过 langchain-mcp-adapters 可以完美对接。这是一种无侵入式的架构,后端服务完全不需要感知 MCP 的存在,全部由网关和 LangChain 完成协议转换和调用。