LangGraph 代理通过状态图处理多步推理,但如果没有搜索工具,它们就无法访问实时网络数据。本教程将 Scavio 添加为 LangGraph 工具节点,以便您的代理可以在网络中搜索推理、合并新结果并引用来源——所有这些都在标准 LangGraph 执行模型中。
前置条件
- Python 3.11+
- langgraph >= 0.2.0 并安装了 langchain-core
- 来自 https://scavio.dev 的 Scavio API 密钥
- LLM 节点的 OpenAI 或 Anthropic API 密钥
操作指南
步骤 1: 为 LangGraph 定义 Scavio 搜索工具
创建一个与 LangChain 兼容的工具来包装 Scavio Search API。该工具遵循标准的 BaseTool 接口,因此 LangGraph 可以在任何工具节点中调用它。
import httpx
from langchain_core.tools import tool
from typing import Optional
SCAVIO_API_KEY = "your-api-key"
@tool
def web_search(query: str, num_results: Optional[int] = 5) -> str:
"""Search the web for current information. Returns titles, URLs, and snippets.
Use this when you need up-to-date facts, recent news, or live data."""
with httpx.Client(timeout=15) as client:
resp = client.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_API_KEY},
json={"query": query, "num_results": num_results}
)
resp.raise_for_status()
results = resp.json().get("results", [])
if not results:
return "No results found for this query."
formatted = []
for i, r in enumerate(results, 1):
formatted.append(
f"{i}. {r.get('title', 'No title')}\n"
f" URL: {r.get('url', '')}\n"
f" {r.get('description', '')[:200]}"
)
return "\n\n".join(formatted)步骤 2: 使用工具节点构建 LangGraph 状态图
使用 LLM 节点和工具节点创建代理图。 LLM决定何时调用搜索工具,工具节点执行调用并将结果返回给LLM。
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_openai import ChatOpenAI
# LLM with tool binding
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [web_search]
llm_with_tools = llm.bind_tools(tools)
# Define the agent node
def agent_node(state: MessagesState):
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
# Build the graph
graph = StateGraph(MessagesState)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools))
# Routing: agent -> tools -> agent (loop until done)
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", tools_condition)
graph.add_edge("tools", "agent")
# Compile
agent = graph.compile()步骤 3: 运行具有搜索和跟踪成本的代理
执行代理,计算它发出的搜索调用次数,并计算 Scavio API 成本。每次搜索费用为 0.005 美元。
from langchain_core.messages import HumanMessage
async def run_with_tracking(question: str) -> dict:
search_calls = 0
final_answer = ""
result = await agent.ainvoke({
"messages": [HumanMessage(content=question)]
})
for msg in result["messages"]:
if hasattr(msg, "tool_calls") and msg.tool_calls:
for tc in msg.tool_calls:
if tc["name"] == "web_search":
search_calls += 1
if hasattr(msg, "content") and msg.content and not hasattr(msg, "tool_calls"):
final_answer = msg.content
cost = search_calls * 0.005
return {
"answer": final_answer,
"search_calls": search_calls,
"cost_usd": cost
}
# Usage
import asyncio
result = asyncio.run(run_with_tracking(
"Compare the top 3 LangGraph alternatives in May 2026"
))
print(f"Search calls: {result['search_calls']}")
print(f"Cost: {result['cost_usd']:.3f}")
print(f"Answer: {result['answer'][:300]}...")Python 示例
import asyncio
import httpx
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_openai import ChatOpenAI
SCAVIO_API_KEY = "your-api-key"
@tool
def web_search(query: str, num_results: int = 5) -> str:
"""Search the web for current information."""
with httpx.Client(timeout=15) as client:
resp = client.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_API_KEY},
json={"query": query, "num_results": num_results}
)
resp.raise_for_status()
results = resp.json().get("results", [])
return "\n".join(
f"{i}. {r['title']} - {r['url']}" for i, r in enumerate(results, 1)
) or "No results found."
tools = [web_search]
llm = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)
def agent_node(state: MessagesState):
return {"messages": [llm.invoke(state["messages"])]}
graph = StateGraph(MessagesState)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools))
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", tools_condition)
graph.add_edge("tools", "agent")
agent = graph.compile()
result = asyncio.run(agent.ainvoke({
"messages": [HumanMessage(content="Top LangGraph alternatives May 2026")]
}))
print(result["messages"][-1].content[:500])JavaScript 示例
// LangGraph.js with Scavio search tool
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { StateGraph, MessagesAnnotation, START, END } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { z } from "zod";
const SCAVIO_API_KEY = "your-api-key";
const webSearch = tool(async ({ query }) => {
const resp = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": SCAVIO_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query, num_results: 5 })
});
const data = await resp.json();
return (data.results || []).map((r, i) => `${i + 1}. ${r.title} - ${r.url}`).join("\n") || "No results.";
}, {
name: "web_search",
description: "Search the web for current information",
schema: z.object({ query: z.string() })
});
const tools = [webSearch];
const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 }).bindTools(tools);
const agentNode = async (state) => {
const response = await llm.invoke(state.messages);
return { messages: [response] };
};
const shouldContinue = (state) => {
const last = state.messages[state.messages.length - 1];
return last.tool_calls?.length ? "tools" : END;
};
const graph = new StateGraph(MessagesAnnotation)
.addNode("agent", agentNode)
.addNode("tools", new ToolNode(tools))
.addEdge(START, "agent")
.addConditionalEdges("agent", shouldContinue)
.addEdge("tools", "agent")
.compile();
const result = await graph.invoke({ messages: [{ role: "user", content: "Top LangGraph alternatives May 2026" }] });
console.log(result.messages.at(-1).content.slice(0, 500));预期输出
Search calls: 2
Cost: $0.010
Answer: Based on current web results, the top 3 LangGraph alternatives in May 2026 are...