zjchina.net

Drogon · ROS 2 · 独立开发笔记

ADR-00112026-11-04

视觉 L0 边界:事实/观点双轨,OCR 快脑、VLM 只做门

拍板:zj

背景

视觉范围已锁定为 A(真视觉:贴图 → 分析),先只做「屏幕理解」。「理解自己屏幕」不算视觉——那是结构化上下文注入(多输入汇聚),不进视觉内核。由此需要定三件契约侧的事:视觉事件如何入时间线、快慢归属、以及 VLM 选型对 L0 边界的影响。

选项

  • A. OCR 与 VLM 都记为 Kind::Observation。 否:观点一旦混进事实轨就不可修订,is_superseded 失去意义。
  • B. 每帧并联 OCR + VLM。 否:每帧都打 VLM 又贵又慢,300ms 快脑预算根本装不下。
  • C. VLM 直接接进视觉内核(内核内建 HTTP/密钥)。 否:违反 L0 边界(禁 HTTP、密钥、权重管理),也绕开 Provider facade 的降级/计费/审计纪律。
  • D. 事实/观点双轨 + OCR→VLM 级联 + 内核只做 Analyzer 门(选定)。

证据

  • 时间线语义本身:事实与观点的区别不是“谁说的“,而是能否被推翻——OCR 文本重观测只产生新 Observation,VLM 结论必须可被 Revision 推翻。
  • 预算事实:快脑硬截止 300ms(ADR-0010),全屏 OCR 已接近上限,VLM 只能按需级联,不能并联。
  • 既有纪律:苏舰台账“不含会话 ID 与原文“决定正文不进时间线;ADR-0005 决定视觉只读、不操作界面。
  • 许可证教训:espeak-ng 证明模型权重与引擎同样能一脚踢换方案(见 AGENTS.md 四条准入)。

拍板一:事实与观点双轨,不新增第二个感知 Kind

  • Kind::Observation(事实轨):OCR 抠出的文本、UI 元素树(后续布局/目标检测)。
    • 来源必须是 Source::Sense;
    • 不绑定轮次(turn = None 合法),跨轮引用用 seq;
    • 不可被修订:同一输入重新观测记为新的 Observation,note 标注观察代次/区域(如 ocr、ui_tree);
    • 正文不进时间线:note 只放短码,OCR 正文留在视觉内核的有界观测存储,按 seq 关联(遵守 ADR-0010 §1 载荷纪律与测试里 note 长度/无空白的断言)。
  • VLM 解读(观点轨)不新增 Kind:复用 Kind::Proposal,来源 Source::Slow,必须挂轮,可被 Kind::Revision 推翻。
  • 依据是时间线语义本身:事实与观点的区别不是“谁说的“,而是能否被推翻。若把 VLM 也记成 Observation,is_superseded 会失去意义(观点不可修订);若把 OCR 记成 Proposal,则事实会被 revision 语义污染。

拍板二:OCR 走快脑、VLM 走慢脑,并且是级联不是并联

  • OCR = 快脑事实源:本地执行,产出 Observation,不占轮次、不需要仲裁,预算含截图与降采样在内 300ms。
  • VLM = 慢脑观点源:产出 Proposal,必须挂轮,受仲裁与 revision 约束,失败走既有三档降级(部分失败 → Degraded;全部失败 → 可重试追问;回复器类失败 → 模板兜底)。
  • 级联而非并联:先用 OCR 得到文本,只在快脑/慢脑判定“需要看懂图“时才把「截图/裁剪区域 + OCR 文本」送给 VLM。理由:每帧都打 VLM 既贵又慢,且 300ms 快脑预算根本容纳不了。
  • 全屏 OCR 可能超 300ms:按区域/降采样分块产出多条 Observation,而不是等整屏;这与“逐句流式“同类,属于实现细节,不进契约。

拍板三:VLM 云优先,本地量化 VLM 后补;L0 只做「图像进/文本出」的门

倾向 云多模态 API 优先,理由:

  1. 对齐既有分层:zjchina-agent 已是 Provider facade,密钥只在上层、降级/计费/审计都有现成纪律;把 7B 量化权重塞进 L0 会把“模型权重 + GPU/NPU“变成长期维护债,违反 AGENTS.md 的 L0 基岩定位(L0 只该是 tokio/rustls/wgpu 这类稳定规格)。
  2. 第一战场景(tidewatch 看盘)是联网桌面产品,离线不是刚需;真正的强离线场景是工厂孪生,那时需要的是本地检测/跟踪模型(SceneVision),不是本地 VLM。
  3. 成本靠级联控制(OCR 先行、VLM 按需),而不是靠本地权重;本地 4bit 7B 的首 token 延迟与质量都不占优。
  4. 但这条倾向不改变 L0 边界:本地与云端都必须是上层注入的 Analyzer 实现。

