feat(PHASE-03): 环境感知 + 稳定性约束 + Windows 防重复试错 + max_turns=12
This commit is contained in:
parent
1d66019529
commit
587331cbb8
12
AGENTS.md
12
AGENTS.md
@ -21,6 +21,10 @@
|
|||||||
4. 使用工具后重新判断结果。
|
4. 使用工具后重新判断结果。
|
||||||
5. 持续循环,直到得到最终答案或出现必须由用户补充的信息。
|
5. 持续循环,直到得到最终答案或出现必须由用户补充的信息。
|
||||||
|
|
||||||
|
当前 harness 是极简实现,优先最小动作,不做不必要的重复试错。
|
||||||
|
|
||||||
|
行动时必须以运行时注入的环境信息为准,特别是平台、shell、工作目录和可用工具列表。
|
||||||
|
|
||||||
未明确说明时,使用以下默认值:
|
未明确说明时,使用以下默认值:
|
||||||
|
|
||||||
- 工作目录:当前进程目录
|
- 工作目录:当前进程目录
|
||||||
@ -35,6 +39,12 @@
|
|||||||
- `Read`:用于读取文件内容。
|
- `Read`:用于读取文件内容。
|
||||||
- `Glob`:用于按模式查找文件。
|
- `Glob`:用于按模式查找文件。
|
||||||
- `Bash`:用于执行必须通过 shell 完成的最小命令。
|
- `Bash`:用于执行必须通过 shell 完成的最小命令。
|
||||||
|
- 只有在 Bash 确实必要时才使用 Bash。
|
||||||
|
- Windows 环境下优先使用兼容写法,不默认使用 `cat <<EOF`、`ls -la` 等 Unix 风格写法。
|
||||||
|
- 同一写入或创建目标,最多尝试 2 种不同 Bash 方案。
|
||||||
|
- 如果两次 Bash 方案失败,应立即收敛:
|
||||||
|
- 先检查路径、文件状态、shell 兼容性。
|
||||||
|
- 如仍不确定,则询问用户,而不是继续盲试。
|
||||||
- 不能虚构工具输出。
|
- 不能虚构工具输出。
|
||||||
- 不能在未验证前声称文件存在、命令成功或修改已生效。
|
- 不能在未验证前声称文件存在、命令成功或修改已生效。
|
||||||
|
|
||||||
@ -42,5 +52,5 @@
|
|||||||
|
|
||||||
- 对不确定性保持诚实。
|
- 对不确定性保持诚实。
|
||||||
- 需要时引用具体文件或命令。
|
- 需要时引用具体文件或命令。
|
||||||
- 默认保持简短,除非用户要求展开。
|
- 输出保持简短直接,不夸大成功状态。
|
||||||
- 若受阻,只询问当前缺失的关键信息。
|
- 若受阻,只询问当前缺失的关键信息。
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
- 重构时:
|
- 重构时:
|
||||||
- 先找全部用法
|
- 先找全部用法
|
||||||
- 避免修改无关文件
|
- 避免修改无关文件
|
||||||
|
- Windows 下先验证 shell 兼容性,再选择命令写法
|
||||||
|
|
||||||
## 沟通风格
|
## 沟通风格
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ packages = ["src/cc_slim"]
|
|||||||
[tool.cc_slim]
|
[tool.cc_slim]
|
||||||
provider = "openai"
|
provider = "openai"
|
||||||
model = "gpt-4.1-mini"
|
model = "gpt-4.1-mini"
|
||||||
max_turns = 8
|
max_turns = 12
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
|
|||||||
@ -2,6 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
import tomllib
|
import tomllib
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
@ -20,7 +22,7 @@ class Config:
|
|||||||
model: str
|
model: str
|
||||||
api_key: str
|
api_key: str
|
||||||
base_url: str | None
|
base_url: str | None
|
||||||
max_turns: int = 8
|
max_turns: int = 12
|
||||||
|
|
||||||
|
|
||||||
def resolve_config(workspace: Path, cli: dict[str, Any]) -> Config:
|
def resolve_config(workspace: Path, cli: dict[str, Any]) -> Config:
|
||||||
@ -40,7 +42,7 @@ def resolve_config(workspace: Path, cli: dict[str, Any]) -> Config:
|
|||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
base_url = _pick(cli.get("base_url"), os.getenv("CC_SLIM_BASE_URL"), file_cfg.get("base_url"), None)
|
base_url = _pick(cli.get("base_url"), os.getenv("CC_SLIM_BASE_URL"), file_cfg.get("base_url"), None)
|
||||||
max_turns_raw = _pick(cli.get("max_turns"), os.getenv("CC_SLIM_MAX_TURNS"), file_cfg.get("max_turns"), 8)
|
max_turns_raw = _pick(cli.get("max_turns"), os.getenv("CC_SLIM_MAX_TURNS"), file_cfg.get("max_turns"), 12)
|
||||||
|
|
||||||
if not api_key:
|
if not api_key:
|
||||||
raise ValueError("缺少 API key,请通过 CLI、环境变量或 .cc-slim.toml 提供。")
|
raise ValueError("缺少 API key,请通过 CLI、环境变量或 .cc-slim.toml 提供。")
|
||||||
@ -120,6 +122,8 @@ class Agent:
|
|||||||
|
|
||||||
def _build_system_prompt(self, workspace: Path) -> str:
|
def _build_system_prompt(self, workspace: Path) -> str:
|
||||||
parts: list[str] = []
|
parts: list[str] = []
|
||||||
|
parts.append(self._build_runtime_summary(workspace))
|
||||||
|
|
||||||
agents = workspace / "AGENTS.md"
|
agents = workspace / "AGENTS.md"
|
||||||
if agents.exists():
|
if agents.exists():
|
||||||
parts.append(agents.read_text(encoding="utf-8"))
|
parts.append(agents.read_text(encoding="utf-8"))
|
||||||
@ -131,6 +135,26 @@ class Agent:
|
|||||||
|
|
||||||
return "\n\n".join(part.strip() for part in parts if part.strip())
|
return "\n\n".join(part.strip() for part in parts if part.strip())
|
||||||
|
|
||||||
|
def _build_runtime_summary(self, workspace: Path) -> str:
|
||||||
|
tool_names = ", ".join(self.tools.keys()) or "(none)"
|
||||||
|
shell_name = self._detect_shell()
|
||||||
|
return "\n".join(
|
||||||
|
[
|
||||||
|
"## 运行环境",
|
||||||
|
f"- 平台: {platform.system() or 'Unknown'}",
|
||||||
|
f"- sys.platform: {sys.platform}",
|
||||||
|
f"- shell: {shell_name}",
|
||||||
|
f"- workspace: {workspace}",
|
||||||
|
f"- 可用工具: {tool_names}",
|
||||||
|
"- 行动时必须以以上运行环境信息为准,不要默认套用 Unix/Linux 命令习惯。",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _detect_shell(self) -> str:
|
||||||
|
if os.name == "nt":
|
||||||
|
return os.getenv("COMSPEC", "Windows shell (likely PowerShell or cmd.exe)")
|
||||||
|
return os.getenv("SHELL", "unknown shell")
|
||||||
|
|
||||||
def _call_openai(self) -> dict[str, Any]:
|
def _call_openai(self) -> dict[str, Any]:
|
||||||
response = self.client.chat.completions.create(
|
response = self.client.chat.completions.create(
|
||||||
model=self.config.model,
|
model=self.config.model,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user