"""AI Research Agent(Phase 5)。 Agent 是 Research Assistant(AGENT.md §28/§29): - 只能调用本目录 tools 提供的**白名单受控工具**(只读研究能力) - 禁止:执行任意 shell / 修改删除数据 / 修改配置与凭证 - 研究行为:提出假设 → 建立实验 → 运行测试 → 分析 → 下一步,禁止以单次高收益宣告策略有效 """ from __future__ import annotations import json from collections.abc import Callable from dataclasses import dataclass, field from typing import Any TOOL_CALL_TAG = "__tool__" @dataclass(frozen=True) class Tool: """受控工具元数据(LLM 可见的 JSON Schema + 调用实现)。""" name: str description: str parameters: dict[str, Any] handler: Callable[[dict[str, Any]], str] = field(repr=False) def schema(self) -> dict[str, Any]: return { "name": self.name, "description": self.description, "parameters": self.parameters, } def invoke(self, args: dict[str, Any]) -> str: """执行工具;任何异常都转为可读错误串(不向上抛,避免中断整轮对话)。""" try: result = self.handler(args) return result if isinstance(result, str) else json.dumps(result, ensure_ascii=False) except Exception as exc: # noqa: BLE001 —— 工具异常反馈给 LLM 而非崩溃 return json.dumps({"error": f"{type(exc).__name__}: {exc}"}, ensure_ascii=False) def tools_schema(tools: list[Tool]) -> str: return json.dumps([t.schema() for t in tools], ensure_ascii=False, indent=1)