Files
NotesAgentic/docs/development/多模态管线与模型运行开发说明.md

155 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 多模态管线与模型运行
更新日期:2026-09-04。阶段 F 实现位于 `feat/multimodal-pipeline`,接口以 `/openapi.json` 为准。
## 安装
API 保留 `backend/.venv`,模型依赖安装到独立的 `backend/.venv-models`。在项目根目录执行:
```powershell
# 默认 CPU
./backend/scripts/install-model-runtime.ps1
# CUDA 显式选装,不安装或修改 NVIDIA 驱动
./backend/scripts/install-model-runtime.ps1 -Device cuda
```
脚本固定 torch/torchaudio 2.9.1,分别选择 CPU / cu128 wheel;其他已验证依赖由 `model-requirements.lock` 锁定。不要求 vLLM、FlashAttention。`APP_MODEL_PYTHON` 可指定模型解释器。
设置 → 模型提供商 → 本地模型提供下载、续传、删除、设备与预算配置。推理不自动下载;“已下载并校验”不代表设备已通过推理验证,最近实际设备与诊断单独显示。
默认 CPU、2 线程、8 GiB 内存预算。独立子进程按需加载,每任务结束释放,取消/超时终止并回收进程。单模型串行执行,排队中的交互向量请求优先于转写,不抢占运行中任务。请求 CUDA 但不可用时回退 CPU,记录原因。任务冻结运行配置。当前要求单 API worker,不支持跨进程调度。
## 模型与许可
| 能力 | 模型 | 固定 revision | 权重许可 |
| --- | --- | --- | --- |
| 默认 Embedding | hotchpotch/bekko-embedding-v1-a8m | c721113d59a1d91b447450324f51c4b3332c924a | MIT |
| 可选 Embedding | ibm-granite/granite-embedding-97m-multilingual-r2 | 835ad14087e140460703cf0fae09f97d469d65c2 | Apache-2.0 |
| 转写、语言识别 | Qwen/Qwen3-ASR-0.6B | 5eb144179a02acc5e5ba31e748d22b0cf3e303b0 | Apache-2.0 |
| 声纹相似度 | iic/speech_eres2netv2_sv_zh-cn_16k-common | 3317286545c587ae682dbc166831d9448780eebb | Apache-2.0 |
来源:[Bekko](https://huggingface.co/hotchpotch/bekko-embedding-v1-a8m)、[Granite](https://huggingface.co/ibm-granite/granite-embedding-97m-multilingual-r2)、[Qwen3-ASR](https://huggingface.co/Qwen/Qwen3-ASR-0.6B)、[ERes2NetV2](https://modelscope.cn/models/iic/speech_eres2netv2_sv_zh-cn_16k-common)。下载固定 revisionHF LFS / ModelScope 校验 SHA-256HF 普通文件校验 Git blob hash。中断保留 .partial,使用 Range 续传;校验失败、磁盘不足和中断分别记录。
## 媒体接口
| 方法与路径 | 行为 |
| --- | --- |
| POST /api/media/attachments?filename=... | 二进制上传,宿主分配 ID25 MiB 上限 |
| GET /api/media/attachments/{id} | 受控读取,支持播放器 Range |
| POST /api/media/transcriptions | 202/queuedlocal_only、diarization、terminology、idempotency_key |
| GET /api/media/transcriptions | 按状态分页查询 |
| GET /api/media/transcriptions/{id} | 状态、分段、原文、修订、进度 |
| GET /api/media/transcriptions/{id}/events | SSEafter / Last-Event-ID 回放 |
| POST /api/media/transcriptions/{id}/cancel | 取消排队或运行任务 |
| POST /api/media/transcriptions/{id}/retry | 新 attempt,保留 previous_job_id |
| PATCH /api/media/transcriptions/{id} | revision 乐观锁校对、重命名 |
| GET /api/media/transcriptions/{id}/revisions | 历史修订 |
| POST /api/media/transcriptions/{id}/notes | Knowledge 写入;任务/修订/选项幂等 |
| POST /api/media/speaker-matches | 两附件声纹比对,支持 local_only |
| GET /api/media/attachments/{id}/cleanup-impact | 清理影响与保留笔记 |
| DELETE /api/media/attachments/{id} | 清理附件、转写正文、修订及术语 |
任务、模型快照、事件和修订写入 SQLite。重启把未完成任务标为 TRANSCRIPTION_INTERRUPTED,不自动重新上传。幂等摘要包含内容、选项、模型和提供商配置;同键不同输入返回 409。
API 优先,无配置或无效结果时本地回退。local_only 禁止远程模型。纯文本附件和既有 sidecar 可导入,但已有真实音频时不使用旁边文本冒充识别。
PyAV 提取音轨至 16 kHz 单声道,最长 1 小时,禁止解码器网络协议。能量分段后交给 Qwen3-ASR,返回片段边界,不宣称逐字对齐。ERes2NetV2 提取片段声纹并按相似度聚类;短片段、同段多人、重叠发言需要人工校对。缺失能力返回 DIARIZATION_UNAVAILABLE;未启用逐字对齐返回 WORD_TIMESTAMPS_UNAVAILABLE。
术语是识别后的替换规则,保留原始文本和来源。重命名只修改显示名,稳定 ID 不变。笔记包含音频与时间跳转链接;重复导出不覆盖用户编辑。清理保留已导出笔记,音频链接失效,已清理任务不可重试。重建索引保留转写与笔记关联。
## 向量空间
生产使用真实模型;HashEmbeddingProvider 仅供测试注入。本地/API 向量都写入按模型空间隔离的 routed_block_vectors;不同模型、revision、接口或维度不混用。旧 128 维测试索引不用于真实查询。
模型不可用时仍可保存 Markdown/FTS;语义查询返回索引未就绪,混合查询可使用全文检索。切换模型后重建全部索引。Benchmark 验证当前空间完整覆盖。
## Token 用量
GET /api/usage 使用带时区的 start/end(左闭右开),支持 provider_id、model、source。页面提供今日、7 天、30 天、自定义时段。
按实际 attempt 保存 request_id、Agent run_id、模型、来源、时间、原始数值与归一化计数。覆盖 Chat、流式、Agent、Embedding、媒体及本地推理。累计快照取最大值并 upsert;回放不新增请求,真实重试有新 attempt。流中断保留已收到计数。
输入缓存按供应商口径归一化,推理不重复加入输出;缓存命中率按完整输入口径加权。缺失为 null,显示每项覆盖数。本地 Embedding 使用真实 tokenizer,其他本地能力不编造 Token。写入失败不影响回复;原始 usage 仅保留数值白名单。本应用观测值不是厂商账单。
## 自定义请求 JSON
request_overrides 每项含 capability、model(空表示全部)、stream(null 表示全部模式)、body。通用规则先于模型规则,同层显式流式规则优先。对象递归合并、数组替换、标量覆盖、null 保持实际值;删除键恢复继承。
```json
{
"capability": "chat",
"model": "special-model",
"stream": true,
"body": {
"stream_options": { "include_usage": true },
"enable_thinking": false
}
}
```
model、消息、系统提示、工具、媒体文件、stream 由宿主管理,冲突拒绝。禁止 body 注入凭据、Header、URL,无模板求值。媒体独立匹配规则,嵌套扩展作为 JSON 文本 multipart 字段,不接收聊天规则。是否支持某扩展由供应商决定。
POST /api/providers/request-preview 不联网,隐藏正文/文件且不包含凭据。表单有格式化、校验、删除规则、预览;Provider version 检测保存冲突,适配器冻结配置。原有连接测试只验证模型列表连通性,不等于厂商推理接受扩展字段。
## 验证记录
### 阶段 F 收尾行为(2026-09-05
CUDA 页面安装入口:**设置 → 模型提供商 → 本地模型 → CUDA 运行组件(可选)**。未安装时显示“下载并安装 CUDA 组件”;安装中展示真实阶段和不定进度条,失败可重试。后端仅运行项目内固定安装脚本,写入独立 `.venv-models-cuda`,检查依赖及 CUDA wheel 后才标记就绪。已有环境会先检查;成功后选择 CUDA 并保存运行设置即可使用,CPU 环境保留。显式 `APP_MODEL_PYTHON` 继续优先,页面提示覆盖关系。当前页面安装支持 Windows,需后端能找到 uv;不会自动安装显卡驱动。
- 模型卡片读取权重目录的实际文件大小,包含未完成下载的文件;下载进度与磁盘占用分别显示。
- 本地任务串行执行;等待队列中交互检索优先级为 0,媒体任务为 10,后台笔记索引为 20。同级 FIFO,不抢占已运行任务。
- 默认 CPU。选择 CUDA 后,设备不可用直接使用 CPU;CUDA 初始化失败或显存不足时先释放原子进程,再用冻结的同一任务配置重试 CPU 一次。其他错误不触发设备重试;用户取消不会启动后续尝试。重试会清除上一尝试的部分转写片段。
- 安装脚本固定 CPU/CUDA wheel 为 `2.9.1+cpu` / `2.9.1+cu128`,避免已有 CPU wheel 被误认为满足 CUDA 安装。可用 `-RuntimeDirectory` 指定独立环境,后端通过 `APP_MODEL_PYTHON` 选择;不自动更换显卡驱动。
- 运行诊断写入应用 SQLite,保留最近 200 条,覆盖本地成功、失败、取消及能力 API 调用/回退事件。仅保留模型、设备、数值耗时、资源、状态码及请求标识,不保存输入、文件路径、密钥或异常全文。排队取消不记作实际模型用量;设备重试有独立 attempt,共享逻辑 request_id。
- 前端同一次提交在响应丢失后复用上传和任务幂等键;收到附件 ID 后只重试创建任务。“重新处理为新任务”明确创建新标识。客户端待提交状态仅在当前页面内存中,已接收任务和结果由后端持久化。
- 转写修订可选择“更新已导出笔记”。后端在 Vault 写锁内校验上次导出内容摘要,保留 note_id 和本地索引限制。用户编辑过正文时返回冲突,不覆盖;旧记录没有摘要时需先创建新笔记。重建索引保留导出基线与关联。
- 用量卡片单列音频实际调用次数、已报告时长和覆盖次数;时长不换算为 Token。重试分别计数,历史未知数据保持“未提供”。
- 请求 JSON 可导入、导出和恢复默认。文件格式为 `{ "version": 1, "request_overrides": [...] }`,只包含扩展规则;服务端复用受保护字段与凭据校验,导入成功仍需保存提供商才生效。
- 请求预览不联网。聊天“发送测试推理请求”使用当前草稿、已保存的凭据引用和固定短消息,支持流式/非流式,不读取知识库、工具或附件,并计入真实用量。更改模型、连接、规则或 JSON 有效性后,旧结果和迟到响应失效;媒体规则继续通过真实媒体操作验收。
独立 CUDA 环境示例(不改变默认 CPU 环境):
```powershell
./backend/scripts/install-model-runtime.ps1 -Device cuda -RuntimeDirectory ./backend/.venv-models-cuda
$env:APP_MODEL_PYTHON = (Resolve-Path ./backend/.venv-models-cuda/Scripts/python.exe).Path
```
设置环境变量后需从同一终端重启后端;CPU 默认仍可用。模型权重与运行环境不提交仓库。
### 2026-09-04 联调修复补充
Windows 热重载不支持异步子进程时使用线程管道兼容路径。本地 Embedding 进入推理前冻结模型与设备配置,向量空间标识来自同一快照;普通 API 成功及失败回退策略保持不变。
仅本地转写新生成的笔记增加 `embedding_local_only: true` frontmatter,索引与后续重建跳过远程 Embedding。它只约束索引,不是通用的笔记联网权限;旧导出笔记需人工补标记。SQLite v6 将策略保存到 Block,重建按普通/仅本地策略分别校验空间和覆盖,统一事务提交。查询在各空间内排序后用 RRF 合并排名,缺少分区时保留全文检索降级。升级已有库后重建一次以同步策略。
搜索记录保存在应用 SQLite,使用 `/api/search/history` GET/DELETE 读取和清空。聊天已接入真实知识库上下文与 Citation。详细原因和验证见[阶段 F 问题与解决方案](../retrospectives/阶段F-Embedding与知识库问题与解决方案.md)。
2026-09-04Windows / Python 3.12 / torch 2.9.1+cpu:后端 472 项、前端 93 项测试通过,类型检查和生产构建通过,仍有既有大 bundle 警告。Edge 真实 API 页面、播放器时长/定位、模型与用量卡片无页面异常。
真实模型完成音频 → 转写 → 片段声纹 → 笔记 → 语义检索闭环。示例来自固定 ModelScope revision;权重和音频不提交仓库。
| 实测 | 结果 |
| --- | --- |
| Bekko 中文小样本 | 384 维;相关相似度 0.495、无关 0.083 |
| Qwen3-ASR 短中文音频 | 加载约 11.1 秒、推理约 6.3 秒、峰值约 5.4 GiB |
| ERes2NetV2 | 同音频 1.000,不同示例说话人 0.090,片段聚类完成 |
| 笔记闭环 | 重复导出同 note_id,语义检索找回同笔记 |
这是功能冒烟,不是代表性课程语料完整质量评估。CUDA 实机、Granite 对照、逐字强制对齐及重叠语音质量未验证。每任务释放模型有加载成本;长音频准确率、阈值与吞吐需要目标机器专项验收。
```powershell
cd backend
.venv/Scripts/python scripts/local-model-smoke.py bekko --download
.venv/Scripts/python scripts/local-model-smoke.py qwen3-asr --download --audio C:/path/to/speech.wav
.venv/Scripts/python scripts/local-model-smoke.py eres2netv2 --download --audio C:/path/to/speech.wav --reference C:/path/to/reference.wav
```
## 2026-09-05 长录音与 Provider 状态补充
- 附件上传上限为 128 MiB。超过 25 MiB 的音频必须明确选择 `local_only=true`;可联网任务仍限制为 25 MiB,前后端及路由均检查。解码时长仍限制为一小时。
- 本地解码允许跳过少量损坏音频包,按包中可取得的时长补静音,并返回 `MEDIA_CORRUPT_PACKETS_SKIPPED:<数量>`。超过 100 个损坏包则失败;缺少有效包时长时无法承诺时间对齐,应结合原音频复核。此处理不能恢复丢失语音。
- Provider 卡片显示“已启用/已停用”,支持直接启停。保存成功后更新状态;失败保留原状态。停用或保存期间禁用测试与刷新模型操作。
- 37 分 16 秒的用户录音已完成 CUDA 转写、片段级说话人聚类、笔记生成、本地向量索引及检索命中。无参考标注,不报告准确率。详见《阶段F收尾验收记录》的长录音补充;此前短样本记录保留为历史证据。