|
|
# 02 架构与目录
|
|
|
|
|
|
源码落地:`D:\ideaProject\gcode`(本次只定结构,不建库)。
|
|
|
|
|
|
## 分层
|
|
|
|
|
|
```text
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
│ ui/detect + tui / repl │
|
|
|
│ 探测终端、画界面、收集确认(y/N、diff) │
|
|
|
├─────────────────────────────────────────────┤
|
|
|
│ graph + session + prompt │
|
|
|
│ ReAct、checkpoint、静态/动态提示词 │
|
|
|
├─────────────────────────────────────────────┤
|
|
|
│ models(注册表 + openai_compatible) │
|
|
|
├─────────────────────────────────────────────┤
|
|
|
│ tools + safety + mcp │
|
|
|
│ 文件/命令沙箱、HITL、Playwright MCP │
|
|
|
└─────────────────────────────────────────────┘
|
|
|
```
|
|
|
|
|
|
依赖只能从上到下。工具不得 `import` 图模块去改全局 `llm` / `agent`。TUI 与 REPL 都只消费 `graph.astream(...)`。
|
|
|
|
|
|
```mermaid
|
|
|
flowchart LR
|
|
|
detect[终端探测] -->|WT 等| tui[自研TUI]
|
|
|
detect -->|conhost| repl[REPL回退]
|
|
|
tui --> graph[LangGraph ReAct]
|
|
|
repl --> graph
|
|
|
graph --> llmCall[llm_call]
|
|
|
llmCall -->|tool_calls| toolNode[tool_node]
|
|
|
toolNode --> safety[HITL]
|
|
|
safety --> llmCall
|
|
|
llmCall -->|纯文本| uiOut[TUI或REPL渲染]
|
|
|
```
|
|
|
|
|
|
## 目录骨架
|
|
|
|
|
|
```text
|
|
|
D:\ideaProject\gcode\
|
|
|
pyproject.toml
|
|
|
README.md
|
|
|
src/gcode/
|
|
|
__init__.py
|
|
|
cli.py # 入口:解析参数、探测、选 UI
|
|
|
config.py # 单一 home:%APPDATA%\gcode 与 <cwd>/.gcode
|
|
|
windows.py # 代码页、默认 PowerShell、Job Object / taskkill
|
|
|
prompt.py
|
|
|
graph.py # StateGraph:llm_call / tool_node
|
|
|
session.py # sqlite checkpointer + thread_id
|
|
|
safety.py # 写文件 diff、shell 确认;回调由 UI 注入
|
|
|
ui/
|
|
|
detect.py
|
|
|
repl.py
|
|
|
tui/ # 自研 Textual,不拷 Qoze CSS/logo
|
|
|
models/
|
|
|
registry.py # 读 models.yaml
|
|
|
openai_compatible.py
|
|
|
tools/
|
|
|
fs.py
|
|
|
shell.py
|
|
|
mcp/
|
|
|
client.py # 薄客户端,默认只起 @playwright/mcp
|
|
|
tests/
|
|
|
test_path_sandbox.py
|
|
|
test_graph_smoke.py
|
|
|
test_detect_terminal.py
|
|
|
```
|
|
|
|
|
|
项目级数据:`<cwd>/.gcode/`(checkpoints、rules、debug log)。
|
|
|
用户级:`%APPDATA%\gcode\`(`models.yaml`、mcp 配置、token 统计)。禁止再混用 `~\.gcode` 与 `%APPDATA%` 两套根。
|
|
|
|
|
|
## Prompt 怎么写
|
|
|
|
|
|
不要一上来写 400 行。对照 Qoze 的 [system_prompt.py](D:\ideaProject\QozeCode-main\utils\system_prompt.py) 学 **静态/动态拆分**,不要整段粘贴。G-CODE 工具清单不同,抄过来模型会去调不存在的 `dispatch_subagent`。
|
|
|
|
|
|
每次加一段之前问:
|
|
|
|
|
|
1. 模型此刻有哪些工具?没实现的一句不写
|
|
|
2. 最容易做错的 3 件事?只为这些写硬规则
|
|
|
3. 静态还是动态?每次一样 → System;会变(cwd、git、rules)→ 动态段
|
|
|
4. 能用代码拦住的不要只靠文字(路径沙箱在 `read_file` 里拒绝)
|
|
|
5. 用失败任务加规则,不用想象加规则
|
|
|
|
|
|
静态段先控制在 80~150 行:身份、工具怎么选、5~8 条硬规则、ReAct 与「连续 3 次无进展就问人」、中文推理、TUI 内避免 emoji。
|
|
|
|
|
|
动态段保持短:cwd、目录树摘要、git 摘要、`.gcode/rules`。
|
|
|
|
|
|
## 多模型
|
|
|
|
|
|
不复制 Qoze 的枚举树。`models.yaml` 示例:
|
|
|
|
|
|
```yaml
|
|
|
providers:
|
|
|
deepseek:
|
|
|
api: openai_compatible
|
|
|
base_url: https://api.deepseek.com/v1
|
|
|
api_key_env: DEEPSEEK_API_KEY
|
|
|
models:
|
|
|
- id: deepseek-v4-flash
|
|
|
vision: false
|
|
|
reasoning: false
|
|
|
- id: deepseek-v4-pro
|
|
|
vision: false
|
|
|
reasoning: true
|
|
|
agicto:
|
|
|
api: openai_compatible
|
|
|
base_url: https://api.agicto.cn/v1
|
|
|
api_key_env: GCODE_API_KEY
|
|
|
models:
|
|
|
- id: deepseek-v4-flash
|
|
|
vision: false
|
|
|
reasoning: false
|
|
|
```
|
|
|
|
|
|
一期只实现 `openai_compatible`。加一家模型改 yaml,不改 Python。`reasoning_content` 尽量显式解析,避免猴子补丁 LangChain。Vertex / Anthropic / Bedrock 有账号再加 adapter。
|
|
|
|
|
|
启动:`gcode --model deepseek-v4-flash` 或 TUI 内选择。
|
|
|
|
|
|
## 人在回路
|
|
|
|
|
|
`safety.py` 定义确认点,UI 注入回调(TUI 用对话框,REPL 用 y/N):
|
|
|
|
|
|
| 动作 | 展示 | 拒绝时 |
|
|
|
|------|------|--------|
|
|
|
| 写文件 / 替换 | unified diff | ToolMessage:「用户拒绝本次写入」 |
|
|
|
| `execute_command` | 完整命令 + 工作目录 | ToolMessage:「用户拒绝执行该命令」 |
|
|
|
|
|
|
读文件、列目录、只读 git 不必确认。安装依赖、构建、测试、`git commit` / `push` 必须走确认(prompt 硬规则 + HITL 双保险)。
|
|
|
|
|
|
## 工具
|
|
|
|
|
|
一期内置:
|
|
|
|
|
|
- `read_file` / `list_dir` / `replace_in_file`:仅 cwd 或用户级 gcode 目录;拒绝 `..`
|
|
|
- `execute_command`:默认 PowerShell;输出 UTF-8 失败则 GBK;超时用 Job Object 或 `taskkill /T`,不要只 `terminate()` 留孤儿
|
|
|
|
|
|
浏览器不进 `tools/` 进程内实现,见下一节。
|
|
|
|
|
|
## Playwright MCP(仅途径 A)
|
|
|
|
|
|
官方:`npx -y @playwright/mcp@latest`。
|
|
|
|
|
|
G-CODE 薄 MCP 客户端:stdio 拉起 → 把 tools 动态交给 `bind_tools`。MCP 只当 Playwright 插座,一期不接 GitHub / Postgres。不复活 Qoze 已注释的 `browser_tool.py`,不默认 `chrome-devtools-mcp`(那是 `--remote-debugging-port=9222` 附着已开 Chrome)。
|
|
|
|
|
|
硬依赖 Node.js / npx。检测不到则 TUI/REPL 提示「浏览器工具不可用,请安装 Node.js」,没有 Python Playwright 备选。
|
|
|
|
|
|
## 相对 Qoze 必须避开的结构
|
|
|
|
|
|
- 模块级全局 `llm`、`agent`、`tools_by_name` 被 MCP/TUI 到处改
|
|
|
- `mcp_tools` / `subagent_tool` 反向 import `qoze_code_agent`
|
|
|
- `qoze_tui.py` 里命令路由、拼消息、token、流式全混在一个 App 类
|
|
|
- 配置路径分裂:`%APPDATA%\qoze` vs `~\.qoze`
|
|
|
- shell 一律按 UTF-8 解码(cmd 的 `dir` 常为 GBK)
|