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
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
对应代码:
- 状态:
MessagesState(messages用operator.add追加) - 节点:
llm_call、tool_node - 边:
should_continue/should_continue_from_tool - 持久化:
AsyncSqliteSaver,库在当前项目.qoze/data/checkpoints.db
这就是 ReAct:Reason(llm_call)→ Act(tool_node)→ Observe(ToolMessage 写回)→ 再 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_command:shell(Windows 也能跑,但没有 Linux 的进程组杀法)read_file/list_files/search_in_files/grep_file/find_files/replace_in_filetavily_search/read_url- skill 四个:activate / list / deactivate / install guide
- MCP 四个:list / activate / deactivate / install guide
dispatch_subagentanalyze_project/find_symbols/trace_importstranscribe_audio/get_current_datetime
注意:
write_file被注释了,写文件靠replace_in_file或execute_commandbrowser_*整段注释,浏览器走 MCP(chrome-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_content→ThinkingWidget - 正文 token →
BotMessageWidget(增量 buffer + 调度器,避免刷爆终端) - 出现 tool_call → 工具状态面板
- ToolMessage 回来 → 标记完成/失败
自己复刻时,第一版可以不流式,等图跑完再 print。流式是体验,不是 Agent 正确性。
10. 和宣传文案不一致的地方(精读避坑)
| README 说法 | 这份源码 |
|---|---|
/plan 三份文档 |
未见实现 |
| Playwright 浏览器工具 | browser_tool.py 注释,改 MCP |
| 语音 | 有 AudioManager / Soniox,Windows 基本不可用 |
| 内置 skills 一堆 | 仓库 skills/ 只有管理器,内置技能目录可能不在这份拷贝里 |
精读原则:调用链以 qoze_code_agent.py + qoze_tui.py 为准。