66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""文件读取工具"""
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════
|
|
# 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",
|
|
)
|
|
|