Skip to content

Stage 4: Tools and Sandbox

Course: DeerFlow Source Code Course · Stage Test Prerequisite: Stages 2 and 3 Corresponding Overview: Tools and Sandbox sections of core-mechanisms.md Goal of This Stage: Trace from a single tool call to middleware, sandbox lease, execution result, and receipt.

Tools are not “strings generated by the model directly given to the shell”. The complete chain is:

AIMessage(tool_calls)
→ Tool Visibility/Policy
→ Parameter Validation
Approval/Read-Before-Write Protection/Budget
→ sandbox lease
→ Execution and output truncation
→ ToolMessage + receipt
Next-generation model

DeerFlow separates ‘tool interface,’ ‘execution environment,’ ‘behavior constraints,’ and ‘result evidence’ to prevent any single model parameter from becoming host privileges.

A tool is a controlled capability, not a thin wrapper of host functions.

The model can only propose calls; whether it is visible, allowed, where it executes, how much result is returned, and how failures are backfilled are all determined by the server.

  1. The toolset is assembled from tools/tools.py, builtins, sandbox tools, memory tools, MCP/skill tools, etc.
  2. agents/middlewares/ applies constraints on calls and results for ReadBeforeWriteMiddleware, ToolOutputBudgetMiddleware, ToolReceiptMiddleware, DanglingToolCallMiddleware, etc.
  3. sandbox/tools.py provides adaptation layers such as bash_tool, read_file_tool, write_file_tool.
  4. sandbox/sandbox.py::Sandbox defines abstract contracts for execution, files, and path resolution.
  5. sandbox/sandbox_provider.py selects the provider; local/local_sandbox_provider.py shows per-thread path mappings.
  6. sandbox/lease.py::SandboxLeaseManager manages concurrent use, acquisition, and release; security.py and path_patterns.py handle out-of-bounds.
  7. Results are only returned to the model after truncation, cleaning, balancing, and receipt recording.
Object Role What it cannot do
Sandbox Isolated execution contract Directly expose host root
SandboxProvider Create/find sandbox Bypass leases
SandboxLeaseManager Concurrency and lifecycle Treat lease as permanent ownership
ReadBeforeWriteMiddleware Prevent blind writes Replace path safety checks
ToolOutputBudgetMiddleware Limit context pollution Delete original evidence
ToolReceiptMiddleware Record the fact of calls Guarantee external side effects succeeded
tool_call
Visible Tools Table
→ Parameters and Strategies
→ ensure_sandbox_initialized
→ acquire/lease sandbox
→ execute_command / read / write
→ truncate + sanitize
→ ToolMessage
→ journal event + receipt

Observation point: bash_tool is just an entry, the real execute_command is implemented by the provider’s Sandbox; virtual paths like /mnt/user-data/... are mapped by the provider and cannot be regarded as absolute paths of the host.

Print the mapping between virtual paths and real paths in the local sandbox. Reference acceptance: The same virtual path falls into the current thread’s workspace and does not fall into other users’ directories.

Experiment B: Triggering Read-Before-Write Protection

Section titled “Experiment B: Triggering Read-Before-Write Protection”

Directly let the model overwrite an unread file, versus read before writing. Observations: the former is rejected by middleware or requires reading first, the latter succeeds; rejection happens before execution.

Call a command that produces a large output. Acceptance criteria: The model side sees bounded content, while the original output can still be located via logs/receipt or files, and truncation is an explicit fact.

  1. Virtual paths must be mapped to the sandbox workspace of the current thread; different threads do not share the same real root directory.
  2. Unread direct overwrites should be rejected by the middleware or require reading first before execution; only after reading first then writing will it be allowed, indicating that the policy occurs before side effects.
  3. When the model encounters bounded content, the original output can still be located according to the receipt/document; truncation markers should not be equated with command failure.
Failure Fact that has occurred Cannot be redone
Sandbox acquisition failed No tool side effects Do not rerun on the host
Command execution timed out Partial side effects may have occurred Cannot assume it was completely unexecuted
Output truncated Call successful Truncation marker cannot be considered a failure
Lease lost Execution rights uncertain Old executor may not continue writing
receipt write failed The tool may have taken effect Cannot blindly repeat the write operation
  1. Why can’t the model directly call the host shell?
  2. What do provider, sandbox, lease, and tool middleware each manage?
  3. After the tool output is truncated, how can it still be proven that the call occurred?
  4. After a file-writing tool times out, why can’t you directly retry overwriting?
  1. Because the model parameters are not trustworthy. Directly calling the host shell would bypass isolation, path mapping, permissions, resource reclamation, and auditing, and could even escalate a dialogue error into host machine damage.
  2. The provider is responsible for creating/managing the sandbox; the sandbox defines the execution and file contract; the lease manages concurrent usage rights and release; middleware manages visibility, policies, approval, budget, pairing, and evidence.
  3. Form a chain of facts through ToolReceiptMiddleware, journal events, truncation markers, and product paths. Truncation only indicates that the content given to the model is bounded; it does not mean the call failed.
  4. Timeouts may occur before, during, or after writing. Direct retries may result in duplicate appends, overwriting of new content, or inconsistencies; one should first read back/check idempotent keys, then decide whether to supplement the write or report an error.
  • backend/packages/harness/deerflow/tools/
  • backend/packages/harness/deerflow/sandbox/tools.py
  • backend/packages/harness/deerflow/sandbox/sandbox.py
  • backend/packages/harness/deerflow/sandbox/lease.py
  • backend/packages/harness/deerflow/sandbox/local/local_sandbox_provider.py
  • backend/packages/harness/deerflow/agents/middlewares/read_before_write_middleware.py
  • backend/packages/harness/deerflow/agents/middlewares/tool_output_budget_middleware.py

Entering stage 5: With both tools and context in place, how does the model instance itself arise from the adaptation of configuration and providers?