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.

8.2 KiB

01 架构与调用链

对照源码:D:\ideaProject\QozeCode-main

1. 它到底是什么

QozeCode 是一个命令行里的 Coding Agent。用户在终端打字,模型按 ReAct 循环自己决定要不要读文件、跑命令、搜网页;过程实时画在 Textual TUI 上。

它不是 IDE 插件,也不是 Web 对话页。核心就三件事:

  • 把“对话状态”交给 LangGraph 管
  • 把“动手能力”做成 LangChain Tool
  • 把“思考 / 工具 / 回复”流式渲染到终端

2. 分层(从外到内)

┌─────────────────────────────────────────────────────────┐
│  启动层  launcher.py + qoze_tui.py:main()               │
│  选模型、读配置、拉起 Textual App                        │
├─────────────────────────────────────────────────────────┤
│  交互层  qoze_tui.py + tui_components/                  │
│  斜杠命令、流式渲染、思考区、工具状态条                   │
├─────────────────────────────────────────────────────────┤
│  决策层  qoze_code_agent.py                             │
│  LangGraph: llm_call ⇄ tool_node                        │
├─────────────────────────────────────────────────────────┤
│  模型层  model_initializer.py + config_manager.py       │
│  ChatOpenAI 等,兼容 OpenAI 协议的自定义 base_url        │
├─────────────────────────────────────────────────────────┤
│  能力层                                                  │
│  tools/ 文件、命令、搜索                                  │
│  skills/ 专家知识注入 prompt                             │
│  qoze_mcp/ 外部 MCP 工具热加载                           │
│  tools/subagent_tool.py 子图并行                         │
└─────────────────────────────────────────────────────────┘

3. 目录对照(只记主干)

路径 职责 改写时优先级
qoze_tui.py Textual App用户输入入口 第二阶段再写MVP 可用纯 CLI
launcher.py 启动选模型 可做成 --model 参数
qoze_code_agent.py 心脏State、图、checkpoint 第一阶段必须吃透并重写
model_initializer.py 按厂商创建 LLM 先只留 DeepSeek / OpenAI 兼容
config_manager.py 读 ini 配置和密钥 先 YAML/ENV 即可
enums.py Provider / ModelType 自己项目用简单字符串就行
utils/system_prompt.py 静态 prompt + 动态上下文 必须理解,缓存设计值得学
tools/ 所有 Function Calling 工具 先 4 个:读文件、改文件、跑命令、列目录
tools/subagent_tool.py 迷你版主图 第三阶段
skills/ SKILL.md 发现与激活 第三阶段
qoze_mcp/ MCP 配置、激活、工具注入 第三阶段
tui_components/ 气泡、流式、thinking widget 第二阶段
utils/git_context.py 把 git status/diff 塞进 prompt 增强阶段很有用

4. 启动调用链

qoze / python qoze_tui.py
        │
        ├─ launcher.ensure_config()     没有配置就写模板
        ├─ get_model_choice()           交互选模型(可用 --model 跳过)
        └─ Qoze(provider, model_type).run()
                │
                ├─ initialize_llm()     得到 ChatOpenAI 实例
                ├─ llm.bind_tools(tools)
                ├─ init_agent()         SQLite checkpointer 编译 StateGraph
                └─ 用户回车
                        └─ process_user_input()

TUI 里以 / 开头的是本地命令,不进图:/clear/skills/mcp/quit/checkpoint
普通自然语言才 agent.astream(...)

5. 一轮对话怎么走完(必须能默画)

主图在 qoze_code_agent.py

START
  │
  ▼
llm_call
  │  拼 SystemMessage(静态 prompt)
  │  + 动态上下文目录树、git、规则、技能
  │  + 历史 messages
  │  → await llm_with_tools.ainvoke(...)
  │
  ├─ 有 tool_calls ──► tool_node ──► 再回到 llm_call
  │                      │
  │                      └─ 多个 tool_call 用 asyncio.gather 并行
  │
  └─ 没有 tool_calls ──► END

对应代码:

  • 状态:MessagesStatemessagesoperator.add 追加)
  • 节点:llm_calltool_node
  • 边:should_continue / should_continue_from_tool
  • 持久化:AsyncSqliteSaver,库在当前项目 .qoze/data/checkpoints.db

这就是 ReActReasonllm_call→ Acttool_node→ ObserveToolMessage 写回)→ 再 Reason

6. Prompt 怎么拼(这是质量关键)

llm_call 每次都会重算上下文,但拆成两段:

放哪 内容 为什么
静态 SystemMessage 角色、工具用法、行为规范 方便 Prompt Caching
动态 第一条 HumanMessage 前面 系统信息、工作目录树、git、.qoze/rules、已激活 skill、memory 每次会变,不能进静态段

还要处理两件脏活:

  • 模型不支持视觉时,剥掉 image_url
  • checkpoint 恢复后assistant 有 tool_calls 但缺 ToolMessage,要补占位,否则 OpenAI 兼容接口会 400

7. 工具层真实情况(以代码为准)

当前挂在主 Agent 上的核心工具:

  • execute_commandshellWindows 也能跑,但没有 Linux 的进程组杀法)
  • read_file / list_files / search_in_files / grep_file / find_files / replace_in_file
  • tavily_search / read_url
  • skill 四个activate / list / deactivate / install guide
  • MCP 四个list / activate / deactivate / install guide
  • dispatch_subagent
  • analyze_project / find_symbols / trace_imports
  • transcribe_audio / get_current_datetime

注意:

  • write_file 被注释了,写文件靠 replace_in_fileexecute_command
  • browser_* 整段注释,浏览器走 MCPchrome-devtools
  • README 的 /plan 在这份代码里没有对应实现,别按文档去找

路径安全:file_tools._resolve_under_cwd 只允许 cwd 和 ~/.qoze/,这是改写时必须保留的约束。

8. 三条插件通道(先分清,再决定要不要抄)

Skill     发现 SKILL.md → 用户 activate → 把文档塞进动态 prompt
MCP       读 mcp_config.json → 拉起外部进程 → 把远端 tools 注入 tools_by_name
Subagent  再建一张同样的小图,工具更少、无 checkpoint、上下文隔离

记忆口诀:

  • Skill = 知识(改 prompt
  • MCP = 外来工具(改 tool 列表)
  • Subagent = 再开一个大脑(改执行拓扑)

9. TUI 流式

tui_components/messages/stream_handler.py 订阅 agent.astream

  • 模型 reasoning_contentThinkingWidget
  • 正文 token → BotMessageWidget(增量 buffer + 调度器,避免刷爆终端)
  • 出现 tool_call → 工具状态面板
  • ToolMessage 回来 → 标记完成/失败

自己复刻时,第一版可以不流式,等图跑完再 print。流式是体验,不是 Agent 正确性。

10. 和宣传文案不一致的地方(精读避坑)

README 说法 这份源码
/plan 三份文档 未见实现
Playwright 浏览器工具 browser_tool.py 注释,改 MCP
语音 AudioManager / SonioxWindows 基本不可用
内置 skills 一堆 仓库 skills/ 只有管理器,内置技能目录可能不在这份拷贝里

精读原则:调用链以 qoze_code_agent.py + qoze_tui.py 为准。