
qwen-code 声明式 Agent 移植Claude Code Agent 文件 Schema 兼容方案与源码实现详解【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本文基于 qwen-code 仓库的设计文档 declarative-agents-port.md完整解析如何把 Claude Code 2.1.168 的声明式 Agent 定义Markdown YAML frontmatter移植到 qwen-code 子代理体系中16 个 frontmatter 字段的反向工程结论、字段级解析与桥接规则、permissionMode → approvalMode映射、per-agentmcpServers/hooks的运行时接线以及五级存储层级的优先级解析。读完你可以直接理解 qwen-code 中.qwen/agents/*.md文件的完整字段语义、宽松解析lenient parse行为以及为什么某些字段被推迟实现。一、移植目标与实现状态该移植工作的目标对应上游 issue #4821 与 #4721是让 Claude Code 的.claude/agents/*.md声明式 Agent 文件能够原样放入 qwen-code 的.qwen/agents/并被正确解析。整个移植采用垂直切片vertical-sliced方式交付第一个 PR#4842带来了核心字段并打通端到端运行时路径permissionMode、maxTurns、color白名单第二个 PR#4870把 YAML 解析器替换为支持块标量的实现参见 yaml-parser-replacement.md后续 PR 把mcpServers与hooks表面化到SubagentConfig上并接线到运行时使 per-agent 的 MCP 服务器与 hooks 在子代理运行时真正生效。当前各字段的落地状态如下引自设计文档的 Implementation status 表字段状态说明permissionMode已交付解析期桥接到 qwen 现有的approvalModemaxTurns已交付接入现有runConfig.max_turns运行时路径color白名单已交付收紧为 Claude Code 的_Y颜色集保留auto旧哨兵mcpServers已交付后续 PR通过 eemeli/yaml序列化保证嵌套 YAML 往返安全运行时通过子代理 Config 包装器合并会话级 Agent 级服务器并强制工具注册表重建hooks已交付后续 PR临时 HookRegistry 条目在子代理派生时注册、onStop时移除v1 全局触发尚无 Agent 级作用域过滤effort推迟qwen 各 provider 尚不存在模型层effort参数memory推迟qwen 的 auto-memory 尚无user/project/local作用域区分isolation推迟运行时归 workflow 移植PR #4732所有per-agent 默认值随其落地initialPrompt推迟需要--agentCLI 标志qwen 尚无主会话 Agent 基础设施skills推迟需要 SkillManager 消费config.skills二、反向工程Claude Code 2.1.168 的 Agent Schema设计文档的 Phase 1 通过对 Claude Code 2.1.168 原生二进制的字符串提取约 342k 行做了对抗性证伪式的反向工程。关键结论是Agent frontmatter 存在两套 schema——影子 schemaIg5仅用于遥测与生产加载器DL7parseAgentFromMarkdown后者是逐字段手工校验、带自定义错误信息的宽松实现另有一套更紧的 JSON 形式 schemaJL7供--agents json与settings.agents使用。2.1 15 1 个 frontmatter 字段#字段类型必填默认枚举 / 约束1namestring非空是—缺失直接返回 null2descriptionstring非空是—Description cannot be empty3modelstring否undefinedinherit大小写不敏感归一化为字面量inherit否则 trim 后透传4toolsstring | array否undefined单 token*→ undefined表示继承全部5disallowedToolsstring | array否undefined若设置了tools则被忽略由调用方强制6effortstring | integer否undefined枚举[low,medium,high,xhigh,max]或整数别名med → medium7permissionModestring否undefined6 值枚举acceptEdits/auto/bypassPermissions/default/dontAsk/plan8mcpServersrecord否undefined每项为 string 或record(string, MCPServerSpec)逐条safeParse9hooksrecord否undefined运行时按 settings.json hooks 形状惰性校验10maxTurnsnumber | string | null否undefined正整数数字或数字字符串均可11skillsstring | array否[]会输出逗号串归一化无*通配12initialPromptstring否undefined纯空白 → undefined仅当 Agent 为主会话时自动提交13memorystring否undefined枚举[user,project,local]14backgroundstring | bool否undefined接受true/false/true/false仅真值归一化为true15isolationstring否undefined枚举仅[worktree]不含none——无隔离即省略字段16colorstring否undefined8 色枚举超出值在解析期被静默丢弃标注为internal仅 UI 展示色一个经对抗性验证的细节skills虽为可选但DL7的输出子句会在 frontmatter 省略该字段时仍然输出skills: []——这会影响下游的相等性检查是移植时必须留意的行为。2.2 解析器与热加载生命周期Claude Code 的 frontmatter 解析不依赖gray-matter/js-yaml而是手写分片器 Bun.YAML.parse分割正则为/^---\s*\n([\s\S]*?)---\s*\n?/YAML 解析失败时先做 tab→2 空格归一化重试仍失败则记 warn 日志并返回{frontmatter: {}, content: body}从不抛异常该分片器被 agents / skills / commands / output-styles 四类加载器共享schema 校验是影子模式Zod v4 的strict().safeParse()结果只用于遥测tengu_frontmatter_shadow_unknown_key/_mismatch生产路径完全以DL7的逐字段宽松校验为准。热加载方面Claude Code 使用 chokidar watcher 监听.claude/agents用户 项目两级300ms 去抖后调用clearAgentDefinitionsCache失效缓存活跃期轮询间隔 2000ms空闲60s 无交互降为 30000ms切换时重建 watcher 实例。插件来源的 Agent 不被 watcher 覆盖需靠/reload-plugins重新装配。2.3 优先级决议Claude Code 的DS()getActiveAgentsFromList按固定顺序把 6 个来源桶迭代进一个以agentType为键的MapMap.set覆盖语义使最后写入的桶获胜[built-in, plugin, userSettings, projectSettings, flagSettings, policySettings] ^ 最高优先级即 policySettings系统托管目录最高built-in 最低同名冲突被静默解决只触发tengu_plugin_name_collision遥测。一个反直觉的行为项目级目录树内层目录先 push、但 last-wins导致外层.claude/agents/压过内层——设计文档明确标记这是陷阱qwen-code 移植决定不镜像该行为。三、qwen-code 现状与架构决策3.1 移植前的基础设施qwen-code 在移植前已具备可观的子代理基础设施SubagentManager 对.qwen/agents/项目级与~/.qwen/agents/用户级中的 markdown YAML frontmatter 文件提供 CRUDSubagentConfig 已有name、description、tools、disallowedTools、approvalMode、systemPrompt、model、runConfig、color、backgroundSubagentLevel 已是五级作用域session project user extension builtinAgent 工具packages/core/src/tools/agent/agent.ts声明subagent_type参数并动态刷新 schema 枚举。3.2 关键架构决策D1–D7决策内容D1复用现有 yaml-parser.ts 处理 frontmatter——与 Claude Code 共享解析器的架构模式一致不引入gray-matter/js-yaml新依赖D2优先级沿用 qwen-code 现有五级session project user extension builtinv1 不镜像 Claude Code 的flagSettings/policySettings桶企业管理目录是 qwen 没有的场景D3扩展现有SubagentValidator手工校验不引入 zod——与 Claude Code 影子 schema 手工校验的形态对齐保持错误信息可读D4v1 不交付 chokidar 热加载依赖冷加载 显式失效changeListenerD5交付--agent nameCLI 标志不采用 Claude Code 的CLAUDE_CODE_AGENT环境变量间接层Config对象直接承载D6对 workflow 移植暴露稳定的 resolver 接口契约frontmattername即 workflow 的agentType字符串键相等、大小写敏感workflow 硬编码的disallowedTools地板[SEND_MESSAGE, EXIT_PLAN_MODE]与 agent 级disallowedTools取并集地板恒生效D7permissionMode与approvalMode双收桥接解析期把permissionMode映射到approvalMode两者同时存在时approvalMode获胜对 qwen 更具体并发出双写遥测D6 同时解决了 #4821设置了tools则忽略disallowedTools与 #4721与 workflow 地板取并集的表面矛盾注册表是笨数据载体两个字段始终独立携带优先级规则放在派发点Agent 工具 / workflow而非解析期。3.3 字段映射表Claude Code 2.1.168 → qwen-codeClaude Code 字段qwen-code 字段适配name/description同名完全一致必填modelmodel接受inherit、fast、具体 model-id、authType:model-idtoolstoolsstring | array逗号串切分*→ undefined继承全部disallowedToolsdisallowedToolsstring | array与tools独立携带permissionModepermissionMode 桥接approvalMode6 值枚举映射表见下节maxTurnsmaxTurns新顶层字段正整数兼容数字字符串由runConfig.max_turns提升而来旧嵌套形式保留为弃用别名mcpServersmcpServersrecord-of-records 浅校验逐 spec 深校验推迟到运行时 MCP loaderhookshooksrecord-of-arrays 浅校验逐 matcher 校验由SessionHooksManager负责backgroundbackground接受 bool 或true/false字符串仅真值 →truecolor未文档化 #16color8 色白名单 qwen 旧auto哨兵超界值静默丢弃isolation推迟枚举仅[worktree]运行时归 workflow PReffort/memory/skills/initialPrompt推迟见第一节状态表四、源码实现字段解析与桥接4.1 枚举常量单一事实源设计文档要求的新模块 agent-frontmatter-schema.ts 是枚举常量的单一事实源逐字镜像 Claude Code 2.1.168PERMISSION_MODE_VALUESacceptEdits、auto、bypassPermissions、default、dontAsk、planCOLOR_VALUESred、blue、green、yellow、purple、orange、pink、cyanparseMaxTurns接受正整数 number 或数字字符串其余一律返回 undefined对应 CC 的W46。permissionMode → approvalMode映射表claudePermissionModeToApprovalMode值得细看permissionModeapprovalMode语义说明defaultdefault工具调用需确认planplan计划模式acceptEditsauto-edit自动批准编辑autoauto-edit同上bypassPermissionsyolo全部自动批准dontAskdefaultClaude 侧dontAsk语义偏限制会弹窗的调用一律拒绝因此映射到同样需要确认的default而非自动批准的auto-edit保留限制意图实现上特意用Map而非普通Record防止调用方传入__proto__/constructor沿原型链取到非字符串值——这是从源码结构中可见的防御性细节。4.2 宽松解析lenient parse的逐字段行为核心解析逻辑在 parseSubagentContent。frontmatter 分割用正则/^---\n([\s\S]*?)\n---\n([\s\S]*)$/随后逐字段处理permissionMode仅当值命中 6 值枚举才保留否则 warn 并丢弃Agent file path has invalid permissionMode x. Dropping field.仅当approvalMode未设置时才桥接即effectiveApprovalMode approvalMode ?? bridgedApprovalMode——与 D7 双写时 qwen 字段赢的决策完全一致maxTurns走parseMaxTurns非法值 warn 并丢弃color白名单内或遗留auto哨兵保留其余静默丢弃并记 warnbackgroundtrue/true→truefalse/false→false其余 warn 并置 undefinedmcpServersparseAgentMcpServers 做 record-of-records 浅校验——非对象整体丢弃、值为标量/数组/null 的条目逐条丢弃并返回空原型对象Object.create(null)避免 YAML 键字面量__proto__触发原型链污染hooksparseAgentHooks 仅保留值为数组的条目逐 matcher 的深校验推迟到SessionHooksManager。这套丢弃而非抛错drop-the-whole-field姿态与 Claude CodeDL7的宽松语义一致一个写坏的mcpServers块不会杀死整个 Agent 定义。源码注释明确说明这与 qwen 早期字段如approvalMode非法值直接抛错拒绝加载的严格姿态有意区分以保护存量.qwen/agents/*.md文件。值得注意的是qwen-code 在此镜像 schema 之外增加了一个executor扩展字段SubagentExecutorSpec声明kind: acp | codex、command、args?让子代理的 turn 交给外部 Agent 进程执行。它与mcpServers/hooks的宽松姿态刻意相反——解析失败时拒绝加载整个定义named executor refusal因为丢弃该字段会让任务静默地以 Qwen 进程内模型代跑形成静默替换。这一 fail-closed 设计在 subagent-manager.ts 中有大量防御注释包括用 YAML 真实 ASTparseDocument而非宽松行式解析探测executor:声称避免块标量中的散文误判。4.3 校验器SubagentValidator 保留了手工校验路线D3 决策的落实名字校验validateName2–50 字符、仅字母数字连字符下划线、不能以-/_开头结尾且拒绝保留名self/system/user/model/tool/config/default/mainmain是/stats归因管线的哨兵子代理占用会静默并入主会话桶description必填非空超过 1000 字符仅告警systemPrompt至少 10 字符超过 10000 字符告警model经resolveModelId解析校验显式写inherit会收到省略即可的提示runConfig.max_turns必须是正整数超过 100 告警max_time_minutes超过 60 告警。五、运行时接线per-agent MCP 服务器与 hooks设计文档声明mcpServers与hooks已从仅携带元数据升级为真正生效。源码中的落地路径5.1mcpServersConfig 包装器 强制注册表重建在 buildSubagentContextOverride 中用deriveConfig派生一个薄的 Config 包装器作为子代理运行时上下文顺带让子代理获得独立的FileReadCache避免继承父进程的已读记录而削弱先读后写约束若 frontmatter 声明了mcpServers则sessionServers与config.mcpServers按键合并——同名键时 Agent 级覆盖会话级more-specific-wins对齐 Claude Code 的scope: agent语义只要存在 per-agent 服务器就绕过hasRebuiltToolRegistry跳过优化强制rebuildToolRegistryOnOverride——否则父级注册表缓存的McpClientManager永远看不到合并后的服务器集合per-agent 发现会静默空转新注册表以skipDiscovery: true构造后从父级回填工具因此 per-agent 服务器需要显式discoverToolsForServer且用Promise.allSettled并行发现——单个挂死的 stdio 服务器不会串行拖慢整个子代理派生单个失败仅记 warn 不阻塞其他服务器返回的cleanup回调持有新注册表其 stdio 子进程/ socket 是父级Config.shutdown够不到的由createAgentHeadless的dispose闭包在子代理终止时执行。5.2hooks临时注册 作用域标注 信任门禁在 createAgentHeadless 中子代理派生时以agent:name:uuid作为作用域调用hookRegistry.addAgentHooks注册 frontmatter hooks返回的 unregister 回调挂到dispose上构造失败路径也会执行防泄漏项目级 Agent 的 hooks 受信任门禁非受信目录untrusted folder中的项目 Agent 可以只读加载但其 hooks 属于仓库提供的代码执行与Config.getProjectHooks()对 settings 文件 hooks 的门禁一致直接忽略并 warn宿主没有 HookSystem 时也仅 warn 忽略不抛错v1 局限与设计文档一致条目驻留注册表期间对同类型的所有事件全局触发尚无 per-agent 作用域过滤。5.3 模型路由model选择器经resolveModelId/buildRuntimeContentGeneratorView解析当选择器指向不同 provider 或调用方要求 per-agent 推理强度时构建专属 ContentGenerator 视图经 AsyncLocalStorage 在运行期生效不影响父进程——这与 SubagentConfig.model 文档的inherit/fast/model-id/authType:model-id四种选择器语义对应。六、存储层级与优先级解析qwen-code 沿用了 D2 决策的五级结构loadSubagent的解析顺序subagent-manager.ts为session → project → user → extension → builtin逐级回退每级之间还插入throwRecordedExecutorRefusal检查——若某级存在一个声明了executor但加载失败的同名文件按名派生直接抛错拒绝而不是回退到低优先级的进程内定义。这正是 4.2 节所述静默替换防护的落点。项目级目录内嵌套目录的冲突qwen-code 明确选择innermost-wins最内层获胜不镜像 Claude Code 意外形成的 outer-wins 行为设计文档 Q5/R6 的显式决策。Agent 工具侧subagent_type参数agent.ts省略时回退到 general-purpose Agentbuiltin-agents.ts 提供内建 Agent如Explore且其模型可被agents.builtin.exploreModel设置覆盖见 applyBuiltinSettings。七、测试与验证路径设计文档给出了 TDD 计划仓库中对应落地的测试文件测试文件覆盖agent-frontmatter-schema.test.ts枚举常量快照与 Claude Code 2.1.168 逐字节对齐、parseMaxTurns/parseAgentMcpServers/parseAgentHooks/parseAgentExecutor、getAgentByName风格的 resolver 导出供 workflow 消费subagent-manager.test.ts全字段往返解析、必填字段缺失、非法枚举值的 warn 丢弃、宽松字段类型background: true、maxTurns: 5、color 白名单、优先级决议validation.test.ts校验器错误与告警路径subagent-manager-override.test.tsper-agent MCP 覆盖与注册表重建packages/core/src/tools/agent/agent.test.tssubagent_type解析与运行时字段管道八、风险与推迟字段的取舍设计文档 Phase 4 列出的风险中与实现直接相关的取舍R2maxTurns提升是破坏性变更旧嵌套形式runConfig.max_turns保留为弃用别名两者同设时顶层maxTurns获胜见 SubagentConfig.maxTurns 注释解析时发 warn记入 CHANGELOGR4携带但无运行时效果的字段effort、memory、skills、initialPrompt在 v1 只做携带设计上要求文档明确 v1 边界避免用户设置后静默无效R7color是 Claude 的internal字段照搬但同样标记 internal不作为用户面向文档内容仅 UI 展示用。推迟理由均可从源码结构印证effort需要 provider 层的推理强度参数qwen 已有reasoningEffort概念见 buildRuntimeContentGeneratorView 中的modelConfig.reasoningEffort但 frontmatter 尚未接通该旋钮isolation的 worktree 运行时归 workflow 移植所有initialPrompt依赖尚不存在的--agent主会话选择器。九、小结qwen-code 的声明式 Agent 移植是一次schema 逐字镜像 运行时按需接线的工程frontmatter 的 16 字段语义、枚举常量、宽松解析姿态与 Claude Code 2.1.168 保持逐字节对齐使.claude/agents/*.md可以原样放入.qwen/agents/permissionMode → approvalMode桥接、mcpServers的合并 强制注册表重建、hooks的临时注册 信任门禁则把仅携带的字段推进为真正生效的运行时能力而 qwen-code 独有的executor扩展用 fail-closed 拒绝加载替代宽松丢弃划定了镜像与扩展之间的边界。对维护者而言枚举事实源在 agent-frontmatter-schema.ts、解析行为在 parseSubagentContent、运行时接线在 buildSubagentContextOverride三处即可覆盖该特性的全部实现面。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考