Qwen Code - 深度技术调研¶
项目概览
调研日期: 2026-02-26
仓库: https://github.com/QwenLM/qwen-code
许可证: Apache-2.0
主要语言: TypeScript
定位: 终端优先 + Skills/SubAgents,围绕 Qwen Coder 协同演进
1. 项目概述¶
Qwen Code 是阿里云通义千问团队的终端 AI coding assistant,特点:
- ✅ Skills 系统: Markdown-based 专业知识包
- ✅ SubAgents: 专门化子代理(general-purpose 等)
- ✅ IDE 深度集成: VS Code, Zed, JetBrains via MCP
- ✅ Qwen OAuth: Device flow with PKCE
- ✅ Variable 模板:
${project_name},${current_directory}等 - ✅ Extension 框架: 支持 Claude, Gemini, native Qwen extensions
2. 代码架构¶
目录结构¶
qwen-code/
├── packages/
│ ├── cli/ # CLI (React+Ink)
│ ├── core/ # Backend engine
│ │ └── src/
│ │ ├── core/ # Client orchestration
│ │ ├── tools/ # 30+ 工具
│ │ ├── subagents/ # SubAgent 系统 (1004 lines in subagent.ts)
│ │ ├── skills/ # Skills 管理 (661 lines in skill-manager.ts)
│ │ ├── qwen/ # Qwen OAuth (1017 lines in qwenOAuth2.ts)
│ │ ├── ide/ # IDE 集成 (860 lines in ide-client.ts)
│ │ └── config/ # 配置 (1735 lines)
│ ├── sdk-typescript/
│ ├── sdk-java/
│ └── vscode-ide-companion/
└── .qwen/skills/
3. 底层实现¶
3.1 Core Agent Loop (Verified)¶
文件: packages/core/src/core/client.ts (Lines 261-384)
主循环方法: sendMessageStream() - 协调整个对话流转的核心引擎
async *sendMessageStream(
request: PartListUnion,
signal: AbortSignal,
prompt_id: string,
options?: { isContinuation: boolean },
turns: number = MAX_TURNS,
): AsyncGenerator<ServerGeminiStreamEvent, Turn> {
// 1. 初始化 Loop Detector
if (!options?.isContinuation) {
this.loopDetector.reset(prompt_id);
}
// 2. 检查会话轮次限制
this.sessionTurnCount++;
if (this.sessionTurnCount > this.config.getMaxSessionTurns()) {
yield { type: GeminiEventType.MaxSessionTurns };
return turn;
}
// 3. 聊天历史压缩 (Context Management)
const compressed = await this.tryCompressChat(prompt_id, false);
// 4. 注入 IDE 上下文 (仅 IDE 模式)
if (this.config.getIdeMode()) {
const { contextParts } = this.getIdeContextParts(...);
this.getChat().addHistory({ role: 'user', parts: contextParts });
}
// 5. 动态系统提醒注入
const systemReminders = [];
if (hasTaskTool && subagents.length > 0) {
systemReminders.push(getSubagentSystemReminder(subagents));
}
if (this.config.getApprovalMode() === ApprovalMode.PLAN) {
systemReminders.push(getPlanModeSystemReminder(...));
}
// 6. 执行当前 Turn
const turn = new Turn(this.getChat(), prompt_id);
const resultStream = turn.run(this.config.getModel(), requestToSent, signal);
// 7. 流式处理响应 + Loop 检测
for await (const event of resultStream) {
if (this.loopDetector.addAndCheck(event)) {
yield { type: GeminiEventType.LoopDetected };
return turn;
}
yield event;
}
// 8. Next Speaker 检查 (自动继续机制)
if (!turn.pendingToolCalls.length && signal && !signal.aborted) {
const nextSpeakerCheck = await checkNextSpeaker(...);
if (nextSpeakerCheck?.next_speaker === 'model') {
yield* this.sendMessageStream([{ text: 'Please continue.' }], ...);
}
}
return turn;
}
关键设计: - Loop Detection: 防止重复模式导致的无限循环 - Context Compression: 自动压缩聊天历史保持上下文窗口 - Multi-turn: 支持最多 100 轮连续对话 - Next Speaker: 智能判断是否需要模型继续输出
3.2 Skills 系统 (Verified)¶
文件: packages/core/src/skills/skill-manager.ts (661 lines)
文件结构¶
- 位置:
.qwen/skills/<skill-name>/SKILL.md - 格式: Markdown + YAML Frontmatter
- 热重载: 基于
chokidar的文件系统监听
Skill 格式示例:
---
name: terminal-capture
description: Automates terminal UI screenshot testing
---
# Instructions...
三级缓存架构¶
// Lines 82-130: Skill 发现with缓存
async listSkills(options: ListSkillsOptions = {}): Promise<SkillConfig[]> {
const levelsToCheck = options.level
? [options.level]
: ['project', 'user', 'extension'];
// 优先级: project > user > extension
// project: 当前项目 .qwen/skills/
// user: ~/.qwen/skills/
// extension: VS Code 扩展自带
for (const level of levelsToCheck) {
const levelSkills = this.skillsCache?.get(level) || [];
for (const skill of levelSkills) {
if (!seenNames.has(skill.name)) {
skills.push(skill);
seenNames.add(skill.name);
}
}
}
}
热重载机制¶
// 使用 chokidar 监听文件变化
private watchSkillsDirectory(level: SkillLevel, skillsDir: string): void {
const watcher = chokidar.watch(skillsDir, {
persistent: true,
ignoreInitial: true,
depth: 2, // 只监听 SKILL.md 文件
});
watcher.on('change', async (filePath) => {
if (filePath.endsWith('SKILL.md')) {
await this.reloadSkill(filePath, level);
}
});
}
关键设计: - 三级覆盖: project 配置覆盖 user,user 覆盖 extension - 热重载: 开发时无需重启即可更新 Skill - YAML 元数据: 支持 name, description, triggers 等配置
3.3 SubAgents 实现 (Verified)¶
文件: packages/core/src/subagents/subagent.ts (1004 lines)
Context Variable Templating¶
模板函数 (lines 29-98):
function templateString(template: string, context: ContextState): string {
const placeholderRegex = /\$\{(\w+)\}/g;
return template.replace(placeholderRegex, (_match, key) =
String(context.get(key)),
);
}
export class ContextState {
private variables = new Map<string, string>();
// 内置变量
this.variables.set('project_name', this.getProjectName());
this.variables.set('current_directory', process.cwd());
this.variables.set('timestamp', new Date().toISOString());
// 模板解析: ${variable_name}
interpolate(template: string): string {
return template.replace(/\$\{(\w+)\}/g, (_, key) =
this.variables.get(key) || '';
);
}
}
SubAgent 执行循环¶
非交互模式 (runNonInteractive):
async runNonInteractive(context: ContextState): Promise<void> {
while (true) {
// 1. 检查终止条件
if (turnCounter >= max_turns) break;
if (time_exceeded) break;
// 2. 调用 LLM
const responseStream = await chat.sendMessageStream(model, messages, promptId);
// 3. 流式处理响应
for await (const streamEvent of responseStream) {
if (streamEvent.type === 'chunk') {
if (resp.functionCalls) functionCalls.push(...resp.functionCalls);
}
}
// 4. 处理工具调用或终止
if (functionCalls.length > 0) {
currentMessages = await this.processFunctionCalls(functionCalls);
} else {
// 无工具调用 - 生成最终答案
this.finalText = roundText.trim();
break;
}
}
}
SubAgentScope 主执行类¶
export class SubAgentScope {
async run(abortSignal: AbortSignal): Promise<SubAgentResult> {
// 1. 初始化 context
const contextState = new ContextState(this.config);
// 2. 创建隔离的工具注册表
const toolRegistry = this.createToolRegistry();
// 3. 应用系统提示with variable 插值
const systemPrompt = contextState.interpolate(
this.promptConfig.systemPrompt
);
// 4. 使用超时 & max turns 约束执行
for (let turn = 0; turn < maxTurns; turn++) {
const response = await this.chat.sendMessage(...);
if (this.shouldTerminate(response)) {
return { status: 'GOAL', output: response };
}
}
}
}
关键设计:
- 隔离执行: 每个 SubAgent 有自己的工具注册表
- 变量模板: ${project_name}, ${current_directory} 等上下文变量
- 流式处理: 支持 chunked response 处理
- 终止条件: max_turns + 超时双重保护
3.4 Qwen OAuth¶
文件: packages/core/src/qwen/qwenOAuth2.ts (1017 lines)
Device Flow (lines 250-350):
async requestDeviceAuthorization(options): Promise<DeviceAuthorizationResponse> {
const response = await fetch(QWEN_OAUTH_DEVICE_CODE_ENDPOINT, {
method: 'POST',
body: objectToUrlEncoded({
client_id: QWEN_OAUTH_CLIENT_ID,
scope: options.scope,
code_challenge: options.code_challenge,
code_challenge_method: options.code_challenge_method,
}),
});
return response.json();
}
Token 管理 (qwen/sharedTokenManager.ts, 27KB):
- 多进程 token 共享with file locking
- 原子 token 刷新操作
3.5 IDE 集成¶
文件: packages/core/src/ide/ide-client.ts (860 lines)
连接管理 (lines 77-206):
export class IdeClient {
async connect(): Promise<void> {
// 1. 检测 IDE (VS Code, Zed, JetBrains)
this.currentIde = detectIde(this.ideProcessInfo, ...);
// 2. 验证 workspace 路径
const { isValid, error } = IdeClient.validateWorkspacePath(...);
// 3. 尝试连接方法(优先级顺序)
// a. HTTP 连接
if (this.connectionConfig?.port) {
await this.establishHttpConnection(this.connectionConfig.port);
}
// b. Stdio 连接
if (this.connectionConfig?.stdio) {
await this.establishStdioConnection(this.connectionConfig.stdio);
}
}
}
Diff View with Mutex (lines 226-280):
async openDiff(filePath: string, newContent: string): Promise<DiffUpdateResult> {
const release = await this.acquireMutex(); // 一次只有一个 diff
try {
return await new Promise<DiffUpdateResult>((resolve) => {
this.diffResponses.set(filePath, resolve);
this.client.request({
method: 'tools/call',
params: {
name: 'openDiff',
arguments: { filePath, newContent },
},
}, CallToolResultSchema, { timeout: IDE_REQUEST_TIMEOUT_MS });
});
} finally {
release();
}
}
4. 关键源文件索引¶
| 文件路径 | 职责 | 代码行数 |
|---|---|---|
packages/core/src/core/client.ts |
主 Agent 循环 (sendMessageStream) |
678 lines |
packages/core/src/subagents/subagent.ts |
SubAgent 执行引擎 + 变量模板 | 1004 lines |
packages/core/src/subagents/subagent-manager.ts |
SubAgent CRUD 管理 | ~300 lines |
packages/core/src/skills/skill-manager.ts |
Skills 加载 + 热重载 + 三级缓存 | 661 lines |
packages/core/src/tools/task.ts |
TaskTool - 子代理委托入口 | ~200 lines |
packages/core/src/ide/ide-client.ts |
IDE MCP 集成 + Diff View | 860 lines |
packages/core/src/qwen/qwenOAuth2.ts |
Qwen OAuth Device Flow | 1017 lines |
packages/core/src/config/ |
配置管理 (1735 lines) | 多文件 |
5. 扩展开发指南¶
4.1 添加 Skills¶
# .qwen/skills/my-skill/SKILL.md
---
name: my-skill
description: Brief description with trigger keywords
---
# Instructions
4.2 创建 SubAgents¶
# .qwen/agents/my-agent.md
---
name: my-agent
description: When to use this agent
tools:
- read_file
- bash
modelConfig:
model: qwen3-coder-plus
runConfig:
max_time_minutes: 15
---
System prompt with ${variable} templating...
6. 评估¶
核心优势
- ✅ Qwen 生态集成: OAuth, API, models
- ✅ Skills + SubAgents: 强大的模块化
- ✅ IDE 集成: 深度 MCP 集成
局限性
- ⚠️ Qwen 倾向: 优化for Qwen生态
- ⚠️ 中文文档: 部分文档中文
推荐场景
- ✅ Qwen 生态集成
- ✅ 代码理解与改造
- ✅ 终端自动化
文档版本: 1.1
最后更新: 2026-02-27
更新记录¶
- v1.1: 添加
sendMessageStream()核心循环实现,SubAgentrunNonInteractive()详细逻辑,Skills 热重载机制,关键源文件索引 - v1.0: 初始版本