feat(multimodal): 完成阶段F运行管理与收尾验收
This commit is contained in:
@@ -1573,3 +1573,19 @@ frontend/src/
|
||||
### Benchmark Embedding 运行归属(阶段 E 集成修复)
|
||||
|
||||
`config_snapshot.local_embedding` 仅表示本地基线;`config_snapshot.embedding` 为 `{ "policy": "per_case", "details": "cases[].embedding" }`。报告与 CaseCompleted 事件的逐样本 `embedding` 包含实际 source(api/local/not_used/unavailable)、model_id、dimensions,以及可选 version、fallback_reason、requested_route、route_version、attempted_space。requested_route 仅含提供商引用、模型、相对端点和维度,不包含 API Key 或凭据引用。FTS 不使用 Embedding,标记 not_used;远程失败或索引不完整回退时记录实际本地模型及原因。
|
||||
|
||||
### 阶段 F 收尾接口补充(2026-09-05)
|
||||
|
||||
CUDA 组件:`GET /api/local-models/runtime-components/cuda` 返回 status、stage、supported、custom_interpreter、cuda_available、可选 torch/error。status 为 checking/not_installed/installing/installed/failed/interrupted;读取只检查现有环境,不下载安装。`POST` 同路径明确触发后台安装,返回 202;重复请求复用当前安装任务。正在推理/排队返回 409 MODEL_IN_USE,缺少 uv 返回 422 UV_NOT_INSTALLED,不支持的平台返回 422 PLATFORM_UNSUPPORTED。阶段进度不冒充字节百分比。关闭后端时回收安装进程树,重启后重新验证环境。
|
||||
|
||||
| 接口/字段 | 行为 |
|
||||
| --- | --- |
|
||||
| `POST /api/media/attachments` | 可选 `Idempotency-Key` Header,16–100 位字母、数字、下划线或连字符。后端持久保存键、文件名、attachment_id 和内容摘要;同键同文件同内容返回同 attachment_id,文件名(含扩展名)或内容不一致返回 409 `IDEMPOTENCY_CONFLICT`。对应附件已清理时返回 409 `IDEMPOTENCY_EXPIRED`,客户端需开始新提交。上传仍受 25 MiB 限制。 |
|
||||
| `TranscriptNoteRequest.update_existing` | 默认 false;true 时将新修订安全写入相同导出选项对应的笔记。无基线返回 409 `NOTE_UPDATE_BASELINE_MISSING`;正文改变返回 409 `NOTE_CONTENT_CONFLICT`。同修订重复调用保持幂等。 |
|
||||
| 本地模型 `disk_bytes` | 权重目录实际字节数;无法读取为 null。与下载 bytes/total 分开。 |
|
||||
| `GET /api/local-models/diagnostics` | `scope=application_last_200_attempts`,应用 SQLite 中最近 200 条诊断,包含调用及回退事件。未实际开始推理时不伪造 actual_device。 |
|
||||
| `GET /api/usage` | 增加 `audio_request_count`、可空的 `audio_seconds`、`audio_covered_requests`,适用原有时间/提供商/模型/来源过滤。次数按 transcription/speaker_matching 实际 attempt;未报告时长不估算。 |
|
||||
| `POST /api/providers/request-rules/validate` | 输入/输出 `{version:1, request_overrides:[...]}`;最多 100 条,复用请求扩展校验,不保存提供商。 |
|
||||
| `POST /api/providers/request-probe` | 输入 `{provider:ProviderCreateRequest, stream:boolean}`;固定短消息真实聊天推理,45 秒超时。成功返回 success/stream/model/message;空响应 422、供应商错误 502、超时 504。只使用 credential_id,不接收明文密钥。 |
|
||||
|
||||
请求预览新增 capability 选择(chat/embedding/transcription/speaker_matching),仍只返回隐藏正文的请求体。实际扩展字段是否被供应商接受,以推理响应为准。
|
||||
|
||||
@@ -93,6 +93,30 @@ POST /api/providers/request-preview 不联网,隐藏正文/文件且不包含
|
||||
|
||||
## 验证记录
|
||||
|
||||
### 阶段 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 成功及失败回退策略保持不变。
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# 阶段 F 收尾验收记录
|
||||
|
||||
日期:2026-09-05。对应 `feat/multimodal-finalization`,基于 `6bdba2c`(阶段 F 主分支合并)。
|
||||
|
||||
## 完成范围
|
||||
|
||||
本轮补齐运行诊断持久化、真实磁盘占用、后台索引优先级、CUDA 设备失败时 CPU 单次重试、上传与任务重试幂等、跨修订安全更新笔记、音频用量分项,以及请求 JSON 导入/导出/重置和实际聊天推理验证。原有 API 优先、无配置/无效响应使用本地模型、local_only 禁止远程调用的流程继续保留。
|
||||
|
||||
具体行为见[开发说明](多模态管线与模型运行开发说明.md),接口见[开发版契约](../contracts/第二阶段接口契约-开发版.md),故障与修复见[问题记录 F-13~F-15](../retrospectives/阶段F-Embedding与知识库问题与解决方案.md)。
|
||||
|
||||
## 自动化与页面验证
|
||||
|
||||
| 项目 | 结果 |
|
||||
| --- | --- |
|
||||
| 后端全量 `python -m pytest -q -p no:cacheprovider` | 559 通过;1 条已有 Starlette/httpx 弃用提示 |
|
||||
| 前端全量 `npm test -- --run` | 29 个文件、102 项通过 |
|
||||
| 类型与生产构建 `npm run build` | vue-tsc 与 Vite 构建通过,仍有既有大 bundle 提示 |
|
||||
| `git diff --check` | 通过 |
|
||||
| 真实页面 | 模型卡片读取实际大小;音频统计显示真实缺失;提供商表单展示请求编辑、恢复默认、导入/导出及推理验证入口 |
|
||||
| 请求 Adapter 验证 | 隔离 HTTP Transport 检查最终流式/非流式请求和扩展字段,不访问外部供应商 |
|
||||
| 失败恢复 | 初始化/OOM 故障注入、CPU 再失败、进程回收、队列顺序、重复提交、修订冲突和旧结果失效均覆盖 |
|
||||
|
||||
## CPU / CUDA 真实模型闭环
|
||||
|
||||
Windows、Python 3.12。保留原 `.venv-models` CPU 环境,独立安装 `.venv-models-cuda`;安装后检查 `torch=2.9.1+cu128`、`cuda_available=True`。显卡为 NVIDIA GeForce RTX 4060 Laptop GPU。
|
||||
|
||||
CPU 与 CUDA 分别创建隔离 Vault、附件目录和 SQLite,只读取固定 revision 权重;运行短中文音频 → Qwen3-ASR → ERes2NetV2 片段聚类 → Markdown 笔记 → Bekko 语义检索 → 修订更新。两次均返回已完成,检索命中同一笔记,更新保留 note_id 和 `embedding_local_only: true`,结束后本地运行队列无活跃任务。CPU 实际设备为 `cpu`,CUDA 各次实际设备为 `cuda:0`。
|
||||
|
||||
| CUDA 环节 | 权重 revision | 加载 / 推理耗时 |
|
||||
| --- | --- | --- |
|
||||
| Qwen3-ASR-0.6B | `5eb144179a02acc5e5ba31e748d22b0cf3e303b0` | 30.375 / 3.234 秒 |
|
||||
| ERes2NetV2 片段聚类 | `3317286545c587ae682dbc166831d9448780eebb` | 5.735 / 0.578 秒 |
|
||||
| Bekko 首次笔记索引 | `c721113d59a1d91b447450324f51c4b3332c924a` | 19.860 / 0.656 秒 |
|
||||
|
||||
这些是单次功能冒烟观察值;运行期间有其他验证任务,不用于宣称吞吐或 CPU/GPU 性能倍率。短样本只产生 1 个片段和 1 个 speaker,不能验证多人重叠语音质量。CUDA OOM 恢复使用故障注入,并非实机显存耗尽测试。
|
||||
|
||||
## 中文 Embedding 小样本对照
|
||||
|
||||
固定 8 篇人工构造的短文,主题为线性代数、死锁、Python 函数、语义检索、光合作用、备份及两个无关干扰项(晚餐、篮球)。6 条改写查询,各有一个预期相关文档;对全部文档做余弦排序。
|
||||
|
||||
| 模型 | revision | Hit@1 / Recall@5 / MRR |
|
||||
| --- | --- | --- |
|
||||
| Bekko A8M | `c721113d59a1d91b447450324f51c4b3332c924a` | 1.0 / 1.0 / 1.0 |
|
||||
| Granite 97M Multilingual r2 | `835ad14087e140460703cf0fae09f97d469d65c2` | 1.0 / 1.0 / 1.0 |
|
||||
|
||||
两者在这 6 条查询上的目标排名均为 1。该结果仅证明中文检索冒烟可运行,样本量不足以区分模型优劣;继续保留 Bekko 默认、Granite 可选。
|
||||
|
||||
## 未关闭的专项验收
|
||||
|
||||
- 带参考转写和说话人标注的真实课程长录音尚未提供,不能报告 CER/WER、DER、阈值或长音频吞吐达标。
|
||||
- 现阶段时间戳为片段级;逐字强制对齐、同段多人/重叠语音仍未实现,不将片段聚类视为完整说话人分离。
|
||||
- 外部供应商特殊 JSON 的兼容性,需要在目标账号和模型上点击实际推理验证;离线协议通过不替代厂商验收。
|
||||
- Tauri/Rust Host 和生产 MCP 沙箱按后续阶段安排;本轮数据持久化在后端 SQLite/Vault,为桌面集成保留稳定接口。
|
||||
|
||||
结论:阶段 F 本轮工程收尾已实现并完成 CPU/CUDA 功能验收;上述质量及外部服务专项保持待验收状态,不标记为全部通过。分支仍需独立审阅后决定合并。
|
||||
@@ -138,6 +138,46 @@ embedding_local_only: true
|
||||
|
||||
## 9. 工程经验
|
||||
|
||||
### F-16:CUDA 选装只有脚本,前端缺少安装入口
|
||||
|
||||
问题:上一轮完成了独立 CUDA 环境安装和 GPU 实测,但页面只有设备下拉框及脚本说明。用户无法从前端下载组件,工程收尾遗漏了可操作入口。
|
||||
|
||||
实际方案:增加独立组件卡片和 GET/POST 状态、安装接口;展示环境检查、下载 PyTorch、安装依赖、验证等真实阶段,失败允许重试。固定脚本、目录和参数,默认 CPU 环境不变;安装成功后 CUDA 模式自动选择已验证环境,显式 Python 覆盖仍优先。推理期间拒绝安装,重复点击不产生多个任务,后端关闭时回收安装子进程树。
|
||||
|
||||
验证:21 项后端相关测试及新增前端组件测试通过,类型检查和构建通过;真实页面已显示本机 `2.9.1+cu128` 组件就绪,默认 CPU 未改变。本轮复用已安装组件验证识别,未重复下载 3 GB 安装包;下载入口、重复请求和失败重试由隔离测试覆盖。
|
||||
|
||||
### F-17:幂等键跨扩展名与请求规则导入竞态
|
||||
|
||||
问题:附件 ID 原先由幂等键哈希和扩展名共同生成,相同键更换扩展名可以创建第二份附件。请求规则导入等待服务端验证期间,用户的新编辑可能被迟到的导入响应覆盖。
|
||||
|
||||
实际方案:后端持久映射幂等键、文件名、附件 ID 和内容摘要,并在 SQLite 写锁中完成查重;文件名或内容变化均返回冲突,已清理的旧附件要求开始新提交。请求规则编辑器为导入和每次草稿变化递增 generation,只接受仍对应当前草稿的响应。
|
||||
|
||||
验证:增加同键跨扩展名冲突,以及导入后继续编辑、迟到响应不覆盖的测试。相关后端 17 项、前端 15 项通过;最终全量后端 559 项、前端 102 项及生产构建通过。
|
||||
|
||||
### F-13:CUDA 失败重试与诊断无法追溯
|
||||
|
||||
问题:设备不可用时能够使用 CPU,但 CUDA 初始化失败、显存不足会直接使任务失败;诊断只留在进程内存中,重启后无法解释当时的失败和回退。
|
||||
|
||||
实际方案:在同一队列占位内完成 CUDA → CPU 单次重试,先回收失败子进程再启动 CPU。只接受初始化失败、CUDA OOM 两类重试原因,普通模型错误不扩大重试范围。转写部分结果随尝试重置,取消仍终止流程。诊断按白名单写入 SQLite,保留最近 200 条,记录请求设备、实际设备、尝试设备、状态和耗时;未知设备不冒充实际使用设备。
|
||||
|
||||
验证:故障注入覆盖初始化失败、OOM、普通错误、CPU 再失败、资源释放顺序、队列顺序和请求用量归属。Windows RTX 4060 Laptop 实机安装 `torch 2.9.1+cu128`,ASR、片段声纹和 Embedding 的实际设备均为 `cuda:0`,完成笔记生成、检索与修订更新。实机正常 CUDA 路径通过;OOM 回退是确定性注入验证,未人为耗尽用户显存。
|
||||
|
||||
### F-14:响应丢失重复上传与转写笔记无法安全更新
|
||||
|
||||
问题:前端每次点击都生成新幂等键,上传或创建任务已成功但响应丢失时,重试可能制造重复附件/任务。已有导出幂等只能返回相同修订,缺少新修订更新原笔记的保护机制。
|
||||
|
||||
实际方案:同一次页面提交冻结文件与选项,复用上传/任务键,已获得的附件 ID 继续使用;主动重新处理才重置标识。后端重复上传校验内容摘要。导出基线保存正文摘要,跨修订更新在 Vault 写锁内核对基线,用户编辑冲突返回 409,允许改为创建新笔记;旧无基线记录不强行覆盖。索引重建不丢失基线,本地限制继续随笔记持久化。
|
||||
|
||||
验证:覆盖上传响应丢失、任务响应丢失、主动重跑、重复键内容冲突、修订更新保持 note_id、重复导出、重建恢复和用户正文冲突。CPU/CUDA 两次真实本地管线均在隔离 Vault/SQLite 中通过检索与修订闭环,不写入用户笔记库。
|
||||
|
||||
### F-15:运行管理与请求配置验收缺项
|
||||
|
||||
问题:下载计数不能反映实际占用,后台索引与交互查询同优先级;音频调用没有独立时长统计;请求规则缺少导入/导出/恢复默认和真实推理验证,草稿改变后旧验证结果可能误导用户。
|
||||
|
||||
实际方案:磁盘大小读取目录文件,查询/媒体/后台索引分别排队;音频次数、已报告时长与 Token 分开聚合,保留覆盖数。规则文件由服务端验证后替换草稿,保存后生效;验证按钮使用固定短消息走实际 Adapter。草稿变化使预览与验证失效,包括无效 JSON 和迟到响应。
|
||||
|
||||
验证:增加实际目录统计、音频缺失值与去重、规则拒绝受保护字段、隔离 HTTP 协议测试及前端迟到响应测试。最终后端 555 项、前端 100 项通过。真实供应商兼容性仍须使用目标账号验证;本轮不将 MockTransport 协议测试称为厂商实测。
|
||||
|
||||
### F-12:普通分割线与元数据头部消歧
|
||||
|
||||
F-11 修复后,`---` 和 `---\n\n# Title\n\n正文` 等合法 Markdown 被误判为未闭合 frontmatter,原先能够保存的笔记被拒绝;库中已有此类文件时全量重建也会失败。
|
||||
|
||||
Reference in New Issue
Block a user