Qwen Code 代码分析
Qwen Code 代码分析
1. 项目概述
Qwen Code 是一个开源的终端 AI Agent 工具,优化用于 Qwen 系列大模型。它帮助用户理解大型代码库、自动化重复工作、加速开发流程。
1.1 核心特性
- 多协议支持:OpenAI / Anthropic / Gemini 兼容 API,Qwen OAuth 免费提供 1000 次/天请求
- Agentic 工作流:内置 Skills、SubAgents 等完整代理工作流
- 终端优先:为命令行开发者设计,支持 VS Code、Zed、JetBrains IDE 集成
- 开源共进:框架与 Qwen3-Coder 模型共同演进
1.2 技术栈
| 类别 | 技术选型 |
|---|---|
| 语言 | TypeScript (严格模式) |
| 运行时 | Node.js >= 20 |
| 模块系统 | ESM |
| UI 框架 | React + Ink (终端渲染) |
| 测试框架 | Vitest |
| 打包工具 | esbuild |
| 包管理 | npm workspaces (monorepo) |
| 代码规范 | ESLint + Prettier |
2. Monorepo 架构
2.1 项目结构
Qwen-Code/
├── packages/ # 核心包
│ ├── cli/ # CLI 终端应用(主程序)
│ ├── core/ # 核心引擎(LLM 交互、工具执行)
│ ├── sdk-typescript/ # TypeScript SDK(供外部使用)
│ ├── sdk-java/ # Java SDK
│ ├── vscode-ide-companion/ # VS Code 插件
│ ├── zed-extension/ # Zed 编辑器插件
│ ├── webui/ # Web UI(待完善)
│ ├── web-templates/ # Web 模板
│ ├── test-utils/ # 共享测试工具
│ └── channels/ # 消息渠道
│ ├── base/ # 基础渠道框架
│ ├── dingtalk/ # 钉钉集成
│ ├── telegram/ # Telegram 集成
│ └── weixin/ # 微信集成
├── integration-tests/ # 集成测试
├── scripts/ # 构建和工具脚本
├── docs/ # 文档
└── 1-docs/ # 架构设计文档(本目录)
2.2 包依赖关系
┌─────────────────────────────────────────────────┐
│ CLI 包 │
│ (React + Ink 终端 UI) │
│ 依赖: core, test-utils │
└──────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ Core 包 │
│ (LLM 客户端、工具系统、Agent 引擎) │
│ 无内部包依赖(独立引擎) │
└──────────────────┬──────────────────────────────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ SDK │ │ Channels │ │ IDE │
│ (TS/Java)│ │ (钉钉等) │ │ (VSCode) │
└──────────┘ └──────────┘ └──────────┘
依赖原则:
core包是独立引擎,不依赖任何内部包cli包依赖core,通过 alias 引用源码(非编译产物)integration-tests依赖编译后的dist/产物channels包相互独立,仅依赖base
2.3 包职责矩阵
| 包 | 职责 | 测试文件数 | 覆盖率要求 |
|---|---|---|---|
| cli | 终端 UI、命令处理、用户交互 | 185 | ✅ |
| core | LLM 交互、工具执行、Agent 引擎 | 209 | ✅ |
| sdk-typescript | 外部 API 封装 | 11 (E2E) | ✅ (>=80%) |
| vscode-ide-companion | VS Code 集成 | - | - |
| channels/base | 渠道框架 | 5 | ❌ |
| channels/dingtalk | 钉钉渠道 | 1 | ❌ |
| channels/weixin | 微信渠道 | 2 | ❌ |
3. 核心架构设计
3.1 整体架构分层
┌────────────────────────────────────────────────────────┐
│ 用户交互层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CLI TUI │ │ Headless │ │ IDE │ │ Channels │ │
│ │ (React) │ │ (脚本) │ │ (VSCode) │ │ (钉钉等) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
├─────┴──────────────┴────────────┴────────────┴─────────┤
│ 配置中枢层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Config (全局配置) │ │
│ │ Storage / Models / Tools / Services / Hooks │ │
│ └──────────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────────┤
│ 核心引擎层 │
│ ┌────────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ GeminiClient│→│ Turn │→│ CoreToolScheduler │ │
│ │ (会话管理) │ │ (单次循环) │ │ (工具调度) │ │
│ └────────────┘ └──────────┘ └──────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────┐ ┌──────────────────┐ │
│ │ ContentGen │ │ ToolRegistry │ │
│ │ (多Provider)│ │ (工具注册与发现) │ │
│ └────────────┘ └──────────────────┘ │
├────────────────────────────────────────────────────────┤
│ Agent 基础设施层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Skills │ │SubAgents │ │ MCP │ │ Hooks │ │
│ │ (技能) │ │ (子代理) │ │ (协议) │ │ (钩子) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├────────────────────────────────────────────────────────┤
│ 服务层 │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────┐ │
│ │ File │ │ Git │ │Session │ │ Shell │ │Cron │ │
│ │ System │ │ Service│ │Service │ │Exec │ │Job │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ └──────┘ │
└────────────────────────────────────────────────────────┘
3.2 核心引擎架构
3.2.1 数据流:用户提示 → 工具执行
1. 用户输入
↓
2. GeminiClient.sendMessageStream()
├─ 触发 UserPromptSubmit Hook(可拦截/修改)
├─ LoopDetector.reset()(重置循环检测)
├─ 注入系统提醒(子代理/计划模式/竞技场)
└─ 返回 AsyncGenerator<ServerGeminiStreamEvent>
↓
3. Turn.run() → chat.sendMessageStream()
├─ ContentGenerator.generateContentStream()
│ └─ 按 AuthType 选择 Provider
│ ├─ Gemini
│ ├─ OpenAI
│ ├─ Anthropic
│ └─ Qwen OAuth
├─ 流式输出 Content / Thought 事件
└─ 输出 ToolCallRequest 事件
↓
4. CoreToolScheduler.schedule()
├─ ToolRegistry.getTool(name)(查找工具)
├─ tool.build(params) → ToolInvocation(参数验证)
├─ 触发 PreToolUse Hook(可拦截/修改/拒绝)
├─ PermissionManager 权限评估(allow/ask/deny)
├─ 如需确认 → 等待用户操作 → yield ToolCallConfirmation
↓
5. ToolInvocation.execute()
├─ 执行工具逻辑(文件操作/Shell/MCP等)
├─ 触发 PostToolUse Hook(成功后)
└─ 或触发 PostToolUseFailure Hook(失败后)
↓
6. Turn 继续循环
├─ 若有更多 tool calls → 回到步骤 4
└─ 若无 tool calls → 输出 Finished 事件 → 触发 Stop Hook
3.2.2 流式事件体系
ServerGeminiStreamEvent (联合类型):
├── Content // LLM 文本内容
├── Thought // LLM 思考过程
├── ToolCallRequest // 工具调用请求
├── ToolCallResponse // 工具调用响应
├── ToolCallConfirmation // 需要用户确认
├── Error // 错误事件
├── ChatCompressed // 对话压缩事件
├── Finished // 响应完成
├── LoopDetected // 检测到循环
├── SessionTokenLimitExceeded // Token 超限
└── Retry // 重试事件
3.2.3 核心组件
| 组件 | 职责 | 关键文件 |
|---|---|---|
| GeminiClient | 会话管理、turn 循环、IDE 上下文、压缩/循环检测 | core/client.ts |
| Turn | 单次 agentic loop turn,流式事件分发 | core/turn.ts |
| CoreToolScheduler | 工具调度器(验证→权限→执行→结果) | core/coreToolScheduler.ts |
| ContentGenerator | 多 Provider 抽象(Gemini/OpenAI/Anthropic/Qwen) | core/contentGenerator.ts |
| ToolRegistry | 工具注册、发现、MCP 工具管理 | tools/tool-registry.ts |
3.3 工具系统设计
3.3.1 设计模式:Builder/Invocation 分离
DeclarativeTool (工具定义)
│
├── build(params) → ToolInvocation (已验证的实例)
│ │
│ ├── getDescription() // 描述
│ ├── getDefaultPermission() // 默认权限
│ ├── getConfirmationDetails()// 确认信息
│ └── execute(signal) // 执行
│ ↓
│ ToolResult
│ ├── llmContent // LLM 历史
│ ├── returnDisplay // 用户显示
│ └── error // 错误信息
3.3.2 工具分类
| 类别 | 工具数 | 示例 |
|---|---|---|
| Read | 多个 | read-file, glob, grep |
| Edit | 多个 | edit, write-file |
| Delete | 多个 | 文件删除工具 |
| Move | 多个 | 文件移动/重命名 |
| Search | 多个 | 搜索相关工具 |
| Execute | 多个 | shell 命令执行 |
| Think | 多个 | 思考/推理工具 |
| Fetch | 多个 | web-fetch |
| Other | 多个 | 管理工具 |
共计 57 个工具文件,覆盖文件操作、Shell 执行、MCP 集成、Web 访问等。
3.3.3 工具确认类型
ToolCallConfirmationDetails (联合类型):
├── ToolEditConfirmationDetails (type: 'edit') // 编辑确认
├── ToolExecuteConfirmationDetails (type: 'exec') // 执行确认
├── ToolMcpConfirmationDetails (type: 'mcp') // MCP 确认
├── ToolInfoConfirmationDetails (type: 'info') // 信息确认
├── ToolPlanConfirmationDetails (type: 'plan') // 计划确认
└── ToolAskUserQuestionConfirmationDetails // 提问确认
确认结果枚举:
ToolConfirmationOutcome:
├── proceed_once // 仅本次允许
├── proceed_always // 本次会话始终允许
├── proceed_always_project// 项目级始终允许
├── proceed_always_user // 用户级始终允许
├── modify_with_editor // 修改后执行
├── restore_previous // 恢复上一次修改
└── cancel // 取消
3.4 Agent 系统架构
3.4.1 Agent 状态机
INITIALIZING → RUNNING → IDLE ⇄ RUNNING → ... → COMPLETED/FAILED/CANCELLED
3.4.2 Agent 三层架构
agents/
├── backends/ → 显示后端(tmux, iTerm2)
├── runtime/ → Agent 执行引擎
│ ├── AgentCore // 基础状态机和生命周期
│ ├── AgentHeadless // 无头 Agent(子代理用)
│ └── AgentInteractive // 交互式 Agent(主会话用)
└── arena/ → 多代理竞技场
├── ArenaManager // 管理多代理会话
└── ArenaAgentClient // 单个代理的客户端代理
3.4.3 Agent 配置接口
| 接口 | 作用 |
|---|---|
PromptConfig |
系统提示 / 初始消息 |
ModelConfig |
模型 ID / 温度 / top-p |
RunConfig |
max_time_minutes / max_turns |
ToolConfig |
可用工具列表 |
3.4.4 子代理系统
配置层级(5 级优先级覆盖):
session > project > user > extension > builtin
存储格式:Markdown 文件 + YAML frontmatter
关键能力:
SubagentManager提供 CRUD + 验证- 支持转换为
AgentHeadless执行 - 支持每代理独立的 ContentGenerator(可配置不同模型/Provider)
3.5 Hooks 钩子系统
3.5.1 组件架构
HookSystem
├── HookRegistry → 钩子注册表(按事件索引)
├── HookRunner → 执行引擎(进程 spawn)
├── HookAggregator → 多钩子结果聚合
├── HookPlanner → 执行计划生成
└── HookEventHandler → 事件触发入口
3.5.2 事件类型(13 种)
| 事件 | 时机 | 能力 |
|---|---|---|
PreToolUse |
工具执行前 | 可拦截/修改 |
PostToolUse |
工具执行后 | 可记录/审计 |
PostToolUseFailure |
工具失败后 | 可重试/告警 |
Notification |
通知发送时 | 可过滤/修改 |
UserPromptSubmit |
用户提交提示时 | 可拦截/修改 |
SessionStart |
会话开始 | 可初始化 |
SessionEnd |
会话结束 | 可清理资源 |
Stop |
响应完成前 | 可修改输出 |
SubagentStart |
子代理启动时 | 可干预配置 |
SubagentStop |
子代理结束时 | 可清理资源 |
PreCompact |
对话压缩前 | 可保留关键信息 |
PermissionRequest |
权限请求时 | 可自动决策 |
3.5.3 Hook 配置来源
Project > User > System > Extensions
类型化输出:
PreToolUseHookOutput→getPermissionDecision()→allow/deny/askPostToolUseHookOutput→ 默认allow(安全模型:默认放行)PermissionRequestHookOutput→ 可修改权限决策
3.6 Skills 技能系统
存储格式:目录 + SKILL.md(YAML frontmatter + markdown body)
层级(4 级优先级覆盖):
project > user > extension > bundled
核心组件:
| 组件 | 职责 |
|---|---|
SkillConfig |
技能配置(name, description, allowedTools, body) |
SkillManager |
加载/缓存/验证 + chokidar 目录监听 |
validateConfig |
YAML 解析 + 必需字段校验 |
3.7 MCP (Model Context Protocol) 集成
OAuth 认证体系:
| 组件 | 职责 |
|---|---|
MCPOAuthProvider |
OAuth 发现 + 授权流程 |
MCPOAuthTokenStorage |
Token 持久化(JSON file) |
KeychainTokenStorage |
系统钥匙链存储(macOS/Linux) |
GoogleAuthProvider |
Google 凭证认证 |
SAImpersonationProvider |
服务账号模拟 |
OAuthUtils |
PRM/RM 元数据发现 |
MCP 工具生命周期:
discoverAllMcpTools()— 连接所有 MCP 服务器并发现工具disconnectServer()/disableMcpServer()— 服务器生命周期管理
4. CLI 架构
4.1 启动流程
入口 (gemini.tsx)
↓
初始化 Config
↓
创建 GeminiClient
↓
启动 UI (React + Ink)
↓
等待用户输入
↓
调用 Core 引擎
↓
流式输出响应
↓
循环等待下一轮
4.2 UI 组件结构
ui/
├── App.tsx // 根组件
├── commands/ // 命令处理
├── components/ // UI 组件
│ ├── Header.tsx // 头部状态栏
│ ├── InputPrompt.tsx // 输入提示框
│ ├── SessionInfo.tsx // 会话信息
│ └── ...
└── hooks/ // React Hooks
技术栈:
- React:组件化 UI
- Ink:终端渲染引擎
- 环境:jsdom(测试需要)
4.3 命令系统
双层命令设计:
| 层级 | 职责 | 示例 |
|---|---|---|
| Slash Commands | 会话控制 | /help, /clear, /auth, /model |
| Agentic Tools | LLM 调用的工具 | edit, read-file, shell |
4.4 交互模式
| 模式 | 描述 | 入口 |
|---|---|---|
| Interactive | 完整 TUI,支持流式输出 | gemini.tsx |
| Headless | 无 UI,适合脚本/CI | nonInteractiveCli.ts |
| YOLO | 自动确认所有操作 | --yolo 参数 |
4.5 审批模式
enum ApprovalMode {
PLAN = 'plan', // 仅计划,不执行
DEFAULT = 'default', // 默认,逐确认
AUTO_EDIT = 'auto_edit', // 自动编辑
YOLO = 'yolo', // 全自动
}
5. 配置系统
5.1 配置层次
| 文件 | 作用域 | 描述 |
|---|---|---|
~/.qwen/settings.json |
用户级 | 全局配置,适用于所有会话 |
.qwen/settings.json |
项目级 | 仅适用于当前项目,覆盖用户级 |
5.2 核心配置字段
| 字段 | 描述 |
|---|---|
modelProviders |
定义可用模型(按协议分组:openai, anthropic, gemini) |
env |
环境变量(API keys 等,最低优先级) |
security.auth.selectedType |
启动时使用的协议 |
model.name |
默认模型 |
5.3 API Key 优先级
export (shell) > .env 文件 > settings.json → env
5.4 存储管理
Storage 类职责:
- 管理全局/项目级持久化路径(
~/.qwen/,<project>/.qwen/) - 支持
AsyncLocalStorage实现并发会话路径隔离 - 存储:会话记录、Token 缓存、用户 memory 等
6. 服务层
6.1 核心服务
| 服务 | 职责 |
|---|---|
FileDiscoveryService |
项目文件发现和索引 |
FileSystemService |
文件读写抽象(支持编码) |
GitService |
Git 操作封装 |
GitWorktreeService |
Git worktree 管理 |
SessionService |
会话持久化和恢复 |
ShellExecutionService |
Shell 命令执行(支持 Pty) |
CronScheduler |
定时任务调度 |
ChatRecordingService |
聊天录制 |
ChatCompressionService |
对话压缩 |
LoopDetectionService |
循环检测 |
7. 沙箱机制
7.1 沙箱类型
| 类型 | 描述 | 环境变量 |
|---|---|---|
| None | 无沙箱,直接执行 | QWEN_SANDBOX=false |
| Docker | Docker 容器隔离 | QWEN_SANDBOX=docker |
| Podman | Podman 容器隔离 | QWEN_SANDBOX=podman |
7.2 沙箱构建
npm run build:sandbox # 构建沙箱镜像
npm run build:all # 构建所有内容(包括沙箱)
配置:
{
"config": {
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.14.3"
}
}
8. 构建系统
8.1 构建流程
npm run build
↓
TypeScript 编译(各包 → dist/)
↓
npm run bundle
↓
esbuild 打包(dist/ → dist/cli.js)
↓
复制资源文件
8.2 构建脚本
| 脚本 | 职责 |
|---|---|
scripts/build.js |
构建所有包 |
scripts/build_sandbox.js |
构建沙箱镜像 |
scripts/build_vscode_companion.js |
构建 VS Code 插件 |
esbuild.config.js |
esbuild 打包配置 |
scripts/bundle.js |
最终打包 |
8.3 发布产物
{
"bin": {
"qwen": "dist/cli.js"
},
"files": [
"dist/",
"README.md",
"LICENSE"
]
}
9. 设计模式总结
| 模式 | 应用位置 | 说明 |
|---|---|---|
| 服务定位器 | Config 类 | 所有子系统通过 Config.getXxx() 获取 |
| Builder/Invocation 分离 | 工具系统 | DeclarativeTool.build() → ToolInvocation.execute() |
| 策略模式 | ContentGenerator | 按 AuthType 动态选择 Provider |
| 状态机 | ToolCall / AgentStatus | 工具调度 7 种状态,Agent 6 种状态 |
| 观察者 | SkillManager/SubagentManager | 文件系统变更通知 |
| 工厂方法 | createContentGenerator() | 按类型创建实例 |
| 装饰器 | LoggingContentGenerator | 包装基础 Generator 添加日志 |
| 异步迭代器 | sendMessageStream() / Turn.run() | 流式事件分发 |
| 优先级覆盖 | Skills/Subagents/Hooks | project > user > extension > bundled/builtin |
| 异步上下文 | Storage.runtimeBaseDirContext | AsyncLocalStorage 隔离并发会话 |
10. 测试架构
10.1 三层测试
| 层级 | 位置 | 文件数 | 并行度 | 重试 | 超时 |
|---|---|---|---|---|---|
| 单元测试 | packages/*/src/ |
394 | 8-16 | 否 | 默认 |
| 集成测试 | integration-tests/ |
41 | 2-4 | 2 | 5 分钟 |
| E2E 测试 | integration-tests/sdk-typescript/ |
11 | 2-4 | 2 | 3 分钟 |
10.2 测试配置
| 包 | 环境 | globals | setupFiles | coverage | reporters |
|---|---|---|---|---|---|
| cli | jsdom | ✅ | ./test-setup.ts |
v8 | default, junit |
| core | node | ❌ | ./test-setup.ts |
v8 | default, junit |
| sdk-typescript | node | ❌ | 无 | v8 (>=80%) | default |
| integration-tests | node | ❌ | globalSetup | ❌ | default |
10.3 测试组织
Colocated 模式(单元测试):
packages/cli/src/
├── ui/commands/
│ ├── helpCommand.ts
│ └── helpCommand.test.ts
独立目录模式(集成/E2E):
integration-tests/
├── cli/
├── interactive/
├── sdk-typescript/
└── fixtures/
11. CI/CD 集成
11.1 CI 脚本
| 脚本 | 职责 |
|---|---|
npm run test:ci |
CI 模式测试(更严格) |
npm run test:integration:sandbox:none |
无沙箱集成测试 |
npm run test:integration:sandbox:docker |
Docker 沙箱测试 |
npm run lint:ci |
CI 代码检查 |
npm run preflight |
全量检查 |
11.2 Preflight 检查
clean → install → format → lint → build → typecheck → test
12. 关键技术选型
12.1 为什么选择 TypeScript?
- 类型安全:严格模式,无
any - 工具链完善:ESLint + Prettier + Vitest
- Node.js 生态:丰富的库支持
12.2 为什么选择 React + Ink?
- 组件化:UI 可复用、可测试
- 终端渲染:Ink 提供类似 Web 的组件体验
- 声明式:UI 与状态解耦
12.3 为什么选择 ESM?
- 标准化:Node.js 原生支持
- Tree-shaking:更好的打包优化
- 未来兼容:模块系统趋势
12.4 为什么选择 Vitest?
- 快速:基于 esbuild,比 Jest 快
- ESM 友好:原生支持
- TypeScript:零配置
- 兼容 Jest:迁移成本低
13. 架构决策记录
13.1 Core 与 CLI 分离
决策:将核心引擎(LLM 交互、工具执行)与 UI(终端渲染)分离为两个包。
原因:
- 支持多种 UI(TUI、Headless、IDE、Web)
- Core 可独立作为 SDK 使用
- 测试隔离更好
13.2 Builder/Invocation 模式
决策:工具系统采用 Builder 验证参数,Invocation 执行逻辑的模式。
原因:
- 验证与执行分离,职责清晰
- 参数验证失败不会进入执行
- 易于测试和 mock
13.3 配置优先级覆盖
决策:Skills/Subagents/Hooks 采用多层级优先级覆盖。
原因:
- 项目级可覆盖用户级设置
- 灵活适配不同场景
- 符合开发者预期
13.4 Alias 引用策略
决策:
- 单元测试通过 alias 引用源码
- 集成测试引用编译产物
原因:
- 单元测试可测试内部逻辑
- 集成测试验证真实行为
- 避免路径泄漏到包外部
14. 总结
Qwen Code 采用分层+模块化的 Monorepo 架构:
- 三层分离:用户交互层 → 配置中枢层 → 核心引擎层 → Agent 基础设施层 → 服务层
- 职责清晰:Core 包独立引擎,CLI 包专注 UI,SDK 包对外提供 API
- 设计模式丰富:服务定位器、Builder/Invocation、策略模式、状态机等
- 测试完善:三层测试体系,Colocated 组织,CI 友好
- 配置灵活:多层级优先级覆盖,多协议认证
这种设计在保持代码质量的同时,提供了极高的扩展性和可维护性。

浙公网安备 33010602011771号