"""文件读取工具""" # ════════════════════════════════════════════════════════════════ # tools/file_reader.py — 文件读取工具 # ════════════════════════════════════════════════════════════════ """ tools/file_reader.py 本地文件读取工具,支持文本文件,限制读取路径防止越权 """ from pathlib import Path from tools.base_tool import BaseTool, ToolResult # 允许读取的根目录(沙箱限制) _ALLOWED_ROOT = Path("./workspace") class FileReaderTool(BaseTool): name = "file_reader" description = "读取本地文件内容,仅限 workspace/ 目录下的文件" parameters = { "path": { "type": "string", "description": "文件路径,相对于 workspace/ 目录", }, "encoding": { "type": "string", "description": "文件编码,默认 utf-8", }, } def execute(self, path: str, encoding: str = "utf-8", **_) -> ToolResult: _ALLOWED_ROOT.mkdir(exist_ok=True) # 路径安全检查:防止目录穿越攻击 target = (_ALLOWED_ROOT / path).resolve() if not str(target).startswith(str(_ALLOWED_ROOT.resolve())): return ToolResult(success=False, output=f"❌ 拒绝访问: 路径超出允许范围") if not target.exists(): # Demo 模式:自动创建示例文件 self._create_demo_file(target) try: content = target.read_text(encoding=encoding) return ToolResult( success=True, output=f"文件 [{path}] 内容:\n{content}", metadata={"path": str(target), "size": target.stat().st_size}, ) except OSError as exc: return ToolResult(success=False, output=f"读取失败: {exc}") @staticmethod def _create_demo_file(path: Path) -> None: """自动创建演示用文件""" path.parent.mkdir(parents=True, exist_ok=True) path.write_text( '{\n "app": "AgentDemo",\n "version": "1.0.0",\n' ' "llm": "claude-sonnet-4-6",\n "tools": ["calculator", "web_search"]\n}\n', encoding="utf-8", )