# PR #860 Multi IM 优化:合并后完整代码分析与跨场景复用报告 > PR #860:[DingTalk-Real-AI/dingtalk-workspace-cli#860](https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pull/860) > > #860 当前标题:`feat(im): harden Multi IM and publish complete Chat Schema` > > #860 当前状态:Open、可合并;核对时共 14 个提交、129 个变更文件,+69,391 / -13,045。 > > 唯一分析范围:`main@6376f294dab0d0a7bd2f5904077aab89383684d1...codex/multi-im-optimization@33ceab6000d5f18eddbc1c0c97544233f5dd42d6`。 > > 报告原则:只分析当前 PR #860 的合并后整体代码。提交来源和中间分支不再作为独立分析单元;所有规模、架构、文件、风险和复用结论均以当前 #860 的完整 diff 为准。 ## 1. 核心优化思路 ### 1.1 一句话结论 PR #860 的本质不是增加一批 IM 命令,而是把 Chat/Event 能力从“分散、可调用”升级为“意图可路由、目标可确定、执行有边界、输出有契约、风险可确认、能力可发现、文档与代码可持续校验”的完整产品链路。 整体优化链路可以概括为: ```text 用户意图 -> Golden Route 唯一入口 -> Typed Resolver 确定目标 -> Identity / Capability Preflight -> Bounded Runtime + Typed Confirmation -> Versioned Result / Error Ledger -> Authored Registry + Selection/Metadata -> Generated Schema + Skill/Reference -> Policy + Drift + Coverage Gates ``` ### 1.2 核心方法总览 | 原有问题 | 核心优化方法 | PR #860 整体落地 | 可复用价值 | |---|---|---|---| | 同一意图有多条相似入口 | **Golden Route**:每个意图只推荐一个默认入口 | 统一发送、回复、读取、搜索、建群和监听入口,原始命令只作有理由的 fallback | 通讯录、文档等领域都可先按意图收敛入口 | | 名称、ID、URL 等目标解析分散 | **Typed Resolver**:业务执行前先得到规范化目标 | 新增 `targetresolver`,全分页检索、精确匹配、歧义和不完整结果均 fail closed | 可扩展为 `DirectoryResolver`、`DocumentTargetResolver` | | 调用前才发现参数、身份或能力不支持 | **Local Validation + Capability Check** | 建立 user/bot/webhook 能力矩阵,并前置校验时间、页大小、回溯窗口和目标互斥 | 适用于邀请、删除、文档覆盖、权限修改等写操作 | | 输出依赖原始 API 或展示文本 | **Versioned Contract**:输出稳定的机器契约 | 建立并发布 `im.message-list.v1`,统一消息、引用、资源、reaction 和 continuation | 可定义联系人、文档、云盘的 v1 投影 | | 分页、导出、下载可能截断或留下半成品 | **Bounded Execution**:限制页数、条数、超时并支持原子提交 | 实现游标停滞检测、完整性账本、安全下载、no-clobber 和跨平台原子替换 | 适用于批量导出、备份、迁移和同步 | | 高风险命令示例可能绕过确认 | **Typed Confirmation**:Runtime 强制停下,确认后才执行 | 写入/破坏型 Schema metadata 与 Runtime gate 对齐,危险示例不预填 `--yes` | 可用于删除联系人、覆盖文档、权限变更等高风险动作 | | 能力可运行但 Agent 不可发现 | **Authored Registry + Generated Schema** | Chat Shortcut 达到 97/97;完整 Catalog 为 26 products / 898 tools | 适用于任何具有大量组合命令的产品域 | | 文档、Schema、Runtime 各自演化 | **Consistency Gates**:把一致性变成 CI 规则 | 增加 Skill Chain、Schema compatibility、interface integrity、generated drift 和 changed-code coverage | 可抽象为所有领域共用的质量框架 | ### 1.3 八个可复用的优化原则 #### 原则一:从“API 优先”转为“意图优先” 先定义用户究竟要完成什么,再为每个意图选择唯一 Golden Route。底层 API、历史命令和兼容别名可以保留,但不应继续出现在默认发现路径中。这样能够显著减少 Agent 在多个相似工具之间随机选择的问题。 #### 原则二:把目标解析变成独立基础设施 所有操作先经过统一 Resolver,将名称、ID、手机号、URL 等输入转换为类型明确的 canonical target。业务命令只消费解析结果,不再各自实现搜索、歧义处理或 ID 猜测。 #### 原则三:把写操作拆成“计划”和“执行” 执行前明确目标、身份、能力、副作用和恢复方式。高风险操作只有在目标唯一且能力满足时才进入执行阶段。这种拆分既降低误操作风险,也让 dry-run、确认和审计更容易实现。 #### 原则四:用版本化域模型隔离上游变化 列表、搜索、读取和写回执都投影到稳定的领域契约。调用方依赖 `messageId`、`canonicalId`、`nextCursor` 等规范字段,而不是供应商原始响应或终端展示文字。 #### 原则五:所有长流程都必须有边界和恢复语义 分页、导出、下载、批量发送不能只保证 happy path;还要定义最大页数、最大条数、游标推进、临时文件、原子替换、checkpoint、部分失败和幂等重试。 #### 原则六:让错误直接驱动下一步动作 错误需要区分未命中、歧义、身份不匹配、能力不足、权限不足、限流和服务端故障,并明确是否可重试以及建议动作。对 Agent 而言,错误是控制流的一部分,而非日志文本。 #### 原则七:把知识一致性纳入工程门禁 Skill 负责路由,Reference 负责细节,Schema 负责机器发现,Runtime 负责真实行为,Policy 负责检查四者闭环。任何新增能力都应同步更新并由测试验证。 #### 原则八:把“正式发布”定义为独立交付阶段 Runtime 中存在一个命令,不等于 Agent 已经能够正确使用它。能力还必须进入 authored Registry,具备 use/avoid hints、风险和确认 metadata,生成 Schema/Agent metadata,并退出临时 exclusion。PR #860 将这一原则具体化为 Chat 97/97、public Shortcut 265/265 和完整 Catalog 898 tools。 ### 1.4 标准执行链路 ```text 用户自然语言意图 | v Golden Route:选择唯一推荐入口 | v Typed Resolver:解析用户、群、会话、消息等目标 | v Preflight Plan:固化 profile、身份、能力和副作用 | v Bounded Runtime:在分页、超时、数量和文件边界内执行 | v Versioned Result / Typed Error Ledger:返回可消费、可恢复结果 | v Authored Registry + Selection/Metadata:正式发布并指导选择 | v Generated Schema + Skill/Reference + Policy:持续防止契约漂移 ``` ### 1.5 复用时应迁移整条链路 不建议只复制某个 shortcut 或 Resolver。若新领域仍缺少稳定输出、错误分类和一致性门禁,局部复用很快会重新产生分散实现。通讯录、文档、云盘、知识库等场景应至少成组迁移以下能力: ```text 意图清单 + Golden Route -> 统一 Resolver -> Plan / Capability Check -> 有界 Runtime -> 版本化结果与结构化错误 -> 分层文档、Schema 与 Policy ``` ## 2. 分析范围与阅读导航 ### 2.1 变更规模与边界 本报告只使用一个三点对比范围:`6376f294...33ceab60`。它表示 PR #860 当前 head 相对当前 PR base 的完整净变化: | 指标 | 当前 PR #860 | |---|---:| | 状态 | Open / mergeable | | 提交数 | 14 | | 变更文件 | 129 | | 新增 / 删除 | +69,391 / -13,045 | | 生产 Go 文件 | 38(+4,028 / -737) | | Go 测试文件 | 30(+4,343 / -88) | | Skill Markdown | 30(+1,166 / -1,577) | | 主要生成 Schema 文件 | 6(+57,215 / -9,229) | #860 变更横跨: - Runtime:`internal/app`、`internal/shortcut`、`internal/helpers`、`internal/errors`。 - 目标解析:新增 `internal/shortcut/targetresolver`。 - Schema:chat/event 的 catalog、agent metadata、hints、registry。 - Skill:mono、chat、event、shared 四类文档链路。 - Policy:新增 Multi IM Skill Chain 检查并扩展上下文预算检查。 - 测试:单元测试、契约测试、mock MCP smoke test、live audit 辅助脚本。 这种范围说明优化目标不是“让某几个命令能跑”,而是建立可持续扩展的产品契约。 69,391 行新增中,大量内容来自 Schema catalog 和 Agent metadata 等生成物,不能把总行数等同于手写业务逻辑。人工设计变更主要集中在: - Chat Shortcut 的 authored CommandRegistry、selection hints 和风险 metadata; - 本地参数校验、确认门禁、cursor 修复、shorthand 和跨平台路径; - Schema 完整性、目标消歧、分页、原子文件及跨平台 changed-code 覆盖测试; - Chat、Event、Shared 和 Mono 四层文档链路重构。 ### 2.2 文档阅读导航 | 阅读目标 | 建议章节 | |---|---| | 快速掌握本次优化的方法论 | 第 1 章“核心优化思路” | | 理解为什么需要改造 | 第 3 章“优化前的典型问题” | | 理解 Skill、Runtime、Schema、Policy 如何协同 | 第 4 章“总体架构” | | 查看具体代码机制 | 第 5 章“核心代码优化分析” | | 查看文档组织变化 | 第 6~8 章 | | 查看验证方法与质量门禁 | 第 9 章 | | 复用到通讯录、文档、云盘和知识库 | 第 10~12 章 | | 查看当前风险收口和整体结论 | 第 13~14 章 | | 查看 Schema 发布、Runtime 收口和文件分类 | 第 15~18 章 | | 查看 Go 生产代码和测试代码的专项总结 | 第 19 章 | | 查看当前 #860 的整体价值与建议 | 第 20~21 章 | ### 2.3 结论类型说明 为避免把“PR 已实现内容”和“建议复用方案”混在一起,后文按以下方式理解: - **本次落地**:能够在该 PR 的代码、Schema 或文档变更中找到直接依据。 - **分析判断**:根据多处变更归纳出的设计意图或工程影响。 - **复用建议**:面向通讯录、文档等新领域给出的迁移设计,并非声称已在本 PR 中实现。 ## 3. 优化前的典型问题 ### 3.1 同一意图存在多条入口 发送消息、查看群消息、回复消息等意图可能分别出现在 shortcut、底层 OpenAPI 命令、脚本和多份教程中。模型或用户容易选中功能相似但约束不同的路径,导致参数漂移、行为差异和排障困难。 ### 3.2 目标解析散落在业务命令中 用户可能输入 conversation ID、openConversationId、群名、用户 ID、手机号或自然语言名称。若每条命令各自解析,就会出现: - 同一个名称在不同命令中命中不同对象; - 多结果时有的命令报错、有的静默选第一个; - ID 类型混用; - profile、tenant 或身份上下文不一致; - 新增一种目标类型时需要修改大量命令。 ### 3.3 列表输出面向人而非机器 原始 API 数据结构大而不稳定,字段含义不统一,分页元信息可能丢失。Agent 需要猜测正文、发送者、消息 ID 和时间字段,后续回复、导出和审计难以可靠衔接。 ### 3.4 长流程缺少完整性边界 消息列表、成员列表、导出、下载等操作天然包含分页或文件写入。如果没有最大页数、最大条数、下一页游标、临时文件和原子替换机制,容易发生无限翻页、半文件、重复结果或悄然截断。 ### 3.5 文档与 Runtime 独立演化 命令已经变更但文档仍指向旧路径,或者 Skill 声称支持某个字段而 Runtime 没有对应输出。问题往往到真实 Agent 调用时才暴露。 ## 4. 总体架构:四层协同 ### 4.1 Skill 层:告诉 Agent 应该走哪条路 Skill 的职责不是罗列所有命令,而是完成意图路由、默认值选择、风险提示和按需加载 Reference。它应该短、稳定、偏决策,不应复制完整 API 手册。 ### 4.2 Runtime 层:保证行为确定且可测试 Runtime 负责统一解析目标、执行能力校验、分页、结果投影、原子文件操作和结构化错误。即使调用者没有阅读文档,Runtime 也应保持安全边界。 ### 4.3 Schema 层:提供机器可发现的真实能力 Schema catalog、agent metadata、selection hints 和 command registry 必须共同描述参数、枚举、返回值和入口选择。Schema 不是生成物附件,而是 Agent 运行时能力的一部分。 ### 4.4 Policy 层:防止三层再次漂移 Policy 检查关键意图是否同时存在合法 Runtime 路由、Schema 暴露和文档说明,并控制 Skill 的上下文体积。它把系统设计转化为可持续执行的合并门槛。 ## 5. 核心代码优化分析 本章按照“目标识别—路由决策—读取契约—长流程执行—错误与兼容治理”的顺序展开。各项机制之间不是孤立关系:Resolver 提供可靠目标,身份/能力矩阵选择执行路径,版本化投影稳定结果,分页与原子写保证完整性,错误和 profile 契约负责失败恢复与上下文一致性。 | 分析主题 | 主要代码区域 | 解决的问题 | |---|---|---| | Target Resolver | `internal/shortcut/targetresolver` | 统一名称、ID 和自然语言目标解析 | | 身份与能力路由 | `internal/shortcut/chat/*contract.go`、`unified_send.go` | 在执行前选择合法发送身份和路径 | | 消息投影 | `chat_message.go`、`smart/chat_messages.go` | 将异构响应转换为稳定消息模型 | | 分页与导出 | conversation/message/group/export 相关实现 | 防止静默截断、无限翻页和半成品文件 | | 事件监听 | `internal/app/event_listen_im.go` | 将 IM 事件封装为显式生命周期能力 | | 错误与上下文 | `internal/errors`、failure classifier、`profilectx` | 提供可行动错误并防止跨 profile 漂移 | | 兼容治理 | 参数别名、Schema metadata、selection hints | 保持旧调用可用,同时引导新调用走 Golden Route | ### 5.1 统一 Target Resolver 新增的 `internal/shortcut/targetresolver/resolver.go` 将目标识别从各业务命令抽离。Resolver 的价值在于把“输入是什么”与“接下来执行什么业务”分开。 统一解析应遵循如下优先级: 1. 显式类型化 ID 优先,避免把合法 ID 当名称搜索。 2. 显式 target kind 优先于启发式判断。 3. 自然语言名称进入搜索,并保留候选集。 4. 唯一命中才自动继续;多结果返回可选择候选。 5. 未命中返回稳定错误码和建议动作。 6. 解析结果携带 profile、tenant、来源和置信信息。 这样做带来三个直接收益: - chat、send、history、members 等命令共享完全一致的目标语义; - 新增手机号、邮箱、unionId 等输入方式时只扩展 Resolver; - 解析阶段可以独立测试,不需要触发真实写操作。 #### 向通讯录迁移 可建立 `DirectoryResolver`: ```text 输入:姓名 / 手机号 / 邮箱 / userId / unionId / 部门名 / deptId 输出:TypedDirectoryTarget kind: user | department canonicalId displayName matchedBy candidates profileContext ``` 删除、调岗、批量邀请等高风险动作只接受唯一、类型确定的解析结果。 #### 向文档迁移 可建立 `DocumentTargetResolver`: ```text 输入:URL / docId / workspaceId / spaceId / 标题 / 路径 输出:TypedDocumentTarget product: doc | drive | wiki resourceType canonicalId containerId matchedBy candidates ``` 这可消除文档 URL、知识库节点 ID、云盘文件 ID 混用造成的问题。 ### 5.2 Unified Send 与身份/能力矩阵 `unified_send.go`、`identity_contract.go` 和 `im_contract.go` 将发送动作从“按 API 名称选命令”提升为“按目标、身份与消息能力选择执行器”。 推荐的决策维度包括: | 维度 | 示例 | |---|---| | 目标 | 单聊、群聊、机器人会话 | | 身份 | 用户身份、机器人身份、应用身份 | | 内容 | text、markdown、card、media | | 能力 | 是否允许主动发送、是否支持回复、是否支持卡片更新 | | 上下文 | profile、tenant、conversation | 这种能力矩阵比在每条命令内写条件分支更易审计。发生能力不匹配时,应在真正发起网络请求前失败,并返回建议使用的 route。 #### 可复用原则 通讯录中的“读取、邀请、修改、删除”,文档中的“读取、创建、编辑、分享、删除”,都应建立身份与能力矩阵。调用者表达的是业务意图,Runtime 决定具体执行入口。 ### 5.3 `im.message-list.v1` 消息投影 消息列表相关代码将上游异构响应投影成稳定的版本化结构。核心思想是:保留原始信息的同时,提供统一的机器字段。 典型规范字段包括: - `messageId` - `conversationId` - `senderId` / `senderName` - `messageType` - `text` 或规范化内容 - `createdAt` - `replyToMessageId` - `raw`(必要时用于诊断) - `hasMore` / `nextCursor` 版本号的意义是允许未来增加或调整字段,而不破坏依赖 v1 的 Agent 与脚本。 #### 可复用原则 - 通讯录:定义 `directory.user-list.v1`、`directory.department-list.v1`。 - 文档:定义 `document.search-result.v1`、`document.block-list.v1`。 - 云盘:定义 `drive.file-list.v1`、`drive.transfer-result.v1`。 不要让自动化直接依赖供应商原始响应;应通过投影层建立稳定域模型。 ### 5.4 分页完整性与有界执行 PR 统一增强了消息、会话和成员列表的分页处理。一个健壮的列表命令至少需要: - 明确单页与自动翻页模式; - 返回 `hasMore` 和 `nextCursor`; - 支持 `maxPages`、`maxItems` 等硬边界; - 检测游标不前进或循环; - 去重并保持稳定顺序; - 明确结果是完整集合还是截断集合; - 在中途失败时返回已完成量和失败位置。 分页不是 UI 细节,而是数据完整性契约。尤其在“先查再写”的 Agent 流程中,静默截断会导致错误对象被操作。 #### 当前需关注的问题 本次分析发现 `+conversation-list` 的单页模式存在一个边界风险:代码可能在解析下一页游标前提前退出,最终出现 `hasMore=true` 但 `nextCursor=0`。这不会否定整体设计,但应在合并前补回归测试并修复。 ### 5.5 原子导出与资源下载 新增或强化的消息导出、资源下载路径采用“临时文件 -> 完整写入 -> 校验 -> 原子替换”的思路,避免失败时留下看似成功的残缺文件。 推荐流程: ```text 解析目标 -> 校验输出路径与权限 -> 创建同目录临时文件 -> 有界分页拉取/流式下载 -> 校验数量、大小或摘要 -> fsync/close -> 原子 rename 到最终路径 -> 返回 typed receipt ``` 失败时删除临时文件,保留已有最终文件,并返回失败阶段、已处理数量及是否可重试。 #### 可复用原则 该模式适用于:通讯录批量导出、文档离线备份、附件下载、云盘迁移、知识库归档以及审批记录导出。 ### 5.6 `+chat-create` 与 `+messages-reply` 补齐这类语义型 shortcut 的意义在于构建完整 Golden Route:用户不需要先理解底层产品 API,再自己拼接参数。命令命名直接对应业务意图,Schema 则描述必要约束。 设计原则是: - Shortcut 只暴露稳定、常用的业务参数; - 复杂底层参数放入高级 Reference; - 输出统一 envelope; - 目标解析复用 Resolver; - 写操作具备 preflight、明确身份和结构化回执。 ### 5.7 `event +listen-im` 新增 IM 事件监听入口,将底层事件总线能力收敛为 Agent 易理解的生命周期命令。文档进一步拆出 keys、lifecycle、operations、output 四个主题,说明事件能力被视为长期运行流程,而不只是一次 API 调用。 事件监听需要明确: - start、status、stop 的生命周期; - profile 与租户隔离; - endpoint/key 的稳定标识; - 前台与后台模式; - 超时、重连和退出语义; - 事件 envelope 与消息投影; - 重复事件、顺序和幂等边界。 #### 可复用原则 通讯录变更事件、文档协作事件、云盘文件事件都可以复用同一监听骨架,只替换事件类型与投影器。 ### 5.8 结构化错误与服务端失败分类 `internal/errors` 与 `server_failure_classifier.go` 将错误从一段不可解析文字升级为类型化结果。稳定错误至少应包含: - `code` - `message` - `stage` - `retryable` - `details` - `suggestedAction` - 必要的 request/trace 标识 关键分类包括:参数错误、目标未命中、目标歧义、身份不匹配、能力不支持、权限不足、限流、服务端失败、分页协议异常和文件提交失败。 错误分类的主要消费者不只是人,还包括 Agent 的下一步决策。因此 `suggestedAction` 应可执行,例如“提供 conversationId”“选择一个候选用户”“切换 bot identity”,而非笼统写“请重试”。 ### 5.9 Profile 一致性 新增 `profilectx` 并调整 auth/profile 相关逻辑,目的是保证目标解析、API 调用和事件监听使用相同的身份上下文。跨 profile 漂移是多租户 CLI 中非常隐蔽的风险:解析可能在 A 租户命中对象,写操作却在 B 租户执行。 可复用规则: 1. profile 在入口解析一次并向下传递; 2. Resolver 结果记录 profile/tenant; 3. 执行器不得静默回退到另一个默认 profile; 4. 批处理账本记录每项使用的 profile; 5. 事件监听 key 包含足够的上下文隔离信息。 ### 5.10 隐藏兼容别名 PR 保留必要的参数或命令别名,但通过 metadata/schema 控制它们不再成为 Agent 的首选入口。这实现了“兼容旧调用,但不继续传播旧范式”。 正确的兼容策略应区分: - Runtime compatibility:旧命令仍可运行; - Discovery visibility:新用户和 Agent 默认看不到旧入口; - Documentation priority:文档只推荐 Golden Route; - Deprecation observability:能够统计旧入口使用情况; - Removal criteria:达到明确阈值后再移除。 ### 5.11 Shortcut 部分修改详解 当前 #860 共修改 44 个 `internal/shortcut` 文件,其中 26 个生产 Go 文件、18 个测试文件。它不是简单增加命令数量,而是把 Shortcut 从“调用 MCP 的便捷包装”升级为以下分层执行系统: ```text Shortcut 声明与 Cobra 编译 -> 本地参数/约束校验 -> 自然目标解析与全量预检 -> 身份/能力路由 -> MCP 读写隔离与 dry-run -> 有界分页或批量执行 -> 统一领域投影 -> 完整性/失败 ledger -> 原子导出或安全资源落盘 ``` #### 5.11.1 Shortcut 框架:声明能力,而不是在命令里重复装配 Cobra `internal/shortcut/types.go` 和 `internal/shortcut/runner.go` 扩展了通用声明模型: | 修改 | Runtime 行为 | 价值 | |---|---|---| | `Flag.Shorthand` | 所有 flag 类型统一通过 Cobra 的 `BoolP/IntP/StringP/StringSliceP` 注册 | `--output` 可安全提供 `-o`,不需要每个命令单独实现 | | `Shortcut.Aliases` | 将兼容命令映射到同一个执行实现 | 保留旧调用,但 canonical Schema 和文档仍只推荐主入口 | | `SinglePositionalAliasFor` | 单个位置参数在校验前归一化为指定 flag;与显式 flag 同时出现时拒绝 | 为搜索类命令提供兼容体验,同时避免形成第二套参数契约 | | `Constraint` | `at_least_one`、`exactly_one`、`mutually_exclusive` 由 Runner 统一执行并投影到 Runtime Schema | Help、Runtime 与 Agent Schema 共用参数关系 | | `Changed`、`StrFirst`、`IntFirst` | 能区分“用户显式输入”和“默认值”,并统一处理兼容别名 | 防止默认值错误覆盖旧参数或触发不适用校验 | Runner 的执行顺序也被固定为:位置参数归一化 → required/enum 校验 → 跨参数约束 → 自定义校验 → 风险确认 → Execute。这样可以保证明显错误和未确认写操作不会到达 MCP。 读写调用进一步分离:`CallMCPData` 只允许符合只读命名契约的工具在 dry-run 中执行;`CallMCPWriteData` 在 dry-run 下直接 fail closed。多步写 Shortcut 必须自己输出 plan,不能借“为了拿返回值”之名在预览阶段执行写入。 #### 5.11.2 Target Resolver:把名称解析变成共享基础设施 `internal/shortcut/targetresolver/resolver.go` 新增了独立解析层,核心类型包括: - `Status`:`resolved`、`ambiguous`、`not_found`、`incomplete`; - `IdentityRequirement`:区分下游需要 `userId`、`openDingTalkId` 或任一稳定身份; - `UserResolution` / `ChatResolution`:返回规范目标、匹配方式和 profile; - `Reader`:只依赖只读 `CallMCPData`,便于 Smart Shortcut、事件入口和测试共同复用。 群目标解析采用以下 fail-closed 算法: 1. `cid...` 形式直接识别为稳定 `openConversationId`,不做名称搜索; 2. 群名调用 `im/search_groups`,每页 10 条,最多 40 页; 3. 对候选按稳定 ID 去重;精确名称优先,但多个精确同名仍然算歧义; 4. `hasMore=true` 时必须存在且推进 `nextCursor`; 5. 满页但无分页元数据、游标停滞或达到页数上限时返回 `resolution_incomplete`; 6. 只有完整候选集中的唯一目标才允许继续执行。 批量解析 `ResolveUsers/ResolveChats` 会先解析全部目标并收集结构化失败;任一目标未命中或有歧义,就在写操作开始前整体停止。它解决了过去每个 `+dm`、`+send-to-group`、`+group-members` 自己搜索并默认取首项的问题。 需要注意:群名解析已经显式证明跨页候选完整;用户解析目前依赖 `search_contact_by_key_word` 单次响应提供完整候选,后续若通讯录接口引入分页,应把相同的完整性规则扩展到用户 Resolver。 #### 5.11.3 身份与能力契约:先判断能不能做,再选择工具 `internal/shortcut/chat/identity_contract.go` 把 `user`、`bot`、`webhook` 三种身份的能力声明为矩阵,而不是散落在各分支的隐含知识: | 身份 | 目标 | 内容 | 自然目标 | 幂等 | 批量 ledger | |---|---|---|---|---|---| | user | 群、单聊 user/openDingTalkId | text、markdown、image、file,audio/video 按 file 发送 | 群名、姓名 | 支持 | 否 | | bot | 单群、多群、批量单聊 | text、markdown | 不支持 | 不支持 | 多群支持 | | webhook | token 所属群 | text、markdown | 不支持 | 不支持 | 否 | `internal/shortcut/chat/im_contract.go` 同时把“不支持什么”也变成可测试契约,例如 thread write、bot rich media、card action callback 和 Range 续传明确标记为不支持,并给出替代路径。这一点很重要:Agent 不应通过试错来发现能力边界。 #### 5.11.4 `+messages-send`:统一发送入口内部做严格能力路由 `internal/shortcut/chat/unified_send.go` 将 user、bot、webhook 的发送入口合并为一个语义命令,但没有把底层差异抹平: - user 目标必须在群、群名、userId、姓名、openDingTalkId 中恰好选择一个; - bot 必须提供 `robot-code`,且群目标与批量单聊目标必须二选一; - webhook 的目标由 token 决定,拒绝额外群或用户目标; - `@` 参数按身份和群聊/单聊分别校验; - `uuid/idempotency-key` 仅允许下层真实支持的 user 路径; - bot/webhook 在网络调用前拒绝 image/file/audio/video; - 内容正文、`media-id` 和本地文件互斥,并自动推导 `msg-type`。 user 的文件发送被拆成上传与消息发送两个动作,上传超时为 10 分钟;dry-run 只输出两步 plan。bot 多群发送最多 100 个目标,`groups-file` 必须是工作区内不超过 1 MiB 的普通文件,解析后去重,再通过 `im.batch-write.v1` 返回逐目标成功/失败账本。 `batch_write.go` 保持输入顺序,单项失败后继续其余项,但不自动重试写操作,因为远端报错时投递状态可能未知。代价是当前批量执行为顺序调用,优先可审计性和限流安全,而不是最大吞吐量。 #### 5.11.5 Smart Shortcut 变薄:只组合公共能力 Smart 层的主要变化不是继续增加本地业务逻辑,而是把已有命令改造成公共组件的适配器: | Shortcut/文件 | 修改后的职责 | |---|---| | `smart/dm.go` | 解析自然用户目标后,委托统一 user markdown 发送器 | | `smart/send_to_group.go` | 解析群名或稳定 ID 后,委托同一发送器 | | `smart/search_msg.go` | 在搜索前完成群和发送者消歧,再使用公共消息投影 | | `smart/at_me.go` | 使用公共目标、分页和消息结果语义 | | `smart/thread_replies.go` | 复用 `ProjectMessageV1`,不再维护线程专属返回结构 | | `smart/resolve.go` | 旧局部解析辅助函数迁移到 `targetresolver` | 这使 Golden Route 保持语义清晰,同时底层身份、消息字段和失败行为只维护一份。 #### 5.11.6 消息读取:从“返回数组”升级为 `im.message-list.v1` `internal/shortcut/chatmsg/chatmsg.go` 建立所有列表、搜索、mget、@me 和线程读取共享的投影核心 `ProjectMessageV1`。消息行统一包含可获得的: ```text messageId / conversationId / threadId sender / senderId / senderType messageType / text / createTime / updateTime reactions / quotedMessage / forwarded / resourceRefs ``` 投影层做了几项关键防御: - 同一语义字段兼容不同下层 key,但不会猜测缺失的 sender type; - 富文本卡片只提取已识别的正文块,普通文本中的 JSON 片段保持原样; - 无法解密的密文显示为明确标记,不把 base64 当正文; - 引用消息做一层投影,避免递归链或循环; - 合并转发递归使用同一投影; - 资源从当前、引用和转发子消息中深度提取,并生成可执行下载参数; - reaction 使用下层已内联的数据,不额外制造网络请求。 Envelope 默认保守地令 `complete=false`。只有拿到可靠分页事实时才允许证明完整;消息接口真正可继续执行的边界是末条消息的 `createTime`,因此 `nextPage` 发布 `time + direction`,而不是盲目把下层 cursor 暴露成 CLI 参数。 #### 5.11.7 有界分页:完整、截断和失败必须可区分 `smart/chat_messages.go` 将消息读取拆为目标编译、单页收集、全量收集、投影、资源下载和导出六个阶段。全量模式具备: - `page-limit` 默认 50、硬上限 500; - `max-results` 结果上限; - 稳定 `messageId` 跨页去重; - 时间边界停滞检测; - `hasMore=true` 但空页、缺边界或缺分页元数据时停止; - `stopReason` 区分 `source_complete`、`single_page`、`page_limit`、`result_limit`、`read_failure` 和 `pagination_error`; - 中途失败保留已读结果,并用 `partial/failures/failedCount` 显式标记。 `chat_conversation.go` 为会话列表加入同类的 `page-all/page-limit`、稳定 ID 去重和游标停滞检测,同时修正一个关键顺序问题:即使只读取一页,也必须在退出前解析 `nextCursor`,否则会出现 `hasMore=true` 却无法继续的结果。 `smart/group_members.go` 则建立 `im.group-members.v1`:用户和机器人分别进入 bucket;用户成员自动翻页并按 `openDingtalkId` 去重,机器人按下层全量结果投影。一个 bucket 失败时,另一个成功 bucket 仍可返回,但整体必须标记 `partial` 和 failures。 #### 5.11.8 本地校验、导出和下载:错误尽量发生在网络调用前 新增的 `smart/local_chat_validation.go` 集中支持: - `RFC3339`、`YYYY-MM-DD HH:mm:ss`、`YYYY-MM-DD` 三种时间格式; - 显式 `limit/count/days` 必须为正数; - 分页默认值只在值为零时回退; - 本地验证错误携带稳定 reason 和可执行 action。 消息 JSON 导出与资源下载共享文件安全边界: 1. 只接受工作目录内相对路径; 2. 同时识别 Unix 根路径、Windows 盘符、反斜杠和 portable `..` 逃逸; 3. 解析父目录 symlink,防止最终路径逃出工作区; 4. 默认 no-clobber,覆盖必须显式声明; 5. 先写同目录临时文件,完成写入、sync、close 后再原子发布; 6. 失败时删除临时文件,不留下看似成功的半成品。 资源下载明确采用整文件重试而非 Range 续传。这个限制被写入能力契约,避免文档或 Agent 宣称不存在的断点续传能力。 #### 5.11.9 新增和强化的 Golden Route - `+chat-create`:先解析所有自然成员和可选群主,整体预检成功后只创建一次;默认群主必须能从当前 profile 中取得并加入成员集合。 - `+chat-update`:群名或稳定 ID 统一解析,只承诺当前真实支持的改名能力。 - `+messages-reply`:可先读取原消息自动补齐引用发送者;dry-run 返回引用上下文和真实 transport plan;成功后补充消息、会话、线程、幂等和 delivery status。 - `+chat-members-list`:统一群目标,区分 users/bots,提供 bucket 完整性。 - `+chat-messages`:统一群聊/单聊自然目标、分页、消息投影、资源和导出。 - `+messages-send`:作为高级统一发送入口;`+dm` 和 `+send-to-group` 继续承担更窄、更易选路的常用入口。 这体现了 Golden Route 的正确含义:不是强迫所有意图只剩一个巨型命令,而是为每类意图保留一个推荐入口,公共执行逻辑在下层复用。 #### 5.11.10 测试重点与整体评价 18 个 Shortcut 测试文件主要固定以下不变量: - 自然目标零命中、多命中、跨页同名和游标停滞时禁止写入; - dry-run 中写调用为零,确认后写调用次数精确; - canonical 参数与隐藏 alias 生成相同 MCP payload; - 消息身份、正文、引用、转发、reaction、资源和分页投影一致; - 页数/结果上限、中途失败和部分结果不会被标记为完整; - Unix/Windows 路径、symlink、no-clobber 和原子发布边界; - 所有公开 Shortcut 均进入 Schema 或有精确、受评审的 exclusion。 Shortcut 改造的核心价值可以概括为:**语义入口仍然轻量,但目标解析、能力判断、完整性证明和安全落盘已经成为 Runtime 强约束。** 当前仍需持续关注三点:`unified_send.go` 参数面较大、下层仍大量使用 `map[string]any` 做兼容投影、用户自然目标的候选完整性尚未像群目标一样显式分页证明。 ## 6. 文档架构优化 本次文档改造的核心是“分层而非堆叠”: ```text SKILL.md 负责意图识别、入口选择和最短路径 | +-- intent-guide.md | 负责意图到命令的系统映射 | +-- contracts.md / runtime-contract.md | 负责稳定输入输出与错误约束 | +-- product/reference docs 负责参数细节、边界与复杂示例 ``` 这类结构可以降低主 Skill 的上下文消耗,同时避免同一规则复制到多处后发生漂移。 ## 7. 逐文档总结(30 份) 文档变更可以先按职责理解,再查看逐文件细节: | 文档组 | 数量 | 主要职责 | 优化方向 | |---|---:|---|---| | Mono | 4 | 全产品入口和产品索引 | 同步 Golden Route,避免复制领域细节 | | Chat Skill 与总览 | 6 | IM 意图路由、总览和公共合同 | 收敛入口,建立独立 contracts | | Chat 卡片 | 4 | 卡片模型与生命周期 | 按 create/update/callback/schema 拆分 | | Chat 领域子文档 | 6 | bot、会话、群、消息及旧工作流 | 对齐新 Runtime,删除重复路由源 | | Event | 6 | IM 事件监听 | 按 keys/lifecycle/operations/output 分层 | | Shared | 4 | 跨产品运行约束 | 统一错误、profile 和 Runtime contract | 下面每份文档均按“改了什么—为什么—如何复用”三个问题进行总结。 ### 7.1 Mono 文档 #### 1. `skills/mono/SKILL.md` - 调整 IM 意图的默认路由和入口说明。 - 将运行时已具备的统一消息、会话和事件能力呈现给单体 Skill。 - 强调优先使用稳定 shortcut,底层命令作为高级或兼容入口。 - 复用价值:任何全产品聚合 Skill 都应只维护意图索引,不复制领域手册。 #### 2. `skills/mono/references/best_practices/01-messaging.md` - 更新消息发送、回复、读取等最佳实践。 - 将自然目标解析、身份选择和分页边界纳入标准流程。 - 复用价值:最佳实践文档应描述决策原则,不只是展示命令样例。 #### 3. `skills/mono/references/products/chat.md` - 对齐新的 chat shortcut、统一输出和能力边界。 - 收敛旧脚本路径,避免文档继续引导 Agent 选择已被 Runtime 吸收的脚本。 - 复用价值:产品 Reference 必须与 Schema 的可发现能力同步更新。 #### 4. `skills/mono/references/products/event.md` - 引入 `event +listen-im` 及其生命周期说明。 - 明确事件监听与普通一次性命令的差异。 - 复用价值:所有长生命周期能力都应提供 start/status/stop 和恢复语义。 ### 7.2 Chat Skill 与总览文档 #### 5. `skills/multi/dingtalk-chat/SKILL.md` - 重构 chat 的核心意图路由,突出 Golden Route。 - 增加统一发送、会话创建、消息回复、列表、导出等入口。 - 将详细合同和高级示例下沉到 Reference,降低主入口复杂度。 - 当前版本为 9,928 bytes,已进入 10,000 bytes 上下文预算,但只剩 72 bytes 余量,应继续把细节下沉到 Reference。 - 复用价值:领域 Skill 应是“路由器”,不是完整 API 百科。 #### 6. `skills/multi/dingtalk-chat/references/01-messaging.md` - 更新端到端消息操作最佳实践。 - 强调先解析目标、再做能力判断、最后执行写操作。 - 补充分页、回复、文件处理等边界。 - 复用价值:将“解析—计划—执行—回执”固化为跨产品写操作模板。 #### 7. `skills/multi/dingtalk-chat/references/chat.md` - 大幅重构 chat 总参考,覆盖新的 shortcut 和 Runtime 契约。 - 统一参数命名、输出结构、错误处理和高级入口定位。 - 减少与子文档的重复,将细节按主题拆分。 - 复用价值:领域总览应负责导航和共享概念,具体操作落到子 Reference。 #### 8. `skills/multi/dingtalk-chat/references/intent-guide.md` - 建立用户意图到 Golden Route 的明确映射。 - 说明目标是人、群、会话或消息时分别如何选择命令。 - 降低“多个命令看起来都能完成任务”造成的路由不确定性。 - 复用价值:通讯录和文档也应维护机器可校验的 intent-route 表。 #### 9. `skills/multi/dingtalk-chat/references/contracts.md` - 新增集中式契约文档。 - 固定消息列表、写操作回执、批处理账本和错误 envelope 等公共结构。 - 将契约与教程分离,便于版本化和自动检查。 - 复用价值:每个领域都应有唯一的 contract source of truth。 #### 10. `skills/multi/dingtalk-chat/references/chat-emoji-list.md` - 对齐 emoji 相关命令、目标和消息标识参数。 - 使轻量功能也遵循统一会话/消息身份语义。 - 复用价值:边缘功能不能绕过统一 Resolver 和契约。 ### 7.3 Chat 卡片文档 #### 11. `skills/multi/dingtalk-chat/references/card/create.md` - 新增卡片创建流程说明。 - 区分卡片定义、实例创建和消息投递,避免一步调用承载过多隐式行为。 - 复用价值:复杂对象创建应拆成资源定义、实例化和发布阶段。 #### 12. `skills/multi/dingtalk-chat/references/card/update.md` - 新增卡片更新操作说明。 - 强调实例标识、可更新字段和生命周期边界。 - 复用价值:文档更新、表格更新等都应区分 create contract 与 update contract。 #### 13. `skills/multi/dingtalk-chat/references/card/callback.md` - 新增卡片回调与交互事件说明。 - 将回调输入视为结构化事件,而不是普通消息文本。 - 复用价值:交互式文档组件、审批回调也可复用事件 envelope。 #### 14. `skills/multi/dingtalk-chat/references/card/schema.md` - 新增卡片 Schema 说明。 - 集中描述字段结构和约束,减少 create/update 文档重复。 - 复用价值:把数据模型与操作教程分开,Schema 可单独校验和演进。 ### 7.4 Chat 领域子文档 #### 15. `skills/multi/dingtalk-chat/references/chat/chat-bot.md` - 更新机器人发送、广播及身份能力说明。 - 旧脚本能力被收敛进 Runtime,文档不再要求 Agent 拼装脚本流程。 - 注意:文档部分位置使用 `results`,而 Runtime 实际批处理字段为 `succeeded`,需统一。 - 复用价值:文档字段名必须由合同或测试生成,避免手工漂移。 #### 16. `skills/multi/dingtalk-chat/references/chat/chat-conversation.md` - 更新会话创建、列表和目标标识说明。 - 补充游标、分页和会话类型边界。 - 复用价值:任何容器类资源都应区分 canonical ID、display name 与 page cursor。 #### 17. `skills/multi/dingtalk-chat/references/chat/chat-group.md` - 更新群成员、群信息与群目标解析流程。 - 统一群名与群 ID 的解析/歧义处理。 - 复用价值:部门名、空间名、目录名同样需要唯一解析和候选返回。 #### 18. `skills/multi/dingtalk-chat/references/chat/chat-message.md` - 更新消息读取、回复、导出和资源下载说明。 - 对齐 `im.message-list.v1` 投影及分页元数据。 - 复用价值:把列表 contract 与后续动作需要的 canonical ID 同时输出。 #### 19. `skills/multi/dingtalk-chat/references/chat/chat-workflows.md`(删除) - 删除与 SKILL、intent guide 和领域子文档大量重叠的工作流集合。 - 旧文档容易形成第二套意图路由源。 - 复用价值:当工作流可由 Golden Route 与合同组合推导时,不应继续维护重复手册。 #### 20. `skills/multi/dingtalk-chat/references/lite-recipes.md`(删除) - 删除过短且容易过时的 recipe 集合。 - 将典型路径并入主 Skill 或更权威的 Reference。 - 复用价值:减少“看似方便但缺少边界”的示例,避免模型只复制 happy path。 ### 7.5 Event 文档 #### 21. `skills/multi/dingtalk-event/SKILL.md` - 增加 IM 监听的意图入口与生命周期路由。 - 将普通事件命令和长期监听操作区分开。 - 复用价值:事件领域 Skill 应首先判断用户要“查询配置”还是“启动持续进程”。 #### 22. `skills/multi/dingtalk-event/references/event-im.md` - 大幅更新 IM 事件总参考。 - 串联配置、启动、运行、输出、停止和排错。 - 将细节拆到四个新子文档,自己承担导航职责。 - 复用价值:长流程总文档应提供生命周期地图而非重复所有参数。 #### 23. `skills/multi/dingtalk-event/references/event-im-keys.md` - 新增 endpoint、listener、profile 等关键标识说明。 - 明确 key 的生成、引用和隔离边界。 - 复用价值:后台任务、同步任务、订阅任务都需要稳定 task key。 #### 24. `skills/multi/dingtalk-event/references/event-im-lifecycle.md` - 新增启动、运行、状态、停止等生命周期说明。 - 描述前后台模式及退出后的状态语义。 - 复用价值:将长任务统一建模为显式状态机。 #### 25. `skills/multi/dingtalk-event/references/event-im-operations.md` - 新增常见操作和运维流程说明。 - 将查看状态、诊断、停止等操作从概念说明中拆出。 - 复用价值:后台服务文档应提供可操作 runbook。 #### 26. `skills/multi/dingtalk-event/references/event-im-output.md` - 新增监听输出格式及字段解释。 - 明确事件 envelope 与 IM 消息投影之间的关系。 - 复用价值:通讯录、文档事件应共享 envelope,仅替换 payload contract。 ### 7.6 Shared 文档 #### 27. `skills/multi/dws-shared/SKILL.md` - 更新多产品共享约束和 Reference 导航。 - 将 Runtime contract 与错误码作为跨领域公共基础设施。 - 复用价值:共享 Skill 只承载真正跨域的稳定规则,领域语义仍留在各自 Skill。 #### 28. `skills/multi/dws-shared/references/error-codes.md` - 扩展结构化错误类别、可重试性和建议动作。 - 支撑 Target Resolver、服务端失败分类和文件流程。 - 复用价值:错误码应跨产品统一命名,领域细节放入 `details`。 #### 29. `skills/multi/dws-shared/references/global-reference.md` - 更新全局参数、profile、输出和运行约束。 - 明确公共规则的权威来源,减少 chat/event 文档重复。 - 复用价值:全局 Reference 应稳定且少变,产品差异通过链接下沉。 #### 30. `skills/multi/dws-shared/references/runtime-contract.md` - 新增 Runtime 公共契约说明。 - 固定 envelope、错误、批处理、分页和 profile 传播等跨产品行为。 - 复用价值:通讯录、文档、云盘接入时应直接复用,而非重新设计返回结构。 ## 8. 被删除脚本的意义 PR 删除 mono 与 multi chat 下的以下脚本: - `bot_broadcast.py` - `chat_export_messages.py` - `chat_history_with_user.py` 删除并不意味着能力减少,而是能力已进入可测试、可发现、可治理的 Runtime。脚本适合验证原型,但长期作为 Agent 主路径会带来参数契约不可发现、错误格式不一致、身份上下文丢失和重复分页逻辑等问题。 迁移准则:当一个脚本成为稳定业务能力后,应将核心逻辑提升到 Runtime,生成 Schema,补充契约测试,然后从主 Skill 中撤下脚本入口。 ## 9. Policy 与测试体系 ### 9.1 Multi IM Skill Chain Policy 新增 `scripts/policy/multi-im-skill-chain`,通过意图测试数据检查关键路径是否在 Skill、Reference、Schema 与 Runtime 之间闭环。其价值是防止以下回归: - 文档推荐了不存在的命令; - Runtime 已新增命令但 Schema 未暴露; - 兼容别名重新成为首选; - 同一意图再次出现多个 Golden Route; - 关键参数或输出字段只在某一层存在。 当前 intent manifest 包含 9 个关键意图路由;PR 描述记录 `make policy` 通过。本文对代码和配置进行了静态核对,没有把该记录扩展表述为 GitHub CI 全绿。 ### 9.2 Context Budget Policy Skill 上下文预算检查用于控制入口文档大小。渐进披露只有在主 Skill 足够短时才有效。当前 chat Skill 为 9,928 bytes,刚好回到 10,000 bytes 预算内;架构方向正确,但余量很小,内容仍需继续去重或下沉。 ### 9.3 测试分层 本次变更配套测试覆盖: - Resolver 的唯一命中、多命中、未命中与输入类型; - unified send 的目标和身份选择; - 消息投影字段; - 分页边界与兼容行为; - 结构化错误和服务端失败分类; - event 命令注册与生命周期; - Schema/shortcut contract; - mock MCP 端到端冒烟路径。 PR 描述记录 `internal/app`、`internal/shortcut/chat` 和 `internal/shortcut/smart` 等重点测试通过。本文分析的是远端 head 的 Git 对象,没有在当前 `main` 工作区切换分支重新运行完整测试;当前 GitHub CI 结论仍应以第 21 章列出的 workflow 状态为准。 ## 10. 面向通讯录场景的复用蓝图 通讯录复用的重点不是照搬 IM 命令,而是把“找群/找人后执行动作”的模式转换为“找用户/找部门后执行动作”。核心差异在于通讯录写操作通常权限更高、影响范围更大,因此 Resolver 的唯一性和执行前计划需要更严格。 ### 10.1 建议 Golden Routes ```text contact +resolve contact +user-get contact +user-list contact +department-list contact +member-list contact +invite contact +update contact +export event +listen-contact ``` ### 10.2 Directory Resolver 集中接受姓名、手机号、邮箱、userId、unionId、部门名称和 deptId。输出唯一 canonical target 或候选列表,不允许高风险写操作在歧义状态下继续。 ### 10.3 类型化契约 建议定义: - `directory.user.v1` - `directory.user-list.v1` - `directory.department.v1` - `directory.member-list.v1` - `directory.batch-write-result.v1` - `directory.change-event.v1` ### 10.4 风险分级 - 只读查询:可在唯一命中后自动执行。 - 邀请/修改:必须显示 preflight plan。 - 删除/批量调岗:要求显式确认、严格唯一目标和可审计账本。 ### 10.5 批处理账本 每条记录包含 input、resolved target、action、status、error、retryable 和 profile。这样部分失败可安全重试,不需要重跑已成功项目。 ## 11. 面向文档、云盘和知识库的复用蓝图 文档类场景与 IM 的共同点是目标输入多样、读取结果需要稳定投影、长流程需要完整性保证;不同点是文档还涉及版本冲突、层级容器、权限继承和内容覆盖。因此在复用 Resolver 与 Runtime contract 的同时,应额外引入 revision/etag 和并发控制。 ### 11.1 建议 Golden Routes ```text doc +resolve doc +create doc +read doc +update doc +export doc +share drive +download drive +upload wiki +resolve-node event +listen-document ``` ### 11.2 Document Target Resolver 统一解析 URL、docId、fileId、spaceId、workspaceId、知识库节点 ID、标题和路径。解析输出必须包含 product/resource type,避免将同形 ID 传给错误产品接口。 ### 11.3 Plan/Execute 分离 写文档、覆盖文件、调整权限、移动知识库节点前先生成计划: ```json { "target": {"type": "document", "id": "..."}, "action": "replace-content", "identity": "user", "expectedRevision": "...", "sideEffects": ["overwrite content"], "recoverability": "revision-history" } ``` 计划可以独立验证权限、版本冲突和副作用;确认后才执行。 ### 11.4 列表与搜索契约 搜索结果必须区分 exact、fuzzy 和 candidate,返回 canonical URL/ID、容器、资源类型、权限摘要以及分页状态。不要只返回标题字符串。 ### 11.5 原子输出与并发控制 - 导出使用临时文件和原子替换。 - 更新使用 revision/etag 防止覆盖他人修改。 - 上传返回摘要、大小和最终资源 ID。 - 长迁移返回 checkpoint 与逐项账本。 ## 12. 可复用的落地模板 ### 12.1 新领域接入步骤 1. 列出用户真实意图,而非先列 API。 2. 为每个意图指定唯一 Golden Route。 3. 设计统一 Target Resolver 和 canonical target。 4. 建立身份/能力矩阵。 5. 定义版本化输入、列表、写回执和错误契约。 6. 为长流程增加 maxItems、maxPages、timeout、checkpoint。 7. 将写操作拆为 plan 和 execute。 8. 生成或同步 Schema metadata/hints/registry。 9. 编写短 Skill、意图指南、合同和分主题 Reference。 10. 增加意图链路、上下文预算、Schema 和契约测试。 ### 12.2 推荐目录结构 ```text skills/multi/dingtalk-/ SKILL.md references/ intent-guide.md contracts.md .md .md .md internal/shortcut/ / resolver/ scripts/policy/ -skill-chain/ ``` ### 12.3 Golden Route 评审问题 - 一个自然语言意图是否只有一个默认入口? - shortcut 是否隐藏了供应商 API 的非必要复杂度? - 兼容入口是否仍可运行但不会被 Agent 优先发现? - Resolver 是否统一处理 ID、名称、URL 和多候选? - 写操作是否在执行前完成身份和能力校验? - 输出是否有稳定版本? - 列表是否明确完整、截断与下一页状态? - 错误是否能指导 Agent 下一步动作? - Skill、Reference、Schema 与 Runtime 是否有自动一致性检查? ## 13. PR #860 当前风险收口状态 | 评审问题 | 当前整体实现 | 判断 | |---|---|---| | `+conversation-list` 单页模式丢失 `nextCursor` | 将 cursor 解析移动到单页退出判断之前,并补充失败边界测试 | **已修复** | | `hasMore=true` 但 cursor 缺失、无效或不前进 | 增加 missing/invalid/stalled cursor 测试 | **已强化** | | Chat Shortcut 未全部进入 Schema | 从 50/97 提升到 97/97,删除 47 项临时 exclusion | **已解决** | | 高风险示例直接展示 `--yes` | 文档和命令示例改为先运行或 `--dry-run`,由 Runtime 要求确认后再追加 `--yes` | **已改善** | | 跨平台输出路径判断和覆盖替换不完整 | 统一处理 Unix/Windows 路径语义,并以 build tags 分离 `os.Rename` 与 `windows.Rename` | **已加固** | | chat bot 文档 `results` 与 Runtime `succeeded` 可能不一致 | 当前 diff 中未见由单一 typed source 自动生成这两个名称 | **仍需确认** | | Chat Skill 上下文预算偏紧 | 已通过拆分 Reference 和删除重复文档缩短主 Skill,仍应持续由 budget policy 约束 | **持续治理** | | 兼容别名缺少退出观测 | 已增强 alias/canonical payload 等价测试,但未建立使用量观测或移除阈值 | **长期项** | | 真实线上语义 | 单元测试和 mock MCP 能证明本地契约,不能证明真实权限、MCP 响应和服务端可用性 | **需要受控 E2E** | ## 14. PR #860 整体架构结论 PR #860 的核心贡献,是把 IM 能力从“散落的命令和教程”升级为“可解析、可计划、可执行、可验证的意图系统”。其最重要的架构资产包括: - Resolver:统一对象身份; - Golden Route:统一意图入口; - Capability Matrix:统一身份与能力判断; - Versioned Contract:统一机器输出; - Bounded Execution:统一分页和长流程边界; - Typed Error Ledger:统一失败处理与恢复; - Progressive Documentation:统一知识组织; - Policy Gates:统一防漂移机制。 这些模式与 IM 本身无强绑定。通讯录的“找人/找部门”、文档的“找资源/改内容”、云盘的“找文件/迁移”、知识库的“找节点/调整结构”都面临同一类问题。复用时应迁移完整链路,而不是只复制某个 shortcut: ```text 意图设计 -> Resolver -> Golden Route -> Plan/Capability Check -> Bounded Runtime -> Versioned Result/Error -> Documentation + Schema + Policy ``` 只有整条链路同时落地,才能真正降低 Agent 的选择成本、减少误操作,并让新领域能力持续演进而不重新陷入命令、文档和实现相互漂移的问题。 --- ## 15. 产品化交付闭环 ### 15.1 为什么 Runtime 完成不等于 PR 完成 PR #860 同时处理两类问题:一类是命令行为本身,另一类是 Agent 能否发现、正确选择并安全执行这些行为。只有 Runtime、Registry、Schema、selection hints、风险 metadata、Skill、Policy 和测试同时闭环,才能把“隐藏在代码里的能力”升级为“可交付产品能力”。 大行数主要来自重新生成并提交的 Schema catalog 和 Agent metadata,不能等同于 6 万余行手写业务逻辑。评审时应将 authored source、Runtime、generated artifacts、文档和测试分开看。 ### 15.2 五个交付收口方向 1. **能力发布完整化**:剩余 47 个公开 Chat Shortcut 从临时 exclusion 转为正式 Registry 和 Schema 能力,使 Chat 达到 97/97。 2. **安全元数据类型化**:为 47 个命令逐一声明 effect、risk、confirmation、idempotency、interface mode 和 runtime gate。 3. **参数前置校验**:时间、页大小、回溯窗口等明显错误在本地拒绝,不再发起无效远端调用。 4. **运行时边界收口**:修复单页 cursor、补 `-o`、增强跨平台输出路径并扩大目标消歧测试。 5. **交付证据闭环**:重新生成 Schema/Agent metadata,强化生成漂移、契约完整性和 changed-code coverage 门禁。 ## 16. Schema 发布与能力治理 ### 16.1 从 50/97 到 97/97 PR #860 在 `internal/cli/schema_command_registry/products/chat.json` 中补齐 47 个 canonical shortcut,并从 `schema_command_exclusions.json` 删除整个 `chat-shortcuts-pending-schema-curation` 临时排除组。 这一步的意义不是单纯“数字补齐”,而是完成以下状态转换: ```text 公开可执行、但 Schema 暂未评审 -> authored Registry 有正式记录 -> selection hints 能指导 Agent 何时用/何时不用 -> metadata 声明风险和确认方式 -> generated catalog/Agent metadata 可发现 -> completeness test 保证不再退回临时排除 ``` 关键指标变化: | 指标 | 改造前 | PR #860 当前 head | |---|---:|---:| | Chat Schema Shortcut | 50 | 97 | | 全部公开 Shortcut Schema 覆盖 | 218 / 265 | 265 / 265 | | Schema catalog 工具数 | 850 | 898 | | Agent metadata 覆盖工具数 | 850 | 898 | | hint tools | 1,852 | 1,948 | | reviewed Agent examples | — | 1,108(1,084 contract + 24 dry-run) | ### 16.2 新发布的 47 个 Shortcut 为便于理解,可按业务域归为五组: #### 会话分类(3) - `+category-add-conversation` - `+category-list-conversations` - `+category-remove-conversation` #### 群管理(13) - `+chat-add-bot` - `+chat-audit-join` - `+chat-get-by-id` - `+chat-members-get` - `+chat-mute-member` - `+chat-quit` - `+chat-remove-bot` - `+chat-role-remove` - `+chat-role-remove-user` - `+chat-transfer-owner` - `+chat-update` - `+chat-update-icon` - `+chat-update-settings` #### 会话状态(7) - `+conversation-clear-messages` - `+conversation-clear-red-point` - `+conversation-hide` - `+conversation-mark-read` - `+conversation-mark-unread` - `+conversation-mute` - `+conversation-set-top` #### Feed 与收藏(4) - `+feed-group-query-item` - `+flag-cancel` - `+flag-create` - `+flag-list` #### 消息与资源(20) - `+messages-add-emoji` - `+messages-add-text-emotion` - `+messages-batch-recall-by-bot` - `+messages-batch-send-by-bot` - `+messages-combine-forward` - `+messages-create-text-emotion` - `+messages-forward` - `+messages-forward-topic` - `+messages-list` - `+messages-recall` - `+messages-recall-by-bot` - `+messages-remove-emoji` - `+messages-remove-text-emotion` - `+messages-resource-download` - `+messages-resource-url` - `+messages-send-by-bot` - `+messages-set-pin` - `+messages-set-top` - `+messages-unset-pin` - `+messages-unset-top` ### 16.3 风险与确认元数据 47 个新增 metadata 的分布非常清晰: | 类型 | 数量 | confirmation | runtime gate | |---|---:|---|---| | 只读 / low | 8 | `not_required` | `none` | | 写操作 / medium | 36 | `user_required` | `typed_yes` | | 破坏性 / high | 3 | `user_required` | `typed_yes` | 三个 high-risk 命令是: - `chat.shortcut_chat_remove_bot` - `chat.shortcut_chat_role_remove` - `chat.shortcut_conversation_clear_messages` 八个只读命令包括分类列表、按 ID 查群、成员获取、Feed 查询、收藏列表、消息列表、资源 URL 和资源下载。其余 39 个命令全部要求用户确认并由 Runtime 的 `typed_yes` 门禁执行。 这种分类实现了三个层面的一致性: 1. Agent 在选择工具时能看到风险; 2. 示例不会把确认当成默认参数; 3. Runtime 即使被直接调用,也会阻止未经确认的写操作。 ### 16.4 Authored Source 与生成物分离 PR #860 的权威编辑源主要是: - `schema_command_registry/products/chat.json`:命令身份和 canonical path; - `schema_hints/selection/chat.json`:`use_when`、`avoid_when` 和 source refs; - `schema_hints/metadata/chat.json`:effect、risk、confirmation 等运行语义。 随后由生成流程更新: - `schema_catalog/catalog.json` - `schema_catalog/tools/chat.json` - `schema_agent_metadata/chat.json` - `schema_agent_metadata/index.json` - `schema_agent_metadata_audit.json` - `runtime-surface-completeness.json` 复用到其他产品时,应优先评审 authored source 的准确性,再验证生成物无漂移。直接手改大型 catalog 会削弱可追溯性。 ### 16.5 Selection Hints 的价值 每个新 Shortcut 都有独立的 `use_when`、`avoid_when` 和 `source_refs`。三者分别解决: - **什么时候用**:把用户意图映射到该工具; - **什么时候不用**:避免和相近工具误路由; - **依据是什么**:从生成结果追溯到 Registry 和文档锚点。 这比只提供命令描述更适合 Agent,因为工具选择的难点通常不是“工具做什么”,而是“在几个相似工具中为什么选这个”。 ## 17. Runtime 安全与正确性加固 ### 17.1 本地参数校验 新增 `internal/shortcut/smart/local_chat_validation.go`,集中实现: - 默认分页上限归一化; - RFC3339、`YYYY-MM-DD HH:mm:ss`、`YYYY-MM-DD` 三类时间格式检查; - 带稳定 reason 和 suggested action 的本地 validation error。 落地命令及边界: | 命令 | 新增校验 | |---|---| | `+chat-messages` | `--time` 格式;`--limit/--size/--page-size > 0` | | `+thread-replies` | 时间格式;显式 `--limit > 0` | | `+at-me` | `--days` 为 1~3650;`--limit > 0` | | `+unread-chats` | 显式 `--count > 0` | 核心原则是“能够在本地确定错误,就不要把它推给远端”。这样能减少无效请求,并让 Schema 约束、CLI 错误和测试保持一致。 ### 17.2 破坏性确认门禁 `internal/helpers/chat.go` 为以下底层命令补上真实 Runtime 检查: - `chat category delete` - `chat clear-messages` 缺少 `--yes` 时,命令返回 `confirmation_required`,列出确认目标和再次执行方式,并保证没有任何下游工具调用。补充测试同时验证:确认前零写入、确认后仅发生一次预期调用。 此外,群解散和升级外部群等示例不再直接展示 `--yes`;升级示例改用 `--dry-run`。这是一个重要的提示工程原则: > 文档示例应该展示安全的第一步,而不是展示绕过交互确认的最终一步。 ### 17.3 单页会话 cursor 修复 `+conversation-list` 的旧实现曾在非 `--page-all` 模式下先退出循环,再解析 `nextCursor`,可能返回 `hasMore=true` 但游标为零。PR #860 将 cursor 解析提前到退出判断之前。 修复后的行为: - 单页结果保留可执行的下一页游标; - `hasMore=true` 但游标缺失、无效或停滞时记录 pagination failure; - `--page-all` 才继续拉下一页; - `--page-limit` 保持有界。 这说明分页契约的完整性不仅适用于“拉完全部”,单页模式同样必须提供可靠 continuation。 ### 17.4 通用 shorthand 支持 `shortcut.Flag` 新增 `Shorthand` 字段,统一注册逻辑从 Cobra 的 `Bool/Int/StringSlice/String` 切换到对应的 `*P` 版本。`+chat-messages --output` 因而获得 `-o`。 该改动是框架级能力,不只服务于当前命令。后续复用时需增加同一命令内 shorthand 唯一性检查,避免不同 flag 争用同一个短参数。 ### 17.5 跨平台资源路径加固 原实现主要依赖当前操作系统的 `filepath.IsAbs/Clean`。在 Unix 上,Windows 盘符路径或反斜杠逃逸未必会按预期识别。PR #860 增加 portable 归一化: - 将反斜杠转换为 `/` 后再检查; - 识别 Unix 绝对路径; - 识别 Windows `C:` 等盘符; - 统一拒绝 `..` 和 `../` 逃逸; - 在父目录、相对路径和最终解析阶段复用同一判断。 - 用 `file_replace.go` 与 `file_replace_windows.go` 的 build tags 分离平台实现:非 Windows 使用 `os.Rename`,Windows 使用 `windows.Rename`,保证覆盖写仍是原子替换。 该模式可直接用于文档导出、云盘下载、通讯录 CSV 导出和任何工作区内文件写入能力。 ### 17.6 目标消歧与 fail-closed 测试 Resolver 实现与测试共同固定以下关键语义: - 稳定 ID 永远不按名称搜索; - 自然目标走 Resolver; - 同名精确匹配仍保持歧义,不能偷偷选一个; - 必须翻完候选页后才判断是否唯一; - cursor 无法推进时 fail closed; - 结构化错误保留候选对象; - 外部联系人只要有可用 openDingTalkId 也可参与解析。 最重要的改进是“跨页后再唯一判定”。如果只看第一页就认为唯一,在第二页存在同名对象时会产生高风险误操作。 ### 17.7 删除过时 hint 子命令 删除 `dws chat send` 和 `dws chat history` 两个仅用于提示的伪子命令。Golden Route 已通过 Shortcut 和 Schema 正式发布后,继续保留 hint 命令会污染命令树并形成第二套发现路径。 兼容不等于永远保留所有提示层。若入口已经由结构化 discovery 接管,应删除无法执行真实业务、只输出迁移提示的旧节点。 ## 18. PR #860 文件分类总结 ### 18.1 129 个文件按职责归类 | 文件组 | 数量 | 主要内容 | |---|---:|---| | 生产 Go | 38 | App、错误、Resolver、Chat/Smart Runtime、契约、Policy 与生成别名 | | Go 测试 | 30 | Resolver、分页、确认、文件安全、事件、契约及 coverage | | Skill Markdown | 30 | Mono、Chat、Event、Shared 四层知识链路 | | Schema / metadata / JSON | 17 | Registry、selection、risk metadata、catalog、Agent metadata 与 intent manifest | | Skill 脚本 | 6 | 删除已被 Runtime 替代的重复 Python 路径 | | 生成/审计脚本 | 6 | Skill 生成、Policy 检查、live audit 及其测试 | | CI / Makefile | 2 | 接入 Policy、兼容性、覆盖率及平台 race 验证 | ### 18.2 Schema 文件逐项总结 | 文件 | 总结 | |---|---| | `schema_command_registry/products/chat.json` | 补齐 canonical Shortcut,成为 Chat 发布权威源 | | `schema_command_exclusions.json` | 删除 47 项“待 Schema 评审”临时豁免,反向完整性转为硬约束 | | `schema_hints/selection/chat.json` | 为 Chat 工具补齐 use/avoid/source refs,强化 Agent 路由 | | `schema_hints/metadata/chat.json` | 为 Chat 工具声明 effect、risk、confirmation、idempotency 和 runtime gate | | `schema_hints/runtime-surface-completeness.json` | 更新 Runtime surface 覆盖指纹与统计 | | `schema_catalog/catalog.json` | 重新生成全局 catalog 索引 | | `schema_catalog/tools/chat.json` | 重新生成 Chat 的完整工具契约和示例 | | `schema_agent_metadata/chat.json` | 生成 Chat Agent 决策元数据 | | `schema_agent_metadata/index.json` | 更新全产品 metadata 索引和 898 工具统计 | | `schema_agent_metadata_audit.json` | 更新 coverage、source/surface hash 和审计结果 | ### 18.3 Runtime 文件逐项总结 | 文件 | 总结 | |---|---| | `internal/helpers/chat.go` | 为删除分类、清空消息补确认门禁;安全化危险示例;删除旧 hint 子命令 | | `shortcut/chat/chat_conversation.go` | 修复单页模式 nextCursor 丢失 | | `shortcut/chat/resource_download.go` | 统一 Unix/Windows 绝对路径和 `..` 逃逸判断 | | `shortcut/chat/file_replace.go` | 非 Windows 构建使用 `os.Rename` 作为原子替换边界 | | `shortcut/chat/file_replace_windows.go` | Windows 构建使用 `windows.Rename` 支持覆盖已有目标 | | `shortcut/runner.go` | 所有 flag 类型支持 shorthand 注册 | | `shortcut/types.go` | `Flag` 契约增加 `Shorthand` | | `smart/local_chat_validation.go` | 新增共享时间、页大小和结构化本地错误辅助函数 | | `smart/chat_messages.go` | 新增时间/页大小校验及 `-o`,复用默认 page limit | | `smart/at_me.go` | 限定 days 和 limit,组合资源下载校验 | | `smart/thread_replies.go` | 限定时间格式和 limit | | `smart/unread_chats.go` | 限定 count 为正数 | | `smart/group_members.go` | 复用统一默认 page limit 逻辑 | ### 18.4 Policy 文件总结 `scripts/policy/multi-im-skill-chain/main.go` 将工作目录、进程退出、文件打开和 Registry build/bind 抽成可注入函数。行为本身没有改变,但各种 setup failure、I/O failure 和 binding failure 现在可在单元测试中稳定覆盖。 这是一种值得复用的“测试接缝”设计:对进程边界做最小注入,不为测试重写业务逻辑。 ### 18.5 安全示例收口示例 #### 1. `skills/mono/references/best_practices/01-messaging.md` - 将发送、机器人批量发送和消息转发示例中的默认 `--yes` 去掉。 - 明确 Runtime 会先返回确认要求;用户确认后才以相同参数追加 `--yes`。 - 保留唯一目标解析、同 profile 稳定 ID 和未知投递状态停止重发等规则。 - 复用价值:所有写操作最佳实践都应展示“安全第一步 + 确认后续步骤”,不能把确认令牌写进首个示例。 #### 2. `skills/mono/references/products/chat.md` - 将“普通群升级为外部群”的正式执行示例改为 `--dry-run`。 - 删除直接带 `--yes` 的示例,保留 extension JSON 的预演方式。 - 复用价值:不可逆转换、权限变更和内容覆盖应优先展示 dry-run/preflight。 ### 18.6 测试体系总结 PR #860 的 30 个 Go 测试文件主要覆盖: - 265/265 public Shortcut Schema 覆盖和 Chat 97/97; - metadata 风险与 Runtime confirmation truth; - 时间、页大小、回溯窗口等本地无网络校验; - natural target、稳定 ID、同名候选和跨页消歧; - 单页/全量分页、cursor 停滞、边界失败和部分结果保留; - 资源下载的绝对路径、反斜杠、symlink、原子 no-clobber; - 确认前零写调用、确认后精确一次调用; - alias 到 canonical payload 的等价性; - Multi IM Policy 的配置、I/O 和失败矩阵; - 跨平台 changed-code coverage。 测试名大量增加 `CrossPlatformCoverage` 前缀,是为了让平台覆盖门禁识别相关测试。其积极意义是证据可追踪;长期应避免让命名约定取代真正的覆盖映射机制。 ## 19. Go 文件优化专项总结 ### 19.1 总体规模与演进主线 PR #860 的 Go 变更应按“生产实现”和“验证实现”分开理解: | 类型 | 文件数 | 新增 / 删除 | 主要任务 | |---|---:|---:|---| | 生产 Go | 38 | +4,028 / -737 | 建立 Resolver、IM Runtime 契约、事件监听、错误/Profile、平台文件替换和 Policy 基座 | | Go 测试 | 30 | +4,343 / -88 | 固定正常路径、失败边界、安全不变量、Schema/Runtime 一致性与跨平台覆盖 | 整体演进不是把更多逻辑堆进单个命令,而是逐步形成以下 Go 代码分层: ```text Cobra / App 层 负责命令注册、参数装配、profile 与进程生命周期 | v Shortcut / Smart 层 负责 Golden Route、业务校验、计划和结果组织 | v Target Resolver / Contract 层 负责规范目标、身份能力、版本化结果和错误语义 | v Helper / Transport 调用层 负责下游工具调用,不重复业务决策 | v Policy + Tests 验证 Runtime、Schema、文档和安全元数据保持一致 ``` ### 19.2 App、错误与上下文文件 | Go 文件 | 主要优化 | 设计价值 | |---|---|---| | `internal/app/event_command.go` | 注册新的 `event +listen-im` 入口 | 将 IM 事件监听纳入正式内建命令树 | | `internal/app/event_listen_im.go` | 新增监听计划编译、自然目标解析、事件类型映射及 consume 生命周期委托 | 把长期事件订阅从参数拼装提升为可验证的计划执行 | | `internal/app/runner.go` | 增强多 profile 执行和错误结果组织 | 批量或多身份运行时保留逐 profile 的失败语义 | | `internal/app/server_failure_classifier.go` | 新增服务端业务失败分类器 | 将后端文本/元数据转换为稳定 reason、retryable 和行动建议 | | `internal/auth/profiles.go` | 收敛 profile 选择和传播方式 | 减少解析与执行使用不同租户身份的风险 | | `internal/profilectx/profile.go` | 新增轻量 profile context | 让 Resolver、Shortcut 和调用层共享明确身份上下文 | | `internal/errors/errors.go` | 扩展 typed error、server diagnostics 和结构化输出字段 | 使错误可被 Agent 解析并驱动后续动作 | | `internal/cli/param_aliases_generated.go` | 发布新增 IM 参数别名 | 兼容旧参数拼写,同时维持 canonical payload | | `internal/helpers/aisearch.go` | 补齐搜索目标兼容与参数映射 | 为自然人名解析提供更稳定的下层搜索入口 | | `internal/helpers/chat.go` | 对齐 Chat 底层命令边界、参数与确认相关行为 | 使 shortcut 与底层 helper 的真实能力一致 | 这一组文件的核心优化是:把 profile、错误和生命周期从“隐式全局状态”变为显式上下文与类型化结果。通讯录、文档等领域若只复用 Resolver 而不复用这些基础设施,仍可能出现跨租户误操作或不可恢复错误。 ### 19.3 Chat Runtime 与契约文件 | Go 文件 | 主要优化 | 设计价值 | |---|---|---| | `internal/shortcut/chat/identity_contract.go` | 新增用户、机器人、webhook 等身份契约 | 把“谁在执行”从命令分支提升为显式类型 | | `internal/shortcut/chat/im_contract.go` | 新增 IM 列表、写结果与公共字段契约 | 为消息、会话和批处理提供稳定域模型 | | `internal/shortcut/chat/unified_send.go` | 重构统一发送的目标解析、身份路由、批量发送和能力拒绝 | 用户表达发送意图,Runtime 决定合法执行器 | | `internal/shortcut/chat/batch_write.go` | 统一批量写去重、成功/失败账本和未知状态 | 支持部分失败审计与安全重试 | | `internal/shortcut/chat/chat_conversation.go` | 增强会话列表分页、投影和完整性字段 | 单页与全量模式都返回可执行 continuation | | `internal/shortcut/chat/chat_group.go` | 对齐群目标、成员和群操作的输入输出 | 群名与稳定 ID 共用同一解析语义 | | `internal/shortcut/chat/chat_message.go` | 统一消息读取、回复及消息字段投影 | 后续回复、转发、下载使用 canonical message identity | | `internal/shortcut/chat/message_export.go` | 新增消息 ledger 的临时写入、校验和原子发布 | 失败时不留下伪成功的半成品文件 | | `internal/shortcut/chat/resource_download.go` | 对齐资源下载与安全输出流程 | 下载与导出共享工作区相对路径和原子提交原则 | | `internal/shortcut/chat/file_replace.go` | 非 Windows 平台使用 `os.Rename` 提供覆盖替换 | 保持平台默认的原子替换语义 | | `internal/shortcut/chat/file_replace_windows.go` | Windows 平台使用 `windows.Rename` | 修复 `os.Rename` 无法覆盖既有目标的差异 | | `internal/shortcut/chat/lark_alignment.go` | 补齐创建群、回复等对齐型 Shortcut | 将常用意图封装成稳定 Golden Route | | `internal/shortcut/chatmsg/chatmsg.go` | 大幅增强 `im.message-list.v1` 投影、消息身份、引用、资源、reaction 和分页 | 隔离下游消息响应差异,为 Agent 提供稳定机器结构 | 这组文件形成了一条清晰的数据流: ```text Target + Identity -> Unified Route -> 下层调用 -> Message/Conversation Projection -> Typed Ledger -> Atomic Export / Resource Reference ``` 其中 `identity_contract.go`、`im_contract.go` 和 `chatmsg.go` 是最值得跨领域复用的部分:前两者展示如何建立类型化边界,后者展示如何把供应商响应投影成稳定领域模型。 ### 19.4 Smart Shortcut 与 Resolver 文件 | Go 文件 | 主要优化 | 设计价值 | |---|---|---| | `internal/shortcut/targetresolver/resolver.go` | 新增统一用户/群目标解析、候选去重、精确匹配、跨页搜索和结构化歧义错误 | 消除各命令独立猜测 ID/名称的根因 | | `internal/shortcut/smart/resolve.go` | 将旧的局部解析逻辑收敛到共享 Resolver | 降低重复实现和规则漂移 | | `internal/shortcut/smart/chat_messages.go` | 重构自然群/用户目标、typed 分页、去重、结果上限与导出 | 将消息历史变为有界、可证明完整的读取流程 | | `internal/shortcut/smart/group_members.go` | 统一群解析、用户/机器人分桶、用户分页和稳定 ID 去重 | 对复杂成员列表公开完整性和失败信息 | | `internal/shortcut/smart/at_me.go` | 对齐群目标解析、消息投影与资源引用 | 特殊消息查询仍复用公共契约 | | `internal/shortcut/smart/search_msg.go` | 统一群与发送者自然目标解析 | 多条件搜索在远端调用前完成消歧 | | `internal/shortcut/smart/dm.go` | 将单聊发送收敛到统一身份和目标规则 | 避免单聊维护另一套发送实现 | | `internal/shortcut/smart/send_to_group.go` | 将群发送收敛到 unified send | 群名、群 ID 和身份选择走同一路由 | | `internal/shortcut/smart/thread_replies.go` | 对齐线程回复的消息投影和资源字段 | 线程场景不再产生独立返回结构 | | `internal/shortcut/runner.go` | 增强 shortcut flag、约束、隐藏兼容参数和确认处理 | Runtime metadata 能真实映射到 Cobra 行为 | | `internal/shortcut/types.go` | 扩展 Shortcut/Flag 类型信息 | 为 Schema 生成和 Runtime 装配提供统一源数据 | 这一层最重要的代码优化是“业务适配器变薄”。Smart 命令不再负责全部搜索、能力选择、分页和投影,而是组合共享 Resolver、contract 和执行器。这种组合方式比复制命令模板更适合通讯录和文档扩展。 ### 19.5 Policy Go 文件 #### `scripts/policy/multi-im-skill-chain/main.go` 该文件新增了一套可执行的一致性检查器,读取意图 manifest、有效 CommandRegistry、selection hints 和 Markdown marker,验证: - 每个关键意图是否有唯一 preferred tool; - fallback 是否有明确 reason code; - forbidden default 是否不会重新成为首选; - source refs 和文档 marker 是否存在; - typed contract 与 event handoff 参考是否漂移; - 已退休脚本是否被重新发布; - confirmation metadata 是否被降级。 它把“评审规则”实现成 Go 程序,能够直接加载项目真实 Registry,而不是靠文本搜索猜测命令是否存在。这种 Policy-as-Code 模式可抽象成通讯录、文档等产品共用的领域 manifest 校验器。 ### 19.6 边界收口类生产 Go 文件逐项总结 除新增核心抽象外,PR #860 还对既有 Runtime 的关键边界做了系统收口: | Go 文件 | 主要优化 | 整体价值 | |---|---|---| | `internal/helpers/chat.go` | 为删除会话分类、清空消息增加真实 `--yes` 门禁;安全化示例;删除旧 hint 命令 | 补齐 Runtime confirmation truth 和 Golden Route 收口 | | `internal/shortcut/chat/chat_conversation.go` | 在单页退出前解析 `nextCursor` | 修复 #860 的 continuation 缺陷 | | `internal/shortcut/chat/resource_download.go` | 兼容 Windows 盘符、反斜杠和 portable `..` 逃逸检查 | 将 #860 文件安全扩展到跨平台 | | `internal/shortcut/runner.go` | 所有 flag 类型改用 Cobra `*P` 注册 | 提供通用 shorthand 基础能力 | | `internal/shortcut/types.go` | `Flag` 新增 `Shorthand` | 让 Runtime、Schema 和 CLI 共享短参数定义 | | `internal/shortcut/smart/local_chat_validation.go` | 新增时间格式、默认 page limit 和结构化本地错误辅助函数 | 将散落校验收敛为公共层 | | `internal/shortcut/smart/chat_messages.go` | 增加时间/页大小校验和 `-o`;复用默认分页函数 | 阻止无效远端调用,改善常用导出体验 | | `internal/shortcut/smart/at_me.go` | 校验 1~3650 天及正数 limit | 为特殊读取命令建立明确资源边界 | | `internal/shortcut/smart/thread_replies.go` | 校验时间格式和正数 limit | 与消息列表使用相同本地校验语义 | | `internal/shortcut/smart/unread_chats.go` | 校验显式 count 大于 0 | 避免负数/零值被静默当作默认值 | | `internal/shortcut/smart/group_members.go` | 复用统一默认 page limit | 去除重复默认值分支 | | `scripts/policy/multi-im-skill-chain/main.go` | 对 getwd、exit、open、Registry build/bind 增加可注入接缝 | 不改变生产行为的前提下完整覆盖失败分支 | 这组代码体现了三种“小而关键”的优化: 1. **修顺序**:cursor 必须先提取,再决定是否继续翻页。 2. **提公共函数**:时间和分页校验不在多个 Smart 命令中复制。 3. **建测试接缝**:只对进程/I/O 边界注入,不侵入业务逻辑。 ### 19.7 Go 测试文件优化总结 #### 核心架构行为测试 首先建立新能力的行为基线: - `targetresolver/resolver_test.go`:唯一命中、多命中、稳定 ID 和结构化候选; - `unified_send_resolution_test.go`:身份、自然群目标、批量发送和写入前失败; - `chatmsg_test.go`:消息身份、正文、引用、reaction、资源和分页投影; - conversation/message/group 相关测试:typed list、分页和兼容字段; - event listen 测试:计划编译、事件映射、生命周期和清理回滚; - failure classifier/errors 测试:JSON、人类输出和服务端诊断; - Multi IM Policy 测试:意图路由、Reference、retired script 和 contract marker。 #### 边界与交付证据测试 其次将测试从“新增功能验证”扩展到“边界与交付证据”: - 将 public Shortcut Schema 覆盖固定为 265/265、Chat 固定为 97/97; - 验证 47 个新增 Schema 项的风险分类与 Runtime gate; - 验证确认前零调用、确认后精确一次调用; - 覆盖 cursor 缺失、无效、停滞、page limit 和后续页失败; - 覆盖跨页同名目标仍保持歧义; - 覆盖 Unix/Windows 路径、symlink、原子写和 no-clobber; - 覆盖 shorthand、alias payload 等价和本地参数失败; - 通过 `CrossPlatformCoverage` 测试集合证明 changed-code coverage。 测试策略可概括为: ```text 行为测试:证明新架构“能正确工作” 交付测试:证明所有公开入口“都被发布、都受约束、所有失败边界都不会越权执行” ``` ### 19.8 Go 层面的核心可复用模式 | 模式 | Go 实现方式 | 适用场景 | |---|---|---| | Typed Resolver | 独立 package + canonical result + structured candidates | 通讯录用户/部门、文档/空间、云盘文件 | | Thin Adapter | Smart Shortcut 组合 Resolver、contract 和 executor | 所有自然语言 Golden Route | | Contract Projection | 独立 projector 将 `map[string]any` 转为稳定 v1 envelope | 列表、搜索、事件、批处理结果 | | Fail Closed | cursor/目标/身份不完整时停止,不猜测默认值 | 写操作和需要完整集合的读取 | | Bounded Collector | page limit、max results、seen cursor、stable dedupe | 成员、文档块、文件、审批记录 | | Atomic Publisher | temp file、close/check、rename、no-clobber | 导出、下载、备份和迁移 | | Typed Confirmation | metadata 声明 + Runtime 强制 gate + 零调用测试 | 删除、覆盖、转移、权限修改 | | Injectable Boundary | 将 getwd/open/exit/build 等边界封装为函数变量 | CLI、Policy 和后台任务的失败分支测试 | | Policy as Code | Go 加载真实 Registry/manifest 并做语义校验 | 跨产品 Schema、Skill 和 Runtime 一致性 | Go 文件层面的核心结论是:PR #860 同时完成“抽象公共机制”“收紧运行边界”和“补齐验证证据”。复用到其他领域时,应优先复制 package 边界、类型契约和测试不变量,而不是复制具体命令函数。 ## 20. 当前 #860 的整体价值与跨场景复用 ### 20.1 从“运行时能力”到“可交付产品能力” PR #860 定义了一个更完整的 Done: | 完成层级 | 判断标准 | 对应 PR | |---|---|---| | 能执行 | Runtime 有真实实现 | #860 | | 行为稳定 | Resolver、分页、typed result/error 有契约 | #860 | | 可发现 | Registry 和 Schema 正式发布 | #860 | | 会选择 | selection hints 有 use/avoid 语义 | #860 | | 执行安全 | local validation、capability 和 typed confirmation 生效 | #860 | | 可维护 | 文档分层、生成漂移、Policy 和覆盖门禁闭环 | #860 | 因此,新领域不能把“代码合入”当成交付完成。一个通讯录或文档 Shortcut 只有同时满足上表六层,才真正适合 Agent 使用。 ### 20.2 通讯录复用重点 在 `DirectoryResolver` 蓝图之上,应补充 PR #860 展示的发布治理: - 每个 user/department Shortcut 都进入 authored Registry; - selection hints 区分“查人”“查关注的人”“查某人最近消息”等易混意图; - 删除、调岗、批量邀请标记为 write/high 并使用 typed confirmation; - 手机号、邮箱格式和分页大小在本地校验; - 联系人导出路径使用 portable workspace-relative 规则; - completeness test 禁止公开 Shortcut 依赖长期 exclusion。 ### 20.3 文档与云盘复用重点 在原有 `DocumentTargetResolver`、revision/etag 和原子导出蓝图之上,应补充: - read、write、destructive 风险 metadata; - 覆盖、删除、权限回收必须 typed confirmation; - 示例默认使用 dry-run 或 plan,不直接带确认参数; - 文档 URL、路径和目标 revision 在本地尽可能校验; - 导出/下载同时识别 Unix 与 Windows 路径语义; - 生成 catalog 必须能追溯到 authored Registry 和 contract Reference。 ### 20.4 推荐的跨产品发布清单 1. Runtime 已实现且有单元测试。 2. 目标解析支持稳定 ID 和自然目标,并对歧义 fail closed。 3. 列表返回完整性与 continuation。 4. 写操作声明 effect/risk/idempotency。 5. Runtime gate 与 metadata 的 confirmation 完全一致。 6. Registry 有 canonical path。 7. Selection hints 有 use/avoid/source refs。 8. Schema 和 Agent metadata 已生成且 drift clean。 9. 文档第一示例不绕过确认。 10. public shortcut completeness、policy 和 changed-code coverage 通过。 ## 21. 综合验证、剩余风险与最终结论 ### 21.1 验证事实需要分层理解 PR #860 当前描述中记录的验证包括: - `DWS_PACKAGE_VERSION=0.0.0-test go test ./internal/app -count=1` - `DWS_PACKAGE_VERSION=0.0.0-test go test ./internal/shortcut/chat ./internal/shortcut/smart -count=1` - `make policy` - generated drift clean - Schema compatibility 与 authoritative interface integrity 通过 - 26 products / 898 tools - 176 个 Runtime confirmation gate - 1,108 个 reviewed Agent examples 上述是 PR 作者提供的本地验证记录。本报告通过 GitHub 状态另行确认:head `33ceab60...` 的 `AI Behavior` 为 success,但 PR-triggered `CI` workflow run #1802 已完成且结论为 failure。因此不能把 PR 描述中的本地验证等同于“GitHub CI 全绿”;失败原因需结合 Actions job/log 单独排查。 ### 21.2 仍需关注的风险 1. **CI 未全绿**:当前 PR 仍为 Open,CI run #1802 显示 failure,应追踪失败 job,确认是否为基础设施、覆盖门禁或真实回归。 2. **大型生成物审查成本**:5 万余行增量主要是生成物,应持续保证 authored source、hash 和 drift check 可重建。 3. **Skill 上下文预算**:当前 Chat Skill 为 9,928 bytes,已回到 10,000 bytes 预算内,但余量仅 72 bytes,应继续把细节下沉到 Reference。 4. **字段文档漂移**:`results` / `succeeded` 等批处理字段仍需合同化校验。 5. **Shorthand 冲突**:框架支持 shorthand 后,应增加每个命令内唯一性和保留字符检查。 6. **确认不是幂等**:39 个写命令的 idempotency 仍为 `unknown`;确认只能降低误触发,不能解决重试重复写入。 7. **命名式覆盖标记**:大量测试依赖 `CrossPlatformCoverage` 前缀,长期可考虑用测试清单或覆盖映射替代命名耦合。 ### 21.3 最终结论 PR #860 将“可靠执行 IM 意图”和“完整、安全、可发现地交付能力”合并为同一套工程系统。其核心方法是: ```text Intent First -> Golden Route -> Typed Target Resolver -> Local Validation + Preflight Capability Check -> Typed Confirmation -> Bounded Runtime -> Versioned Result / Error Ledger -> Authored Registry + Selection/Metadata -> Generated Schema / Agent Metadata -> Policy + Drift + Coverage Gates ``` 这条链路可以直接作为通讯录、文档、云盘、知识库等新领域的标准交付模板。最关键的认知是:**Runtime 正确只是起点;只有发现、选择、安全、契约和门禁同时闭环,能力才真正完成产品化。**