问题描述:
当 PilotDeck 的 TokenSaver/自动路由模块选择了一个 provider/model 后,如果该模型调用失败(配额不足/上下文超限/认证错误等),前端 UI 或 QQ Bot 通道仅展示类似 错误:request (74815 tokens) exceeds the available context size (65536 tokens) 的消息,完全不体现是哪个 provider 和 model 报的错。
用户在前端无法直观判断:
当前错误是哪个供应商/模型产生的,比如欠费都不能立即知道应该向哪个供应商缴费、路由决策链路是怎样的(Judge 判断结果 → 选择的 tier → 最终 provider/model → fallback 链状态),出错的是主路由还是 TokenSaver Judge 模型。
当前仅有一条 console.log 路由决策日志(RouterRuntime.ts L263-265),但出错时不会与错误信息关联输出。
pilotdeck_router_execute_failed 事件(RouterRuntime.ts L569-578)已经包含了 provider 和 model,但未能传播到前端 GatewayEvent.error 事件中。
建议方案
方案一(最小侵入):
将 provider/model 注入 GatewayEvent.error 在 pilotdeck_router_execute_failed → RouterRuntime.ts L607 的 yield { type: "error", error: lastError } 前的某处,或在 execute() 方法尾部,将实际请求的 provider/model 附加到 error 对象中,使其传播到前端的 GatewayEvent.error:
CanonicalModelError 增加可选字段 routeModel?: string(或类似命名),既保留原始错误语义又不破坏兼容性;
在 fallback 链全部耗尽、最终 yield error 的节点(L569-608),将 lastAttempt 的 provider/model 填入 lastError
前端(qq-render.ts、Web UI 的 EventStream)和 QQ Channel, 消费时只需读取这些新增字段即可显示完整诊断信息 。
方案二(推荐,影响面可控)
在 GatewayEvent.error 中新增 routeInfo 字段
GatewayEvent.error(gateway/protocol/types.ts L157)的当前定义:
| { type: "error"; message: string; code?: string; recoverable: boolean }
建议扩展为:
| { type: "error"; message: string; code?: string; recoverable: boolean; routeInfo?: { provider: string; model: string; tier?: string; resolvedFrom?: string; scenarioType?: string } } ,
然后在 InProcessGateway.ts 的 turn_failed 事件映射处(L698-706)或 mapAgentEvent 的事件处理逻辑中,附加 routeInfo 数据。
具体来说:
TurnRunner.ts 的 createErrorResult() 或 AgentLoop 的 turn_failed 事件构建处可以保留 routeInfo ,
或者更简单的方案:在 RouterRuntime.ts 的 execute() 方法中 yield { type: "error", error: lastError } 之前,将 lastDecision 的 provider/model/tier 等信息注入到 error 对象 。
方案三(零侵入), 利用已有的 RouterEventBus 机制
RouterEventBus(router/protocol/events.ts)已经定义了 RouterExecuteFailedEvent,包含了 provider/model/scenarioType。
但 Producer 只在 RouterRuntime.ts L569-578 中 emit,目前没有任何 Consumer 读取它。
在 Gateway 层添加一个对 RouterExecuteFailedEvent 的 Consumer(例如在 createLocalGateway.ts 中,事件总线构建处),一旦收到该事件,将其中携带的 provider/model 注入到关联的 GatewayEvent.error 中发出。这样无需修改事件类型定义,改动量最小。
代码参考
现有路由决策日志(RouterRuntime.ts L263-265):
console.log(
`[router] decision: tier=${tokenSaverTier}, model=${selection.provider}/${selection.model}, orchGate=${orchGate}, alreadyOrch=${alreadyOrchestrating}, resolvedFrom=${resolvedFrom}`,
);
现有失败事件(RouterRuntime.ts L569-578):
if (lastError && lastAttempt) {
events.emit({
type: "pilotdeck_router_execute_failed",
sessionId: ctx.sessionId,
turnId: ctx.turnId,
scenarioType: lastDecision.scenarioType,
provider: lastAttempt.provider,
model: lastAttempt.model,
error: lastError,
});
现有 CanonicalModelError(model/protocol/errors.ts L14-26):
export type CanonicalModelError = {
provider: string; // ✅ 已有 provider
// ❌ 缺少 model 字段
code: CanonicalModelErrorCode | (string & {});
message: string;
// ...
};
前端 Consumer 不通透:
QQ: qq-render.ts L13-14:只输出 event.message
Web: InProcessGateway.ts L698-706:turn_failed 只映射 event.error.code 和 event.error.message
所有 GatewayEvent.error 事件(gateway/protocol/types.ts)缺少 model/provider 字段
期望效果
当 "供应商/DeepSeek-V4-Flash" 报 insufficient_user_quota 时,管理员在前端(Web UI / QQ Bot)直接看到类似:
[路由: tokenSaver → medium → 供应商/DeepSeek-V4-Flash ]
错误:insufficient_user_quota(配额不足,余额 ¥0.00)
而不是现在的:
错误:request (74815 tokens) exceeds the available context size (65536 tokens)
其他相关点
CanonicalModelError 已有 provider 字段但没有 model 字段,建议增加以保持完整诊断信息
RouterDecision 已经包含 resolvedFrom(explicit / scenario / tokenSaver / fallback),可一并纳入前端展示
此功能建议在 Gateway 层(mapAgentEvent / mapTurnCompleted)做最后的 assembly,避免将路由细节泄漏到所有 channel 实现中
问题描述:
当 PilotDeck 的 TokenSaver/自动路由模块选择了一个 provider/model 后,如果该模型调用失败(配额不足/上下文超限/认证错误等),前端 UI 或 QQ Bot 通道仅展示类似 错误:request (74815 tokens) exceeds the available context size (65536 tokens) 的消息,完全不体现是哪个 provider 和 model 报的错。
用户在前端无法直观判断:
当前错误是哪个供应商/模型产生的,比如欠费都不能立即知道应该向哪个供应商缴费、路由决策链路是怎样的(Judge 判断结果 → 选择的 tier → 最终 provider/model → fallback 链状态),出错的是主路由还是 TokenSaver Judge 模型。
当前仅有一条 console.log 路由决策日志(RouterRuntime.ts L263-265),但出错时不会与错误信息关联输出。
pilotdeck_router_execute_failed 事件(RouterRuntime.ts L569-578)已经包含了 provider 和 model,但未能传播到前端 GatewayEvent.error 事件中。
建议方案
方案一(最小侵入):
将 provider/model 注入 GatewayEvent.error 在 pilotdeck_router_execute_failed → RouterRuntime.ts L607 的 yield { type: "error", error: lastError } 前的某处,或在 execute() 方法尾部,将实际请求的 provider/model 附加到 error 对象中,使其传播到前端的 GatewayEvent.error:
CanonicalModelError 增加可选字段 routeModel?: string(或类似命名),既保留原始错误语义又不破坏兼容性;
在 fallback 链全部耗尽、最终 yield error 的节点(L569-608),将 lastAttempt 的 provider/model 填入 lastError
前端(qq-render.ts、Web UI 的 EventStream)和 QQ Channel, 消费时只需读取这些新增字段即可显示完整诊断信息 。
方案二(推荐,影响面可控)
在 GatewayEvent.error 中新增 routeInfo 字段
GatewayEvent.error(gateway/protocol/types.ts L157)的当前定义:
| { type: "error"; message: string; code?: string; recoverable: boolean }
建议扩展为:
| { type: "error"; message: string; code?: string; recoverable: boolean; routeInfo?: { provider: string; model: string; tier?: string; resolvedFrom?: string; scenarioType?: string } } ,
然后在 InProcessGateway.ts 的 turn_failed 事件映射处(L698-706)或 mapAgentEvent 的事件处理逻辑中,附加 routeInfo 数据。
具体来说:
TurnRunner.ts 的 createErrorResult() 或 AgentLoop 的 turn_failed 事件构建处可以保留 routeInfo ,
或者更简单的方案:在 RouterRuntime.ts 的 execute() 方法中 yield { type: "error", error: lastError } 之前,将 lastDecision 的 provider/model/tier 等信息注入到 error 对象 。
方案三(零侵入), 利用已有的 RouterEventBus 机制
RouterEventBus(router/protocol/events.ts)已经定义了 RouterExecuteFailedEvent,包含了 provider/model/scenarioType。
但 Producer 只在 RouterRuntime.ts L569-578 中 emit,目前没有任何 Consumer 读取它。
在 Gateway 层添加一个对 RouterExecuteFailedEvent 的 Consumer(例如在 createLocalGateway.ts 中,事件总线构建处),一旦收到该事件,将其中携带的 provider/model 注入到关联的 GatewayEvent.error 中发出。这样无需修改事件类型定义,改动量最小。
代码参考
现有路由决策日志(RouterRuntime.ts L263-265):
现有失败事件(RouterRuntime.ts L569-578):
现有 CanonicalModelError(model/protocol/errors.ts L14-26):
前端 Consumer 不通透:
QQ: qq-render.ts L13-14:只输出 event.message
Web: InProcessGateway.ts L698-706:turn_failed 只映射 event.error.code 和 event.error.message
所有 GatewayEvent.error 事件(gateway/protocol/types.ts)缺少 model/provider 字段
期望效果
当 "供应商/DeepSeek-V4-Flash" 报 insufficient_user_quota 时,管理员在前端(Web UI / QQ Bot)直接看到类似:
[路由: tokenSaver → medium → 供应商/DeepSeek-V4-Flash ]
错误:insufficient_user_quota(配额不足,余额 ¥0.00)
而不是现在的:
错误:request (74815 tokens) exceeds the available context size (65536 tokens)
其他相关点
RouterDecision 已经包含 resolvedFrom(explicit / scenario / tokenSaver / fallback),可一并纳入前端展示
此功能建议在 Gateway 层(mapAgentEvent / mapTurnCompleted)做最后的 assembly,避免将路由细节泄漏到所有 channel 实现中