67 lines
2.5 KiB
Python
67 lines
2.5 KiB
Python
"""Executor 接口:工具调用的总入口(DESIGN §7.5 落地清单 #5)。
|
|
|
|
`AgentLoop` 不直接调 `tool.execute`,而是 `executor.call_tool(name, args, ctx)`。
|
|
Backend 内部 dispatch:
|
|
|
|
- `HostExecutor`(本步引入):全部 tools in-process,沿用原 `Tool.execute` 行为
|
|
- `DockerExecutor`(Step 3 引入):`shell` / `run_python` 走 `docker exec`,
|
|
其余按 §7.5 #6 信任域二分仍走 host —— 此时 DockerExecutor 内部组合 HostExecutor
|
|
|
|
接口形状刻意保持 backend 无关:`call_tool(name, args, ctx)` 不暴露 `docker exec` /
|
|
`docker cp` / `docker stats` 等 Docker 假设。未来切 gVisor / Firecracker / e2b 时
|
|
应用层零改动,只换 backend driver(§7.5 #5 / §7.9 升级触发表)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any, Callable, Dict, List, Optional
|
|
from uuid import UUID
|
|
|
|
|
|
@dataclass
|
|
class ExecCtx:
|
|
"""每次 tool 调用的执行上下文。
|
|
|
|
带身份 / 范围 / 取消钩子;arg-irrelevant 信息从 args 剥离。
|
|
host backend 当前只用 cancel_check;docker backend 会:
|
|
- user_id → 找 / 起 per-user 容器
|
|
- working_dir → 拼 `docker exec --workdir /workspace/<wd_name>`
|
|
- task_id → 临时文件命名空间 `/tmp/zcbot/<task_id>/`
|
|
- cancel_check → 轮询期间响应停止按钮(主动 `kill -- -PGID`)
|
|
"""
|
|
user_id: UUID
|
|
task_id: UUID
|
|
working_dir: Path
|
|
cancel_check: Optional[Callable[[], bool]] = None
|
|
|
|
|
|
@dataclass
|
|
class ToolResult:
|
|
"""工具调用统一返回。
|
|
|
|
现状所有 `Tool.execute` 都返 str,docker backend 后续可能要带 stdout/stderr/
|
|
exit_code 分离。这里先留单 content 字段(LLM 拿到的就是这串),exit_code 作
|
|
backend 内部使用 hint(0=ok / 1=tool 抛异常 / 2=参数非法 / 124=timeout 等),
|
|
不影响 LLM 接口。
|
|
"""
|
|
content: str
|
|
exit_code: int = 0
|
|
|
|
|
|
class Executor(ABC):
|
|
"""工具调度抽象 —— 见模块 docstring。"""
|
|
|
|
@abstractmethod
|
|
def call_tool(self, name: str, args: Dict[str, Any], ctx: ExecCtx) -> ToolResult:
|
|
"""执行单次 tool 调用。永远返 ToolResult,不抛异常(异常包成 exit_code=1)。"""
|
|
|
|
@abstractmethod
|
|
def schemas(self) -> List[Dict[str, Any]]:
|
|
"""暴露给 LLM 的 OpenAI tool schema 列表;`AgentLoop._stream_llm` 用。"""
|
|
|
|
@abstractmethod
|
|
def has_tool(self, name: str) -> bool:
|
|
"""schema 列表覆盖的 tool 名;主要给测试 / 诊断用。"""
|