DeepChat Light OCR 后续加固:调度、持久化与生命周期跟进契约的规范解读

DeepChat Light OCR 后续加固:调度、持久化与生命周期跟进契约的规范解读 DeepChat Light OCR 后续加固调度、持久化与生命周期跟进契约的规范解读【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat本文基于 DeepChat 仓库中的 Light OCR Follow-up Hardening 规范系统讲解该规范记录的当前已加固行为与待定跟进项并结合 src/main/ocr 下的调度器、运行时服务、附件路由等源码说明八图资源上限、可用性重试、缓存清空忙语义、有界优先队列等机制的实际实现方式以及消费端 steer 载荷、helper 冷启动、快照预算等跟进项各自需要先定义的持久化、兼容性或调度契约。读完后你将能够理解 DeepChat 本地 OCR 管线中队列准入与内存准入分离的设计边界以及每项跟进为何必须先有契约与测试才能动手。规范定位一份本地 SDD 跟进记录该规范在 docs/issues/light-ocr-follow-up-hardening/spec.md 中明确了自己的状态Status: open persistence, scheduling, and lifecycle follow-ups并特别说明这是一个由明确决定产生的本地 SDDSpec-Driven Development记录不创建 GitHub issue。规范开篇就划定了证据边界未勾选的任务项unchecked item本身不构成已测量应用延迟的证据当前源码行为与尚未完成的验收工作被显式区分开。这种先区分现状与待办、再要求每项变更先有契约的写法是本文所有小节的基本阅读框架。当前行为仓库中已经落地的四个加固点八图资源上限只约束真实 OCR 候选规范第一条当前行为是八张图的资源上限现在只作用于真实的 OCR 候选在混合轮次中纯视觉vision-only图片与走视觉通路的图片即使超过第八个附件也保留其图像表示而显式 OCR 与自动 OCR 仍被约束在八张候选以内。仓库源码印证了这一点。在 attachmentCapabilityRouter.ts 中定义const MAX_OCR_IMAGES_PER_TURN 8 const MAX_OCR_DOCUMENTS_PER_TURN 1 const MAX_TURN_OCR_TEXT_TOKENS 16_000关键在于候选收集的顺序prepare()方法逐附件遍历时只要模型支持视觉且表示偏好不是ocr_text图片就会在 L239-L253 直接解析为kind: image并continue根本不进入imageCandidates列表只有真正需要 OCR 的图片才会被 push 到imageCandidates。随后的resolveOcrCandidates()L314-L378才执行imageCandidates.slice(0, MAX_OCR_IMAGES_PER_TURN)截断对超出部分打上image_limit_exceeded不可用标记。也就是说第八张之后的视觉图不受影响只有第八张之后的OCR 候选会被拒绝。这条上限在更底层还有一道冗余防线imageTextExtractionService.ts 中MAX_TURN_IMAGES 8extractBatch()会再抛一次batch_image_limit_exceeded同文件还定义了单轮源字节上限MAX_TURN_SOURCE_BYTES 120 * 1024 * 1024。单图 OCR 文本上限来自 attachment.tsATTACHMENT_OCR_MAX_TOKENS 8_000、ATTACHMENT_PDF_OCR_MAX_TOKENS 16_000、ATTACHMENT_PDF_OCR_MAX_PAGE_SPANS 100、ATTACHMENT_OCR_MAX_TEXT_CHARACTERS 128_000。getAvailability()对不可用结果重试规范称OcrRuntimeService.getAvailability()会重试不可用结果工具链变更会失效化invalidate可用性并在活跃属主释放后退役过期资源。对应实现在 ocrRuntimeService.tsasync getAvailability(): PromiseOcrRuntimeAvailability { // ... if (this.availabilityPromise) { const current await this.availabilityPromise if (current.status available) return current this.availabilityPromise null // 不可用结果被丢弃下次调用重新 resolve } this.availabilityPromise this.resolver.resolve() return await this.availabilityPromise }只有available状态会被缓存复用unavailable结果会把availabilityPromise置空让下一次调用重新走OcrRuntimeAssetResolver.resolve()这就是重试不可用结果的具体语义。失效化逻辑在refreshAvailability()L77-L98工具链变更时先清空availabilityPromise若存在已建资源则串行等待closingResources后检查isResourcesBusy()——忙时只把resourcesStale标记为true等活跃属主释放后由getResources()的 stale 分支在 L164-L174 处置空闲时才真正释放旧资源并重建。在活跃属主释放之后退役过期资源正是这一 stale 标记机制的行为。clearCache()的显式忙语义规范要求当抽取正持有资源时clearCache()上报OcrRuntimeBusyError而不是静默忽略。源码中错误类定义在 ocrRuntimeService.tsexport class OcrRuntimeBusyError extends Error { constructor() { super(OCR cache cannot be cleared while extraction is active) this.name OcrRuntimeBusyError } }clearCache()L140-L146通过isResourcesBusy()判定资源是否被占用。isResourcesBusy()L246-L256的判定口径是图片/文档抽取服务存在活跃 flight、进程宿主queuedRequests 0或宿主状态为starting/busy/stopping之一。任何一条命中即抛OcrRuntimeBusyError调用方可以据此决定重试或提示而不必猜测缓存是否真的被清了。OcrExtractionScheduler有界、可取消、带公平上限的双队列规范第四条当前行为对应 ocrExtractionScheduler.ts 的全部核心设计这是本规范中最值得逐行对照的部分const MAX_CONSECUTIVE_INTERACTIVE_TASKS 4 const DEFAULT_MAX_PENDING_TASKS 8有界双队列interactiveQueue与backgroundQueue两条队列加上正在执行的任务总长度达到maxPendingTasks默认 8时schedule()直接以OcrSchedulerError(queue_full)拒绝实现bounded interactive/background queues。队列内 FIFOqueueFor(priority)返回对应数组任务按 push 顺序shift()出队同一优先级内严格先进先出。取消入队时若提供了AbortSignal会注册一次性abort监听任务尚在队列中时removeQueuedTask()将其摘除并以cancelled拒绝已经开始执行的任务则通过AbortController信号向下游预处理、识别请求传播取消。连续交互任务上限 4 的公平约束takeNextTask()L99-L115中只有当背景队列非空且交互队列为空或consecutiveInteractiveTasks 4时才执行背景任务一旦执行了背景任务计数器归零。这保证背景抽取不会被无限期的交互洪峰饿死正是规范中four-interactive-task fairness bound的实现。关闭语义close()会把两条队列中所有任务以closed拒绝并清空队列已排队的 abort 监听也被移除避免泄漏。错误码枚举只有三个cancelled | closed | queue_fullL138-L149调用侧在 attachmentCapabilityRouter.ts 的mapExtractionFailure()中把queue_full映射为用户可见的ocr_queue_full原因throwIfCancelled()L915-L926则负责把各层cancelled归一为AbortError。规范还有一句容易忽略的定性Snapshot byte reservation still occurs before scheduling; queue admission and memory admission remain separate.快照字节预留仍发生在调度之前队列准入与内存准入保持分离。在 imageTextExtractionService.ts 的extract()中可以清晰看到这一顺序先snapshotReader(input)读取不可变快照再reserveSnapshot(snapshot)向OcrSourceSnapshotBudget申请字节额度之后才通过extractSnapshot()进入调度器extractBatch()则额外在批内累计sourceBytes并对照MAX_TURN_SOURCE_BYTES抛batch_source_bytes_exceeded。也就是说能不能进内存快照预算与能不能进队列调度器有界性是两个独立闸门这为下一节的跟进项留下了明确讨论空间。快照预算当前是硬拒绝作为上述分离准入的背景ocrSourceSnapshotBudget.ts 定义了默认maxSnapshots 8、maxBytes 120MB的全局预算reserve()超限时直接抛OcrSourceSnapshotBudgetError没有排队等待、没有取消路径——imageTextExtractionService.ts的reserveSnapshot()把这个错误归一为queue_fullL473-L483。这正是后文把硬拒绝换成有界可取消准入跟进项要评估的现状。Deferred Findings先契约、后实现的跟进项规范把尚未实现的发现按优先级分为四类。以下按原文结构逐组展开并指出每组对应的仓库证据点。高优先级快速跟进消费端 steer 载荷的 OCR 副本已被消费的 steer 行仍保留携带 OCR 的payload_json。需要定义一个规范的脱敏消费载荷canonical redacted consumed payload保留队列生命周期元数据、但不再保留第二份 OCR 副本在变更持久化契约之前先安全迁移并验证重试/历史行为。从源码结构看payload_json列承载于 deepchatPendingInputs.ts 定义的待输入表并由 pendingInputStore.ts 读写。steer插队指令消息的 OCR 文本在发送后仍会随payload_json持久化形成与消息正文并存的第二份 OCR 副本。跟进项的验收前提是脱敏后的载荷必须让重试与历史回放行为可验证不能只改存储不改语义——这也呼应了规范验收标准中隐私清理绝不删除与用户消息一起存储的持久附件表示见下文。性能与调度组这组五项跟进中每一项都能在源码中找到明确的现状锚点可信缓存命中不应启动 helperAvoid helper startup on trustworthy cache hits。现状是imageTextExtractionService.ts 的runExtraction()必须先调processHost.prepare()拿到LightOcrEngineStatus引擎身份bundleId、provider chain、precision再拼出OcrArtifactIdentity去查artifactStore.find()——缓存键包含引擎实际执行的 provider 链因此查缓存之前必须先让 helper 引擎就绪。跟进方向是在干净空闲关闭后保留可信的最近已知身份先做缓存查找未命中或身份漂移identity drift时再启动后校验。任务清单里对应项Avoid helper startup on trustworthy cache hits仍未勾选。快照字节预留移到有界可取消内存准入之后Replace hard global reservation rejection with bounded cancellable admission。现状如前节所述OcrSourceSnapshotBudget.reserve()是硬抛错。规范要求保留不可变输入快照与既有调度器的优先级/公平契约且变更前必须实测留存字节数与多会话并发行为——不允许凭直觉改准入顺序。OCR 存在性检测不应重扫全文本当前轮次里有没有 OCR的检测会重新解析完整 transcript跟进方向是把该标志折入既有 tape/chat 投影遍历而不是再加一次历史遍历。当前轮路由重复 base64 解析与归一化现状可见于 attachmentCapabilityRouter.ts 的normalizeLlmFriendlyImageDataUrl()——每次prepare()都会重新正则匹配并去空白校验 data URL。跟进方向是内部复用可信的准备载荷trusted prepared payload同时保留在模型能力或设置变化时的权威重路由。token 分配依赖输入顺序applyTurnOcrTextBudget()L740-L815与applyBatchTokenBudget()imageTextExtractionService.ts都按数组顺序遍历用remainingTokens / remainingItems的均分给每个附件定预算——排在后面的附件分到的预算取决于前面的实际消耗即规范所称 input-order dependent。跟进方向是评估 token 感知的公平分配在所有成功图片间保留有用的头/尾上下文截断本身已按 head-tail 保留实现见buildHeadTailText()L534-L550。兼容性与生命周期组资产修复后的可用性恢复验证getAvailability()重试上文已述在资产修复而非工具链变更场景下同样恢复以及 busy 抽取属主释放后缓存统计的刷新既有的 retry 与显式 busy 行为必须继续被测试覆盖。异常退出后的临时目录清理应用非正常终止后清理陈旧的私有 OCR 临时目录但不能触碰存活进程使用的目录。tape 投影优化当源消息集合不可能包含 OCR 元数据时避免重建 tape projection v4。安全与可维护性组把附件文件名与 MIME 标签视为不可信 prompt 数据与 OCR 文本保持一致的编码或分隔处理防注入视角的元数据一致性稳定的附件序号让混合图像/OCR元数据不会复用令人困惑的标签从共享 shadcn dropdown 原语中撤掉领域特定指针行为保留在附件组件或领域包装器内对应验收标准共享 UI 原语不包含 Light OCR 特有交互行为加固 legacyaccepted兼容字段确保 ACP 与未来调用方不会把三态结果中的非 accepted解释为成功。显式非发现Explicit Non-findings规范明确不做的事规范专门列出了两类不要做的结论这在跟进型文档中同样重要不恢复被删除的 remote placeholder 句子。非图片附件内容仍由正常文件准备路径表示占位句只会让纯图片输入看起来有意义。不要把报告出的死代码一次性全删。规范点名attachmentFallbackPolicy是活跃路径在 attachmentCapabilityRouter.ts 中直接驱动user_skipped_image_content标记并贯穿 SessionClient.ts、composerSubmit 等多处渲染层与主进程代码取消/结果码仍是类型化协议的一部分每个死代码候选需要单独的可达性检查。验收标准每项变更的准入门槛规范给出五条验收标准它们实质上是把先契约后实现落成可核对的门槛验收标准含义每项仅在持久化/兼容性/调度契约被定义并测试后才实现禁止边做边定契约隐私清理绝不删除与用户消息共存的持久附件表示steer 载荷脱敏不得损伤消息事实调度变更保持有界、可取消、无跨会话饥饿与OcrExtractionScheduler现有契约对齐性能变更附带 before/after 测量helper 启动数、延迟、分配、transcript 遍历次数禁止无测量依据的性能优化共享 UI 原语不含 Light OCR 特有交互行为dropdown 指针行为回迁到领域层任务清单与源码对照规范末尾的 Task Checklist 当前只有一项勾选[x] Restrict the eight-image resource limit to OCR candidates.这与上文源码证据完全吻合——八图上限收窄到 OCR 候选是当前行为第一条。其余未勾选项逐条对应前文分析的实现位置Scrub consumed steer payloads — 锚点pendingInputStore.tsAvoid helper startup on trustworthy cache hits — 锚点imageTextExtractionService.ts 中 prepare→find 顺序Replace hard global reservation rejection — 锚点ocrSourceSnapshotBudget.tsEliminate redundant transcript and base64 processing — 锚点attachmentCapabilityRouter.tsAdd availability/cache-clear recovery semantics — 锚点ocrRuntimeService.tsHarden prompt metadata and attachment numberingMove attachment pointer handling out of the shared shadcn primitiveLifecycle and projection optimizations with focused benchmarks如何在仓库中继续验证如果你想沿本文的线索自查建议按以下路径阅读规范本体docs/issues/light-ocr-follow-up-hardening/spec.md附件路由与八图上限src/main/ocr/attachmentCapabilityRouter.ts调度与公平约束src/main/ocr/ocrExtractionScheduler.ts运行时生命周期可用性重试 / 忙语义src/main/ocr/ocrRuntimeService.ts快照预算与批次字节上限src/main/ocr/ocrSourceSnapshotBudget.ts、src/main/ocr/imageTextExtractionService.tstoken/字符上限常量src/shared/types/attachment.ts持久化侧 payload 表src/main/session/data/tables/deepchatPendingInputs.ts需要说明的是本文所有当前行为均以该仓库当前源码为准Deferred Findings部分是待实现的计划在对应契约与测试落地之前不代表仓库当前已具备的能力。【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考