feat(PHASE-05): 最小 Write 工具 + 工具优先级优化(写文件调用次数大幅下降)

This commit is contained in:
hc 2026-04-10 20:53:10 +08:00
parent 587331cbb8
commit 67f6467da0
4 changed files with 33 additions and 1 deletions

2
.gitignore vendored
View File

@ -14,7 +14,7 @@ venv/
*.log
logs/
.tmp/
test/
# IDE
.vscode/
.idea/

View File

@ -38,8 +38,10 @@
- `Read`:用于读取文件内容。
- `Glob`:用于按模式查找文件。
- 创建或覆盖文件时,优先使用 `Write` 工具。
- `Bash`:用于执行必须通过 shell 完成的最小命令。
- 只有在 Bash 确实必要时才使用 Bash。
- 不要用 Bash 拼接文件内容。
- Windows 环境下优先使用兼容写法,不默认使用 `cat <<EOF`、`ls -la` 等 Unix 风格写法。
- 同一写入或创建目标,最多尝试 2 种不同 Bash 方案。
- 如果两次 Bash 方案失败,应立即收敛:

View File

@ -20,6 +20,7 @@
- 重构时:
- 先找全部用法
- 避免修改无关文件
- 需要创建或覆盖文件时,优先使用 `Write` 工具,而不是 `Bash`
- Windows 下先验证 shell 兼容性,再选择命令写法
## 沟通风格

View File

@ -43,6 +43,21 @@ def build_default_tools(workspace: Path) -> list[Tool]:
},
execute=lambda data: glob_tool(workspace, data),
),
Tool(
name="Write",
description="创建或覆盖工作区内的文本文件。",
input_schema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "相对工作区的文件路径"},
"content": {"type": "string", "description": "要写入的完整文本内容"},
"encoding": {"type": "string", "description": "文件编码,默认 utf-8"},
},
"required": ["path", "content"],
"additionalProperties": False,
},
execute=lambda data: write_tool(workspace, data),
),
Tool(
name="Bash",
description="在工作区中执行一条 shell 命令。",
@ -82,6 +97,20 @@ def glob_tool(workspace: Path, data: dict[str, Any]) -> str:
return "\n".join(matches[:200])
def write_tool(workspace: Path, data: dict[str, Any]) -> str:
path = _safe_path(workspace, str(data["path"]))
if path.exists() and path.is_dir():
return f"目标是目录,不能写入文件: {path.relative_to(workspace)}"
if not path.parent.exists():
return f"父目录不存在: {path.parent.relative_to(workspace)}"
existed = path.exists()
encoding = str(data.get("encoding") or "utf-8")
path.write_text(str(data["content"]), encoding=encoding)
action = "已覆盖" if existed else "已创建"
return f"{action}文件: {path.relative_to(workspace)}"
def bash_tool(workspace: Path, data: dict[str, Any]) -> str:
command = str(data["command"])
proc = subprocess.run(