# DWS Multi IM 错误专项优化方案 > 日期:2026-08-03 > 分支:`codex/multi-im-optimization` > 当前提交:`30314311` > 评测基线:`2a8c6c87`,155 条 accepted 轨迹、74 次失败调用 ## 1. 结论 74 次失败已经能够完整归因,不应继续使用“API 业务错误”或“其他命令失败”这类粗分类: | 根因 | 次数 | 判断 | |---|---:|---| | 群名解析歧义 | 37 | Runtime 正确 fail-closed;评测夹具和 Agent 消歧流程问题 | | 稳定 ID 进入群名参数 | 17 | Shortcut 目标参数合同不一致 | | 群名进入稳定 ID 参数并打到后端 | 7 | Shortcut 目标参数合同不一致,且缺少调用前校验 | | 不存在的命令或 flag | 11 | 选路/Help 漂移及缺少无歧义兼容别名 | | `auth whoami` 不存在 | 1 | MCP 错误误导后的错误恢复分支 | | MCP 内部依赖不可用 | 1 | 后端 `queryToolMeta` 内部连接拒绝;CLI 错分为业务错误 | | **合计** | **74** | | 其中真正带 `NETWORK_ERROR / UNAVAILABLE / Connection refused` 证据的只有 1 次。它不是用户侧公网故障,也不是 `+conversation-list` 参数拼装错误,而是 MCP 后端内部依赖不可用。 ## 2. 本地复测 ### 2.1 真实 MCP 使用当前提交重新构建 CLI,执行: ```bash dws chat +conversation-list --page-all --format json --timeout 60 ``` 6 轮已验证结果全部为: ```text exit=0 complete=true count=100 failedCount=0 ``` 此外,评测提交 `2a8c6c87` 重新构建后同一命令也成功;该提交到当前提交之间,`+conversation-list` 的 MCP 工具名、参数和分页代码没有变化。 ### 2.2 原始失败确定性回放 本地 loopback MCP 返回评测中的等价内容: ```json { "success": false, "server_error_code": "NETWORK_ERROR", "technical_detail": "McpService.queryToolMeta ... StatusCode.UNAVAILABLE ... Connection refused", "retryable": true } ``` 当前 CLI 稳定输出: ```text category=api reason=business_error message=business error: success=false hint=Check required parameters and values server_error_code=NETWORK_ERROR retryable=true ``` 这证明需要修的是中央错误分类:诊断字段已经保留,但没有参与 `reason/message/hint` 决策。 ### 2.3 其他代表错误复测 当前 Runtime 可稳定复现: 1. `+chat-members-list --group ` 在调用后端前失败,并提示“请使用 --group 或 --chat”,但当前命令的稳定 ID 参数实际是 `--conversation-id`;提示与命令合同矛盾。 2. `+chat-messages --group <群名>` 把群名直接交给只接受 CID 的下层,后端返回 `openCid or cid is required`;应在 CLI 内先解析,不能打到后端。 3. `+search-msg --text ...` 返回 `unknown_flag`;当前正式参数是 `--query`。 ## 3. P0:修中央错误分类 ### 3.1 分类顺序 修改 `internal/app/runner.go`,在通用 `business_error` 之前执行结构化分类: ```text ExtractServerDiagnostics → ClassifyServerFailure → 构造 typed Error → 无已知类别才回退 business_error ``` 建议新增独立分类器,例如 `internal/app/server_failure_classifier.go`。输入为 MCP 内容和 `ServerDiagnostics`,输出: ```go type ServerFailureClass struct { Reason string Origin string Stage string ExecutionStarted *bool Hint string Actions []string } ``` 首批规则: | 条件 | reason | origin | stage | |---|---|---|---| | `NETWORK_ERROR` 且 detail 含 `queryToolMeta`、`UNAVAILABLE` | `backend_dependency_unavailable` | `mcp_gateway` | `tool_metadata_lookup` | | `PARAM_ERROR`、缺 required 参数 | `invalid_request` | `dingtalk_api` 或 `mcp_gateway` | `tool_validation` | | 权限/Scope | `permission_denied` | 实际返回方 | `authorization` | | 资源不存在 | `resource_not_found` | `dingtalk_api` | `tool_execution` | | 无结构化证据 | `business_error` | `unknown` | `unknown` | 兼容期可以用 `technical_detail` 补充 stage,但 `server_error_code` 必须是主分类依据。原始 `trace_id`、`technical_detail` 和 `retryable` 原样保留。 ### 3.2 重试边界 当前响应没有稳定的 `execution_started` 字段,因此第一阶段只修分类和恢复提示,不对所有工具自动重试。 - 后端应优先在 `queryToolMeta` 内部做短重试,因为它明确发生在业务执行前; - CLI 只有在后端提供 `execution_started=false` 后,才可对读写命令统一安全重试; - 过渡期最多只对最终 Schema 标记为只读的命令重试一次; - 写命令执行状态未知时禁止自动重试。 ## 4. P0:统一群目标参数合同 24 次失败来自同一个设计问题:不同 Shortcut 对 `--group` 的含义不同。 统一公开合同: | 参数 | 统一语义 | |---|---| | `--group` | 接受群名或 `openConversationId`;CLI 自动识别 | | `--conversation-id` | 显式稳定 ID | | `--chat-query` | 显式自然群名 | | `--chat` / `--open-conversation-id` | 只作为无歧义兼容别名,不作为文档默认入口 | 新增共享入口,例如: ```go ResolveChatTarget(rt, directValue, queryValue) ``` 规则: 1. directValue 形似 CID:直接返回,不进行群名搜索; 2. directValue 不是 CID:按自然群名走完整分页唯一解析; 3. queryValue 形似 CID:规范化为稳定 ID,而不是送入群名搜索; 4. 零命中或多命中继续 fail-closed; 5. 任何自然群名都不得直接发送到只接受 CID 的后端工具。 首批应用: - `internal/shortcut/smart/group_members.go` - `internal/shortcut/smart/chat_messages.go` - `internal/shortcut/smart/search_msg.go` - 复核 `send_to_group.go`、`at_me.go` 和 `chat/chat_group.go`,消除同类局部实现。 同时修改 `ResolveChat` 的类型错误:不要输出与调用命令不一致的具体 flag,改为 typed `target_type_mismatch`,并提示读取当前 leaf Help。 ## 5. P0:处理 11 次命令/flag 漂移 只增加语义唯一、不会制造第二入口的兼容项: | 观察到的错误 | 决策 | |---|---| | `+search-msg --text`(3 次) | 增加 `--text` → `--query` 隐藏兼容别名 | | `+search-msg --text-query`(1 次) | 增加 `--text-query` → `--query` 隐藏兼容别名 | | `+chat-messages --page-size`(1 次) | 增加 `--page-size` → `--limit` 隐藏兼容别名 | | `+chat-messages --open-conversation-id`(1 次) | 增加稳定 ID 兼容别名 | | `+chat-members-list --chat`(2 次) | 增加稳定 ID 兼容别名 | | `+chat-members-list --open-conversation-id`(2 次) | 增加稳定 ID 兼容别名 | | `+chat-rename`(1 次) | 作为 `+chat-update` 的隐藏兼容别名,`--group` 复用群名/CID 唯一解析;Skill 默认仍只推荐 reviewed `chat group rename --id --name` | 所有新增 flag 必须进入真实 Cobra、Shortcut constraints、Schema 参数投影和示例校验;不能只改 Skill。 `auth whoami` 不建议补一个语义不清的新命令。MCP 内部依赖错误修正后,恢复动作应直接提示“稍后重试并保留 trace ID”,不得诱导 Agent 检查认证。认证确需检查时只推荐现有 `dws auth status` 和 `dws profile list`。 ## 6. P1:同名群与评测口径 37 次 `resolution_ambiguous` 是安全机制正常工作,不能通过默认第一个候选来消除。 需要同时处理两层: 1. 真实产品:继续返回结构化 candidates,Agent 展示候选并停止写入;可在下层真实提供时增加群主、群类型等可理解的区分字段。 2. 固定评测:所有要求命中 `dws测试群01/02` 的 case 都必须在 accepted run 前保证目标名称唯一,而不是只处理 H15/H18/H20;夹具状态和恢复记录继续留在 Prompt 外。 评测报告应把错误维度分开: ```text agent_error request_contract_error expected_resolution_stop business_error infrastructure_error recovered_infrastructure_error ``` 若环境真实存在同名群,`resolution_ambiguous` 应计入安全停机事实,不应伪装成业务成功;若 case 设计要求唯一群,则应判夹具失败并重跑,而不是污染 Agent clean。 ## 7. 测试与验收门禁 ### 7.1 错误分类 - `success=false + NETWORK_ERROR + queryToolMeta/UNAVAILABLE` → `backend_dependency_unavailable`; - `success=false + PARAM_ERROR` → `invalid_request`; - 无诊断字段的 `success=false` 保持 `business_error`; - JSON 输出中的 `reason/hint/retryable/server_error_code/trace_id` 与 typed error 一致; - 不再对内部依赖错误提示“检查参数或认证”。 ### 7.2 目标合同 对 `+chat-members-list`、`+chat-messages`、`+search-msg` 分别覆盖: - `--group ` 不搜索,且下层收到同一 CID; - `--group ` 唯一解析后下层只收到 CID; - `--conversation-id ` 不搜索; - `--chat-query ` 唯一解析; - `--chat-query ` 不再触发“ID 被当群名”; - 多候选时下层业务调用为 0; - 群名绝不进入 `openCid/cid/openConversationId` 参数。 ### 7.3 兼容参数 - 每个兼容 alias 与 canonical flag 产生完全相同的 invocation; - alias 与 canonical 同时出现时按 constraints 拒绝,禁止静默覆盖; - Help 只突出 canonical flag,Schema 保留 Agent 可执行事实; - Skill 和 reference 不把兼容 alias 写成新的默认路线。 ### 7.4 最终门禁 ```bash gofmt -w DWS_PACKAGE_VERSION=0.0.0-test go test ./internal/errors ./internal/transport ./internal/app ./internal/shortcut/targetresolver ./internal/shortcut/chat ./internal/shortcut/smart go generate ./internal/cli ./scripts/policy/check-generated-drift.sh ./scripts/policy/check-schema-catalog.sh ./scripts/policy/check-skill-commands.sh DWS_PACKAGE_VERSION=0.0.0-test go test ./... ``` 真实环境至少再跑: ```bash dws chat +conversation-list --page-all --format json dws chat +chat-members-list --group <真实群名或CID> --format json dws chat +chat-messages --group <真实群名或CID> --limit 5 --format json dws chat +search-msg --chat-query <真实群名> --query <关键词> --format json ``` 验收不是简单要求 74 变 0,而是: - 1 次 MCP 内部故障被正确分类并进入独立可靠性指标; - 24 次目标类型错误在 CLI 内确定性消除; - 11 次无歧义 flag 漂移不再发生; - 1 次错误认证分支不再由误导 hint 触发; - 37 次同名群由正确夹具或显式用户消歧处理,Runtime 继续禁止猜第一个候选。 ## 8. 优化实施拆分 优化按错误归属拆成四个可以独立验证的交付单元,避免 Runtime、Skill 和评测夹具混在同一个改动里。 ### 8.1 交付 A:错误语义与恢复动作 范围: - `internal/app/runner.go` - `internal/transport/diagnostics.go` - `internal/errors/` - 新增中央 server failure classifier 及测试 交付结果: 1. `NETWORK_ERROR + queryToolMeta/UNAVAILABLE` 不再落入 `business_error`; 2. JSON 输出具备稳定的 `origin/stage/reason`; 3. 参数、权限、资源和基础设施错误给出不同恢复动作; 4. 未确认 `execution_started=false` 前不对写请求自动重试; 5. 错误提示不再诱导 Agent 执行无关认证命令。 该交付只改变错误解释和恢复合同,不改变 `+conversation-list` 的请求拼装。 ### 8.2 交付 B:统一群目标 Runtime 范围: - `internal/shortcut/targetresolver/` - `internal/shortcut/smart/group_members.go` - `internal/shortcut/smart/chat_messages.go` - `internal/shortcut/smart/search_msg.go` - 其他调用 `ResolveChat` 的 Chat Shortcut 交付结果: 1. 群名和 CID 在进入下层前完成类型规范化; 2. 稳定 CID 永远不进入 `search_groups`; 3. 自然群名永远不直接进入 `openCid/cid/openConversationId`; 4. 相同的参数名在高频 Shortcut 中表达相同语义; 5. 零命中、多候选和类型不匹配都使用 typed resolution error。 这是消除 24 次目标类型错误的主要 Runtime 改动。 ### 8.3 交付 C:兼容参数、Schema 与 Skill 收敛 范围: - 对应 Shortcut 的真实 Cobra flags/constraints - `internal/cli/schema_hints/metadata/chat.json` - `internal/cli/schema_hints/selection/chat.json` - `skills/multi/dingtalk-chat/SKILL.md` - 精确 Chat references - 生成的 Schema/Agent metadata/Catalog 交付结果: 1. 增加经过评审的无歧义兼容 flag,但 Help/Skill 只推荐 canonical flag; 2. 修改群名只推荐现有 reviewed `chat group rename --id --name`;已观测的 `+chat-rename` 仅作为隐藏兼容别名,不成为第二默认入口; 3. `unknown command/unknown flag` 后只查精确 leaf Help并最多重试一次; 4. 示例必须通过真实 Cobra 和 Schema constraints; 5. Skill 不保存固定测试群名、CID 或评测 case 文案。 ### 8.4 交付 D:评测夹具与 judge 分层 范围:评测仓库或 runner,不进入 DWS Runtime 业务逻辑。 交付结果: 1. 所有依赖指定测试群名的 case 在 accepted run 前保证目标唯一; 2. 夹具创建、隔离和恢复有稳定 ID 审计记录,但不注入 Prompt; 3. `expected_resolution_stop`、`request_contract_error` 和 `infrastructure_error` 分开统计; 4. infrastructure retry 即使恢复成功,也保留独立可靠性计数; 5. clean 不再把正确的安全停机和错误请求拼装混成一种失败。 ## 9. 评测拟合方案 ### 9.1 拟合对象 只拟合以下稳定模式: - 错误结构:`server_error_code`、MCP stage、retryability; - 意图结构:群名、稳定 CID、搜索关键词、分页参数; - 命令合同:canonical flag、无歧义兼容 alias、constraints; - 安全语义:多候选禁止默认第一项、写操作禁止不安全重试; - 结果合同:complete/partial/failures 和 typed error。 禁止拟合: - `H4/H8/...` 等 case 编号; - `dws测试群01/02` 字面值; - 评测中出现的固定 CID、用户 ID 或 trace ID; - 针对某条 Prompt 的字符串分支; - 为提高 clean 而把非零退出统一改为成功。 ### 9.2 数据集拆分 采用三层数据集: | 数据集 | 用途 | 内容 | |---|---|---| | observed | 修复已观察问题 | 当前 155 条 accepted、74 次失败调用 | | variant | 验证泛化 | 替换群名、CID、关键词、flag 顺序、是否显式指定 ID | | safety holdout | 防止过拟合 | 同名群、零命中、跨 profile ID、写请求执行状态未知、分页中途失败 | Runtime 单元测试和本地 MCP E2E 使用合成稳定 ID,例如 `cid-test-a`,不得依赖真实评测资源。真实环境 smoke 只验证集成连通性和输出合同,不把真实业务数据写入测试快照。 ### 9.3 变体矩阵 每个群目标型 Shortcut 至少覆盖: | 维度 | 变体 | |---|---| | 目标输入 | 唯一群名、CID、模糊群名、同名群、零命中 | | 参数入口 | canonical flag、兼容 alias、canonical+alias 冲突 | | 命令类型 | 只读、普通写、高风险写 | | 后端错误 | 参数错误、权限错误、资源不存在、限流、内部依赖不可用 | | 执行阶段 | 调用前、元数据查询、业务执行、分页后续页 | | 结果状态 | complete、partial、fail-closed、retry recovered | 兼容 alias 只需证明与 canonical invocation 等价,不为每个 alias 复制完整业务测试矩阵。 ### 9.4 回归顺序 ```text typed unit tests → fake MCP CLI E2E → Schema/Skill policy → observed 31×5 回放 → variant 集 → safety holdout → 小规模真实环境 smoke ``` 只有前四层通过后才看 clean 指标。若 clean 提升但 safety holdout 出现自动选第一个群、重复写或 partial 冒充 complete,则判优化失败。 ### 9.5 指标 主指标: - 请求合同错误数; - 首次 canonical 命令/flag 命中率; - 自然目标到稳定 ID 的一次成功率; - false success、错误写入和重复写入; - 错误分类准确率; - complete/partial 真实性。 分层可靠性指标: - MCP/网关基础设施失败率; - 安全自动重试恢复率; - 未恢复 infrastructure error 数; - trace ID 可关联率。 次级指标: - clean run 比例; - 每 case 业务 CLI 次数; - unknown command/flag 后的纠错次数; - Token 和执行时长。 clean 不能替代主指标,也不能通过过滤真实失败直接提升。 ## 10. 预期收敛与发布判定 基于当前 74 次失败,实施后的预期归宿是: | 当前错误 | 目标状态 | |---|---| | MCP 内部依赖失败 1 次 | 正确分类;满足安全条件时恢复,否则进入 infrastructure 指标 | | 错误认证分支 1 次 | 由精确恢复动作消除 | | 目标类型错误 24 次 | Runtime 规范化后消除 | | 命令/flag 漂移 11 次 | canonical 路由或兼容 alias 消除 | | 同名群歧义 37 次 | 唯一夹具下正常执行;真实歧义下继续安全停机 | 发布必须同时满足: 1. observed 集中的确定性合同错误归零; 2. variant 集不依赖固定群名和 CID; 3. safety holdout 的误选、误写和重复写为 0; 4. MCP 基础设施错误分类准确,不再显示参数/认证误导; 5. 全仓、Schema、Skill 和真实只读 smoke 全部通过; 6. 评测报告同时给出 clean、业务正确性、安全停机和基础设施可靠性,不再只给一个混合分数。 ## 11. 当前分支实施与验证记录 > 实施日期:2026-08-03 > 实施分支:`codex/multi-im-optimization` ### 11.1 已实施 1. 中央错误分类已在两条 HTTP MCP 失败路径生效: - `NETWORK_ERROR` / `queryToolMeta` / `UNAVAILABLE` 分类为 `backend_dependency_unavailable`; - 输出 `origin` / `stage` / `reason`,保留 trace、server code 和 retryable; - 后端未显式返回时不猜测 `execution_started`; - multi-profile 聚合保留错误恢复语义、resolution candidates 和诊断字段。 2. 群目标合同已收敛到 `ResolveChatTarget`: - CID 不进入 `search_groups`; - 群名唯一解析后才传入业务工具; - 零命中、多候选、不完整分页继续 fail-closed; - 已应用到成员、会话消息、消息搜索、@ 我、群机器人/邀请、按群发送和群重命名。 3. 11 次命令/flag 漂移的 Runtime 兼容已补齐: - `--text` / `--text-query`、`--page-size`、`--open-conversation-id`、成员命令的 `--chat`; - `+chat-rename` 作为 `+chat-update` 的隐藏兼容别名,群名依然在写前唯一解析; - canonical 与 alias 同时出现时拒绝静默覆盖。 4. Chat Skill、群/消息 reference、reviewed selection hints 与生成 Schema Catalog 已同步。 ### 11.2 自动验证 - 本地可控 MCP 重放通过真实 `executeInvocation → HTTP MCP → success=false → typed Error` 路径; - 聚焦错误、resolver、Chat Shortcut 和 app 回归通过; - `go generate ./internal/cli` 通过; - `check-generated-drift.sh` 通过; - `check-schema-catalog.sh` 通过:26 products / 849 tools; - `check-skill-commands.sh` 通过:1009 executable command paths; - Skill `quick_validate.py` 通过; - Agent 示例合同通过:1047 total / 1023 contract / 24 dry-run; - `DWS_PACKAGE_VERSION=0.0.0-test go test ./...` 全仓通过; - `go build -o /dws ./cmd` 真实构建通过。仓库文档中的裸 `go build ./cmd` 会因根目录已有 `cmd/` 目录而报输出同名冲突,因此验证时显式指定了临时 `-o`。 ### 11.3 真实环境 smoke 使用当前登录 profile 和本次构建产物执行,不保存真实群名或 CID 到仓库: - `+conversation-list --page-all`:完整返回; - `+chat-members-list --group <真实群名>`:唯一解析成功,`complete=true`; - `+chat-members-list --open-conversation-id <真实CID>`:兼容 alias 成功; - `+chat-messages --group <真实群名>` 与 `--open-conversation-id/--page-size`:均成功,单页返回正确标记 `complete=false / hasMore=true / stopReason=single_page`; - `+search-msg --chat-query <真实CID> --text-query <无命中词>`:不搜群名,搜索完整返回; - `+chat-rename --group <真实CID> ... --dry-run`:返回 `executed=false`且 invocation 使用正确 CID,未修改真实群。 ### 11.4 仓库外后续项 评测夹具唯一性与 judge 分层属于评测 runner,不在本 DWS CLI 仓库中。Runtime 修复后应重跑 31×5 observed、variant 和 safety holdout;真实同名群仍必须保持 `resolution_ambiguous` 安全停机,不以默认第一个候选换 clean。