Files
dingtalk-workspace-cli/docs/pr-860-multi-im-optimization-analysis.md

1717 lines
87 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-<domain>/
SKILL.md
references/
intent-guide.md
contracts.md
<domain>.md
<topic-a>.md
<topic-b>.md
internal/shortcut/
<domain>/
<domain>resolver/
scripts/policy/
<domain>-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 正确只是起点;只有发现、选择、安全、契约和门禁同时闭环,能力才真正完成产品化。**