Tools: Give Your LLM Arms
Tool calling is how LLMs act on the world. This article covers the full stack: how LangChain converts your functions to JSON schemas, how the model decides which tool to call and when, the complete bind_tools → tool_calls → ToolMessage cycle, production patterns like InjectedToolArg and tool artifacts, cost math for tool-heavy agents, and the failure modes that will hit you in production.
Quick Reference
- →@tool turns any Python function into a callable tool — docstring + type hints become the JSON schema
- →bind_tools([tools]) attaches schema descriptions to a model; tool calls appear in AIMessage.tool_calls
- →tool_call_id must match between AIMessage and ToolMessage — mismatches cause cryptic provider errors
- →InjectedToolArg hides runtime params (user_id, db conn) from the model's schema
- →response_format='content_and_artifact' lets tools return data downstream without polluting model context
- →Tool schemas cost input tokens on every call — binding 5 tools adds ~1,000 tokens per request
- →Always return a ToolMessage even on failure — an error string lets the model self-correct
What Tool Calling Actually Is
Tool calling is a structured request protocol. The model outputs a JSON object describing which function to call and what arguments to pass. Your code does the actual execution and returns the result. The model only sees what you give back as a ToolMessage.
The tool calling cycle: LLM requests tools, tools return results, LLM decides next step
The four-step cycle: (1) you bind tools to a model, (2) the model returns tool_calls instead of text, (3) your code executes the function and wraps the result in a ToolMessage, (4) the model continues with the result in context. The model can request zero, one, or multiple tool calls in a single response. When it requests multiple, your code should execute all of them before sending ToolMessages back.