Use this file to discover all available pages before exploring further.
Tools are functions that agents and language models can invoke to interact with external systems, perform computations, or retrieve information. LangChain provides a flexible system for defining, validating, and executing tools.
@tooldef search_docs(query: str) -> str: """Search LangChain documentation for relevant information. Use this when the user asks about: - LangChain features and capabilities - Code examples and tutorials - API reference and function signatures Do not use for: - General web search - Real-time information - Non-LangChain topics """ return search_api(query)print(search_docs.description)
from langchain_core.messages import HumanMessage, ToolMessagemessages = [HumanMessage(content="What's the weather in SF?")]# Get AI response with tool callsai_message = model_with_tools.invoke(messages)messages.append(ai_message)# Execute each tool callfor tool_call in ai_message.tool_calls: tool_name = tool_call["name"] tool_args = tool_call["args"] # Find and invoke the tool if tool_name == "get_weather": result = get_weather.invoke(tool_args) elif tool_name == "get_time": result = get_time.invoke(tool_args) # Add tool result to messages messages.append(ToolMessage( content=result, tool_call_id=tool_call["id"] ))# Get final responsefinal_response = model_with_tools.invoke(messages)print(final_response.content) # "The weather in SF is sunny and 72°F"
from langchain_core.tools import tool@tooldef divide(a: float, b: float) -> str: """Divide two numbers. Args: a: Numerator b: Denominator (must be non-zero) """ try: if b == 0: return "Error: Cannot divide by zero" result = a / b return f"Result: {result}" except Exception as e: return f"Error: {str(e)}"# Tool returns error message instead of raisingresult = divide.invoke({"a": 10, "b": 0})print(result) # "Error: Cannot divide by zero"
For tools that should raise exceptions:
@tool(handle_tool_error=True)def risky_operation(value: int) -> str: """Operation that might fail.""" if value < 0: raise ValueError("Value must be positive") return f"Result: {value * 2}"# Exceptions are caught and returned as error messagesresult = risky_operation.invoke({"value": -5})print(result) # "Error: Value must be positive"
from langchain_core.tools import create_retriever_toolfrom langchain_community.vectorstores import FAISSfrom langchain_openai import OpenAIEmbeddings# Create vector store retrievervectorstore = FAISS.from_texts( ["LangChain is a framework", "Python is a language"], embedding=OpenAIEmbeddings())retriever = vectorstore.as_retriever()# Convert to toolretriever_tool = create_retriever_tool( retriever, name="search_docs", description="Search documentation for relevant information")# Use like any other toolresult = retriever_tool.invoke({"query": "What is LangChain?"})
The description is critical for LLM tool selection:
@tooldef search(query: str) -> str: """Search for current information on the web. Use this tool when you need: - Recent news or events (last 24 hours) - Real-time data (stock prices, weather) - Information not in your training data - Verification of facts Do NOT use for: - General knowledge questions - Mathematical calculations - Code generation Args: query: Specific search terms. Be precise and use keywords. Returns: Top search results with titles, snippets, and URLs. """ return search_api(query)
Validate inputs with Pydantic
Use Pydantic models for type safety:
from pydantic import BaseModel, Field, field_validatorclass EmailInput(BaseModel): to: str = Field(description="Recipient email") subject: str = Field(min_length=1, max_length=200) body: str = Field(min_length=1) @field_validator("to") @classmethod def validate_email(cls, v: str) -> str: if "@" not in v: raise ValueError("Invalid email address") return v@tool(args_schema=EmailInput)def send_email(to: str, subject: str, body: str) -> str: """Send an email.""" # Email sending logic return "Email sent"
Return user-friendly messages
Tool outputs should be clear and actionable:
@tooldef database_query(sql: str) -> str: """Execute SQL query.""" try: results = db.execute(sql) if not results: return "Query executed successfully. No rows returned." return f"Found {len(results)} rows:\n{format_results(results)}" except Exception as e: return f"Query failed: {str(e)}\nPlease check the SQL syntax."
Keep tool scope focused
Single-purpose tools are easier for LLMs to use correctly:
# Good: Focused tools@tooldef get_user_email(user_id: str) -> str: """Get user email address.""" return db.get_user(user_id).email@tooldef get_user_age(user_id: str) -> int: """Get user age.""" return db.get_user(user_id).age# Avoid: Kitchen sink tools@tooldef get_user_data(user_id: str, field: str) -> Any: """Get any user field.""" return getattr(db.get_user(user_id), field)