How the Tool‑Calling Agent Architecture Works in Open‑Code‑Review
The tool‑calling agent in open‑code‑review lets an LLM invoke external utilities during code review by constructing a schema‑driven prompt, parsing JSON function calls from the model, and dispatching execution to concrete tool implementations.
The open‑code‑review repository implements a tool‑calling agent that bridges large language model reasoning with external system capabilities. This architecture enables the agent to perform actions like running git commands, triggering CI pipelines, or querying issue trackers while maintaining a clean separation between LLM decision‑making and tool execution. The design centers on three layers: prompt construction with tool schemas, LLM response parsing, and dynamic tool dispatch.
Architecture Overview
The tool‑calling agent architecture consists of three tightly coupled layers that transform an LLM's intent into executable actions.
1. Prompt and Schema Generation
The agent begins by loading tool descriptors from toolsconfig/tools.json. Each descriptor defines:
- Tool name and description
- JSON Schema for parameters
- Implementation class path
In open_code_review/agent.py, the ToolCallingAgent class constructs a system prompt that injects this tool catalog. The LLM receives explicit instructions on which functions it may call and what arguments each requires.
// toolsconfig/tools.json – example entry
{
"name": "git_diff",
"description": "Returns the diff between two git refs",
"parameters": {
"type": "object",
"properties": {
"repo_path": {"type": "string"},
"base": {"type": "string"},
"head": {"type": "string"}
},
"required": ["repo_path", "base", "head"]
},
"implementation": "open_code_review.tools.git_diff.GitDiffTool"
}
2. LLM Invocation and Function‑Call Parsing
The invoke() method in open_code_review/agent.py sends the constructed prompt to the configured LLM. The response is inspected for a function_call field containing JSON structured data.
When detected, the agent:
- Validates the JSON against the schema from
toolsconfig/tools.json - Instantiates a
ToolCallobject with the tool name and parsed arguments - Proceeds to dispatch rather than returning raw text
# Simplified flow from agent.py
response = llm_client.chat(messages=messages, tools=tool_schemas)
if response.get("function_call"):
tool_call = ToolCall(
name=response["function_call"]["name"],
arguments=json.loads(response["function_call"]["arguments"])
)
# Proceed to dispatch layer
else:
# Return direct LLM text response
return response["content"]
3. Tool Dispatch and Execution
The dispatch layer in open_code_review/tool_dispatcher.py resolves tool names to concrete implementations. Each tool inherits from BaseTool defined in open_code_review/tools/base_tool.py, which enforces a consistent run() interface.
The dispatcher:
- Dynamically imports the class specified in
implementation - Instantiates the tool with runtime configuration
- Calls
run()with validated arguments - Captures stdout/stderr and returns sanitized output
The tool output is then wrapped into a new message structure and sent back to the LLM, creating a feedback loop for multi‑turn reasoning.
Code Implementation Examples
Creating a Custom Tool
All tools inherit from BaseTool and implement the run() method with typed parameters matching their JSON schema.
# open_code_review/tools/my_tool.py
from .base_tool import BaseTool
class MyTool(BaseTool):
"""Echoes input text back to the agent."""
def run(self, message: str) -> str:
"""
Parameters must match the 'parameters' schema
in toolsconfig/tools.json exactly.
"""
return f"Tool output: {message}"
Register in toolsconfig/tools.json:
{
"name": "my_tool",
"description": "Echoes the supplied string back",
"parameters": {
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "Text to echo"
}
},
"required": ["message"]
},
"implementation": "open_code_review.tools.my_tool.MyTool"
}
Using the Tool‑Calling Agent
from open_code_review.agent import ToolCallingAgent
# Initialize with model configuration
agent = ToolCallingAgent(model="gpt-4o-mini")
review_prompt = """
Review this pull request. If you need additional context
about file changes, use the git_diff tool. Explain your reasoning.
"""
# Single call handles full loop: prompt → LLM → parse → dispatch → result
response = agent.invoke(review_prompt)
# Response contains final LLM answer after any tool calls complete
print(response.final_answer)
Manual Tool Dispatch (Testing and Debugging)
from open_code_review.tool_dispatcher import ToolDispatcher
dispatcher = ToolDispatcher()
# Execute tool directly without LLM involvement
result = dispatcher.run_tool(
name="git_diff",
args={
"repo_path": "/path/to/repo",
"base": "main",
"head": "feature-branch"
}
)
print(result) # Raw diff output that would feed back to LLM
Core Source Files
| File | Purpose |
|---|---|
open_code_review/agent.py |
ToolCallingAgent class — prompt building, LLM interaction, function‑call detection |
open_code_review/tool_dispatcher.py |
ToolDispatcher class — dynamic tool resolution and execution |
open_code_review/tools/base_tool.py |
BaseTool abstract class — interface contract for all tools |
open_code_review/tools/git_diff.py |
Reference implementation showing git command execution |
toolsconfig/tools.json |
Central registry of available tools with JSON schemas |
Summary
- Schema‑driven prompts make tool capabilities explicit to the LLM via
toolsconfig/tools.json - JSON function‑call parsing extracts structured tool requests from model responses in
agent.py - Dynamic dispatch through
tool_dispatcher.pyisolates execution logic from LLM reasoning - BaseTool abstraction in
open_code_review/tools/base_tool.pyenables consistent tool development - Feedback loop architecture allows multi‑turn tool use where previous results inform subsequent LLM reasoning
Frequently Asked Questions
How does the agent prevent invalid tool calls?
The ToolCallingAgent validates incoming function_call JSON against the schema defined in toolsconfig/tools.json before dispatch. Missing required parameters or type mismatches trigger an error response that the LLM receives as feedback, allowing it to correct and retry.
Can tools maintain state between calls?
Tool implementations in open_code_review/tools/ are designed as stateless functions. Any persistence (caching, session data) is handled externally through the ToolDispatcher configuration or passed explicitly via arguments in each call.
What LLM providers are supported?
The ToolCallingAgent in agent.py abstracts provider details through a client interface. The repository supports OpenAI‑compatible APIs and can extend to other providers by implementing the chat completion interface with function‑call response formats.
How do I add a tool that requires authentication?
Store credentials in environment variables or a secrets manager, then access them within your tool's run() method. Never expose secrets in toolsconfig/tools.json — that file should only contain schema and implementation metadata, not runtime configuration values.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →