Skip to content

fix(capabilities): unavailable 判定只认供应商码,生产链路永不命中 #211

Description

@wxrbyte

现象

CapabilityExecutor 用一张供应商码白名单判断「暂时不可用」,但生产链路的错误根本不会带这些码,
于是 unavailable 状态永不产生,四类「不可用」全部被记成 failed。

// packages/shared/src/capabilities/executor.ts:14-19
const UNAVAILABLE_ERROR_CODES = {
  LONGBRIDGE_NOT_INSTALLED: true,
  LONGBRIDGE_NOT_AUTHED: true,
  LONGBRIDGE_RATE_LIMITED: true,
  LONGBRIDGE_TIMEOUT: true,
} as const;

而生产环境构建 capability registry 时用的是 router 版 fetchers:

// apps/electron/src/main/kernelHost.ts:349-359
const routerFetchers = createRouterFetchers(this.providerRouter, { … });
const capabilityFetchers = demoData ? withDemoDataFallback(routerFetchers) : routerFetchers;
this.registry = createFullRegistry(capabilityFetchers);

createRouterFetchers 失败时抛的是归一化后的 provider 码:

// packages/shared/src/providers/router-fetchers.ts:109
throw new ProviderFetchError(result.error);

该码由 providers/longbridge/adapter.ts 的 toProviderError 产生(:81-108):

供应商码(白名单里的) 归一化码(真实到达的) 语义
LONGBRIDGE_NOT_AUTHED AUTH_EXPIRED 会话过期 / 未登录
LONGBRIDGE_RATE_LIMITED RATE_LIMITED 限流
LONGBRIDGE_TIMEOUT TIMEOUT 超时

两张码表零交集 ⇒ isUnavailableError()(executor.ts:175-181)恒为 false ⇒
classifyStatus()(:169-173)永远返回 failed。

期望

上述四类「can't serve right now」记成 unavailable,与状态机定义一致。

判据(源码自述契约)

  1. executor.ts:9-13 注释写明这张表存在的理由:

    Provider error codes that mean "can't serve right now" (missing CLI, missing auth, rate limited, or the provider timed out) rather than a data/logic failure. These map a run to 'unavailable'.
    注意它自称是 provider error codes —— 而列出的却是供应商 CLI 码。

  2. router-fetchers.ts:40-44 明确了 caller 应当基于归一码分支:

    Normalized failure thrown by the router-backed fetchers. Carries the stable ProviderError.code and user-safe message so callers can branch on the machine code without importing vendor types.

  3. kernelHost.ts:326 同向声明业务层看不到供应商码:

    Business layers see only the neutral capability …

  4. providers/resilience.ts:39-43 已确立规范 —— 不要在各处散落字符串匹配:

    Free-form provider code strings are normalized into this closed union by classifyFailure; routing/retry/breaker decisions always switch on the kind, never on string-matching scattered across call sites.

  5. 现有测试是测试替身:executor.test.ts:52-63 直接构造 code: 'LONGBRIDGE_NOT_AUTHED',
    所以一直绿;真实链路不会产生该码 —— 典型的「测试替身 vs 真实实现语义漂移」。

影响

finance-tool-registry.ts:98-102 按状态决定抛给 Agent 的错误码:

function failureCode(status: string) {
  if (status === 'cancelled') return 'RUN_CANCELLED';
  if (status === 'unavailable') return 'CAPABILITY_UNAVAILABLE';
  return 'CAPABILITY_FAILED';
}

因此「未登录 / 限流 / 超时」这三类可恢复情况,对 Agent 一律表现为 CAPABILITY_FAILED
(不可恢复的失败),而不是 CAPABILITY_UNAVAILABLE —— 影响 Agent 的失败归因与后续重试/提示策略;
evaluation/langfuse/exporter.ts:203 也把两者分别统计。

复现

bun test packages/shared/src/capabilities/executor.test.ts --isolate
# 修复前:10 pass / 3 fail(三条均为 Expected: "unavailable" / Received: "failed")

新增用例只用「router 链路上真实会到达的码」构造失败,不引入任何供应商类型。

建议修复方向

把归一化码补进同一张表(保持两张表的并集,兼容直接用 @finagent/longbridge-tools fetchers 的调用方):

const UNAVAILABLE_ERROR_CODES = {
  LONGBRIDGE_NOT_INSTALLED: true,
  LONGBRIDGE_NOT_AUTHED: true,
  LONGBRIDGE_RATE_LIMITED: true,
  LONGBRIDGE_TIMEOUT: true,
  AUTH_EXPIRED: true,
  RATE_LIMITED: true,
  TIMEOUT: true,
} as const;

PROVIDER_ERROR 不在其列:toProviderError 的 default 分支也用它,纳入会把真正的数据/逻辑失败
误判为「不可用」。这也暴露一个相关但独立的问题:LONGBRIDGE_NOT_INSTALLED 被压成通用的
PROVIDER_ERROR(adapter.ts:101-102),因此在 router 链路上无法与「请求失败」区分 ——
那需要在 adapter 侧给出更具体的码,本次不纳入。

验证方式

  • 修复前 bun test packages/shared/src/capabilities/executor.test.ts --isolate = 10 pass / 3 fail
    (三条新用例全部 Expected: "unavailable" / Received: "failed"),修复后 = 13 pass / 0 fail;
  • bun test packages/shared/src/capabilities --isolate = 46 pass / 0 fail;
  • bun run typecheck 五个工作区 exit 0。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

claimedClaimed by a contributor and currently in progress

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions