












Qwen Code 是一个开源的终端 AI Agent 工具,优化用于 Qwen 系列大模型。它帮助用户理解大型代码库、自动化重复工作、加速开发流程。
| 类别 | 技术选型 |
|---|---|
| 语言 | TypeScript (严格模式) |
| 运行时 | Node.js >= 20 |
| 模块系统 | ESM |
| UI 框架 | React + Ink (终端渲染) |
| 测试框架 | Vitest |
| 打包工具 | esbuild |
| 包管理 | npm workspaces (monorepo) |
| 代码规范 | ESLint + Prettier |
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/ # 架构设计文档(本目录)
┌─────────────────────────────────────────────────┐
│ 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| 包 | 职责 | 测试文件数 | 覆盖率要求 |
|---|---|---|---|
| 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 | ❌ |
┌────────────────────────────────────────────────────────┐
│ 用户交互层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 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 │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ └──────┘ │
└────────────────────────────────────────────────────────┘
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
ServerGeminiStreamEvent (联合类型):
├── Content // LLM 文本内容
├── Thought // LLM 思考过程
├── ToolCallRequest // 工具调用请求
├── ToolCallResponse // 工具调用响应
├── ToolCallConfirmation // 需要用户确认
├── Error // 错误事件
├── ChatCompressed // 对话压缩事件
├── Finished // 响应完成
├── LoopDetected // 检测到循环
├── SessionTokenLimitExceeded // Token 超限
└── Retry // 重试事件
| 组件 | 职责 | 关键文件 |
|---|---|---|
| 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 |
DeclarativeTool (工具定义)
│
├── build(params) → ToolInvocation (已验证的实例)
│ │
│ ├── getDescription() // 描述
│ ├── getDefaultPermission() // 默认权限
│ ├── getConfirmationDetails()// 确认信息
│ └── execute(signal) // 执行
│ ↓
│ ToolResult
│ ├── llmContent // LLM 历史
│ ├── returnDisplay // 用户显示
│ └── error // 错误信息
| 类别 | 工具数 | 示例 |
|---|---|---|
| Read | 多个 | read-file, glob, grep |
| Edit | 多个 | edit, write-file |
| Delete | 多个 | 文件删除工具 |
| Move | 多个 | 文件移动/重命名 |
| Search | 多个 | 搜索相关工具 |
| Execute | 多个 | shell 命令执行 |
| Think | 多个 | 思考/推理工具 |
| Fetch | 多个 | web-fetch |
| Other | 多个 | 管理工具 |
共计 57 个工具文件,覆盖文件操作、Shell 执行、MCP 集成、Web 访问等。
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 // 取消
INITIALIZING → RUNNING → IDLE ⇄ RUNNING → ... → COMPLETED/FAILED/CANCELLED
agents/
├── backends/ → 显示后端(tmux, iTerm2)
├── runtime/ → Agent 执行引擎
│ ├── AgentCore // 基础状态机和生命周期
│ ├── AgentHeadless // 无头 Agent(子代理用)
│ └── AgentInteractive // 交互式 Agent(主会话用)
└── arena/ → 多代理竞技场
├── ArenaManager // 管理多代理会话
└── ArenaAgentClient // 单个代理的客户端代理
| 接口 | 作用 |
|---|---|
PromptConfig |
系统提示 / 初始消息 |
ModelConfig |
模型 ID / 温度 / top-p |
RunConfig |
max_time_minutes / max_turns |
ToolConfig |
可用工具列表 |
配置层级(5 级优先级覆盖):
session > project > user > extension > builtin
存储格式:Markdown 文件 + YAML frontmatter
关键能力:
SubagentManager 提供 CRUD + 验证AgentHeadless 执行HookSystem
├── HookRegistry → 钩子注册表(按事件索引)
├── HookRunner → 执行引擎(进程 spawn)
├── HookAggregator → 多钩子结果聚合
├── HookPlanner → 执行计划生成
└── HookEventHandler → 事件触发入口
| 事件 | 时机 | 能力 |
|---|---|---|
PreToolUse |
工具执行前 | 可拦截/修改 |
PostToolUse |
工具执行后 | 可记录/审计 |
PostToolUseFailure |
工具失败后 | 可重试/告警 |
Notification |
通知发送时 | 可过滤/修改 |
UserPromptSubmit |
用户提交提示时 | 可拦截/修改 |
SessionStart |
会话开始 | 可初始化 |
SessionEnd |
会话结束 | 可清理资源 |
Stop |
响应完成前 | 可修改输出 |
SubagentStart |
子代理启动时 | 可干预配置 |
SubagentStop |
子代理结束时 | 可清理资源 |
PreCompact |
对话压缩前 | 可保留关键信息 |
PermissionRequest |
权限请求时 | 可自动决策 |
Project > User > System > Extensions
类型化输出:
PreToolUseHookOutput → getPermissionDecision() → allow/deny/askPostToolUseHookOutput → 默认 allow(安全模型:默认放行)PermissionRequestHookOutput → 可修改权限决策存储格式:目录 + SKILL.md(YAML frontmatter + markdown body)
层级(4 级优先级覆盖):
project > user > extension > bundled
核心组件:
| 组件 | 职责 |
|---|---|
SkillConfig |
技能配置(name, description, allowedTools, body) |
SkillManager |
加载/缓存/验证 + chokidar 目录监听 |
validateConfig |
YAML 解析 + 必需字段校验 |
OAuth 认证体系:
| 组件 | 职责 |
|---|---|
MCPOAuthProvider |
OAuth 发现 + 授权流程 |
MCPOAuthTokenStorage |
Token 持久化(JSON file) |
KeychainTokenStorage |
系统钥匙链存储(macOS/Linux) |
GoogleAuthProvider |
Google 凭证认证 |
SAImpersonationProvider |
服务账号模拟 |
OAuthUtils |
PRM/RM 元数据发现 |
MCP 工具生命周期:
discoverAllMcpTools() — 连接所有 MCP 服务器并发现工具disconnectServer() / disableMcpServer() — 服务器生命周期管理入口 (gemini.tsx)
↓
初始化 Config
↓
创建 GeminiClient
↓
启动 UI (React + Ink)
↓
等待用户输入
↓
调用 Core 引擎
↓
流式输出响应
↓
循环等待下一轮
ui/
├── App.tsx // 根组件
├── commands/ // 命令处理
├── components/ // UI 组件
│ ├── Header.tsx // 头部状态栏
│ ├── InputPrompt.tsx // 输入提示框
│ ├── SessionInfo.tsx // 会话信息
│ └── ...
└── hooks/ // React Hooks
技术栈:
双层命令设计:
| 层级 | 职责 | 示例 |
|---|---|---|
| Slash Commands | 会话控制 | /help, /clear, /auth, /model |
| Agentic Tools | LLM 调用的工具 | edit, read-file, shell |
| 模式 | 描述 | 入口 |
|---|---|---|
| Interactive | 完整 TUI,支持流式输出 | gemini.tsx |
| Headless | 无 UI,适合脚本/CI | nonInteractiveCli.ts |
| YOLO | 自动确认所有操作 | --yolo 参数 |
enum ApprovalMode {
PLAN = 'plan', // 仅计划,不执行
DEFAULT = 'default', // 默认,逐确认
AUTO_EDIT = 'auto_edit', // 自动编辑
YOLO = 'yolo', // 全自动
}
| 文件 | 作用域 | 描述 |
|---|---|---|
~/.qwen/settings.json |
用户级 | 全局配置,适用于所有会话 |
.qwen/settings.json |
项目级 | 仅适用于当前项目,覆盖用户级 |
| 字段 | 描述 |
|---|---|
modelProviders |
定义可用模型(按协议分组:openai, anthropic, gemini) |
env |
环境变量(API keys 等,最低优先级) |
security.auth.selectedType |
启动时使用的协议 |
model.name |
默认模型 |
export (shell) > .env 文件 > settings.json → env
Storage 类职责:
~/.qwen/, <project>/.qwen/)AsyncLocalStorage 实现并发会话路径隔离| 服务 | 职责 |
|---|---|
FileDiscoveryService |
项目文件发现和索引 |
FileSystemService |
文件读写抽象(支持编码) |
GitService |
Git 操作封装 |
GitWorktreeService |
Git worktree 管理 |
SessionService |
会话持久化和恢复 |
ShellExecutionService |
Shell 命令执行(支持 Pty) |
CronScheduler |
定时任务调度 |
ChatRecordingService |
聊天录制 |
ChatCompressionService |
对话压缩 |
LoopDetectionService |
循环检测 |
| 类型 | 描述 | 环境变量 |
|---|---|---|
| None | 无沙箱,直接执行 | QWEN_SANDBOX=false |
| Docker | Docker 容器隔离 | QWEN_SANDBOX=docker |
| Podman | Podman 容器隔离 | QWEN_SANDBOX=podman |
npm run build:sandbox # 构建沙箱镜像
npm run build:all # 构建所有内容(包括沙箱)
配置:
{
"config": {
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.14.3"
}
}
npm run build
↓
TypeScript 编译(各包 → dist/)
↓
npm run bundle
↓
esbuild 打包(dist/ → dist/cli.js)
↓
复制资源文件
| 脚本 | 职责 |
|---|---|
scripts/build.js |
构建所有包 |
scripts/build_sandbox.js |
构建沙箱镜像 |
scripts/build_vscode_companion.js |
构建 VS Code 插件 |
esbuild.config.js |
esbuild 打包配置 |
scripts/bundle.js |
最终打包 |
{
"bin": {
"qwen": "dist/cli.js"
},
"files": [
"dist/",
"README.md",
"LICENSE"
]
}
| 模式 | 应用位置 | 说明 |
|---|---|---|
| 服务定位器 | 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 隔离并发会话 |
| 层级 | 位置 | 文件数 | 并行度 | 重试 | 超时 |
|---|---|---|---|---|---|
| 单元测试 | packages/*/src/ |
394 | 8-16 | 否 | 默认 |
| 集成测试 | integration-tests/ |
41 | 2-4 | 2 | 5 分钟 |
| E2E 测试 | integration-tests/sdk-typescript/ |
11 | 2-4 | 2 | 3 分钟 |
| 包 | 环境 | 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 |
Colocated 模式(单元测试):
packages/cli/src/
├── ui/commands/
│ ├── helpCommand.ts
│ └── helpCommand.test.ts
独立目录模式(集成/E2E):
integration-tests/
├── cli/
├── interactive/
├── sdk-typescript/
└── fixtures/
| 脚本 | 职责 |
|---|---|
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 |
全量检查 |
clean → install → format → lint → build → typecheck → test
any决策:将核心引擎(LLM 交互、工具执行)与 UI(终端渲染)分离为两个包。
原因:
决策:工具系统采用 Builder 验证参数,Invocation 执行逻辑的模式。
原因:
决策:Skills/Subagents/Hooks 采用多层级优先级覆盖。
原因:
决策:
原因:
Qwen Code 采用分层+模块化的 Monorepo 架构:
这种设计在保持代码质量的同时,提供了极高的扩展性和可维护性。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。