面向 LLM Agent 的基于图的工具检索引擎
零依赖核心。从 OpenAPI、MCP、Python 函数收集工具,将工具间关系组织为图,只将 LLM 需要的工具精准检索并传递。
LLM Agent 可使用的工具正在飞速增长。 一个电商平台可能有 1,200 个以上的 API endpoint,公司内部系统可能跨多个服务拥有 500 个以上的函数。
问题很简单。
不可能每次都把所有工具定义放进 context window。
常见的解决方案是向量搜索。 将工具描述嵌入向量空间,找到与用户请求最接近的工具。
但实际的工具使用与文档检索不同。
- 有些工具需要与下一步工具串联。
- 有些工具必须一起调用。
- 有些工具是 read-only 的,有些工具是 destructive 的。
- 有些工具以前一个工具的结果为前提。
也就是说,工具不是孤立的文本片段,而是构成工作流的执行单元。
graph-tool-call 正是聚焦于此。 它不把工具视为简单的列表,而是当作有关系的图来处理,通过多信号混合检索只将 LLM 所需的工具传递给它。
举个例子,假设用户这样说。
取消订单并处理退款
向量搜索可以找到 cancelOrder。
但实际执行通常需要以下流程。
listOrders → getOrder → cancelOrder → processRefund
也就是说,重要的不是"找到一个相似的工具",而是包含当前所需工具和后续工具的完整执行流程。
graph-tool-call 将这些关系建模为图。
┌──────────┐
PRECEDES │listOrders│ PRECEDES
┌─────────┤ ├──────────┐
▼ └──────────┘ ▼
┌──────────┐ ┌───────────┐
│ getOrder │ │cancelOrder│
└──────────┘ └─────┬─────┘
│ COMPLEMENTARY
▼
┌──────────────┐
│processRefund │
└──────────────┘
graph-tool-call 以如下流水线运行。
OpenAPI / MCP / 代码 → 收集 → 分析 → 组织 → 检索 → Agent
检索阶段同时使用多个信号。
- BM25: 关键词匹配
- Graph traversal: 基于关系扩展
- Embedding similarity: 语义相似度
- MCP annotations: read-only / destructive / idempotent / open-world 提示
这些信号通过 weighted Reciprocal Rank Fusion (wRRF) 融合。
- 零依赖 — 核心仅使用 Python 标准库,按需添加 extras
- 从 OpenAPI / Swagger / MCP / Python 函数 自动收集工具
- 生成并利用工具关系图
- 基于 BM25 + 图 + 嵌入 + annotation 的混合检索
- History-aware retrieval
- Cross-encoder reranking
- MMR diversity
- LLM 增强本体
- 重复工具检测与合并
- HTML / GraphML / Cypher 导出
- 与 ai-api-lint 集成自动清理 spec
graph-tool-call 在以下场景中尤其有效。
- 工具数量多,难以将全部放入 context 时
- 相比简单相似度,调用顺序 / 关系信息更重要时
- 需要反映 MCP annotation 的 retrieval 时
- 需要将多个 API spec 或多个服务的工具统一为一个检索层时
- 希望 Agent 根据之前的调用历史更好地找到下一个工具时
核心包零依赖 — 仅使用 Python 标准库。 按需安装所需功能:
pip install graph-tool-call # core (BM25 + graph) — 无依赖
pip install graph-tool-call[embedding] # + 嵌入, cross-encoder reranker
pip install graph-tool-call[openapi] # + OpenAPI YAML 支持
pip install graph-tool-call[mcp] # + MCP 服务器模式
pip install graph-tool-call[all] # 全部所有 extras
| Extra | 安装的包 | 用途 |
|---|---|---|
openapi |
pyyaml | YAML OpenAPI spec 解析 |
embedding |
numpy | 语义搜索 (连接外部 Ollama/OpenAI/vLLM) |
embedding-local |
numpy, sentence-transformers | 本地 sentence-transformers 模型 |
similarity |
rapidfuzz | 重复工具检测 |
langchain |
langchain-core | LangChain 集成 |
visualization |
pyvis, networkx | HTML 图导出, GraphML |
dashboard |
dash, dash-cytoscape | 交互式仪表板 |
lint |
ai-api-lint | API spec 自动修复 |
mcp |
mcp | MCP 服务器模式 |
pip install graph-tool-call[lint]
pip install graph-tool-call[similarity]
pip install graph-tool-call[visualization]
pip install graph-tool-call[dashboard]
pip install graph-tool-call[langchain]uvx graph-tool-call search "user authentication" \
--source https://petstore.swagger.io/v2/swagger.jsonQuery: "user authentication"
Source: https://petstore.swagger.io/v2/swagger.json (19 tools)
Results (5):
1. getUserByName
Get user by user name
2. deleteUser
Delete user
3. createUser
Create user
4. loginUser
Logs user into the system
5. updateUser
Updated user
from graph_tool_call import ToolGraph
# 从官方 Petstore API 生成 tool graph
tg = ToolGraph.from_url(
"https://petstore3.swagger.io/api/v3/openapi.json",
cache="petstore.json",
)
print(tg)
# → ToolGraph(tools=19, nodes=22, edges=100)
# 工具检索
tools = tg.retrieve("create a new pet", top_k=5)
for t in tools:
print(f"{t.name}: {t.description}")该规范下 top_k=5 基准,Recall@5 98.3%。
以 MCP 服务器模式运行 — 任何 MCP 兼容的 agent 只需一条配置即可使用工具搜索:
该服务器提供 5 个工具:search_tools、get_tool_schema、list_categories、graph_info、load_source。
MCP 服务器多了,工具名称列表会占用大量 token。 MCP Proxy 将它们打包到一个服务器后面 — 172 个工具 → 3 个 meta-tool,每轮节省 ~1,200 token。
Step 1. 用现有 MCP 服务器创建 backends.json:
// ~/backends.json
{
"backends": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp", "--headless"]
},
"my-api": {
"command": "uvx",
"args": ["some-mcp-server"],
"env": { "API_KEY": "sk-..." }
}
},
"top_k": 10,
"cache_path": "~/.cache/mcp-proxy-cache.json"
}Embedding 可选。 有 Ollama 的话加
"embedding": "ollama/qwen3-embedding:0.6b"启用跨语言搜索。没有也能用 BM25 关键词搜索。
Step 2. 注册到 Claude Code:
claude mcp add -s user tool-proxy -- \
uvx "graph-tool-call[mcp]" proxy --config ~/backends.jsonStep 3. 删除原来的单独服务器(避免重复):
claude mcp remove playwright -s user
claude mcp remove my-api -s userStep 4. 重启 Claude Code 后确认:
claude mcp list
# tool-proxy: ... - ✓ Connected使用 retrieve() 搜索后转换为 OpenAI function-calling 格式。兼容任何 OpenAI 兼容 API:
from openai import OpenAI
from graph_tool_call import ToolGraph
from graph_tool_call.langchain.tools import tool_schema_to_openai_function
tg = ToolGraph.from_url(
"https://petstore3.swagger.io/api/v3/openapi.json",
cache="petstore.json",
)
tools = tg.retrieve("create a new pet", top_k=5)
openai_tools = [
{"type": "function", "function": tool_schema_to_openai_function(t)}
for t in tools
]
client = OpenAI() # 或 OpenAI(base_url="http://localhost:11434/v1") for Ollama
response = client.chat.completions.create(
model="gpt-4o",
tools=openai_tools, # 只传 5 个而非全部 248 个
messages=[{"role": "user", "content": "create a new pet"}],
)Anthropic Claude API
from anthropic import Anthropic
from graph_tool_call import ToolGraph
tg = ToolGraph.from_url("https://api.example.com/openapi.json")
tools = tg.retrieve("cancel an order", top_k=5)
anthropic_tools = [
{
"name": t.name,
"description": t.description,
"input_schema": {
"type": "object",
"properties": {
p.name: {"type": p.type, "description": p.description}
for p in t.parameters
},
"required": [p.name for p in t.parameters if p.required],
},
}
for t in tools
]
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
tools=anthropic_tools,
messages=[{"role": "user", "content": "取消我的订单"}],
max_tokens=1024,
)已有 tool-calling 代码?一行代码自动过滤:
from graph_tool_call import ToolGraph
from graph_tool_call.middleware import patch_openai
tg = ToolGraph.from_url("https://api.example.com/openapi.json")
patch_openai(client, graph=tg, top_k=5) # ← 添加这一行
response = client.chat.completions.create(
model="gpt-4o",
tools=all_248_tools,
messages=messages,
)# Anthropic 同样支持
from graph_tool_call.middleware import patch_anthropic
patch_anthropic(client, graph=tg, top_k=5)pip install graph-tool-call[langchain] langgraph将 50~500+ 个工具转换为 2个 meta-tool(search_tools + call_tool):
from graph_tool_call.langchain import create_gateway_tools
# 将 62 个工具转为 2 个 gateway meta-tool
gateway = create_gateway_tools(all_tools, top_k=10)
# 只需 2 个工具传入 agent
agent = create_react_agent(model=llm, tools=gateway)| 绑定全部工具 | Gateway(2个) | |
|---|---|---|
| 62 tools | ~6,090 tokens/turn | ~475 tokens/turn |
| Token 减少 | — | 92% |
每轮自动只绑定相关工具到 LLM:
from graph_tool_call.langchain import create_agent
agent = create_agent(llm, tools=all_200_tools, top_k=5)LangChain Retriever(返回 Document)
from graph_tool_call import ToolGraph
from graph_tool_call.langchain import GraphToolRetriever
tg = ToolGraph.from_url("https://api.example.com/openapi.json")
retriever = GraphToolRetriever(tool_graph=tg, top_k=5)
docs = retriever.invoke("cancel an order")
for doc in docs:
print(doc.page_content) # "cancelOrder: Cancel an existing order"
print(doc.metadata["tags"]) # ["order"]graph-tool-call 验证两件事。
- 只把检索到的部分工具给 LLM,性能能否保持或提升?
- 检索器本身能否将正确工具排进前 K 名?
评价在相同的用户请求集上对比了以下配置。
- baseline: 将全部工具定义原样传给 LLM
- retrieve-k3 / k5 / k10: 只传递检索到的前 K 个工具
- + embedding / + ontology: 在 retrieve-k5 基础上添加语义检索和 LLM 本体增强
模型使用 qwen3:4b (4-bit, Ollama)。
- Accuracy: LLM 最终是否选择了正确的工具
- Recall@K: 检索阶段正确工具是否在前 K 名内
- Avg tokens: 传递给 LLM 的平均 token 数
- Token reduction: 相对 baseline 的 token 节省率
- 小规模 API (19~50 tools) 中 baseline 本身就很强。 在这个区间,graph-tool-call 的主要价值是在保持接近原有精度的情况下节省 64~91% 的 token。
- 大规模 API (248 tools) 中 baseline 崩溃到 12%。 而 graph-tool-call 维持 78~82% 精度。此时它不是优化,而是必需的检索层。
全流水线对比
指标解读
- End-to-end Accuracy: LLM 最终是否成功选择了正确的工具或完成了正确的 workflow
- Gold Tool Recall@K: 在 retrieval 阶段,指定的 canonical gold tool 是否在前 K 名内
- 两个指标衡量的对象不同,因此不一定总是一致。
- 特别是在允许可替代工具或等效 workflow 也算正确的评价中,
End-to-end Accuracy与Gold Tool Recall@K可能不完全一致。- baseline 没有 retrieval 阶段,因此
Gold Tool Recall@K不适用。
| Dataset | Tool 数 | Pipeline | End-to-end Accuracy | Gold Tool Recall@K | Avg tokens | Token reduction |
|---|---|---|---|---|---|---|
| Petstore | 19 | baseline | 100.0% | — | 1,239 | — |
| Petstore | 19 | retrieve-k3 | 90.0% | 93.3% | 305 | 75.4% |
| Petstore | 19 | retrieve-k5 | 95.0% | 98.3% | 440 | 64.4% |
| Petstore | 19 | retrieve-k10 | 100.0% | 98.3% | 720 | 41.9% |
| GitHub | 50 | baseline | 100.0% | — | 3,302 | — |
| GitHub | 50 | retrieve-k3 | 85.0% | 87.5% | 289 | 91.3% |
| GitHub | 50 | retrieve-k5 | 87.5% | 87.5% | 398 | 87.9% |
| GitHub | 50 | retrieve-k10 | 90.0% | 92.5% | 662 | 79.9% |
| Mixed MCP | 38 | baseline | 96.7% | — | 2,741 | — |
| Mixed MCP | 38 | retrieve-k3 | 86.7% | 93.3% | 328 | 88.0% |
| Mixed MCP | 38 | retrieve-k5 | 90.0% | 96.7% | 461 | 83.2% |
| Mixed MCP | 38 | retrieve-k10 | 96.7% | 100.0% | 826 | 69.9% |
| Kubernetes core/v1 | 248 | baseline | 12.0% | — | 8,192 | — |
| Kubernetes core/v1 | 248 | retrieve-k5 | 78.0% | 91.0% | 1,613 | 80.3% |
| Kubernetes core/v1 | 248 | retrieve-k5 + embedding | 80.0% | 94.0% | 1,728 | 78.9% |
| Kubernetes core/v1 | 248 | retrieve-k5 + ontology | 82.0% | 96.0% | 1,699 | 79.3% |
| Kubernetes core/v1 | 248 | retrieve-k5 + embedding + ontology | 82.0% | 98.0% | 1,924 | 76.5% |
如何解读此表
- baseline 是不经 retrieval、将全部工具定义直接传给 LLM 的结果。
- retrieve-k 系列只将检索到的部分工具传给 LLM,因此 retrieval 质量和 LLM 选择能力共同影响性能。
- 所以 baseline 精度为 100% 并不意味着 retrieve-k 精度也必须是 100%。
Gold Tool Recall@K衡量的是 retrieval 是否将 canonical gold tool 放入 top-k,End-to-end Accuracy衡量的是最终任务执行是否成功。- 因此在允许可替代工具或等效 workflow 的评价中,两个数值可能不完全一致。
核心解读
- Petstore / GitHub / Mixed MCP 等工具数较少或中等规模时,baseline 本身就很强。 在这个区间,graph-tool-call 的主要价值是在不大幅损失精度的前提下大幅减少 token。
- Kubernetes core/v1 (248 tools) 等工具数较多时,baseline 会因上下文过载而急剧崩溃。 而 graph-tool-call 通过检索缩小候选范围,将性能从 12.0% 恢复到 78.0~82.0%。
- 实践中 retrieve-k5 是最佳默认值。 token 效率与性能平衡良好,在大数据集上添加 embedding / ontology 时还可获得额外提升。
下表是 LLM 之前阶段,即单独衡量 retrieval 本身质量的结果。 这里仅使用 BM25 + 图遍历,不包含嵌入和本体。
指标解读
- Gold Tool Recall@K: 在 retrieval 阶段,指定的 canonical gold tool 是否在前 K 名内
- 此表展示的不是最终 LLM 选择精度,而是检索器构建候选集的能力。
- 因此此表需要与上面的 End-to-end Accuracy 表一起阅读。
- 即使 retrieval 将 gold tool 放入 top-k,最终 LLM 也不一定总能选对。
- 反之,在 end-to-end 评价中允许可替代工具或等效 workflow 算正确的情况下,最终精度与 gold recall 可能不完全一致。
| Dataset | Tool 数 | Gold Tool Recall@3 | Gold Tool Recall@5 | Gold Tool Recall@10 |
|---|---|---|---|---|
| Petstore | 19 | 93.3% | 98.3% | 98.3% |
| GitHub | 50 | 87.5% | 87.5% | 92.5% |
| Mixed MCP | 38 | 93.3% | 96.7% | 100.0% |
| Kubernetes core/v1 | 248 | 82.0% | 91.0% | 92.0% |
- Gold Tool Recall@K 展示的是 retrieval 将正确工具包含在候选集中的能力。
- 小数据集中
k=5就能获得很高的 recall。 - 大数据集中增加
k可以提高 recall,但传给 LLM 的 token 也会相应增加。 - 因此实际运营中不仅要看 recall,还要综合考虑 token 成本和最终 end-to-end accuracy。
- Petstore / Mixed MCP 中
k=5就能将几乎所有正确工具包含在候选集中。 - GitHub 中
k=5和k=10之间存在 recall 差异,如需更高 recall,k=10可能更有利。 - Kubernetes core/v1 等工具数较多的情况下,
k=5就已获得 91.0% 的 gold recall。 也就是说,仅靠检索阶段就能在大幅压缩候选集的同时保留大部分正确工具。 - 总体而言
retrieve-k5是最实用的默认值。k=3更轻量但可能遗漏部分正确工具,k=10的 recall 收益相对于 token 成本可能偏大。
在最大的数据集 Kubernetes core/v1 (248 tools) 上,在 retrieve-k5 基础上添加额外信号进行对比。
| Pipeline | End-to-end Accuracy | Gold Tool Recall@5 | 解读 |
|---|---|---|---|
| retrieve-k5 | 78.0% | 91.0% | 仅 BM25 + 图即为 strong baseline |
| + embedding | 80.0% | 94.0% | 更好地召回语义相似但表述不同的查询 |
| + ontology | 82.0% | 96.0% | LLM 生成的关键词/示例查询大幅改善检索质量 |
| + embedding + ontology | 82.0% | 98.0% | 精度保持不变,gold recall 达到最高 |
- embedding 弥补了 BM25 遗漏的语义相似性。
- ontology 在工具描述简短或不规范时扩展可检索的表达本身。
- 两者结合时 end-to-end accuracy 的提升幅度可能有限,但将正确工具纳入候选集的能力达到最强。
注册 200 个简单工具并通过 LangChain 代理调用时的端到端准确率。
- Direct (D): 将 200 个工具定义一次性传给 LLM
- Graph (G): 通过 graph-tool-call 网关管理工具 (search → call, 2 轮)
| 模型 | D-准确 | G-准确 | D-轮次 | G-轮次 | D-最小% | G-最小% | D-Token | G-Token | 节省率 | D-时间 | G-时间 | D-Tool | G-Tool | D-LLM | G-LLM |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| gpt-4.1 | 60.0% | 80.0% | 1.0 | 2.0 | 100% | 100% | 52,587 | 6,639 | 87.4% | 15.5s | 17.6s | 0.00s | 0.04s | 15.50s | 17.55s |
| gpt-5.2 | 60.0% | 100.0% | 1.0 | 2.0 | 100% | 100% | 53,645 | 10,508 | 80.4% | 20.5s | 17.1s | 0.00s | 0.03s | 20.48s | 17.06s |
| gpt-5.4 | 60.0% | 100.0% | 1.0 | 2.0 | 100% | 100% | 60,035 | 14,049 | 76.6% | 18.2s | 17.0s | 0.00s | 0.06s | 18.23s | 16.96s |
| claude-sonnet-4-20250514 | 100.0% | 100.0% | 1.0 | 2.0 | 100% | 100% | 196,183 | 17,349 | 91.2% | 58.2s | 49.4s | 0.00s | 0.03s | 58.23s | 49.35s |
| claude-sonnet-4-6 | 100.0% | 100.0% | 1.0 | 2.0 | 100% | 100% | 198,665 | 20,074 | 89.9% | 67.0s | 69.4s | 0.00s | 0.03s | 67.02s | 69.33s |
| claude-haiku-4-5 | 100.0% | 100.0% | 1.0 | 2.0 | 100% | 100% | 197,845 | 19,714 | 90.0% | 23.7s | 22.8s | 0.00s | 0.03s | 23.73s | 22.73s |
列说明: 准确 = 准确率,轮次 = 平均代理轮次,最小% = 最小工具调用成功率,Token = 总消耗 token,节省率 = token 节省(D→G),时间 = 实际耗时,Tool = 工具执行时间,LLM = LLM 推理时间
核心发现
- GPT 系列模型直接传递 200 个工具时准确率降至 60%,使用 graph-tool-call 后恢复到 80–100%。
- Claude 系列模型无论哪种方式都保持 100% 准确率,但 graph-tool-call 实现了 89–91% 的 token 节省。
- Graph 模式增加 1 轮(search → call),但由于上下文缩小,总延迟持平甚至下降。
- 所有模型的 token 节省率在 76.6%–91.2% 范围内。
# 检索质量测量(快速,无需 LLM)
python -m benchmarks.run_benchmark
python -m benchmarks.run_benchmark -d k8s -v
# 流水线基准(LLM 对比)
python -m benchmarks.run_benchmark --mode pipeline -m qwen3:4b
python -m benchmarks.run_benchmark --mode pipeline --pipelines baseline retrieve-k3 retrieve-k5 retrieve-k10
# 保存基线并比较
python -m benchmarks.run_benchmark --mode pipeline --save-baseline
python -m benchmarks.run_benchmark --mode pipeline --difffrom graph_tool_call import ToolGraph
# 从文件(JSON / YAML)
tg = ToolGraph()
tg.ingest_openapi("path/to/openapi.json")
# 从 URL — 自动探索 Swagger UI 中的所有 spec 组
tg = ToolGraph.from_url("https://api.example.com/swagger-ui/index.html")
# 缓存 — 一次构建,即时复用
tg = ToolGraph.from_url(
"https://api.example.com/swagger-ui/index.html",
cache="my_api.json",
)
# 支持: Swagger 2.0, OpenAPI 3.0, OpenAPI 3.1from graph_tool_call import ToolGraph
mcp_tools = [
{
"name": "read_file",
"description": "读取文件",
"inputSchema": {"type": "object", "properties": {"path": {"type": "string"}}},
"annotations": {"readOnlyHint": True, "destructiveHint": False},
},
{
"name": "delete_file",
"description": "永久删除文件",
"inputSchema": {"type": "object", "properties": {"path": {"type": "string"}}},
"annotations": {"readOnlyHint": False, "destructiveHint": True},
},
]
tg = ToolGraph()
tg.ingest_mcp_tools(mcp_tools, server_name="filesystem")
tools = tg.retrieve("删除临时文件", top_k=5)MCP annotation (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) 被用作检索信号。
查询意图会自动分类:读取查询优先返回 read-only 工具,删除查询优先返回 destructive 工具。
from graph_tool_call import ToolGraph
tg = ToolGraph()
# Public MCP endpoint
tg.ingest_mcp_server("https://mcp.example.com/mcp")
# 本地/私有 MCP endpoint 需要显式允许
tg.ingest_mcp_server(
"http://127.0.0.1:3000/mcp",
allow_private_hosts=True,
)ingest_mcp_server() 会调用 HTTP JSON-RPC tools/list 获取工具列表,
并在保留 MCP annotation 的情况下直接注册到 graph 中。
远程收集默认安全策略:
- 默认阻止 private / localhost host
- 限制远程响应大小
- 限制 redirect 次数
- 拒绝意外的 content-type
from graph_tool_call import ToolGraph
def read_file(path: str) -> str:
"""读取文件内容。"""
def write_file(path: str, content: str) -> None:
"""写入文件内容。"""
tg = ToolGraph()
tg.ingest_functions([read_file, write_file])从 type hint 提取参数,从 docstring 提取描述。
from graph_tool_call import ToolGraph
tg = ToolGraph()
tg.add_tools([
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询城市当前天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
},
},
},
])
tg.add_relation("get_weather", "get_forecast", "complementary")在 BM25 + 图遍历基础上可以添加基于嵌入的语义检索。 无需重型依赖 — 连接外部 embedding 服务器 (Ollama, OpenAI, vLLM 等) 或使用本地 sentence-transformers。
pip install graph-tool-call[embedding] # 仅 numpy (~20MB)
pip install graph-tool-call[embedding-local] # + sentence-transformers (~2GB, 本地模型)# Ollama(推荐 — 轻量、跨语言支持)
tg.enable_embedding("ollama/qwen3-embedding:0.6b")
# OpenAI
tg.enable_embedding("openai/text-embedding-3-large")
# vLLM / llama.cpp / OpenAI 兼容服务器
tg.enable_embedding("vllm/Qwen/Qwen3-Embedding-0.6B")
tg.enable_embedding("vllm/model@http://gpu-box:8000/v1")
tg.enable_embedding("llamacpp/model@http://192.168.1.10:8080/v1")
tg.enable_embedding("http://localhost:8000/v1@my-model")
# Sentence-transformers(需 embedding-local extra)
tg.enable_embedding("sentence-transformers/all-MiniLM-L6-v2")
# 自定义 callable
tg.enable_embedding(lambda texts: my_embed_fn(texts))启用嵌入后权重会自动重新调整。也可以手动调优。
tg.set_weights(keyword=0.1, graph=0.4, embedding=0.5)一次构建的图可以直接保存并复用。
# 保存
tg.save("my_graph.json")
# 加载
tg = ToolGraph.load("my_graph.json")
# from_url() 中用 cache= 选项自动保存/加载
tg = ToolGraph.from_url(url, cache="my_graph.json")完整图结构(节点、边、关系类型、权重)全部保留。
启用嵌入检索后保存时,还会一并保留:
- embedding vector
- 可恢复的 embedding provider 配置
- retrieval weights
- diversity 配置
这意味着 ToolGraph.load() 之后无需重新构建 embedding,
就能直接恢复 hybrid retrieval 状态。
使用 cross-encoder 模型进行二次重排序。
tg.enable_reranker() # 默认: cross-encoder/ms-marco-MiniLM-L-6-v2
tools = tg.retrieve("取消订单", top_k=5)先用 wRRF 缩小候选范围,再将 (query, tool_description) 对联合编码进行更精确的排序调整。
减少重复结果,获取更多样化的候选。
tg.enable_diversity(lambda_=0.7)传入之前调用过的工具名称可以改善下一步检索。
# 首次调用
tools = tg.retrieve("查找订单")
# → [listOrders, getOrder, ...]
# 第二次调用
tools = tg.retrieve("现在取消", history=["listOrders", "getOrder"])
# → [cancelOrder, processRefund, ...]已使用的工具会降权,图上处于下一步的工具会升权。
可以调整各信号的贡献度。
tg.set_weights(
keyword=0.2, # BM25 文本匹配
graph=0.5, # 图遍历
embedding=0.3, # 语义相似度
annotation=0.2, # MCP annotation 匹配
)可以用 LLM 构建更丰富的工具本体。 适用于类别生成、关系推理、检索关键词扩展。
tg.auto_organize(llm="ollama/qwen2.5:7b")
tg.auto_organize(llm=lambda p: my_llm(p))
tg.auto_organize(llm=openai.OpenAI())
tg.auto_organize(llm="litellm/claude-sonnet-4-20250514")支持的 LLM 输入
| 输入 | 包装类型 |
|---|---|
OntologyLLM 实例 |
直接使用 |
callable(str) -> str |
CallableOntologyLLM |
OpenAI 客户端(含 chat.completions) |
OpenAIClientOntologyLLM |
"ollama/model" |
OllamaOntologyLLM |
"openai/model" |
OpenAICompatibleOntologyLLM |
"litellm/model" |
litellm.completion 包装器 |
可以跨多个 API spec 检测并合并重复工具。
duplicates = tg.find_duplicates(threshold=0.85)
merged = tg.merge_duplicates(duplicates)
# merged = {"getUser_1": "getUser", ...}# 交互式 HTML (vis.js)
tg.export_html("graph.html", progressive=True)
# GraphML (Gephi, yEd)
tg.export_graphml("graph.graphml")
# Neo4j Cypher
tg.export_cypher("graph.cypher")使用 ai-api-lint 在收集前自动清理 OpenAPI spec。
pip install graph-tool-call[lint]tg = ToolGraph.from_url(url, lint=True)| 场景 | 仅向量搜索 | graph-tool-call |
|---|---|---|
| "取消我的订单" | 返回 cancelOrder |
listOrders → getOrder → cancelOrder → processRefund |
| "读取并保存文件" | 返回 read_file |
read_file + write_file(COMPLEMENTARY 关系) |
| "删除旧记录" | 返回与"删除"匹配的任意工具 | destructive 工具优先排名 |
| "现在取消"(history) | 无上下文 | 已用工具降权,下一步工具升权 |
| 多个 Swagger spec 中有重复工具 | 结果包含重复 | 跨源自动去重 |
| 1,200 个 API endpoint | 慢且噪声多 | 按类别组织 + 图遍历精准检索 |
# 一行命令搜索(收集 + 检索一步完成)
graph-tool-call search "cancel order" --source https://api.example.com/openapi.json
graph-tool-call search "delete user" --source ./openapi.json --scores --json
# MCP 服务器
graph-tool-call serve --source https://api.example.com/openapi.json
graph-tool-call serve --graph prebuilt.json
graph-tool-call serve -s https://api1.com/spec.json -s https://api2.com/spec.json
# 构建并保存图
graph-tool-call ingest https://api.example.com/openapi.json -o graph.json
graph-tool-call ingest ./spec.yaml --embedding --organize
# 从预构建图检索
graph-tool-call retrieve "query" -g graph.json -k 10
# 分析、可视化、仪表板
graph-tool-call analyze graph.json --duplicates --conflicts
graph-tool-call visualize graph.json -f html
graph-tool-call info graph.json
graph-tool-call dashboard graph.json --port 8050ToolGraph 方法
| 方法 | 描述 |
|---|---|
add_tool(tool) |
添加单个工具(格式自动检测) |
add_tools(tools) |
添加多个工具 |
ingest_openapi(source) |
从 OpenAPI / Swagger spec 收集 |
ingest_mcp_tools(tools) |
从 MCP tool list 收集 |
ingest_mcp_server(url) |
直接从 MCP HTTP 服务器收集 |
ingest_functions(fns) |
从 Python callable 收集 |
ingest_arazzo(source) |
收集 Arazzo 1.0.0 工作流 spec |
from_url(url, cache=...) |
从 Swagger UI 或 spec URL 构建 |
add_relation(src, tgt, type) |
手动添加关系 |
auto_organize(llm=...) |
工具自动分类 |
build_ontology(llm=...) |
构建完整本体 |
retrieve(query, top_k=10) |
工具检索 |
validate_tool_call(call) |
校验并自动纠正 tool call |
assess_tool_call(call) |
按执行策略返回 allow/confirm/deny 判定 |
enable_embedding(provider) |
启用混合嵌入检索 |
enable_reranker(model) |
启用 cross-encoder 重排序 |
enable_diversity(lambda_) |
启用 MMR 多样性 |
set_weights(...) |
调优 wRRF 融合权重 |
find_duplicates(threshold) |
检测重复工具 |
merge_duplicates(pairs) |
合并已检测的重复工具 |
apply_conflicts() |
检测/添加 CONFLICTS_WITH 边 |
analyze() |
生成运行分析报告 |
save(path) / load(path) |
序列化 / 反序列化 |
export_html(path) |
导出交互式 HTML 可视化 |
export_graphml(path) |
导出 GraphML 格式 |
export_cypher(path) |
导出 Neo4j Cypher 语句 |
dashboard_app() / dashboard() |
生成 / 启动仪表板 |
suggest_next(tool, history=...) |
基于图推荐下一步工具 |
| 功能 | 纯向量方案 | graph-tool-call |
|---|---|---|
| 工具来源 | 手动注册 | Swagger / OpenAPI / MCP 自动收集 |
| 检索方式 | 简单向量相似度 | 多阶段混合(wRRF + rerank + MMR) |
| 行为语义 | 无 | MCP annotation-aware retrieval |
| 工具关系 | 无 | 6 种关系类型,自动检测 |
| 调用顺序 | 无 | 状态机 + CRUD + response→request 数据流 |
| 去重 | 无 | 跨源重复检测 |
| 本体 | 无 | Auto / LLM-Auto 模式 |
| History | 无 | 已用工具降权,下一步升权 |
| Spec 质量 | 假设 spec 质量好 | ai-api-lint 自动修复集成 |
| LLM 依赖 | 必需 | 可选(无也可用,有则更好) |
| 文档 | 描述 |
|---|---|
| 架构 | 系统概述、流水线层、数据模型 |
| WBS | 工作分解结构 — Phase 0~4 进展 |
| 设计 | 算法设计 — spec 标准化、依赖检测、检索模式、调用顺序、本体模式 |
| 研究 | 竞争分析、API 规模数据、电商模式 |
| OpenAPI 指南 | 如何编写能生成更好工具图的 API 规范 |
欢迎贡献。
# 开发环境设置
git clone https://github.com/SonAIengine/graph-tool-call.git
cd graph-tool-call
pip install poetry
poetry install --with dev
# 运行测试
poetry run pytest -v
# 代码检查
poetry run ruff check .
poetry run ruff format --check .
# 运行基准测试
python -m benchmarks.run_benchmark -v