横切关注点 — 错误处理、日志、遥测与性能
1. 错误处理架构
1.1 错误类型层次
代码库定义了一个多层级的错误类体系,所有自定义错误最终继承自 Error:
- 基类
ClaudeError(src/utils/errors.ts) — 设置this.name = this.constructor.name,是所有内部错误的根类。 - 中止错误 —
AbortError和 SDK 的APIUserAbortError通过isAbortError()统一检测;该函数也兼容 DOMException 的name === 'AbortError'模式。 - Shell 错误 —
ShellError携带stdout、stderr、code、interrupted四个字段,用于统一处理子进程失败。 - 配置解析错误 —
ConfigParseError携带filePath和后备的defaultConfig。 - Axios 错误分类 —
classifyAxiosError()将 HTTP 客户端错误归类为auth、timeout、network、http、other五种类型。 - 遥测安全错误 —
TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS在构造时分离用户可见消息和遥测安全消息。
领域特定错误分布在各个模块中:
StopTaskError(src/tasks/stopTask.ts) — 带有code字段区分not_found、not_running、unsupported_type。ToolExecutionError— 工具执行过程中的运行时错误。BridgeFatalError/BridgeHeadlessPermanentError(src/bridge/) — Bridge 模式下的致命/永久错误。CannotRetryError/FallbackTriggeredError(src/services/api/withRetry.ts) — API 重试耗尽或模型降级时的错误。McpAuthError/XaaTokenExchangeError(src/services/mcp/) — MCP 协议认证相关错误。- 辅助函数
toError()、errorMessage()、shortErrorStack()、isFsInaccessible()提供了通用的错误处理基础设施。
1.2 工具执行错误
工具调用执行时产生的错误通过 Task 系统的 catch 边界捕获。每个 Task 类型(LocalShellTask、LocalAgentTask、InProcessTeammateTask)实现自己的错误处理逻辑:
AbortError在 agent 任务中被特殊处理 — 提取部分结果 (extractPartialResult) 后生成通知而非静默丢弃。Bash 任务中则静默抑制 "exit code 137" 通知以减少噪音。StopTaskError区分任务不存在、未在运行和不支持的类型,让调用方可以根据error.code决定处理策略。MaxFileReadTokenExceededError(src/tools/FileReadTool/) — 文件读取超过 token 上限时抛出,触发调用方改用分块读取。SessionBranchingError(src/utils/sessionBranching.ts) — 会话分支操作失败时的特定错误。ConversationStartupError(src/server/services/conversationService.ts) — 服务器端会话恢复失败时的错误。- 工具执行器在上层 catch 边界中将错误序列化为
tool_result内容,确保 LLM 可以感知错误并调整行为。
1.3 模型调用错误与恢复策略
withRetry() (src/services/api/withRetry.ts) 实现了完整的 API 调用重试引擎:
- 默认最大重试次数 为 10(可通过
CLAUDE_CODE_MAX_RETRIES环境变量覆盖)。 - 错误分类: 529 过载、429 速率限制(含 fast-mode overage)、401 认证过期、403 token 吊销、ECONNRESET/EPIPE 连接中断、400 max_tokens context overflow。
- 退避策略: 指数退避 + 随机 jitter(
BASE_DELAY_MS * 2^(attempt-1) + jitter),优先使用Retry-After响应头。 - Fast Mode 降级: 短延迟直接重试(保留 prompt cache),长延迟触发 cooldown 切换为标准速度。
- 模型降级: 连续 3 次 529 错误后触发
FallbackTriggeredError,切换到 fallback 模型。 - 持久模式 (
CLAUDE_CODE_UNATTENDED_RETRY): 无限重试 429/529,最大退避 5 分钟,6 小时上限,每 30 秒输出心跳防止会话超时。 - 背景请求不重试: 非前台
QuerySource(如摘要、标题、建议)在 529 时直接放弃,避免容量级联放大。FOREGROUND_529_RETRY_SOURCES集合中仅包含用户等待结果的查询源。 - 认证错误处理链: 401/403 错误触发 OAuth token 刷新 (
handleOAuth401Error)、AWS credential cache 清理 (clearAwsCredentialsCache)、GCP credential cache 清理 (clearGcpCredentialsCache)。CCR 模式下 401/403 被视为瞬态错误(网络抖动)而非坏凭证。 - Context Overflow 恢复:
parseMaxTokensContextOverflowError()解析 "input length andmax_tokensexceed context limit" 错误消息,提取inputTokens、contextLimit,自动计算调整后的max_tokens并重试。至少保留 1000 token 安全缓冲区和 3000 输出 token 下限。 - 连接池管理: ECONNRESET/EPIPE 错误触发
disableKeepAlive(),在重试时创建新连接而非复用可能损坏的 keep-alive socket。 - Mock 错误集成: Ant 员工通过
/mock-limits命令触发的模拟速率限制错误 (checkMockRateLimitError) 在重试引擎中受到特殊处理 — 不被视为可重试错误,确保测试场景不会进入无限重试循环。
1.4 致命错误 vs 可恢复错误
- 可恢复错误: 网络抖动、速率限制、认证过期、token 吊销 — 通过重试、token 刷新、连接池禁用等机制自动恢复。
- 致命错误:
BridgeFatalError、CannotRetryError(重试耗尽后)、配置解析失败 — 需要用户干预或进程重启。
2. 日志系统
2.1 控制台日志与文件日志
logForDebugging()(src/utils/debug.ts) — 双重输出到 stderr 和文件。支持日志级别过滤(CLAUDE_CODE_DEBUG_LOG_LEVEL从 verbose 到 error),CLAUDE_CODE_DEBUG环境变量启用输出。CLAUDE_CODE_DEBUG_FILTER按模块名称过滤减少噪音。- 缓冲写入:
BufferedWriter模式将高频日志积累到内存缓冲区后批量写入磁盘,减少细粒度 I/O 的系统调用开销。日志文件位于{CLAUDE_CONFIG_HOME}/debug.log,使用符号链接跟踪最新会话,便于快速定位当前 session 的日志。 logError()(src/utils/log.ts) — 专用错误日志函数,捕获完整错误栈和结构化元数据,输出到标准错误流。- 日志文件轮转: 通过
getClaudeConfigHomeDir()确定日志目录路径,registerCleanup在进程退出时确保缓冲区排空。CLAUDE_CODE_FORCE_FULL_LOGO 等构建时常量控制日志展示格式。 - 调试过滤器:
parseDebugFilter()和shouldShowDebugMessage()允许按来源模块名称精确控制哪些调试消息可见,在开发高噪音模块时可以只关注特定子系统的输出。
2.2 LogSelector UI 组件 (src/components/LogSelector.tsx)
一个功能完整的日志浏览器,提供:
- 模糊搜索 (Fuse.js) — 按会话标题、摘要、内容搜索。
- 标签分类 — 按 agent 名称、自定义标签、项目过滤。
- 树形浏览 — 按日期层级组织日志。
- 对话预览 — 在侧面板显示选中会话的摘要消息。
- 智能搜索 — 可以通过 AI 驱动的语义搜索查找相关日志。
- 分页加载 (
onLoadMore) — 支持无限滚动。
2.3 诊断工具 (/doctor 命令 & DiagnosticsService)
DiagnosticsService(src/server/services/diagnosticsService.ts) — 捕获console.error、console.warn、process.on('uncaughtException')、process.on('unhandledRejection'),将诊断事件写入文件系统。7 天保留期,50MB 上限。- REST API (
src/server/api/diagnostics.ts) — 提供GET /api/diagnostics/status、GET /api/diagnostics/events、POST /api/diagnostics/export等端点。导出为压缩的 tar.gz,自动脱敏 API key、token 等敏感信息。 DiagnosticsTrackingError(src/services/diagnosticTracking.ts) — 内部跟踪诊断事件类型的错误。
3. 遥测与分析
3.1 GrowthBook 集成 (src/services/analytics/growthbook.ts)
GrowthBook 提供 feature flags 和 A/B 测试能力:
- 远程评估:
remoteEval: true模式下,服务器端评估 feature flag 值,客户端缓存到内存 (remoteEvalFeatureValues) 和磁盘 (cachedGrowthBookFeatures在~/.claude.json)。 - 用户属性: 发送
deviceID、platform、organizationUUID、accountUUID、subscriptionType、rateLimitTier等用于定向。 - 缓存策略:
getFeatureValue_CACHED_MAY_BE_STALE()— 非阻塞,优先内存缓存,后备磁盘缓存,适用于启动关键路径。getDynamicConfig_BLOCKS_ON_INIT()— 阻塞直到初始化完成(最多 5 秒超时)。checkSecurityRestrictionGate()— 安全检查相关 gate,等待 re-init 完成以确保值新鲜。checkGate_CACHED_OR_BLOCKING()— 磁盘缓存为 true 时快速返回,false 时等待服务器确认(避免 false 误阻止用户功能)。
- 实验曝光: 每个 feature 在一个 session 内只记录一次曝光事件 (
loggedExposures去重),通过logGrowthBookExperimentTo1P发送到第一方遥测。 - 周期刷新: 非 Ant 构建每 6 小时刷新,Ant 构建每 20 分钟刷新。刷新时重建
remoteEvalFeatureValues并同步到磁盘。 - 认证变更:
refreshGrowthBookAfterAuthChange()销毁旧客户端并使用新认证头重建,防止 API key 变更后返回过期值。 - 环境变量覆盖: Ant 员工可通过
CLAUDE_INTERNAL_FC_OVERRIDES全局覆盖 feature flag(用于评估工具)。
3.2 遥测 Sink 架构 (src/services/analytics/sink.ts)
分析系统采用 sink 模式,支持热插拔遥测后端:
AnalyticsSink接口 (src/services/analytics/index.ts) — 定义logEvent()同步和logEventAsync()异步方法,以及attachAnalyticsSink()注册函数。OSS 构建中所有实现体为空(no-op),降低二进制体积。initializeAnalyticsGates()/initializeAnalyticsSink()(src/services/analytics/sink.ts) — OSS 构建中为空函数。在完整构建中会初始化 GrowthBook 和 Datadog 后端。sinkKillswitch.ts— 通过 GrowthBook feature flag 在运行时动态禁用遥测发送,用于紧急情况下的数据收集开关。- 元数据标记 —
AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS类型确保只有在调用方显式声明"此元数据不包含敏感信息"时才能传递,编译期防止数据泄露。
3.3 第一方事件日志 (src/services/analytics/firstPartyEventLogger.ts)
Ant 内部构建使用第一方事件日志系统:
- 实验曝光日志:
logGrowthBookExperimentTo1P()记录 A/B 实验的分组信息和用户属性。 - 批处理配置: 通过
tengu_1p_event_batch_config动态配置批量发送策略。使用onGrowthBookRefresh订阅配置变更,在运行时重建 LoggerProvider。 - 条件启用:
is1PEventLoggingEnabled()检查隐私级别、用户认证状态等前置条件,只有满足所有条件时才启用。
3.4 Datadog (src/services/analytics/datadog.ts)
OSS 构建中 Datadog 遥测被禁用(initializeDatadog 返回 false,trackDatadogEvent 函数体为空)。接口保留以便启动代码不需要对 OSS 变体特殊处理。Datadog 用于生产环境的性能监控、错误追踪和自定义指标仪表板。
3.5 分析配置 (src/services/analytics/config.ts)
isAnalyticsDisabled() 在以下情况下禁用分析:NODE_ENV=test、Bedrock/Vertex/Foundry 第三方提供商、隐私级别为 no-telemetry 或 essential-traffic 模式。
3.6 成本与令牌用量追踪 (src/cost-tracker.ts)
- 全局状态:
getTotalCostUSD()、getTotalInputTokens()、getTotalOutputTokens()、getTotalAPIDuration()等函数从 bootstrap state 读取聚合数据。 - 按模型统计:
getUsageForModel()按模型名跟踪inputTokens、outputTokens、cacheReadInputTokens、cacheCreationInputTokens、costUSD。 - 会话持久化:
saveCurrentSessionCosts()将当前 session 的成本写入 project config;restoreCostStateForSession()在恢复 session 时重建成本状态。 - 成本展示:
formatTotalCost()输出格式化成本摘要(总成本、API 时长、代码变更行数、按模型的令牌使用量)。 - exit 回调:
useCostSummary()(src/costHook.ts) 在process.on('exit')时自动输出成本摘要并保存 session 数据。
4. 性能工程
4.1 流式工具执行
工具执行采用流式架构(QueryEngine 中的 AsyncGenerator),LLM 输出 token 流式到达的同时,工具结果可以并行返回:
QueryEngine将 LLM 的流式响应解析为TextBlock、ToolUseBlock、ContentBlockStop等事件,边解析边交付给 UI 渲染。- 工具并行: 当 LLM 发起多个工具调用时,
tools.ts中的调度器可以并行执行无依赖的工具(如同时读取多个文件),减少总等待时间。流式响应持续接收新的工具调用请求。 - 前台 vs 背景: 前台
QuerySource(如repl_main_thread)使用完整的重试、提示缓存和速率限制逻辑;背景任务(摘要、标题生成)使用轻量级路径以减少延迟。 QuerySource分类: 定义在src/constants/querySource.ts,包括repl_main_thread、repl_main_thread:outputStyle:custom、sdk、agent:custom、compact、hook_agent、auto_mode等 20+ 种类型,影响重试策略和日志记录。
4.2 提示缓存 (Prompt Caching)
- 缓存写入控制 (
skipCacheWrite) — 某些场景(如快速诊断查询)跳过缓存写入以节省 cache creation token 成本。适用于可丢弃的中间查询,如标题生成、会话摘要等。 - 缓存读取追踪 —
cache_read_input_tokens和cache_creation_input_tokens通过cost-tracker.ts分模型追踪。API 响应中的usage.cache_read_input_tokens和usage.cache_creation_input_tokens字段在addToTotalSessionCost()中累加。 - Fast Mode 缓存保留 — 在 fast mode 中,短延迟重试使用相同的模型名以保留 prompt cache(减少 cache creation 消耗);长延迟触发 cooldown 切换到标准模型时,失去缓存被视为可接受成本。
- Cache Break Detection (
PROMPT_CACHE_BREAK_DETECTIONfeature flag) — 监控和检测 prompt cache 命中率异常下降的场景,帮助诊断缓存失效原因。 - 成本核算:
calculateUSDCost()函数根据模型单价、输入/输出/cache read/cache creation token 用量计算精确成本。未知模型通过hasUnknownModelCost()标记。
4.3 上下文预取
- 内存预取:
memory系统在 agent 启动时主动从磁盘加载相关记忆片段。 - 技能预取:
skills系统预加载已启用的技能描述和工具定义。 - 推测执行 (
SpeculationState): 在用户输入到达之前,系统可以推测性地启动任务执行。
4.4 令牌估算
getContextWindowForModel() 和 getModelMaxOutputTokens() 提供按模型的上下文窗口和输出上限。parseMaxTokensContextOverflowError() 解析 API 返回的 context overflow 错误并自动调整 max_tokens 参数。
4.5 速率限制模拟 (src/services/rateLimitMocking.ts)
Ant 员工可以通过 /mock-limits 命令模拟各种速率限制场景(429、529、fast-mode cooldown),用于测试 UI 行为和恢复逻辑。与 withRetry() 集成,确保模拟错误不触发真实重试。
4.6 背景任务卸载
在 stop hooks 中使用 fire-and-forget 模式(void 前缀 + .catch()),确保关键路径不被背景任务阻塞。例如:LogSelector.tsx 中的日志写入、onChangeAppState 中的 side-effect 通知。
5. 跨组件通信模式
5.0 Signal 模式 (src/utils/signal.ts)
createSignal<T>() 提供轻量级的事件信号原语,与 Store 不同,Signal 不持有状态快照,仅用于通知"某事发生了":
- 订阅/取消:
subscribe(listener)返回取消函数,与 React useEffect 的 cleanup 模式天然兼容。 - 类型安全: 通过泛型
Args指定事件参数类型。 - 去重:
clear()移除所有监听器,用于 dispose 和 reset 路径。 - 使用场景: GrowthBook 刷新通知 (
refreshedsignal)、设置变更通知、onChangeAppState中的事件广播。 - 与 Store 的区别: Signal 无
getState(),不存储值,仅做事件通知。在代码库中替换了 ~15 处重复的new Set<Listener>()+subscribe/notify模式。
6. 状态管理性能
6.1 AppState 可观察存储 (createStore 模式)
createStore<T>() (src/state/store.ts) 是一个轻量级不可变状态存储,模式灵感来源于 Redux 但更简洁:
getState()— O(1) 引用读取,无任何开销。setState(updater)— 接收(prev: T) => T更新函数。通过Object.is做引用相等性检查,避免无变更时通知监听器。onChange回调在每次有效状态变更时同步触发,接收{ newState, oldState }上下文。subscribe(listener)— 基于Set<Listener>的发布-订阅,返回取消订阅函数。无额外分配。AppStateStore类型 (src/state/AppStateStore.ts) 继承自Store<AppState>,添加了AppState的默认值工厂函数getDefaultAppState()。完整的 AppState 包含tasks、toolPermissionContext、settings、isUltraplanMode、viewingAgentTaskId等顶级字段。- 不可变性保证:
setState使用 updater 函数模式,每次变更产生新的状态对象。React 渲染层依赖引用相等性进行短路优化。
6.2 onChangeAppState 副作用处理器 (src/state/onChangeAppState.ts)
一个集中式 onChange 处理器,在 AppState 变更时协调跨系统的副作用:
- 权限模式同步(CCR/SDK)
- 会话元数据变更通知
- 设置变更应用 (
applySettingsChange) - API key 缓存清理
这种模式避免了 React 组件中散落的 useEffect 链,让状态变更的副作用可预测且可测试。
6.3 Selector 模式 (src/state/selectors.ts)
纯函数选择器从 AppState 派生计算状态:
getViewedTeammateTask()— 提取当前查看的 teammate 任务getActiveAgentForInput()— 确定用户输入路由目标(leader / viewed / named_agent)
选择器保持轻量、无副作用,便于组合和测试。
6.4 React Context 订阅
AppStateProvider (src/state/AppState.tsx) 使用 React Context + useSyncExternalStore 将存储桥接到 React 渲染周期。VoiceProvider 通过 feature('VOICE_MODE') 进行死代码消除 — 外部构建中它是一个无操作的包裹器。useSettingsChange hook 监听设置文件变更并增量更新状态。
7. 构建优化
7.1 Feature Flag 死代码消除 (bun:bundle feature())
Bun 的编译时 feature() 函数允许在构建阶段消除未使用的代码分支:
scripts/build.ts定义了完整的实验特性列表(如VOICE_MODE、AGENT_MEMORY_SNAPSHOT、BASH_CLASSIFIER等 50+ 个 flag)。- 外部构建默认只启用
VOICE_MODE。 - 构建命令
bun build --feature=<flag>将 feature flag 注入编译步骤,未启用的分支在 tree-shaking 中被完全移除。 - 这使得 ant-internal 代码可以直接嵌入仓库,而外部构建不携带任何内部逻辑。
7.2 React Compiler 输出 (src/components/LogoV2/)
LogoV2/ 目录中的组件(LogoV2.tsx、AnimatedClawd.tsx、Feed.tsx、WelcomeV2.tsx 等)使用了 React Compiler (react/compiler-runtime) 自动记忆化输出。编译器将 useMemo/useCallback 模式自动化,减少手动优化的工作量。
7.3 WASM 二进制文件
VAD(Voice Activity Detection)服务 (src/friend/voice/vad-service.ts) 使用 onnxruntime-web 的 WASM 后端(而非 onnxruntime-node 原生插件),因为 Bun 不支持 Node-API 原生插件。WASM 二进制文件 (onnxruntime-web) 随 dist 打包。
7.4 Build 脚本 (scripts/build.ts)
构建管道使用 Bun 原生打包器,生成单一可执行文件:
- 预构建 Friend VRM 前端: 检查
src/components/friend/frontend/dist/index.html是否存在,不存在时执行npm run build。 - 主构建: 使用
bun build --compile --target bun --minify --bytecode生成单个可执行文件。--minify减小二进制体积,--bytecode编译为 Bun 字节码提升启动速度。 - 编译时常量注入:
MACRO.VERSION— 语义版本号(开发版附加 git SHA 和时间戳)。MACRO.BUILD_TIME— ISO 8601 构建时间戳。MACRO.FEEDBACK_CHANNEL— 反馈渠道(OSS 构建为github)。MACRO.VERSION_CHANGELOG— 最近的 git commit 日志或指向 GitHub 的 URL。
- 外部模块排除:
@ant/*、audio-capture-napi、image-processor-napi、modifiers-napi、url-handler-napi通过--external排除捆绑,减小二进制体积并允许运行时加载原生模块。 - 环境变量定义:
process.env.USER_TYPE='external'、process.env.CLAUDE_CODE_FORCE_FULL_LOGO='true'、process.env.CCR_FORCE_BUNDLE='true'等定义确保构建产物运行于正确模式。 - 完整特性集: 使用
--feature-set=dev-full启用全部实验特性(50+ feature flags),默认仅启用VOICE_MODE。 - 输出产物: 开发版本输出到
./Codev,发布版本输出到./dist/cli。
8. 资源管理
8.1 子进程管理
- 音频采集 (
arecord/sox) — 通过child_process.spawn管理,采样率 16kHz,16-bit PCM。 - Whisper STT (
src/services/voice/whisperSTT.ts) — 本地语音识别子进程,管理其生命周期并处理输出解析。 - Tauri 子进程 — 在 Friend 桌面模式下启动和管理 Tauri shell 进程。
- 子进程超时 —
RipgrepTimeoutError、StallTimeoutError确保子进程不会无限期挂起。cleanupRegistry.ts注册进程清理回调,在优雅关闭时统一终止。
8.2 定时器和超时
- GrowthBook 周期刷新 —
setupPeriodicGrowthBookRefresh()使用setInterval(非 Ant: 6h,Ant: 20min),unref()保证不阻止进程退出。 - API 重试退避 —
sleep()+AbortSignal组合,支持中途取消。 - 持久重试心跳 — 每 30 秒输出系统消息防止空闲超时。
- 优雅关闭 —
gracefulShutdown.ts中的CleanupTimeoutError确保关闭过程不会无限阻塞。
8.3 优雅关闭与清理注册表
cleanupRegistry.ts 和 gracefulShutdown.ts 实现了进程级资源清理系统:
registerCleanup()(src/utils/cleanupRegistry.ts) — 全局Set<() => Promise<void>>注册表。任何模块可以注册异步清理函数,返回取消注册函数。runCleanupFunctions()使用Promise.all并行执行所有清理任务。gracefulShutdown()(src/utils/gracefulShutdown.ts) — 主关闭流程,按顺序执行:onExit('signal-exit')捕获 SIGTERM/SIGINT,设置isShuttingDown标志。- 同步恢复终端模式(退出 alt screen、恢复光标、禁用 mouse tracking、恢复 Kitty 键盘模式)。
runCleanupFunctions()并行运行所有注册的异步清理。- 关闭 Datadog(
shutdownDatadog())和第一方事件日志。 - 最终日志发送(会话成本、诊断事件)。
CleanupTimeoutError防止清理阶段无限阻塞。
8.4 文件句柄管理
- SSE 客户端 — EventSource 连接在设置/清理生命周期中管理,使用
AbortController确保断开。 - 音频文件 — 语音录制生成临时 WAV 文件,
FriendService负责生命周期管理。 - 调试日志 —
BufferedWriter管理文件写入,registerCleanup注册关闭回调。 - 诊断事件 —
DiagnosticsService使用文件系统存储诊断事件,7 天保留期和 50MB 上限防止磁盘膨胀。
8.5 VAD 内存状态管理
SileroVad (src/friend/voice/vad-service.ts) 在 ONNX 推理会话中维护 LSTM 状态张量 (stateH、stateC)。每个音频帧(512 采样 / 32ms)处理后更新状态。关键优化:
- RMS 能量过滤: 低于
rmsThreshold(默认 ~-48dBFS)的帧跳过 ONNX 推理,直接视为静音。在高噪声环境中可以减少约 60% 的推理次数。 - 状态重置:
reset()方法清空 LSTM 状态和累积缓冲区,用于会话间清理。 - 预处理触发:
preSpeechTriggerFrames(默认 10 帧 / 320ms)过滤短时噪音爆发,避免误触发。
架构决策记录
| 决策 | 理由 |
|---|---|
错误使用 class extends Error 并显式设置 this.name |
确保压缩构建中 instanceof 失效时仍可通过 name 属性识别错误类型 |
重试引擎使用 generator (yield) |
允许在重试间隔中间向调用方输出系统消息(心跳、进度) |
GrowthBook 使用 remoteEval: true |
服务器端评估避免客户端下载完整规则集,减少网络负载和延迟 |
| VAD 使用 WASM 而非原生插件 | 兼容 Bun 运行时(Node-API 原生插件在 Bun 中导致 segfault) |
| 选择器使用纯函数而非 memoized selector | AppState 不可变性保证引用相等性检查足够高效,不需要额外记忆化开销 |
| Feature flag 死代码消除在构建时而非运行时完成 | 显著减小外部构建的二进制体积,消除内部代码泄漏风险 |
| Datadog / 遥测在 OSS 构建中默认禁用 | 保持开放源代码版本的隐私友好特性,同时保持接口兼容性 |