Tools System
Tools are single-purpose utilities that the agent invokes directly for immediate operations. Soothe follows a single-purpose tool design pattern (RFC-101) where each tool performs exactly one operation, eliminating the mode/action indirection that creates cognitive load for the LLM.
Design Philosophy: Single-Purpose Tools
The core insight is that LLMs choose tools by reading their names and descriptions. A tool named execute(mode="shell", action="run") forces the model to reason about two decisions — what mode and what action — before invoking it. A tool named run_command(command="ls") requires only one. The single-purpose pattern reduces the decision space, improves tool-selection accuracy, and makes failure modes predictable.
The pattern’s rules:
- One tool = one operation (no mode/action parameters)
- Naming convention:
{verb}_{noun}(e.g.,read_file,run_command) - Type-safe parameters via Pydantic schemas with descriptions
- Direct, predictable behavior — no hidden side effects
The deprecated contrast is the “unified dispatch” anti-pattern, where a single execute() tool takes a mode parameter to switch between shell, python, and file operations. This is harder for the LLM to use correctly and harder to debug when it fails.
Toolkit Organization
Soothe organizes tools into domain-specific toolkits — modules of related tools. This grouping is both organizational (code structure) and semantic (helps the agent understand capability domains).
| Toolkit | Domain | Key Tools |
|---|---|---|
| execution | Shell & Python execution | run_command, run_python, run_background, tail_background_log, kill_process |
| file_ops | File I/O & editing | read_file, write_file, edit_file, glob, grep, ls |
| wizsearch | Web search | wizsearch_search, wizsearch_crawl |
| deepxiv | Academic papers | deepxiv_search, deepxiv_paper_brief, deepxiv_read_section |
| audio/video/image | Media analysis | transcribe_audio, analyze_video, analyze_image |
| data | Data inspection | inspect_data, summarize_data, check_data_quality |
| http_requests | HTTP operations | requests_get, requests_post, requests_delete |
| datetime | Time utilities | current_datetime |
→ Source: packages/soothe/src/soothe/toolkits/
Langchain Ecosystem Integration
A defining design decision: Soothe only builds tools that the langchain ecosystem doesn’t already provide. The toolkits __init__.py is explicitly documented as “only those not available in the langchain ecosystem.” This avoids duplication and leverages battle-tested implementations.
| Tool | Ecosystem Source |
|---|---|
run_command |
wraps langchain_community.ShellTool |
run_python |
wraps langchain_experimental.PythonREPLTool |
read_file, write_file, glob, grep, ls |
from deepagents.FilesystemMiddleware |
wizsearch_search |
from wizsearch library |
transcribe_audio |
OpenAI Whisper |
Soothe’s custom additions — edit_lines, insert_lines, delete_lines, apply_diff — extend langchain’s file tools with surgical editing capabilities that don’t exist in the ecosystem. These enable precise line-range edits instead of full-file rewrites, which is critical for large files where token efficiency matters.
Security Model
All tools integrate with OperationSecurityProtocol for workspace boundary enforcement. The security model has three layers:
- Workspace resolution —
resolve_workspace_for_tool_execution()determines the active workspace from the runtime context, with a fallback to the daemon’s work directory. - Path checking —
WorkspaceToolOperationSecurityvalidates that all file paths fall within the workspace boundary, preventing access to files outside the sandbox. - Virtual path translation — for sandboxed environments, virtual paths are translated to real filesystem paths transparently.
Execution tools (shell, Python) additionally enforce timeouts (default 60s), strip ANSI escape sequences for clean output, cap synchronous stdout length, and run background processes as daemons with PID tracking, log files under <workspace>/.soothe/background/, and optional log retention via tools.execution.background_log_retention_days.
Event Naming Convention
Tools emit wire events following RFC-101 patterns. The naming differs by operation type:
- Atomic operations (single-shot, synchronous):
soothe.tool.<component>.<verb>— e.g.,soothe.tool.file_ops.read - Async operations (observable lifecycle):
soothe.tool.<component>.<action>_started/completed/failed— e.g.,soothe.tool.file_ops.search_started
Events are registered at module load via register_event() and imported in the plugin’s __init__.py for side-effect registration.
Extension Pattern: Creating a Custom Tool
Before creating a custom tool, ask: does langchain already provide this? If yes, use the ecosystem version. If no, the pattern is:
- Define a Pydantic input schema with
Field(description=...)for each parameter — the description is what the LLM reads to decide usage. - Create a
BaseToolsubclass (or a simple callable) with a clearnameand detaileddescription. - Register via
@tooldecorator on a@pluginclass. - Optionally register domain events for observability.
A minimal example:
from soothe_sdk.plugin import plugin, tool
@plugin(name="my-tools", version="1.0.0")
class MyToolsPlugin:
@tool(name="analyze_data", description="Analyze CSV data and return statistics")
def analyze_data(self, file_path: str) -> dict:
return {"rows": 100, "columns": 5}
For tools needing workspace security, resolve the workspace from the runtime context and apply WorkspaceToolOperationSecurity before any file access. Tools receive an optional runtime parameter for this purpose.
Tool Resolution
Tools are resolved at agent-build time via resolve_tools(), which takes tool group names from config, a PolicyProtocol instance, workspace boundary, and runtime context. Policy checks (tool:invoke:<name>) gate which tools are available to the agent. The resolved tool set is passed to the AgentBuilder.
Gotchas and Non-Obvious Behavior
- Pagination:
read_filecaps at ~50 lines by default — useoffset/limitfor large files. The agent must paginate explicitly; the tool won’t auto-load entire files. - Backup before deletion:
delete_filecreates timestamped backups in a.backupsdirectory before deletion — this is a safety net, not optional. - Background processes:
run_backgroundreturns a PID immediately; the process runs as a daemon. Usekill_process(pid)to terminate. Long-running commands should always use background mode to avoid timeout. requests_postsignature: takes a JSON string withurlanddatakeys (not separate parameters), which is non-obvious — the agent must serialize the request body as a JSON string.- Deepxiv token cost:
deepxiv_get_full_papercan consume enormous token budgets — preferdeepxiv_read_sectionfor token-efficient access to specific sections.
Related RFCs
| RFC | Title |
|---|---|
| RFC-101 | Tool Interface & Event Naming |
| RFC-600 | Plugin Extension System (@tool decorator) |
| RFC-901 | Operation Security Protocol |
| Previous: Subagents Architecture | Next: MCP Integration |