langgraph-tools
SKILL.md
LangGraph Tool Calling
Integrate tool calling into LangGraph workflows.
Basic Tool Binding
from langchain_core.tools import tool
from langchain_anthropic import ChatAnthropic
@tool
def search_database(query: str) -> str:
"""Search the database for information."""
return db.search(query)
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to a recipient."""
email_service.send(to, subject, body)
return f"Email sent to {to}"
# Bind tools to model
tools = [search_database, send_email]
model = ChatAnthropic(model="claude-sonnet-4-20250514")
model_with_tools = model.bind_tools(tools)
# Agent node
def agent_node(state: State):
response = model_with_tools.invoke(state["messages"])
return {"messages": [response]}
ToolNode for Execution
from langgraph.prebuilt import ToolNode
from langgraph.graph import StateGraph, START, END
# Create tool execution node
tool_node = ToolNode(tools)
# Build agent graph
builder = StateGraph(MessagesState)
builder.add_node("agent", agent_node)
builder.add_node("tools", tool_node)
# Routing based on tool calls
def should_continue(state: MessagesState) -> str:
last_message = state["messages"][-1]
if last_message.tool_calls:
return "tools"
return END
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
builder.add_edge("tools", "agent") # Return to agent after tool execution
graph = builder.compile()
Force Tool Calling
# Force model to call at least one tool
model.bind_tools(tools, tool_choice="any")
# Force specific tool
model.bind_tools(tools, tool_choice="search_database")
# Structured output via tool (guaranteed schema)
from pydantic import BaseModel
class SearchResult(BaseModel):
query: str
results: list[str]
confidence: float
model.bind_tools([SearchResult], tool_choice="SearchResult")
Dynamic Tool Selection
from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer("all-MiniLM-L6-v2")
# Pre-compute tool embeddings
TOOL_EMBEDDINGS = {
tool.name: embedder.encode(tool.description)
for tool in all_tools
}
def select_relevant_tools(query: str, all_tools: list, top_k: int = 5) -> list:
"""Select most relevant tools based on query."""
query_embedding = embedder.encode(query)
similarities = [
(tool, cosine_similarity(query_embedding, TOOL_EMBEDDINGS[tool.name]))
for tool in all_tools
]
sorted_tools = sorted(similarities, key=lambda x: x[1], reverse=True)
return [tool for tool, _ in sorted_tools[:top_k]]
def agent_with_dynamic_tools(state: State):
"""Bind only relevant tools to reduce context."""
relevant_tools = select_relevant_tools(
state["messages"][-1].content,
all_tools,
top_k=5
)
model_bound = model.bind_tools(relevant_tools)
response = model_bound.invoke(state["messages"])
return {"messages": [response]}
Tool Interrupts (Approval Gates)
from langgraph.types import interrupt
@tool
def delete_user(user_id: str) -> str:
"""Delete a user account. Requires approval."""
# Interrupt for human approval
response = interrupt({
"action": "delete_user",
"user_id": user_id,
"message": f"Approve deletion of user {user_id}?",
"risk_level": "high"
})
if response.get("approved"):
db.delete_user(user_id)
return f"User {user_id} deleted successfully"
return "Deletion cancelled by user"
@tool
def transfer_funds(from_account: str, to_account: str, amount: float) -> str:
"""Transfer funds between accounts. Requires approval for large amounts."""
if amount > 1000:
response = interrupt({
"action": "transfer_funds",
"from": from_account,
"to": to_account,
"amount": amount,
"message": f"Approve transfer of ${amount}?"
})
if not response.get("approved"):
return "Transfer cancelled"
execute_transfer(from_account, to_account, amount)
return f"Transferred ${amount} from {from_account} to {to_account}"
Streaming from Tools
from langgraph.config import get_stream_writer
@tool
def long_running_analysis(data: str) -> str:
"""Analyze data with progress updates."""
writer = get_stream_writer()
writer({"status": "starting", "progress": 0})
for i, chunk in enumerate(process_chunks(data)):
writer({
"status": "processing",
"progress": (i + 1) * 10,
"current_chunk": i
})
writer({"status": "complete", "progress": 100})
return "Analysis complete"
Error Handling in Tools
@tool
def api_call_with_retry(endpoint: str) -> str:
"""Call external API with automatic retry."""
for attempt in range(3):
try:
response = requests.get(endpoint, timeout=10)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
if attempt == 2:
return f"Error: Failed after 3 attempts - {str(e)}"
time.sleep(2 ** attempt) # Exponential backoff
Parallel Tool Execution
from langgraph.prebuilt import ToolNode
# ToolNode executes multiple tool calls in parallel by default
tool_node = ToolNode(tools)
# If agent returns multiple tool_calls, they execute concurrently
# Results are returned in order matching the tool_calls
Key Decisions
| Decision | Recommendation |
|---|---|
| Tool count | 5-10 tools max per agent (use dynamic selection for more) |
| Approval gates | Use interrupt() for destructive/high-risk operations |
| Error handling | Return error strings, don't raise (lets agent recover) |
| Streaming | Use get_stream_writer() for long-running tools |
Common Mistakes
- Too many tools (context overflow, poor selection)
- Raising exceptions in tools (crashes agent loop)
- Missing tool descriptions (LLM can't choose correctly)
- Not using
tool_choicewhen specific tool is required
Evaluations
See references/evaluations.md for test cases.
Related Skills
langgraph-supervisor- Supervisor agents with tool-calling workerslanggraph-human-in-loop- Approval gates for dangerous toolslanggraph-streaming- Stream tool execution progresslanggraph-routing- Route based on tool resultslanggraph-state- Track tool call history in statefunction-calling- General LLM function calling patterns
Capability Details
bind-tools
Keywords: bind_tools, tool calling, function calling, LLM tools Solves:
- Attach tools to language models
- Enable function calling in agents
- Configure tool selection behavior
tool-node
Keywords: ToolNode, execute tools, tool execution, prebuilt Solves:
- Execute tool calls from LLM responses
- Handle parallel tool execution
- Integrate tools into graph workflows
dynamic-tools
Keywords: dynamic, select tools, many tools, relevance Solves:
- Handle large tool inventories
- Select relevant tools per query
- Reduce context usage
tool-interrupts
Keywords: interrupt, approval, gate, human review, dangerous Solves:
- Add approval gates to dangerous tools
- Implement human oversight
- Control high-risk operations
Weekly Installs
6
Repository
yonatangross/orchestkitGitHub Stars
91
First Seen
Feb 6, 2026
Security Audits
Installed on
claude-code5
opencode4
github-copilot4
gemini-cli4
codex3
antigravity3