feat: 增强 core/agents 工具和 API
- 新增 loop.py Agent 运行循环 - 优化 memory.py 记忆模块 - 扩展 api/routes.py 接口 - 更新 tools 模块:builtin.py, manager.py, __init__.py - 新增 .env.example 配置示例 - 更新 requirements.txt 依赖 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,19 +21,55 @@ from agents.tools.builtin import (
|
||||
from agents.tools.manager import ToolManager
|
||||
|
||||
|
||||
def create_default_registry() -> ToolRegistry:
|
||||
def create_default_registry(use_sandbox: bool = False) -> ToolRegistry:
|
||||
"""Create a tool registry with default tools.
|
||||
|
||||
Args:
|
||||
use_sandbox: Whether to use sandbox for shell execution
|
||||
|
||||
Returns:
|
||||
Tool registry with built-in tools
|
||||
"""
|
||||
registry = ToolRegistry()
|
||||
# Register built-in tools
|
||||
for tool in get_builtin_tools():
|
||||
for tool in get_builtin_tools(use_sandbox=use_sandbox):
|
||||
registry.register(tool)
|
||||
return registry
|
||||
|
||||
|
||||
# Import sandbox tools from nanobot (optional)
|
||||
try:
|
||||
from nanobot.agent.tools.sandbox_execution import (
|
||||
SandboxType,
|
||||
SandboxCodeExecutionTool,
|
||||
SandboxBashTool,
|
||||
get_sandbox_tools,
|
||||
)
|
||||
from nanobot.agent.tools.bwrap_sandbox import (
|
||||
BwrapSandbox,
|
||||
get_bwrap_sandbox,
|
||||
execute_in_bwrap,
|
||||
)
|
||||
from nanobot.agent.tools.gvisor_sandbox import (
|
||||
GvisorSandbox,
|
||||
get_gvisor_sandbox,
|
||||
execute_in_gvisor,
|
||||
)
|
||||
SANDBOX_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
SandboxType = None
|
||||
SandboxCodeExecutionTool = None
|
||||
SandboxBashTool = None
|
||||
get_sandbox_tools = None
|
||||
BwrapSandbox = None
|
||||
get_bwrap_sandbox = None
|
||||
execute_in_bwrap = None
|
||||
GvisorSandbox = None
|
||||
get_gvisor_sandbox = None
|
||||
execute_in_gvisor = None
|
||||
SANDBOX_AVAILABLE = False
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Tool",
|
||||
"ToolRegistry",
|
||||
@@ -48,4 +84,16 @@ __all__ = [
|
||||
"CalculatorTool",
|
||||
"GetTimeTool",
|
||||
"BashTool",
|
||||
# Sandbox tools
|
||||
"SANDBOX_AVAILABLE",
|
||||
"SandboxType",
|
||||
"SandboxCodeExecutionTool",
|
||||
"SandboxBashTool",
|
||||
"get_sandbox_tools",
|
||||
"BwrapSandbox",
|
||||
"GvisorSandbox",
|
||||
"get_bwrap_sandbox",
|
||||
"get_gvisor_sandbox",
|
||||
"execute_in_bwrap",
|
||||
"execute_in_gvisor",
|
||||
]
|
||||
|
||||
@@ -2,12 +2,24 @@
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from nanobot.agent.tools.base import Tool
|
||||
|
||||
# Import sandbox (optional - graceful fallback if not available)
|
||||
try:
|
||||
from nanobot.agent.tools.bwrap_sandbox import BwrapSandbox, get_bwrap_sandbox
|
||||
from nanobot.agent.tools.sandbox_execution import SandboxType
|
||||
SANDBOX_AVAILABLE = True
|
||||
except ImportError:
|
||||
BwrapSandbox = None
|
||||
get_bwrap_sandbox = None
|
||||
SandboxType = None
|
||||
SANDBOX_AVAILABLE = False
|
||||
|
||||
|
||||
class ReadFileTool(Tool):
|
||||
"""Read file contents."""
|
||||
@@ -361,8 +373,18 @@ class GetTimeTool(Tool):
|
||||
class BashTool(Tool):
|
||||
"""Execute bash commands."""
|
||||
|
||||
def __init__(self, workspace: Path | None = None):
|
||||
def __init__(self, workspace: Path | None = None, use_sandbox: bool = False):
|
||||
"""Initialize bash tool.
|
||||
|
||||
Args:
|
||||
workspace: Workspace path
|
||||
use_sandbox: Whether to use sandbox for execution (recommended for untrusted code)
|
||||
"""
|
||||
self._workspace = workspace
|
||||
self._use_sandbox = use_sandbox
|
||||
self._sandbox = None
|
||||
if use_sandbox and SANDBOX_AVAILABLE:
|
||||
self._sandbox = get_bwrap_sandbox()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@@ -370,11 +392,13 @@ class BashTool(Tool):
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
if self._use_sandbox:
|
||||
return "Execute a bash command in an isolated sandbox and return its output."
|
||||
return "Execute a bash command and return its output."
|
||||
|
||||
@property
|
||||
def parameters(self) -> dict[str, Any]:
|
||||
return {
|
||||
params = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {"type": "string", "description": "Command to execute"},
|
||||
@@ -386,8 +410,17 @@ class BashTool(Tool):
|
||||
},
|
||||
"required": ["command"],
|
||||
}
|
||||
return params
|
||||
|
||||
async def execute(self, command: str, timeout: int = 30, **kwargs: Any) -> str:
|
||||
# Use sandbox if enabled
|
||||
if self._use_sandbox and self._sandbox:
|
||||
try:
|
||||
return await self._sandbox.execute_command(command, timeout)
|
||||
except Exception as e:
|
||||
return f"Error executing in sandbox: {str(e)}\nFalling back to direct execution."
|
||||
|
||||
# Direct execution (no sandbox)
|
||||
try:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
command,
|
||||
@@ -410,11 +443,12 @@ class BashTool(Tool):
|
||||
return f"Error executing command: {str(e)}"
|
||||
|
||||
|
||||
def get_builtin_tools(workspace: Path | None = None) -> list[Tool]:
|
||||
def get_builtin_tools(workspace: Path | None = None, use_sandbox: bool = False) -> list[Tool]:
|
||||
"""Get list of all built-in tools.
|
||||
|
||||
Args:
|
||||
workspace: Optional workspace path for file operations
|
||||
use_sandbox: Whether to use sandbox for shell execution (recommended for untrusted code)
|
||||
|
||||
Returns:
|
||||
List of Tool instances
|
||||
@@ -427,5 +461,5 @@ def get_builtin_tools(workspace: Path | None = None) -> list[Tool]:
|
||||
WebSearchTool(),
|
||||
CalculatorTool(),
|
||||
GetTimeTool(),
|
||||
BashTool(workspace),
|
||||
BashTool(workspace, use_sandbox=use_sandbox),
|
||||
]
|
||||
|
||||
@@ -14,22 +14,24 @@ logger = logging.getLogger(__name__)
|
||||
class ToolManager:
|
||||
"""Manages tools for the agent."""
|
||||
|
||||
def __init__(self, workspace: Path | None = None):
|
||||
def __init__(self, workspace: Path | None = None, use_sandbox: bool = False):
|
||||
"""Initialize tool manager.
|
||||
|
||||
Args:
|
||||
workspace: Optional workspace path
|
||||
use_sandbox: Whether to use sandbox for shell execution (recommended for untrusted code)
|
||||
"""
|
||||
self.workspace = workspace
|
||||
self.use_sandbox = use_sandbox
|
||||
self.registry = ToolRegistry()
|
||||
self._load_builtin_tools()
|
||||
|
||||
def _load_builtin_tools(self) -> None:
|
||||
"""Load all built-in tools."""
|
||||
tools = get_builtin_tools(self.workspace)
|
||||
tools = get_builtin_tools(self.workspace, use_sandbox=self.use_sandbox)
|
||||
for tool in tools:
|
||||
self.registry.register(tool)
|
||||
logger.info(f"Loaded {len(tools)} built-in tools")
|
||||
logger.info(f"Loaded {len(tools)} built-in tools (sandbox: {self.use_sandbox})")
|
||||
|
||||
def register_tool(self, tool: Any) -> None:
|
||||
"""Register a custom tool.
|
||||
|
||||
Reference in New Issue
Block a user