因此 zjchina-vision(L0)边界:

  • 自带:图像解码/缩放/区域裁剪、本地 OCR 推理、VisionEvent 事件化、有界观测存储(按 seq 可回查正文)。
  • 只做门:Analyzer trait(图像 + OCR 文本 → 文本/结构),实现放上层(Provider facade)。
  • 禁止:内核内不得出现 HTTP 客户端、API 密钥、模型下载/权重管理、领域词表(股票/K 线/财报一律在上层绑定)。
  • 隐私硬规则:屏幕可能含隐私内容,默认只用本地 OCR;上云前必须裁剪/脱敏,或经用户显式授权——与苏舰台账“不含会话 ID 与原文“同一纪律。
  • 许可证:OCR 引擎与模型权重同样要过 AGENTS.md 四条准入(espeak-ng 的教训:许可证可能直接换掉引擎,不只是登记);zjchina-speech 与后续 zjchina-vision 都需加入 scripts/gen-dependencies.py 的嵌套 workspace 列表,否则清单与 copyleft 拦截都会漏。

不做:场景感知(摄像头/检测跟踪)、文档视觉(表格/版面)、UI 自动点击或事件注入(视觉只读,不得操作界面)、模型路由与权重管理。

拍板四:多模态走同一 facade,但用独立 trait

  • 供应商首发 Qwen-VL(DashScope):中文财报/公告理解与国内直连优先;DashScope 提供 OpenAI 兼容端点, 可复用 zjchina-agent 既有 ChatRequest 形状。备选 GPT-4V(需海外 key),本地 Qwen2-VL 按拍板三后补。
  • 不扩展 LlmProvider(它只做「prompt → 文本」,Reqwest 15s 超时纪律不变),在同一 crate 内新增 VisionProvider::describe(prompt, images),共用 LlmError 与 client 构建纪律。理由:文本与多模态的计费、 降级路径、输入大小护栏不同,塞进同一 trait 会逼供应商实现声明“不支持“。
  • 密钥与隐私:DASHSCOPE_API_KEY 只在上层(SSR/桌面侧)读取,绝不进 C++ 与前端;日志不得记录图像内容 (base64/URL/文件路径)与密钥;审计只记 model、耗时、token 用量、图片张数。
  • 计费护栏:沿用调度器「每标的每 Agent 一次/日」;单次最多 2 张图,长边压到 ≤1280、JPEG q≈80; 只送裁剪区域或整屏缩略图,不送原始分辨率。

上层接线(Analyzer → 时间线)

视觉内核 observe() -> (obs_id, events)          正文留在内核环
  └─ 上层 append(Source::Sense, Kind::Observation, note="ocr")   -> seq_fact
       └─ 记录 obs_id <-> seq_fact(事实轨不挂轮)
VisionProvider::describe(裁剪图 + OCR 文本) -> Analysis{ text, confidence }
  ├─ 解析失败 -> Kind::Degraded(note="vlm"),保留 OCR 事实,继续可用
  ├─ 成功    -> append(Source::Slow, Kind::Proposal, turn=上层定义, note="vlm")  -> seq_opinion
  └─ 同一图重新解读 -> 新 Proposal + Revision(revises=seq_opinion, note="fresh_frame")

纪律:

  • turn 由上层生成(一次解读一个轮次),Analyzer 不定义轮次;
  • 解读正文存上层,时间线只留 seq 与短码;
  • VLM 失败是第一档(Degraded),不是 fallback_after_slow_failure;后者只用于「全部能力失败 → 可重试」。

实现纪律(已独立成文)

原先的三条实现注记(VisionEvent::Text 带几何、OCR 不在 UI 主线程、单进程单版本 ONNX Runtime) 连同 TTS 三级兜底,已升为正式条目 ADR-0012;此处不再重复,避免两处漂移。

日期:2026-11-04(拍板四与上层接线补充于 2026-11-05,实现注记补于 2026-11-06,2026-11-07 升为 ADR-0012)。拍板者 zj。

← 返回决策日志