20 KiB
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,执行:
dws chat +conversation-list --page-all --format json --timeout 60
6 轮已验证结果全部为:
exit=0
complete=true
count=100
failedCount=0
此外,评测提交 2a8c6c87 重新构建后同一命令也成功;该提交到当前提交之间,+conversation-list 的 MCP 工具名、参数和分页代码没有变化。
2.2 原始失败确定性回放
本地 loopback MCP 返回评测中的等价内容:
{
"success": false,
"server_error_code": "NETWORK_ERROR",
"technical_detail": "McpService.queryToolMeta ... StatusCode.UNAVAILABLE ... Connection refused",
"retryable": true
}
当前 CLI 稳定输出:
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 可稳定复现:
+chat-members-list --group <cid>在调用后端前失败,并提示“请使用 --group 或 --chat”,但当前命令的稳定 ID 参数实际是--conversation-id;提示与命令合同矛盾。+chat-messages --group <群名>把群名直接交给只接受 CID 的下层,后端返回openCid or cid is required;应在 CLI 内先解析,不能打到后端。+search-msg --text ...返回unknown_flag;当前正式参数是--query。
3. P0:修中央错误分类
3.1 分类顺序
修改 internal/app/runner.go,在通用 business_error 之前执行结构化分类:
ExtractServerDiagnostics
→ ClassifyServerFailure
→ 构造 typed Error
→ 无已知类别才回退 business_error
建议新增独立分类器,例如 internal/app/server_failure_classifier.go。输入为 MCP 内容和 ServerDiagnostics,输出:
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 |
只作为无歧义兼容别名,不作为文档默认入口 |
新增共享入口,例如:
ResolveChatTarget(rt, directValue, queryValue)
规则:
- directValue 形似 CID:直接返回,不进行群名搜索;
- directValue 不是 CID:按自然群名走完整分页唯一解析;
- queryValue 形似 CID:规范化为稳定 ID,而不是送入群名搜索;
- 零命中或多命中继续 fail-closed;
- 任何自然群名都不得直接发送到只接受 CID 的后端工具。
首批应用:
internal/shortcut/smart/group_members.gointernal/shortcut/smart/chat_messages.gointernal/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 是安全机制正常工作,不能通过默认第一个候选来消除。
需要同时处理两层:
- 真实产品:继续返回结构化 candidates,Agent 展示候选并停止写入;可在下层真实提供时增加群主、群类型等可理解的区分字段。
- 固定评测:所有要求命中
dws测试群01/02的 case 都必须在 accepted run 前保证目标名称唯一,而不是只处理 H15/H18/H20;夹具状态和恢复记录继续留在 Prompt 外。
评测报告应把错误维度分开:
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>不搜索,且下层收到同一 CID;--group <name>唯一解析后下层只收到 CID;--conversation-id <cid>不搜索;--chat-query <name>唯一解析;--chat-query <cid>不再触发“ID 被当群名”;- 多候选时下层业务调用为 0;
- 群名绝不进入
openCid/cid/openConversationId参数。
7.3 兼容参数
- 每个兼容 alias 与 canonical flag 产生完全相同的 invocation;
- alias 与 canonical 同时出现时按 constraints 拒绝,禁止静默覆盖;
- Help 只突出 canonical flag,Schema 保留 Agent 可执行事实;
- Skill 和 reference 不把兼容 alias 写成新的默认路线。
7.4 最终门禁
gofmt -w <modified-go-files>
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 ./...
真实环境至少再跑:
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.gointernal/transport/diagnostics.gointernal/errors/- 新增中央 server failure classifier 及测试
交付结果:
NETWORK_ERROR + queryToolMeta/UNAVAILABLE不再落入business_error;- JSON 输出具备稳定的
origin/stage/reason; - 参数、权限、资源和基础设施错误给出不同恢复动作;
- 未确认
execution_started=false前不对写请求自动重试; - 错误提示不再诱导 Agent 执行无关认证命令。
该交付只改变错误解释和恢复合同,不改变 +conversation-list 的请求拼装。
8.2 交付 B:统一群目标 Runtime
范围:
internal/shortcut/targetresolver/internal/shortcut/smart/group_members.gointernal/shortcut/smart/chat_messages.gointernal/shortcut/smart/search_msg.go- 其他调用
ResolveChat的 Chat Shortcut
交付结果:
- 群名和 CID 在进入下层前完成类型规范化;
- 稳定 CID 永远不进入
search_groups; - 自然群名永远不直接进入
openCid/cid/openConversationId; - 相同的参数名在高频 Shortcut 中表达相同语义;
- 零命中、多候选和类型不匹配都使用 typed resolution error。
这是消除 24 次目标类型错误的主要 Runtime 改动。
8.3 交付 C:兼容参数、Schema 与 Skill 收敛
范围:
- 对应 Shortcut 的真实 Cobra flags/constraints
internal/cli/schema_hints/metadata/chat.jsoninternal/cli/schema_hints/selection/chat.jsonskills/multi/dingtalk-chat/SKILL.md- 精确 Chat references
- 生成的 Schema/Agent metadata/Catalog
交付结果:
- 增加经过评审的无歧义兼容 flag,但 Help/Skill 只推荐 canonical flag;
- 修改群名只推荐现有 reviewed
chat group rename --id --name;已观测的+chat-rename仅作为隐藏兼容别名,不成为第二默认入口; unknown command/unknown flag后只查精确 leaf Help并最多重试一次;- 示例必须通过真实 Cobra 和 Schema constraints;
- Skill 不保存固定测试群名、CID 或评测 case 文案。
8.4 交付 D:评测夹具与 judge 分层
范围:评测仓库或 runner,不进入 DWS Runtime 业务逻辑。
交付结果:
- 所有依赖指定测试群名的 case 在 accepted run 前保证目标唯一;
- 夹具创建、隔离和恢复有稳定 ID 审计记录,但不注入 Prompt;
expected_resolution_stop、request_contract_error和infrastructure_error分开统计;- infrastructure retry 即使恢复成功,也保留独立可靠性计数;
- 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 回归顺序
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 次 | 唯一夹具下正常执行;真实歧义下继续安全停机 |
发布必须同时满足:
- observed 集中的确定性合同错误归零;
- variant 集不依赖固定群名和 CID;
- safety holdout 的误选、误写和重复写为 0;
- MCP 基础设施错误分类准确,不再显示参数/认证误导;
- 全仓、Schema、Skill 和真实只读 smoke 全部通过;
- 评测报告同时给出 clean、业务正确性、安全停机和基础设施可靠性,不再只给一个混合分数。
11. 当前分支实施与验证记录
实施日期:2026-08-03
实施分支:codex/multi-im-optimization
11.1 已实施
- 中央错误分类已在两条 HTTP MCP 失败路径生效:
NETWORK_ERROR/queryToolMeta/UNAVAILABLE分类为backend_dependency_unavailable;- 输出
origin/stage/reason,保留 trace、server code 和 retryable; - 后端未显式返回时不猜测
execution_started; - multi-profile 聚合保留错误恢复语义、resolution candidates 和诊断字段。
- 群目标合同已收敛到
ResolveChatTarget:- CID 不进入
search_groups; - 群名唯一解析后才传入业务工具;
- 零命中、多候选、不完整分页继续 fail-closed;
- 已应用到成员、会话消息、消息搜索、@ 我、群机器人/邀请、按群发送和群重命名。
- CID 不进入
- 11 次命令/flag 漂移的 Runtime 兼容已补齐:
--text/--text-query、--page-size、--open-conversation-id、成员命令的--chat;+chat-rename作为+chat-update的隐藏兼容别名,群名依然在写前唯一解析;- canonical 与 alias 同时出现时拒绝静默覆盖。
- 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 <temp>/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。