Agent Canvas/Tools & Integration/ — Tools & Integration
Extensible framework for external integrations and tool calling
ToolBase with validation and error handling; registry for lookup; 24 tools: code_exec (sandboxed Python), tavily (web search), retrieval (RAG), sql_executor, web_crawler, email, api_request, tool_invoke, and 16 more. Each supports max_retries, fallback, and timeout.
📚 What is this & why does it exist?
Tools enable workflows to interact with the outside world: code execution, web search, databases, emails, APIs. Without tools, workflows are isolated. The framework provides sandboxing, error handling, and resource limits. — Replace with 2-4 plain-English sentences explaining what this sub-module does and why it exists. No jargon. Target audience: someone who hasn't read the code.
💡 Real-world analogy
Like stdlib modules: ToolBase = abstract signature; each tool (code_exec, tavily, sql_executor) = implementation; registry = module namespace. Agent calls by name at runtime. — Replace with a vivid, concrete metaphor mapping this sub-module's core concept to everyday life (restaurant, airport, library, highway, etc.). 3-5 sentences.
🤔 Why Is It Designed This Way?
Tools called by name at runtime enable LLMs to decide what to use. Sandboxing (RestrictedPython) isolates untrusted code. Retries with backoff handle transient API failures. Explicit registry (not reflection) avoids footguns. — 150+ words explaining the design choices: what pattern does this follow and why? What constraints shaped the implementation? What trade-offs were made and what was sacrificed?
| Design Choice | What Was Chosen | What Was Rejected | Why |
|---|---|---|---|
| Tool lookup | Registry with factories (explicit) | Dynamic import by name (reflection) | Explicit registry is safe; reflection risks unintended side effects and circular imports |
| Code execution | RestrictedPython sandbox | Unrestricted eval() / exec() | Sandbox prevents compromise of host; restricts imports and dangerous builtins |
| Error handling | Retry + fallback + reroute | Fail-fast exception propagation | Resilience is critical for unreliable external tools; fallback enables graceful degradation |
| Resource limits | Timeout per tool, memory limit for code_exec | No limits (rely on environment) | Explicit limits prevent hung workflows and resource exhaustion |
Contents
Architecture
graph TB
BASE["ToolBase
Abstract Base"]
REGISTRY["Tool
Registry"]
BASE --> REGISTRY
CODE["code_exec
Sandbox
RestrictedPython"] --> REGISTRY
TAVILY["tavily
Web Search
Tavily API"] --> REGISTRY
RETRIEVAL["retrieval
RAG Query
Vector DB"] --> REGISTRY
SQL["sql_executor
SQL Query
Database"] --> REGISTRY
CRAWLER["web_crawler
HTML Parse
BeautifulSoup"] --> REGISTRY
EMAIL["email
SMTP
Send Mail"] --> REGISTRY
API["api_request
HTTP
GET/POST"] --> REGISTRY
INVOKE["tool_invoke
Recursive
Call Tool"] --> REGISTRY
Tool Execution Lifecycle
flowchart TD
Lookup["Tool Lookup
By Name"]
Init["__init__
Configure"]
Validate["Validate
Params"]
Execute["_execute()
Run Tool"]
Retry{"Transient
Error?"}
Success["Success
Return Result"]
Fallback["Fallback
exception_default_value"]
Lookup --> Init
Init --> Validate
Validate --> Execute
Execute --> Retry
Retry -->|Yes| Execute
Retry -->|No| Success
Retry -->|Exhausted| Fallback
style Success fill:#c8e6c9
style Fallback fill:#ffccbc
Key Classes
| Class / Function | File | Purpose |
|---|---|---|
ToolBase | tools/base.py | Abstract base with init, validate_params, run; all tools inherit |
CodeExecTool | tools/code_exec.py | Executes Python in RestrictedPython sandbox; isolated globals; timeout enforced |
TavilyTool | tools/tavily.py | Web search integration; queries Tavily API; returns results with snippets |
RetrievalTool | tools/retrieval.py | RAG database query; retrieves relevant documents from vector store |
SqlExecutorTool | tools/sql_executor.py | Executes SQL with parameterized queries; prevents injection; returns rows as JSON |
Data Structures
| Type | Name | Description |
|---|---|---|
| dict | params | Input parameters for tool (e.g., {query: "python", num_results: 5}) |
| dict | output | Tool output: {success: bool, result: any, error: str, duration_ms: int} |
| dict | schema.inputs | Parameter schema: [{name, type, default, description}] |
| list | retries | Retry history: [{attempt, error, backoff_ms, timestamp}] |
| int | timeout_sec | Execution timeout; raises exception if exceeded |
Interactions
| Direction | Partner | Interface | Description |
|---|---|---|---|
| → | Agent Component | tool_registry.get(name) | Agent looks up tool by name; factory instantiates with args |
| → | External APIs | requests.get/post(), database.query() | Tools make outbound calls; failures trigger retry logic |
| → | Code Sandbox | RestrictedPython.execute(code) | code_exec tool runs untrusted Python safely in restricted environment |
| ← | Canvas | Canvas records tool call in history | Canvas logs all tool calls for audit and performance profiling |
Configuration
| Knob / Parameter | Default | Description |
|---|---|---|
max_retries | 3 | Retry attempts on transient error (timeout, 5xx, rate limit) |
backoff_multiplier | 2 | Exponential backoff: wait = base_delay * (multiplier ^ attempt) |
timeout_sec | 30 | Max execution time; TimeoutError raised if exceeded |
exception_default_value | null | Return this if all retries exhausted |
Directory Map
Source tree for Tools & Integration
submod_name/
├── file_a.ext ← Role: brief annotation
├── file_b.ext ← Role: brief annotation
├── file_c.ext ← Role: brief annotation
└── index.ext ← Role: entry point
📑 Annotated Code Walkthrough
The most important code in this sub-module, annotated with WHY each piece exists:
run(params) — Execute tool with arguments
// Replace with actual code from the source
void key_function() {
// ← WHY: Explain the purpose of this line
// ← WHY: What constraint or requirement drives this?
}
Line-by-line:
- Line N: What this does and WHY — what would break if removed
- Line M: The design choice here — why this approach over the obvious alternative
⚠️ Edge Cases & Boundaries
| Edge Case | What Happens | Where in Code | Risk Level |
|---|---|---|---|
| API rate limit (429) | Treated as transient; retry with exponential backoff | TavilyTool.run() | 🔴 High — without backoff, could hammer API; exhausts quota |
| Code sandbox escape attempt (e.g., eval, __import__) | RestrictedPython prevents execution; SyntaxError or NameError raised | CodeExecTool.run() | 🟢 Low — sandbox prevents harm |
| SQL injection (e.g., "'; DROP TABLE users; --") | Parameterized queries escape input; executed as data, not SQL syntax | SqlExecutorTool.run() | 🟢 Low — parameterized queries prevent injection |
| Tool timeout (LLM API slow, network hangs) | Timeout exception caught; retry logic applies; if all fail, exception_default_value returned | ToolBase.run() | 🔴 High — timeout prevents hung workflows |
| Recursive tool_invoke (A → B → A) | Call depth tracked; error if depth > max_depth (default 10) | ToolInvokeTool.run() | 🔴 High — prevents stack overflow |
| Concurrent access | How it's handled | function_name:line | 🔴 High |
Deep-Dive Highlights
Core mechanisms from this sub-module have been extracted into dedicated focus pages for in-depth exploration. Each page covers one algorithm, protocol, or subsystem with diagrams, code walks, and configuration details.
Deep-Dive Pages
Code Examples
Example: Typical Usage
// Replace with actual code example
void example() {
// ...
}
Tool Failure Modes & Recovery Strategies
Tools fail in predictable ways. RAGFlow implements tiered recovery strategies for each failure mode.
Failure Mode Classification
| Mode | Example | Is Retriable? | Recovery Strategy |
|---|---|---|---|
| Transient Network | Connection timeout, DNS lookup failed, socket reset | ✓ Yes | Exponential backoff retry; max 5 attempts |
| Transient Service | HTTP 503 Service Unavailable, API rate limit (429) | ✓ Yes | Exponential backoff; may include jitter to avoid thundering herd |
| Permanent Service | HTTP 404 Not Found, HTTP 401 Unauthorized, invalid API key | ✗ No | Use exception_default_value (fallback output) or exception_goto (jump to error handler) |
| Invalid Input | Parameter validation failed, malformed request body | ✗ No | Log error; use exception_default_value or abort |
| Resource Limit | Memory exhausted in code_exec sandbox, query result too large | ✗ No | Use exception_default_value; increase limits if possible |
| Timeout | Tool execution exceeded timeout duration | ✓ Yes (maybe) | Retry with longer timeout; if still fails, use fallback |
Retry Configuration Example
{
"nodes": [
{
"id": "web_search_1",
"type": "tavily",
"config": {
"query": "{{llm_1@output.search_query}}",
"max_results": 10,
"timeout": 30,
"retry_max": 3,
"retry_backoff_ms": 100,
"exception_default_value": {
"results": [],
"error": "Search service unavailable; using empty results"
}
}
}
]
}
Tool Monitoring & Observability
- Execution time: Each tool execution step in history includes duration_ms. Use to identify bottlenecks (e.g., if tavily searches are taking >5s, investigate network or service latency).
- Retry counts: If a tool is retrying frequently, log and alert. Could indicate flaky service or misconfigured timeout.
- Exception patterns: If same tool always fails with same error (e.g., "API key invalid"), fix configuration before next run.
- Fallback activation: If exception_default_value is used, workflow continues but with degraded data. Monitor for this and adjust strategy if too frequent.
Advanced Tool Patterns & Integration
Beyond basic tool invocation, RAGFlow supports sophisticated patterns for composition, fallbacks, and context passing.
Tool Composition Patterns
- Sequential Chaining: Output of one tool flows as input to next. Example: retrieval → llm_answer (context-aware). Via variable references (tool_a@result → tool_b@context).
- Conditional Execution: Tools run only if preconditions met (Switch nodes). Example: run code_exec if code snippet detected, else skip.
- Fallback Chains: Multiple tools with fallback semantics. web_crawler fails → try api_request; both fail → return error message. Via exception_default_value or exception_goto.
- Iteration Parallelism: No built-in multi-threading, but Iteration node runs tool N times across list. Each iteration gets isolated sub-DAG; results aggregated.
- Dynamic Tool Selection: Tool name determined at runtime. Agent component inspects LLM output to pick next tool (ReAct pattern, tool_choice semantics).
Error Handling & Resilience Tiers
| Tier | Strategy | When to Use |
|---|---|---|
| Tier 1: Instant | Return exception_default_value immediately on first failure | Non-critical tools; failure is acceptable |
| Tier 2: Retry with Backoff | Retry 3-5 times with exponential backoff (100ms, 200ms, 400ms, 800ms...) | Transient failures expected (network glitch, rate limit). Default. |
| Tier 3: Fallback + Propagate | Retry, then try fallback tool, then propagate error to exception handler | Critical path; multiple fallback options (web search → cache → error). |
| Tier 4: Manual Intervention | Pause workflow; log for human review; wait for manual tool result injection | High-stakes decisions; no good fallback (approve/reject document). |
Tool Context & State Management
- Tool Isolation: Each tool invocation is isolated; no persistent state between calls (unless explicitly managed). Useful for determinism and testability.
- Context Injection: Tools can receive workflow context via params. Example: retrieval tool gets search_keywords param from LLM reasoning step.
- Output Caching: Component outputs cached in _outputs[component_id]. Tools that are deterministic (same input → same output) can skip re-execution if node already run.
- User Context: Some tools (email, api_request) may need user credentials or request ID. Passed via workflow params or global variables (user_id, auth_token, etc.).