2026-02-28 08:21:35 +00:00
|
|
|
|
# ════════════════════════════════════════════════════════════════
|
2026-03-09 05:37:29 +00:00
|
|
|
|
# tools/code_executor.py
|
2026-02-28 08:21:35 +00:00
|
|
|
|
# ════════════════════════════════════════════════════════════════
|
2026-03-09 05:37:29 +00:00
|
|
|
|
"""沙箱代码执行工具(从配置读取 timeout / sandbox)"""
|
2026-02-28 08:21:35 +00:00
|
|
|
|
|
|
|
|
|
|
import io
|
|
|
|
|
|
import contextlib
|
|
|
|
|
|
import time
|
|
|
|
|
|
from tools.base_tool import BaseTool, ToolResult
|
2026-03-09 05:37:29 +00:00
|
|
|
|
from config.settings import settings
|
2026-02-28 08:21:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CodeExecutorTool(BaseTool):
|
|
|
|
|
|
name = "code_executor"
|
|
|
|
|
|
description = "在沙箱环境中执行 Python 代码片段,返回标准输出"
|
|
|
|
|
|
parameters = {
|
2026-03-09 05:37:29 +00:00
|
|
|
|
"code": {"type": "string", "description": "要执行的 Python 代码"},
|
|
|
|
|
|
"timeout": {"type": "integer", "description": "超时时间(秒)"},
|
2026-02-28 08:21:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_SAFE_BUILTINS = {
|
|
|
|
|
|
"print": print, "range": range, "len": len,
|
|
|
|
|
|
"int": int, "float": float, "str": str, "list": list,
|
|
|
|
|
|
"dict": dict, "tuple": tuple, "set": set, "bool": bool,
|
|
|
|
|
|
"abs": abs, "max": max, "min": min, "sum": sum,
|
|
|
|
|
|
"enumerate": enumerate, "zip": zip, "map": map,
|
|
|
|
|
|
"sorted": sorted, "reversed": reversed,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 05:37:29 +00:00
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
cfg = settings.tools.code_executor
|
|
|
|
|
|
self._timeout = cfg.timeout
|
|
|
|
|
|
self._sandbox = cfg.sandbox
|
|
|
|
|
|
self.logger.debug(
|
|
|
|
|
|
f"⚙️ CodeExecutor timeout={self._timeout}s, sandbox={self._sandbox}"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def execute(self, code: str, timeout: int | None = None, **_) -> ToolResult:
|
|
|
|
|
|
timeout = timeout or self._timeout
|
2026-02-28 08:21:35 +00:00
|
|
|
|
stdout_buf = io.StringIO()
|
2026-03-09 05:37:29 +00:00
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
exec_globals = (
|
|
|
|
|
|
{"__builtins__": self._SAFE_BUILTINS}
|
|
|
|
|
|
if self._sandbox
|
|
|
|
|
|
else {"__builtins__": __builtins__}
|
|
|
|
|
|
)
|
2026-02-28 08:21:35 +00:00
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
with contextlib.redirect_stdout(stdout_buf):
|
2026-03-09 05:37:29 +00:00
|
|
|
|
exec(compile(code, "<agent_sandbox>", "exec"), exec_globals) # noqa: S102
|
|
|
|
|
|
elapsed = (time.perf_counter() - start) * 1000
|
2026-02-28 08:21:35 +00:00
|
|
|
|
output = stdout_buf.getvalue() or "(无输出)"
|
|
|
|
|
|
return ToolResult(
|
|
|
|
|
|
success=True,
|
2026-03-09 05:37:29 +00:00
|
|
|
|
output=f"执行成功 ({elapsed:.1f}ms) [sandbox={self._sandbox}]:\n{output}",
|
|
|
|
|
|
metadata={"elapsed_ms": elapsed, "sandbox": self._sandbox},
|
2026-02-28 08:21:35 +00:00
|
|
|
|
)
|
|
|
|
|
|
except Exception as exc:
|
2026-03-09 05:37:29 +00:00
|
|
|
|
return ToolResult(success=False, output=f"执行错误: {type(exc).__name__}: {exc}")
|