How the Tencent/WeKnora ReAct Agent Handles Retrieval, MCP Tools, and Skill Sandboxes Each Turn
The ReAct Agent processes every turn through a strict think-analyze-act-observe cycle, assembling a fresh toolbox that includes the retrieval MCP tool andskill-specific sandboxes to enable dynamic knowledge access and isolated code execution.
The Tencent/WeKnora codebase implements a production-grade ReAct (Reasoning + Acting) agent that manages complex workflows through iterative turns. On each iteration, the AgentEngine dynamically configures available capabilities, ensuring the LLM can retrieve external knowledge via MCP (Model Context Protocol) tools and execute user-provided skills within isolated sandboxes.
The ReAct Turn Lifecycle in AgentEngine
The core orchestration logic resides in internal/agent/engine.go, where the AgentEngine struct manages the reasoning loop. Each turn follows four distinct phases: think, analyze, act, and observe. Inside the executeLoop method (approximately line 429), the engine constructs a new toolbox for every iteration, ensuring the LLM operates with the most current context and execution environments.
The loop respects configuration limits defined in internal/types/agent.go, specifically the MaxIterations field that prevents infinite reasoning chains. When the engine initializes a turn, it evaluates the current AgentState to determine which tools—retrieval MCP clients, sandbox editors, or static utilities—should be made available for that specific iteration.
Retrieval via the MCP Tool Interface
MCPTool Implementation Details
The retrieval capability is encapsulated in internal/agent/tools/mcp_tool.go, which defines the MCPTool struct with a hardcoded tool ID of retrieval. When the LLM decides to fetch external knowledge during the analyze phase, the engine invokes MCPTool.Run:
// inside internal/agent/tools/mcp_tool.go
func (t *MCPTool) Run(ctx context.Context, req *ToolRequest) (*ToolResponse, error) {
// Build RetrieveParams from the LLM‑generated query
rp := &types.RetrieveParams{
Query: req.Arguments["query"].(string),
TopK: req.Arguments["top_k"].(int),
// … include tenant‑wide RetrievalConfig
}
// Call the MCP service
res, err := t.mcpClient.Retrieve(ctx, rp)
if err != nil {
return nil, err
}
// Marshal the RetrieveResult into the tool response
return &ToolResponse{
Output: res,
Metadata: map[string]any{"source": "mcp"},
}, nil
}
The Retrieve RPC processes the query against the vector store and returns a RetrieveResult (defined in internal/types/retriever.go), which the engine formats as an observation for the next reasoning step.
Managing Retrieval Context Across Turns
The engine maintains retrieval continuity through the RetainRetrievalHistory flag in the agent state. When enabled, previously retrieved documents are re-fed into the prompt context of subsequent turns, allowing the ReAct Agent to build cumulative knowledge without redundant queries. Each retrieval result is injected into the conversation history as a <tool> observation block, ensuring the LLM can reference specific chunks during the next think phase.
Skill Sandboxes and Per-Turn Isolation
Dynamic Sandbox Initialization
Skill execution requires isolated environments configured per tenant. At the start of a turn involving skill invocation, the engine calls SkillLoader.Load from internal/agent/skills/loader.go. This method reads the TenantSandboxConfig (defined in internal/types/tenant_sandbox_config_entity.go) to initialize a remote sandbox container:
// inside internal/agent/skills/loader.go
func (l *SkillLoader) Load(ctx context.Context, skillID string) (*Sandbox, error) {
cfg, err := l.tenant.GetSandboxConfig(skillID)
if err != nil {
return nil, err
}
// Initialise a remote sandbox container (Dockerfile.sandbox)
sb, err := sandbox.NewRemote(cfg.Image, cfg.Env)
if err != nil {
return nil, err
}
return sb, nil
}
The sandbox machinery creates an isolated execution environment using the container image specified in the tenant configuration, ensuring that user-provided code cannot affect the host system or other sessions.
Sandbox Tool Execution
Once initialized, the sandbox is exposed to the LLM through tools defined in internal/agent/tools/sandbox_edit.go (including sandbox_edit and sandbox_exec). These tools' Run methods transmit code and commands to the sandbox process via the MCP tool-exposure protocol referenced in internal/agent/tools/mcp_exposure.go. The sandbox executes the payload and returns structured output—including file modifications, stdout, and stderr—which the engine formats as the observation for that turn. Any artifacts generated (such as new knowledge chunks) are stored in the conversation state for potential retrieval in future iterations.
Assembling the Turn-Level Toolbox
The AgentEngine constructs the available toolset fresh on every iteration through a buildTools method pattern:
// snippet from internal/agent/engine.go
func (e *AgentEngine) buildTools(state *AgentState) []Tool {
tools := []Tool{
mcp.NewRetrievalTool(e.mcpClient),
}
if state.ActiveSkill != "" {
sb, _ := e.skillLoader.Load(ctx, state.ActiveSkill)
tools = append(tools, sandbox.NewEditTool(sb))
}
// add static tools (file mutation, knowledge search, …)
return tools
}
This approach guarantees that:
- The retrieval MCP tool is always present for knowledge access.
- Sandbox tools are only included when
state.ActiveSkillindicates a specific skill is active for that turn. - A new sandbox instance is created if the LLM requests a different skill identifier, ensuring environment isolation.
Summary
- The ReAct Agent in Tencent/WeKnora processes each turn through a four-phase loop (
think → analyze → act → observe) implemented ininternal/agent/engine.go. - Retrieval MCP tools (
internal/agent/tools/mcp_tool.go) provide vector-store access via theRetrieveRPC, with results managed throughRetainRetrievalHistorysettings ininternal/types/agent.go. - Skill sandboxes are instantiated per-turn via
SkillLoader.Loadininternal/agent/skills/loader.go, using tenant-specific configurations frominternal/types/tenant_sandbox_config_entity.go. - The toolbox is reconstructed fresh on every iteration, conditionally including sandbox tools only when an active skill is specified, ensuring safe, isolated code execution.
Frequently Asked Questions
How does the ReAct Agent decide when to use the retrieval MCP tool?
The decision occurs during the analyze phase of the ReAct loop. The LLM evaluates the current conversation state and available tools; if it determines that external knowledge is required to answer the query or complete the task, it generates a tool call for the retrieval ID. The AgentEngine then executes MCPTool.Run from internal/agent/tools/mcp_tool.go to fetch relevant chunks from the vector store.
What happens to the sandbox environment between turns?
Each turn operates with a fresh toolbox assembly. If the ActiveSkill in the agent state changes or a new skill is requested, the engine invokes SkillLoader.Load to create a new sandbox instance with a clean environment. This ensures that state from previous code executions does not leak between turns, maintaining strict isolation as configured in internal/types/tenant_sandbox_config_entity.go.
How does the Agent handle retrieval history across multiple turns?
The RetainRetrievalHistory boolean field in the agent configuration (internal/types/agent.go) controls this behavior. When enabled, the engine preserves RetrieveResult objects from previous turns and includes them in the prompt context fed to the LLM during the next think phase. This allows the ReAct Agent to perform multi-hop reasoning by referencing earlier retrieved documents without re-querying the knowledge base.
Where is the maximum iteration limit configured for the ReAct loop?
The iteration cap is defined by the MaxIterations field in the agent configuration struct located in internal/types/agent.go. The executeLoop method in internal/agent/engine.go checks this limit before initiating each new turn, terminating the loop gracefully if the conversation reaches the configured threshold without resolving the task.
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 →