You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.2 KiB

02 架构与目录

源码落地:D:\ideaProject\gcode(本次只定结构,不建库)。

分层

┌─────────────────────────────────────────────┐
│  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(...)。

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渲染]

目录骨架

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 学 静态/动态拆分,不要整段粘贴。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 示例:

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)