Native 能力层
color-diff 语法高亮、file-index 模糊搜索、yoga-layout 终端布局引擎、upstreamproxy 代理中继、bridge 远程会话桥接
职责概述
解决的问题:CC 的一些核心能力需要高性能——语法高亮要快、文件搜索要快、终端布局计算要快。以前这些用 Rust/C++ 原生模块实现,但原生二进制导致跨平台分发困难。这一层把所有原生模块用纯 TypeScript 重写,消除了原生依赖,同时保持性能可接受。
应用场景:① 代码差异的彩色渲染(新增行绿色、删除行红色)② 输入文件路径时的模糊搜索补全 ③ 终端界面的 Flexbox 布局计算 ④ 容器环境中的 MITM 代理设置 ⑤ 远程开发时本地 IDE 驱动容器内的 CC。
一句话理解:就像 Electron 之于桌面 App——用 Web 技术替代原生开发,牺牲一点极致性能,换来跨平台和可维护性。
架构设计
Native 模块架构图
~2580 行纯 TS
Flexbox 引擎
justify / align / wrap
多级布局缓存
~500+ 行纯 TS
差异渲染
word-diff 算法
ANSI TrueColor 输出
~300+ 行纯 TS
模糊搜索
边界/驼峰/连续加分
异步分块构建
relay + config
网络代理
CA 证书链拼接
NO_PROXY 白名单
20+ 文件
远程会话
核心数据流
1. Yoga Layout 计算流程
2. Upstream Proxy 初始化流程
CLAUDE_CODE_REMOTE=1 && CCR_UPSTREAM_PROXY_ENABLED=1/run/ccr/session_token 读取,设置 prctl(PR_SET_DUMPABLE, 0)SSL_CERT_FILEstartUpstreamProxyRelay() — 本地 CONNECT→WebSocket 代理HTTPS_PROXY + NO_PROXY(排除 Anthropic API、GitHub、npm 等域名)关键类型与接口
FileIndex API (native-ts/file-index/index.ts)
// native-ts/file-index/index.ts — 核心接口
export type SearchResult = {
path: string
score: number // 越低越好,最佳匹配 = 0.0
}
export class FileIndex {
loadFromFileList(fileList: string[]): void
loadFromFileListAsync(fileList: string[]): {
queryable: Promise<void> // 首个 chunk 就绪即可搜索
done: Promise<void> // 全部索引构建完成
}
search(query: string, limit: number): SearchResult[]
}
Yoga Node 核心 (native-ts/yoga-layout/index.ts)
// native-ts/yoga-layout/index.ts:403-466 — Node 类(核心字段)
export class Node {
style: Style
layout: Layout
children: Node[]
measureFunc: MeasureFunction | null
isDirty_: boolean
// 多级布局缓存
_hasL: boolean // 单条目 layout 缓存
_cIn: Float64Array // 多条目缓存输入(4 slot × 8 float)
_cOut: Float64Array // 多条目缓存输出(4 slot × 2 float)
_fbGen: number // flexBasis 缓存的 generation
// 快速跳过标志
_hasAutoMargin: boolean
_hasPosition: boolean
_hasPadding: boolean
_hasBorder: boolean
_hasMargin: boolean
calculateLayout(ownerWidth?: number, ownerHeight?: number): void
}
Color Diff 接口 (native-ts/color-diff/index.ts)
// native-ts/color-diff/index.ts:52-69
export type Hunk = {
oldStart: number; oldLines: number
newStart: number; newLines: number
lines: string[]
}
export type NativeModule = {
ColorDiff: typeof ColorDiff
ColorFile: typeof ColorFile
getSyntaxTheme: (themeName: string) => SyntaxTheme
}
Bridge API (bridge/bridgeApi.ts)
// bridge/bridgeApi.ts:68 — 工厂函数签名
export function createBridgeApiClient(deps: BridgeApiDeps): BridgeApiClient
// deps 包含: baseUrl, getAccessToken, runnerVersion,
// onAuth401 (OAuth 刷新), getTrustedDeviceToken
设计模式与亮点
1. 多级布局缓存(Yoga)
Yoga 实现了三层缓存策略:单条目 _hasL(layout pass 结果)、四条目 _cIn/_cOut(覆盖 scroll 场景下同一节点看到多组不同输入的情况)、flexBasis _fbGen(基于 generation 的跨计算缓存)。CPU Profile 显示,1000 节点基准测试中 ~67% 的 resolveEdges4Into 调用操作全零边数组——快速路径通过 _hasPadding/_hasBorder/_hasMargin 标志单分支跳过。
2. 时间分块异步索引(FileIndex)
loadFromFileListAsync() 不按数量分块而是按时间分块(CHUNK_MS = 4ms):慢速机器自动获得更小的分块,保持 UI 响应性。首个 chunk 就绪即返回 queryable promise——270K 文件索引中约 5-10ms 即可开始搜索部分结果。
3. 失败开放设计(UpstreamProxy)
initUpstreamProxy() 的每一步都失败开放:token 文件缺失、CA 下载失败、relay 启动失败——任何错误仅记录日志并返回 { enabled: false }。一个破损的代理配置绝不会中断正常工作会话。Token 文件在 relay 确认启动后才 unlink,确保 supervisor 重启可重试。
4. Generation-based 缓存新鲜度
Yoga 和 FileIndex 都使用 generation 标记缓存有效性。Yoga 的 _generation 在每次 calculateLayout() 时递增,允许同一 generation 内的缓存命中(即使节点仍标记 dirty——virtual scroll 的新挂载项)。跨 generation 的 dirty 节点缓存被视为陈旧。这种设计将 1593 节点新鲜挂载的 105K 次 layoutNode 访问降至约 10K 次。
5. Highlight.js 惰性加载(Color-Diff)
Color-diff 通过闭包惰性加载 highlight.js(190+ 语言语法,~50MB),避免模块评估时即加载。这是解决 Windows CI 上 beforeEach/afterEach 超时问题的关键优化——测试文件通过间接依赖引入 color-diff 时不再付出启动成本。
开发者实践指南
如何扩展 Yoga 布局能力
- 在
native-ts/yoga-layout/enums.ts中添加新的枚举值 - 在
defaultStyle()中添加默认值 - 在
Node类中添加对应的 setter/getter - 在
layoutNode()的对应 STEP 中添加处理逻辑 - 更新缓存比较逻辑——确保新维度参与缓存键匹配
getYogaCounters() 检查 visited/measured/cacheHits/live 计数。如果 cacheHits 远低于预期,检查 _hasL/_cN 是否在 dirty 时被正确清除。
如何添加新的 FileIndex 评分规则
- 修改
native-ts/file-index/index.ts中的评分常量(SCORE_MATCH,BONUS_BOUNDARY等) - 在搜索函数中添加新的 bonus/penalty 计算
- 注意
posBuf是Int32Array(MAX_QUERY_LEN)——查询长度上限为 64 字符
如何调试 UpstreamProxy
- 设置
CLAUDE_CODE_REMOTE=1+CCR_UPSTREAM_PROXY_ENABLED=1 - 检查
/run/ccr/session_token是否存在且可读 - 查看 relay 日志——所有步骤都有
logForDebugging()输出 - 验证
NO_PROXY列表是否覆盖了不应代理的目标(Anthropic API、GitHub 等)
架构师决策指南
纯 TS 重写 vs 原生模块的决策
三个 native-ts 模块都经历了从 Rust/C++ NAPI 到纯 TypeScript 的迁移。这一决策的核心权衡:
- 优势: 消除平台特定二进制(不再需要 Linux x64/arm64、macOS arm64/x64、Windows 的分别编译和分发);安装体积减少;调试更简单(无 dlopen 失败、无 ABI 兼容性问题)。
- 代价: 性能降低——Yoga 的 C++ 实现在 1000 节点基准上约 2-3ms,纯 TS 版约 6-7ms(通过缓存优化到可用范围)。FileIndex 的 nucleo 是 Rust 实现,纯 TS 版在大索引(270K+ 文件)上首次构建更慢(通过异步分块缓解)。
- 决策点: 当性能成为瓶颈时,可通过
feature()gate 在 Ant 内部构建中保留原生模块路径,外部构建使用纯 TS。
Bridge 模块的职责边界
bridge/ 包含 20+ 文件,覆盖了 REST API 客户端、JWT 工具、权限回调、远程会话运行、调试工具等。关键的设计决策是将 bridge 定位为 API 层而非业务逻辑层——它不包含任何 Claude Code 的核心逻辑,仅负责将本地操作转发到远程执行环境。这使得 bridge 可以被 IDE 插件、CCR 容器、Desktop App 等不同场景复用。
NO_PROXY 的维护负担
upstreamproxy.ts 中的 NO_PROXY_LIST 是硬编码的域名列表。每次添加新的第三方依赖(如新的包管理器仓库),都需要考虑是否加入排除列表。更健壮的方案是通过配置文件管理,但这与 CCR 容器的无状态设计冲突——当前硬编码是合理的折衷。
◈ 可视化处理拓扑图
Phase 1 — Color-Diff 差异渲染管线
从终端颜色检测到语法高亮再到 ANSI 输出的完整渲染管线,支持 truecolor/256色/ANSI 三种终端模式。
Phase 2 — File-Index 模糊搜索与 Yoga 布局计算
Phase 3 — UpstreamProxy 代理中继
CCR 容器环境下的透明代理建立流程,每步失败开放。
⇉ 核心处理流程详解
Native 能力层的五个模块各自独立,但共享一个核心设计理念:从原生二进制依赖迁移到纯 TypeScript 实现,消除跨平台编译和 NAPI 绑定的复杂性。以下以 color-diff 差异渲染、file-index 模糊搜索、yoga-layout 布局计算 和 upstreamproxy 代理中继 四条核心流程展开。
ColorDiff.render()(L860-932)首先调用 detectColorMode()(L95-99)检测终端颜色能力(truecolor/256色/ANSI),然后调用 buildTheme()(L282-362)根据主题名构建包含前景/背景/装饰色的完整主题映射。最后 detectLanguage()(L422-451)根据文件扩展名和首行内容推断语法语言 — color-diff/index.ts:860-868highlightLine()(L504-538)处理:先通过 highlight.js 语法高亮(使用 hljs 10.x/11.x 兼容的 scope/kind 字段),然后对修改行执行 wordDiffStrings()(L604-636)的字符级差异计算。差异范围通过 applyBackground()(L768-817)叠加到语法高亮之上 — color-diff/index.ts:504-636intoLines()(L819-826)将 Block 数组转换为 ANSI 转义序列字符串。colorToEscape()(L129-145)根据颜色模式(truecolor 用 38;2;r;g;b,256 色用 38;5;index,ANSI 用 3/4 位码)生成终端转义序列。每行包含行号 + 标记(+/-/ )+ 背景 + 语法高亮的完整信息 — color-diff/index.ts:129-162FileIndex 的 buildAsync()(L95-133)将文件列表按 ~8-12k 条分 chunk,每处理一个 chunk 后通过 yieldToEventLoop()(L325-327)让出事件循环,避免阻塞 UI。indexPath()(L156-167)为每个路径预计算:小写形式 + a-z 字母位图 + 路径长度。位图提供了 O(1) 的快速拒绝能力 — file-index/index.ts:95-167search()(L173-290)先通过字母位图快速过滤(O(1) 拒绝不含查询字符的路径),然后对幸存路径执行模糊匹配。智能大小写:全小写查询不区分大小写,含大写则区分。匹配评分考虑路径边界加分(scoreBonusAt L297-303)和 camelCase 加分 — file-index/index.ts:173-303calculateLayout()(L927-963)是布局入口。它调用 layoutNode()(L1058-1902)递归计算每个节点的尺寸和位置。热路径上的多级缓存:cacheWrite()(L969-1012)比较当前 style + 约束的哈希,命中时直接返回缓存布局。computeFlexBasis()(L2059-2172)和 resolveFlexibleLengths()(L2182-2282)处理弹性布局核心算法 — yoga-layout/index.ts:927-963, 1058-1902initUpstreamProxy()(L79-153)首先检查三个前置条件:CLAUDE_CODE_REMOTE(必须在 CCR 容器中)→ CCR_UPSTREAM_PROXY_ENABLED(服务端通过 StartupContext 注入)→ session token 文件存在。通过后下载 CA 证书包并设置 PR_SET_DUMPABLE=0(L112, 通过 libc FFI 阻止 ptrace)— upstreamproxy.ts:79-153startUpstreamProxyRelay()(relay.ts:155-174)启动本地 TCP 监听,每个连接先累积 HTTP CONNECT 请求(handleData L295-342),解析目标地址后通过 openTunnel()(relay.ts:344-428)建立到 Anthropic 的 WebSocket 通道。后续数据通过 encodeChunk/decodeChunk(relay.ts:66-103)的轻量帧协议双向转发 — relay.ts:155-428★ 设计精华
1. 位图快速拒绝——O(1) 路径过滤
FileIndex.indexPath()(L156-167)为每个文件路径预计算一个 26 位整数位图,每一位对应 a-z 中的一个字母。搜索时先对查询字符串做位图 AND 操作,如果结果的位图是查询位图的子集,才进入昂贵的模糊匹配。这一步可以拒绝约 89% 的路径(对于常见查询如"test"),将实际需要评分的路径数量降低一个数量级。
// file-index/index.ts:156-167
private indexPath(i: number): void {
const lp = this.paths[i]!.toLowerCase()
this.lowerPaths[i] = lp
this.pathLens[i] = len
let bits = 0
for (let j = 0; j < len; j++) {
const c = lp.charCodeAt(j)
if (c >= 97 && c <= 122) bits |= 1 << (c - 97) // a-z 映射到 bit 0-25
}
this.charBits[i] = bits
}
// 搜索时:if ((charBits[i] & needleBits) !== needleBits) → 跳过
2. Yoga 多级布局缓存——热路径优化
yoga-layout/index.ts 实现了多级缓存策略:calculateLayout() 在 L932-934 重置访问计数器,layoutNode() 通过 cacheWrite()(L969-1012)比较当前 style 快照 + 父约束是否匹配缓存。命中时跳过整个布局递归,直接返回缓存的 Layout 对象。sameFloat()(L113-115)使用 a === b || (isNaN(a) && isNaN(b)) 做 NaN 安全的浮点比较。
// yoga-layout/index.ts:969-1012
function cacheWrite(node: Node, performLayout: boolean, ...): boolean {
// 比较 style 快照 + 父约束
if (sameFloat(cached.ownerWidth, ownerWidth) &&
sameFloat(cached.ownerHeight, ownerHeight) &&
// ... 所有 style 字段一致
) {
commitCacheOutputs(node, performLayout) // 命中:直接返回
return true
}
// 未命中:更新缓存并重新计算
}
3. 异步分 chunk 构建——不阻塞事件循环
FileIndex.buildAsync()(L95-133)将文件列表索引化过程拆分为多个 chunk,每处理约 8-12k 条路径后调用 yieldToEventLoop() 让出控制权。这使得即使有 10 万级文件,索引构建也不会阻塞 UI 渲染和用户输入。
// file-index/index.ts:95-133
private async buildAsync(
fileList: string[], markQueryable: () => void,
): Promise {
const CHUNK_SIZE = 8192
for (let i = 0; i < fileList.length; i += CHUNK_SIZE) {
const end = Math.min(i + CHUNK_SIZE, fileList.length)
const chunk = fileList.slice(i, end)
this.buildIndex(chunk)
if (i === 0) markQueryable() // 首个 chunk 后即可查询
await yieldToEventLoop() // 让出事件循环
}
}
yieldToEventLoop() 的实现通常是一个 Promise.resolve().then() 的微任务——它比 setTimeout(fn, 0) 更轻量,但仍然允许渲染和 I/O 回调在 chunk 之间执行。首个 chunk 后调用 markQueryable() 实现了"渐进可用"——用户不需要等待全部文件索引完成就能开始搜索。4. CONNECT→WebSocket 中继——代理透明化
upstreamproxy 在 CCR 容器中建立透明代理:本地工具(Bash/MCP/LSP)通过 HTTP_PROXY=http://127.0.0.1:{port} 发出请求,proxy 拦截 CONNECT 请求,通过 WebSocket 隧道转发到 Anthropic 的 egress 网关。工具代码无需知道代理的存在——getUpstreamProxyEnv()(upstreamproxy.ts:160-199)将代理环境变量注入到每个子进程。
// upstreamproxy.ts:160-199
export function getUpstreamProxyEnv(): Record {
if (!state.enabled) return {}
return {
HTTP_PROXY: `http://127.0.0.1:${state.port}`,
HTTPS_PROXY: `http://127.0.0.1:${state.port}`,
NO_PROXY: NO_PROXY_LIST.join(','), // 排除 Anthropic 内部域名
NODE_EXTRA_CA_CERTS: state.caBundlePath,
}
}
HTTP_PROXY / HTTPS_PROXY / NODE_EXTRA_CA_CERTS 是事实标准环境变量,几乎所有 HTTP 库都自动尊重。这意味着 Bash/MCP/LSP/hooks 等子进程无需任何代码修改就能通过代理通信。NO_PROXY_LIST 硬编码排除 Anthropic 内部域名,避免循环代理。◈ Agent 实践借鉴 — 客服 Agent 平台能力设计
一、场景映射:客服 agent 的跨平台部署挑战
客服 agent 可能部署在三种截然不同的环境:云端 Docker 容器(有完整的语音识别 API、大模型服务、弹性扩缩容)、本地服务器(GPU 有限、网络可能受限、但有局域网 CRM 系统直连)、门店智能终端(有摄像头可扫条码、有本地数据库、但没有云端语音 API)。CC 用纯 TS 重写消除原生依赖以实现跨平台一致性,客服 agent 也面临同样的问题——同一个 agent 在不同环境下必须有统一的表现。
二、借鉴 CC + 客服改造
实践 1:平台能力检测 + 优雅降级(借鉴 CC 的 UpstreamProxy 环境检测)
CC 的 initUpstreamProxy() 检测 CCR 环境变量,每一步都失败开放。客服 agent 启动时也需要检测所在环境的能力——没有语音 API 就纯文字模式,没有摄像头就禁用扫码功能,没有本地数据库就用远程 API。
// 借鉴 upstreamproxy.ts:79-153 的环境检测 + 失败开放模式
interface PlatformCapabilities {
canUseVoice: boolean // 语音识别/合成
canUseCamera: boolean // 摄像头扫条码
canUseLocalDB: boolean // 本地数据库(门店场景)
canUseGPU: boolean // 本地推理能力
canUseInternet: boolean // 外网访问(门店可能断网)
}
class PlatformDetector {
private capabilities: PlatformCapabilities
constructor() {
this.capabilities = {
// 逐项检测,每项失败不影响其他项(失败开放)
canUseVoice: this.checkVoiceAPI(),
canUseCamera: this.checkCameraAccess(),
canUseLocalDB: this.checkLocalDB(),
canUseGPU: this.checkGPU(),
canUseInternet: this.checkInternet(),
}
console.log('平台能力检测结果:', this.capabilities)
}
private checkVoiceAPI(): boolean {
try {
// 云端:检测 ASR 服务是否可达
if (process.env.ASR_ENDPOINT) return true
// 本地:检测 Whisper 模型是否可用
return fs.existsSync('/usr/local/models/whisper')
} catch { return false } // 失败开放:不可用就不启用
}
private checkCamera(): boolean {
try {
// 门店终端:检测 /dev/video* 设备
const devices = fs.readdirSync('/dev').filter(f => f.startsWith('video'))
return devices.length > 0
} catch { return false }
}
// 获取能力报告,供上层判断
getCapabilities(): Readonly<PlatformCapabilities> {
return this.capabilities
}
}
// 使用:agent 启动时检测一次,运行时按能力路由
const platform = new PlatformDetector()
const caps = platform.getCapabilities()
// 没有语音 API 就关闭语音入口
if (!caps.canUseVoice) {
channelRegistry.disable('phone')
console.warn('语音 API 不可用,已关闭电话渠道')
}
实践 2:统一平台接口(借鉴 CC 的 Shell Provider 抽象)
CC 用 ShellProvider 接口统一了 Bash 和 PowerShell。客服 agent 的平台差异更大——语音、图像、本地存储都需要统一接口,让上层业务代码不关心底层是云端 API 还是本地模块。
// 借鉴 shellProvider.ts 的 Provider 抽象模式
interface SpeechService {
recognize(audioBuffer: Buffer): Promise<string> // 语音转文本
synthesize(text: string): Promise<Buffer> // 文本转语音
}
interface VisionService {
scanBarcode(imageBuffer: Buffer): Promise<string> // 扫条码
ocr(imageBuffer: Buffer): Promise<string> // 图片文字识别
}
interface StorageService {
queryProduct(barcode: string): Promise<Product | null>
saveInteraction(record: InteractionRecord): Promise<void>
}
// 云端实现:调用远程 API
class CloudSpeechService implements SpeechService {
async recognize(audioBuffer: Buffer): Promise<string> {
const resp = await fetch(`${process.env.ASR_ENDPOINT}/recognize`, {
method: 'POST', body: audioBuffer,
})
return resp.json().then(r => r.text)
}
}
// 本地实现:调用本地 Whisper 模型
class LocalSpeechService implements SpeechService {
async recognize(audioBuffer: Buffer): Promise<string> {
const whisper = require('whisper.cpp') // 本地 NAPI 模块
return whisper.transcribe(audioBuffer)
}
}
// 工厂函数:根据平台能力自动选择实现
function createSpeechService(caps: PlatformCapabilities): SpeechService {
if (caps.canUseInternet) return new CloudSpeechService() // 云端优先
if (caps.canUseGPU) return new LocalSpeechService() // 本地降级
return new TextOnlyFallback() // 纯文字兜底
}
实践 3:知识库搜索的预过滤(借鉴 CC 的位图快速拒绝思路)
CC 的 FileIndex 用 26 位字母位图预过滤 89% 的路径。客服场景的知识库搜索同样需要预过滤——10 万条 FAQ/商品条目中,先用轻量条件排除不可能匹配的,再做昂贵的语义搜索。
// 借鉴 file-index/index.ts:156-167 的位图预过滤思路
// (不需要抄位图算法本身,用标准搜索引擎的预过滤即可)
interface KnowledgeEntry {
id: string
category: string // 'product' | 'faq' | 'policy' | 'troubleshoot'
title: string
keywords: string[]
tags: string[]
}
class KnowledgeSearchEngine {
private entries: KnowledgeEntry[] = []
// 预过滤索引:按类别和标签建立倒排索引
private categoryIndex = new Map<string, number[]>()
private tagIndex = new Map<string, number[]>()
// 步骤 1:轻量预过滤(类似位图快速拒绝)
preFilter(query: SearchQuery): KnowledgeEntry[] {
let candidateIndices: number[] | null = null
// 按类别过滤
if (query.category) {
const indices = this.categoryIndex.get(query.category) ?? []
candidateIndices = candidateIndices
? intersect(candidateIndices, indices)
: indices
}
// 按标签过滤
if (query.tags && query.tags.length > 0) {
for (const tag of query.tags) {
const indices = this.tagIndex.get(tag) ?? []
candidateIndices = candidateIndices
? intersect(candidateIndices, indices)
: indices
}
}
// 如果没有过滤条件,返回全部
const result = candidateIndices ?? this.entries.map((_, i) => i)
return result.map(i => this.entries[i])
}
// 步骤 2:对幸存条目做昂贵的语义搜索
async search(query: SearchQuery): Promise<KnowledgeEntry[]> {
const candidates = this.preFilter(query)
// 只对候选条目做向量相似度搜索(昂贵的操作)
return this.semanticSearch(query.text, candidates, query.limit)
}
}
// 使用示例:搜索退货相关的 FAQ
const results = await knowledgeBase.search({
text: '买的东西坏了怎么退',
category: 'faq',
tags: ['refund', 'after_sales'],
limit: 5,
})
三、落地清单
- 平台能力检测 + 优雅降级——启动时检测环境能力,运行时按能力路由,每项检测独立失败开放
- 减少原生依赖——客服 agent 尽量用纯 JS/HTTP 实现核心功能,避免 NAPI/Rust 依赖导致跨环境部署困难
- 统一平台接口——语音/图像/存储用 Provider 接口抽象,云端和本地各自实现,上层不感知差异
- 位图算法的具体实现——太底层,客服场景用标准搜索引擎(Elasticsearch/Meilisearch)的预过滤即可
- Yoga 布局引擎——客服面板用 Web CSS 渲染,不需要终端布局引擎
- UpstreamProxy 的 CONNECT→WebSocket 中继——客服场景不需要 MITM 代理
四、常见坑
- 平台检测只在启动时做一次——门店终端的网络可能时断时续,启动时检测到有外网但运行中断网了。必须加定时重检或请求失败时重检逻辑。
- 降级模式的功能缺失导致客户困惑——语音 API 不可用时降级为纯文字,但电话渠道的客户根本无法打字。降级策略必须考虑渠道特性:电话断网应该转人工,而不是降级为文字。
- 知识库搜索跳过预过滤直接全量语义搜索——10 万条 FAQ 全量做向量相似度计算,单次搜索耗时可能超过 5 秒。先用类别/标签预过滤将候选集缩小到千级,再做语义搜索。
- 本地模型和云端模型的输出格式不一致——同一个
SpeechService接口的两个实现返回格式不同(云端返回 JSON,本地返回纯文本)。必须统一接口契约并做集成测试。
代码索引
| 文件 | 行数 | 说明 |
|---|---|---|
native-ts/yoga-layout/index.ts | ~2580 | 完整 Flexbox 布局引擎,含多级缓存、wrap、baseline |
native-ts/yoga-layout/enums.ts | ~100 | FlexDirection/Align/Justify/Wrap/Edge 等枚举定义 |
native-ts/color-diff/index.ts | ~500+ | 语法高亮 + word-diff + ANSI TrueColor 渲染 |
native-ts/file-index/index.ts | ~300+ | nucleo 风格模糊搜索,异步分 chunk 构建 |
upstreamproxy/upstreamproxy.ts | ~200+ | CCR upstream proxy 初始化:token/CA/relay/NO_PROXY |
upstreamproxy/relay.ts | ~200 | CONNECT→WebSocket 本地代理中继 |
bridge/bridgeApi.ts | ~200+ | Bridge REST API 客户端工厂函数 |
bridge/bridgeConfig.ts | ~100 | Bridge 配置管理 |
bridge/sessionRunner.ts | ~200 | 远程会话运行器 |
bridge/remoteBridgeCore.ts | ~200 | 远程 Bridge 核心逻辑 |
bridge/jwtUtils.ts | ~100 | JWT 令牌工具 |
bridge/trustedDevice.ts | ~100 | 可信设备令牌管理 |
bridge/types.ts | ~150 | Bridge 类型定义(BridgeConfig, PermissionResponse 等) |
bridge/debugUtils.ts | ~100 | Bridge 调试工具 |
bridge/replBridgeHandle.ts | ~100 | REPL Bridge 句柄管理 |
bridge/replBridgeTransport.ts | ~100 | REPL Bridge 传输层 |