DeepSeek Reasonix Tool Interface: A Complete Guide to LLM Capability Abstraction
The Tool interface is a Go abstraction that defines five methods—Name(), Description(), Schema(), Execute(), and ReadOnly()—which allow large language models to discover, validate, and invoke capabilities within the DeepSeek-Reasonix agent framework.
DeepSeek-Reasonix is an agentic system that enables LLMs to interact with the file system and execute commands safely. At the heart of this system lies a strict contract between the model and the runtime: the Tool interface defined in [internal/tool/tool.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/tool/tool.go). This interface transforms raw JSON tool calls from the model into concrete Go operations while enforcing security boundaries and read-only constraints.
Understanding the Tool Interface Contract
Every capability exposed to the LLM must satisfy the Tool interface. According to the source code in internal/tool/tool.go, the interface requires five methods:
type Tool interface {
Name() string
Description() string
Schema() json.RawMessage
Execute(ctx context.Context, args json.RawMessage) (string, error)
ReadOnly() bool
}
The purpose of each method is as follows:
Name()– Returns the unique string identifier the model uses to request the tool (e.g.,"write_file"or"bash").Description()– Provides a human-readable explanation shown to users and sent to the model as context.Schema()– Returns ajson.RawMessagecontaining the JSON Schema definition for the tool's parameters, which constrains what the model can generate as arguments.Execute()– Accepts a context and raw JSON arguments, performs the actual operation, and returns a textual result or error to feed back into the model conversation.ReadOnly()– Reports whether the tool has observable side effects on the host system. This boolean is critical for parallel execution; the agent can only parallelize calls when all tools in a batch returntrue.
Implementing a Concrete Tool
Concrete implementations follow a consistent struct-based pattern. The repository ships with builtin tools located in internal/tool/builtin/, where each tool is a struct implementing all five interface methods.
Consider the write_file implementation in [internal/tool/builtin/writefile.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/tool/builtin/writefile.go):
type writeFile struct {
roots []string // Optional confinement roots for security
workDir string // Working directory for relative path resolution
}
func (writeFile) Name() string {
return "write_file"
}
func (writeFile) Description() string {
return "Write content to a file at the specified path."
}
func (writeFile) Schema() json.RawMessage {
// Returns JSON Schema: {type: "object", properties: {path: {...}, content: {...}}}
return schemaWriteFile
}
func (writeFile) ReadOnly() bool {
return false // Has side effects (modifies filesystem)
}
func (w writeFile) Execute(ctx context.Context, args json.RawMessage) (string, error) {
// 1. Unmarshal args into struct {Path, Content}
// 2. Resolve and validate path against w.roots
// 3. Preserve original file encoding and write content
// 4. Return human-readable confirmation string
}
The Execute method handles the complete lifecycle: parsing the model-generated JSON, applying security constraints through the roots field, performing the I/O operation, and returning a formatted result string that the agent feeds back to the LLM.
Tool Registration and Lifecycle
Tools enter the system through an initialization-time registration pattern. Each builtin file contains an init() function that calls tool.RegisterBuiltin, adding the tool to a global map before main() executes.
func init() {
tool.RegisterBuiltin(writeFile{})
}
At runtime, the Registry struct (defined in internal/tool/tool.go, lines 97-255) manages the active toolset. Key methods include:
Add(t Tool)– Inserts a tool into the registry and canonicalizes its JSON schema.Get(name string)– Retrieves a tool by itsName()string for dispatch.Schemas()– Exports a slice ofprovider.ToolSchemastructures, sorted by name, for injection into the LLM's system prompt.
This architecture separates definition (the interface), implementation (individual structs), and discovery (the registry), allowing both builtin and plugin-based tools to coexist in the same agent instance.
Runtime Execution Flow
When the LLM emits a tool call, the agent follows a strict dispatch pattern. As implemented in [internal/agent/agent.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/agent/agent.go), the flow mirrors this simplified example:
func (a *Agent) dispatchToolCall(reg *tool.Registry, call provider.ToolCall) (string, error) {
t, ok := reg.Get(call.Name)
if !ok {
return "", fmt.Errorf("unknown tool: %s", call.Name)
}
// Execute with timeout context
return t.Execute(ctx, call.Arguments)
}
The parallelization constraint relies on the ReadOnly() method. Before executing a batch of tool calls, the agent inspects each tool's ReadOnly status. If any tool returns false, the entire batch executes sequentially to prevent race conditions with side-effecting operations.
Summary
- The Tool interface in
internal/tool/tool.godefines the five-method contract (Name,Description,Schema,Execute,ReadOnly) that all capabilities must implement. - Concrete implementations like
writeFileininternal/tool/builtin/writefile.goare lightweight structs that parse JSON arguments, enforce security boundaries, and return formatted results. - Registration occurs via
tool.RegisterBuiltinininit()functions, populating a global registry before program startup. - The Registry (
tool.golines 97-255) provides runtime lookup and exports JSON schemas to the LLM. - Read-only safety determines execution strategy: tools returning
truefromReadOnly()enable parallel execution, while side-effecting tools force sequential processing.
Frequently Asked Questions
What is the purpose of the ReadOnly() method in the Tool interface?
The ReadOnly() method reports whether a tool modifies system state. According to the implementation in internal/tool/tool.go, this boolean enables the agent to optimize execution by parallelizing only read-only tool calls. If any tool in a batch returns false, the agent executes them sequentially to prevent conflicts between side-effecting operations.
How does the registry handle tool schema validation?
The registry's Add() method canonicalizes JSON schemas when tools are registered, as seen in internal/tool/tool.go (lines 97-255). This preprocessing ensures that when Schemas() is called, the LLM receives properly formatted provider.ToolSchema objects without runtime parsing overhead.
Can I add custom tools to DeepSeek-Reasonix without modifying the core codebase?
Yes. You can implement the Tool interface in a separate package and call tool.RegisterBuiltin(yourTool{}) in an init() function. Because registration happens at import time through the init() pattern, your custom tool automatically joins the builtin registry and becomes available to the LLM through the standard Registry.Get() dispatch mechanism.
What happens if a tool's Execute method returns an error?
When Execute returns a non-nil error, the agent captures this error string and feeds it back to the LLM as part of the conversation context. This allows the model to retry with corrected arguments or explain the failure to the user, creating a robust feedback loop for error recovery.
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 →