基于大模型的视频智能问答桌面应用(Agent)开发(含视频分段+ASR识别+代理配置)

一个“可本地运行、可工程讲解、可稳定演示”的视频问答桌面应用:导入本地视频后自动完成 ASR 转写、文本分段、向量化入库;提问时通过轻量 Agent 编排输出“答案 + 时间戳”,并支持运行时代理配置(无需重启)。

源码地址

1. 前言与背景

视频正在成为知识载体的主流形态:课程录播、会议纪要、产品宣讲、技术分享……但“检索体验”仍停留在拖进度条、猜时间点。这个项目的目标,是把“视频可检索”做成一个工程上能落地的最小闭环:

  • 导入一个本地视频后,自动完成:语音识别(ASR)→ 文本分段(Chunk)→ 向量化(Embedding)→ 向量库入库(Chroma)
  • 用户提问时,系统像一个轻量 Agent:先判断意图(找时间点 / 摘要 / 综合)→ 检索相关片段 →(可选)调用大模型生成摘要 → 返回答案 + 时间戳
  • 面向真实网络环境:首次运行往往需要下载 Whisper/Embedding 模型,因此提供“设置页可视化代理配置”,并做到保存后立刻生效(无需重启)

这不是一个“只讲概念”的 Demo,而是一个尽量贴近真实工程约束的桌面应用原型。


2. 整体架构与技术栈

2.1 数据流

find_timestamp

summarize / both

本地视频文件

ASR:faster-whisper 转写

Chunk:文本分段 + 时间戳

Embedding:sentence-transformers

Vector DB:Chroma Persistent

用户问题

IntentClassifier:意图分类

VectorStore.query:语义检索 TopK

候选片段 + 时间段

返回时间段与片段

OSCI(OpenAI 兼容)LLM 摘要

摘要回答 + 时间段

2.2 技术栈

  • 桌面壳:pywebview(轻量、无 Node/Electron 依赖,工程演示成本低)
  • 后端:FastAPI(接口清晰、可单独调试,便于未来拆分为纯 Web 服务)
  • ASR:faster-whisper(本地推理,更适合隐私与离线场景)
  • Embedding:sentence-transformers(成熟可靠,召回效果稳定)
  • 向量库:ChromaDB Persistent(轻量持久化,适合桌面应用/原型)
  • LLM:OSCI(OpenAI 兼容标准接口,便于适配国内合规模型服务)
  • 配置:YAML(默认配置)+ JSON(本地运行时设置,包含代理、LLM 模型实例等)

默认配置位于 config/config.yaml,示例(节选):

asr:
  provider: whisper
  model: base
  language: zh

embedding:
  provider: sentence-transformers
  model: all-MiniLM-L6-v2

chunk:
  max_length: 500
  overlap: 50

2.3 为什么选用 OSCI(OpenAI 兼容)协议接入大模型

在很多文章里,“模型选择”会被等同于“选 ChatGPT 还是 Claude”。但对工程落地来说,更关键的是“用什么接口协议把模型能力接进系统里”。本项目选择 OSCI(OpenAI 兼容)有三个核心原因:工程抽象统一、可替换性强、合规与部署选择更灵活

先明确一个事实:ChatGPT 与 Claude 是“具体厂商/产品”,而 OSCI 是“接口协议形态”。协议的价值在于把上层业务(摘要、问答)与下层模型服务解耦。

2.3.1 统一协议带来的工程收益
  • 一套请求/响应结构即可覆盖多家模型服务:只需要配置 base_url / model / timeout / temperature,不需要在业务代码里写多套 SDK 适配逻辑
  • 更易做“运行时切换”:设置页新增/切换模型实例后立即生效,且不影响上层 Agent 编排
  • 更易做“失败降级”:当 LLM 不可用或 Key 未配置时,摘要自动降级为抽取式摘要,保证整体链路可演示、可用
2.3.2 与 ChatGPT、Claude 的对比(从工程接入视角)

下面的对比是“接入方式与工程侧影响”的对比,不是模型能力评测:

维度 OSCI(OpenAI 兼容协议) ChatGPT(OpenAI 生态) Claude(Anthropic 生态)
角色 协议/接口形态 具体服务与模型体系 具体服务与模型体系
接入成本 低:统一协议,配置驱动 中:依赖 OpenAI API 形态与账号/网络 中到高:通常需要单独的协议与 SDK 适配
可替换性 高:切换供应商通常只改 base_url/model 中:在 OpenAI 体系内替换容易,跨体系需适配 中:在 Anthropic 体系内替换容易,跨体系需适配
国内合规落地 友好:可接入国内 OpenAI 兼容端点或自建网关 取决于网络、账号体系与合规策略 取决于网络、账号体系与合规策略
代理/网络策略 更可控:统一走一个 base_url,便于用代理或网关治理 受网络与服务策略影响较大 同左
2.3.3 为什么不直接绑定某一家 SDK

直接绑定某一家厂商 SDK 的典型问题:

  • 一旦切换模型供应商,需要重写调用与错误处理逻辑
  • 不同厂商对参数、消息格式、流式返回的细节差异,会侵入业务层(Agent/摘要模块)
  • 对桌面应用来说,网络环境多变,统一走协议更便于做代理、网关、审计与降级

因此,本项目的策略是:上层只依赖“OpenAI 兼容的 chat completion 调用形态”,并通过配置选择接入哪家模型服务。这使得“是否用 ChatGPT/Claude/国内模型”变成部署与配置问题,而不是代码重构问题。


3. 核心功能模块实现

这一节按“从导入到问答”的主链路拆解,重点讲清楚:每个模块的职责边界、输入输出与工程化取舍。

3.1 桌面端:pywebview + 本地 HTTP 服务一体化

项目采用“本地 FastAPI 服务 + pywebview 加载本地网页”的模式:

  • 后端在后台线程启动(监听 127.0.0.1:8765
  • pywebview 打开桌面窗口,加载 http://127.0.0.1:8765/
  • 前端用原生 HTML/JS(降低依赖与构建成本,利于文章展示)

关键实现位置:

  • desktop_main.py:启动 uvicorn + 创建 pywebview 窗口
  • api/server.py:FastAPI 路由 + 挂载 ui/ 为静态站点

工程价值:

  • UI/后端职责清晰,易于定位问题
  • 未来要升级成纯 Web 或更复杂前端框架,迁移成本低

3.2 视频处理:ASR 转写 + 文本分段(Chunk)

视频处理主入口是后端的 POST /api/videos/process,整体链路:

  1. 校验视频文件存在
  2. 注册视频记录(写入 data/videos.json,状态为 processing)
  3. VideoProcessor.process():转写 → 分段
  4. VectorStore.add_chunks():embedding → 入库 Chroma
  5. 更新视频记录:chunk 数、时长、done/error
3.2.1 ASR:faster-whisper 转写

核心是把 Whisper 的输出统一为工程内部的数据结构 Transcript(文本 + 起止时间):

segments, _info = model.transcribe(video_path, language=self.language)
transcripts = [
  Transcript(text=s.text.strip(), start_time=s.start, end_time=s.end, confidence=s.avg_logprob)
  for s in segments
]

这样的好处是:后续 Chunk、Embedding、检索、Agent 都基于统一抽象,不被底层 ASR SDK 绑定。

3.2.2 分段(Chunk):滑窗 + overlap 的工程化策略

分段目标不是做 NLP 里的“完美切句”,而是最大化两件事:

  • 检索时语义尽量完整(避免句子在 chunk 边界被截断)
  • chunk 规模可控(降低 embedding 计算成本,提高检索质量)

当前实现采用“按字符长度切块 + overlap 重叠”的策略:

  • max_chars:单块最大字符数(默认 500)
  • overlap_chars:块之间重叠字符数(默认 50)

为什么 overlap 重要:

  • 用户问题常命中“跨句子/跨段落”的边界
  • 无 overlap 时,相关信息被拆散,召回质量会明显下降
3.2.3 当前视频转文字不准确的局限与优化思路

当前项目的 ASR 能力已经足以支撑 Demo 和基础检索,但“视频转文字”并不是一个百分之百准确的过程,尤其在真实视频场景里,误差会直接影响后续检索和问答质量。当前主要局限包括:

  • 音频质量不稳定:

    • 如果原视频存在背景音乐、环境噪声、回声、收音距离远等情况,ASR 很容易出现漏词、错词
    • 对朗诵、戏曲、多人对话、口音较重的内容,识别难度也会明显上升
  • 专有名词和领域词识别偏弱:

    • 通用 ASR 模型对人名、地名、专业术语、古诗词、品牌名等内容不一定敏感
    • 一旦关键词识别错误,后续向量检索就可能命不中真正相关的片段
  • 时间戳与语义边界不总是完全对齐:

    • Whisper 输出的是转写片段,天然更偏“语音切分”而不是“语义切分”
    • 这会导致文本虽然大体正确,但某些句子的起止时间不够精细,影响“精确定位到某一句”的体验
  • 错误会沿链路向后传递:

    • 转写错了,Chunk 就会错
    • Chunk 错了,Embedding 和检索结果也会偏
    • 最终用户看到的问答结果,表面像是“大模型回答不准”,实际上根因可能是前面的 ASR 文本就已经有偏差

针对这些问题,后续可以从以下几个方向优化:

  • 提升音频预处理质量:

    • 在转写前增加降噪、音量归一化、静音切分等预处理步骤
    • 对较长视频可先抽离音频并做统一采样率转换,减少模型输入波动
  • 升级或切换更适合场景的 ASR 模型:

    • 当前使用的是通用本地模型,优点是轻量、易部署
    • 如果更关注准确率,可以尝试更大尺寸的 Whisper 模型,或接入阿里云/腾讯云等更适合中文场景的 ASR 服务
  • 引入术语热词或后处理纠错:

    • 对固定场景(课程、企业宣讲、诗词朗读、医学/法律等)可维护术语表
    • 在 ASR 输出后做一轮轻量文本纠错,把高频误识别词映射回正确写法
  • 优化分段与检索策略:

    • 在当前字符滑窗之外,增加基于标点、停顿或句子边界的切分策略
    • 对检索结果做多片段重排,降低单次误识别对最终回答的影响
  • 增加“可校正”的产品能力:

    • 未来可以支持展示转写文本、允许用户手动修正关键片段
    • 一旦关键文本被修正,再重新分段和入库,整体问答质量会明显提升

3.3 向量库:EmbeddingModel + Chroma Persistent

核心思路是“封装与懒加载”:

  • EmbeddingModel:懒加载 SentenceTransformer,只在首次用到时加载模型
  • VectorStore:懒加载 chromadb.PersistentClient 与 collection,并负责 video_id 维度的过滤查询

入库的关键点:

  • ids: video_id_chunkIndex,便于定位与删除
  • metadatas: 存储 video_idstart_timeend_time,为“时间戳问答”提供结构化依据

3.4 问答 Agent:Intent → Retrieval → Answer(含大模型摘要与降级)

问答入口为 POST /api/query,核心流程:

  1. 解析会话(session_id)与当前视频(video_id)
  2. 轻量会话记忆:对“继续/展开/刚才那段”等追问补全上下文
  3. IntentClassifier:规则关键词分类(find_timestamp / summarize / both)
  4. VectorStore.query:检索 top_k 片段(默认 5)
  5. 根据 intent 输出:
    • find_timestamp:直接返回时间段与片段
    • summarize/both:把检索片段组织成上下文,调用 LLM 摘要

大模型调用具备“工程兜底”:

  • 未配置 API Key → 自动降级为抽取式摘要
  • 调用异常 → 捕获并降级,保证接口稳定、演示不中断

3.5 代理配置:真实网络环境下的“必需品”(无需重启)

现实问题:Whisper / sentence-transformers 等模型首次运行常需要访问外网(如 huggingface.co),在许多网络环境会超时。

项目提供设置页的“网络代理”能力:

  • GET /api/settings:读取当前代理与模型状态
  • POST /api/settings:保存代理(host/port),并立刻设置 HTTP_PROXY/HTTPS_PROXY 环境变量
  • 代理持久化在 config/local_settings.jsonnetwork.proxy

保存后无需重启即可对后续下载/请求生效,这对桌面 Demo 的可用性提升非常明显。


4. 核心难点与解决方案

4.1 模型下载/网络不通

解决方案:把代理能力做成“产品级设置项”,而不是让用户手动改环境变量或改代码。保存后立即应用代理,最大化首次运行成功率。

4.2 MacOS 环境依赖与 ABI 兼容性

本地推理链路常见“安装能过、运行崩溃/segfault”的问题,尤其与 NumPy/底层推理库 ABI 相关。

当前工程实践中,把 NumPy 版本固定为稳定锚点,并与 SciPy/sklearn 做匹配锁定是非常重要的(桌面应用追求“可运行可演示”,比“追新版本”更关键)。

4.3 资源占用与线程数

本地推理会引入 OpenMP/OpenBLAS/PyTorch 等多线程计算。线程数不受控容易造成 CPU 占满、界面卡顿,甚至触发更难排查的稳定性问题。

解决方案:在后端启动阶段限制 native threads,并尽量把推理变成可控、可复现的运行方式。

4.4 密钥治理:不落盘明文

LLM 摘要是可选能力,但一旦涉及 API Key,就必须把“密钥不进配置文件”当作硬约束。

解决方案:模型实例信息可落盘(base_url/model/timeout 等),但密钥优先存系统 Keychain/凭据库,不可用时回退本地加密存储;前端只显示掩码与“已配置/未配置”状态。


5. 功能演示与效果展示

5.1 配置模型实例

  • 选择新增模型模版

在这里插入图片描述

  • 配置对应的模型的key
    在这里插入图片描述

  • 保存后效果
    在这里插入图片描述

  • temperature 的作用:

    • 用来控制模型输出的“发散程度”或“随机性”
    • 值越低,回答越稳定、越保守,更适合本项目这种“基于视频片段做问答/摘要”的场景
    • 值越高,回答会更灵活,但也更容易出现措辞漂移、总结过度发挥的问题
    • 对视频问答来说,通常建议从 0.2 ~ 0.7 开始;如果更强调“忠于检索结果”,可以把值调低一些
  • timeout_s 的作用:

    • 表示单次大模型请求的超时时间(单位:秒)
    • 如果模型响应较慢、网络较差,超时时间过短会导致请求提前失败
    • 如果超时时间过长,用户会感觉界面一直在等待,交互反馈变差
    • 本项目里它的本质是“稳定性与等待时长”的平衡参数,通常可以先使用默认值 60s
  • 为什么这一项要做成“模型实例配置”:

    • 不同模型供应商、不同网络环境,响应速度和输出风格差异很大
    • base_urlmodeltemperaturetimeout_s 做成可配置项后,切换模型时不需要改代码,只需要在设置页调整即可

5.2 网络代理配置

在这里插入图片描述

  • 代理的作用:

    • 让应用访问外部模型资源或接口时,先经过本地代理转发
    • 本项目里最典型的场景是首次下载 Whisper / sentence-transformers 相关模型,或访问外部兼容接口时网络不稳定
    • 配置后,后端会把代理同步到 HTTP_PROXY / HTTPS_PROXY 环境变量,对后续请求立即生效,无需重启
  • huggingface.co 等资源到底是什么:

    • huggingface.co 可以理解为开源 AI 模型社区和模型文件分发平台,很多常见的模型权重、配置文件、tokenizer 文件都会从这里下载
    • 对本项目来说,sentence-transformers 在首次加载 embedding 模型时,通常会去 Hugging Face 拉取对应模型文件
    • 某些 Whisper 相关依赖、模型配套文件,或其他第三方 AI 组件,也可能在初始化时访问类似的外部模型仓库
    • 所以这里说的“访问 huggingface.co 等资源”,本质上不是在访问某个普通网页,而是在拉取模型运行所需的文件和依赖元数据
  • 为什么这些资源很关键:

    • 如果本地还没有缓存模型,应用第一次运行时必须先把模型文件下载到本机,后续才能完成 embedding 或语音识别
    • 一旦这些文件下载失败,常见表现就是:视频处理启动了,但在模型初始化阶段超时、报错,或者一直卡在加载中
    • 这也是为什么很多用户会觉得“代码没问题,但项目就是跑不起来”,根因往往不是业务逻辑,而是模型资源没有成功拉取
  • 为什么要配置代理:

    • 有些网络环境下无法稳定访问 huggingface.co 等外部资源,模型下载会超时
    • 即使接口本身可访问,模型首次拉取、依赖初始化时也常常因为网络抖动而失败
    • 对桌面应用来说,用户更希望“在设置页填一个 IP 和端口就能解决问题”,而不是手动修改终端环境变量
  • 什么情况下建议优先配置:

    • 第一次启动后模型一直下载失败
    • 日志里出现连接超时、读取超时、无法连接外部模型仓库等错误
    • 需要通过代理访问的外部兼容模型服务
  • 这一能力的工程意义:

    • 把“网络问题”从代码层转成配置层处理
    • 降低首次运行失败率
    • 让模型下载、外部推理接口访问和后续调试都更可控

5.3 导入视频,选择文件 → 开始处理 → 处理成功(片段数、状态)

​ 视频素材为一段将进酒的的京剧朗读视频,时常为1分46秒,处理后得到分片数,该页面支持反复上传解析。

在这里插入图片描述

5.4 视频库:列表展示 → 选为当前视频 → 删除视频记录

解析完成之后的视频库列表记录中可以直接选定某个视频开始对话。选择操作栏中的“选为当前视频”。

在这里插入图片描述

5.5 问答:

  • 时间定位类:“视频里什么时候讲到 XXX?”

在这里插入图片描述

  • 摘要类:“总结一下视频讲了什么?”
    在这里插入图片描述

  • 多轮追问:“展开说说刚才那段”

在这里插入图片描述

  • 为什么要预设问答选项:
    • 因为这个项目并不是一个完全开放式的大模型聊天产品,而是一个“围绕视频内容检索与摘要”的垂直问答系统
    • 预设几类典型问题,可以更稳定地覆盖项目的核心能力:时间定位、内容总结、多轮追问
    • 对演示和博客读者来说,预设问题能更直观地看到系统不同分支的行为差异,而不是把结果混在一起
    • 从产品角度看,预设问法也是一种“引导用户提问”的方式,能降低首次使用门槛,提高命中有效检索的概率
  • 当前项目的决策循环依据是什么:
    • 第一步是看“用户问题属于哪一类”,这里不是让大模型自由发挥判断,而是先经过 IntentClassifier 做规则分类
    • 当前分类依据主要来自关键词规则,例如“什么时候、时间点、定位”更偏向时间定位;“总结、概括、讲了什么”更偏向摘要
    • 第二步是根据分类结果去执行不同路径:
      • 如果是 find_timestamp,优先返回相关片段及其时间段
      • 如果是 summarizeboth,先检索相关片段,再调用 LLM 生成摘要
    • 第三步会结合当前视频上下文和最近一轮会话记忆,决定是否补全“追问语义”,比如“继续说”“刚才那段”
    • 所以这个项目的“决策循环”本质上不是复杂的自主规划,而是一个可解释的确定性流程:问题分类 → 向量检索 → 按意图分支输出 → 会话记忆补全
  • 为什么这里采用“规则 + 检索 + 摘要”的决策方式:
    • 对视频问答场景来说,用户真正关心的大多是“在哪一段讲过”和“这段内容在讲什么”
    • 先做规则分类,再做检索和摘要,能够保证流程稳定、输出可解释,也更适合工程化落地
    • 相比完全交给大模型自由决策,这种方式更容易控制成本、排查问题,并减少无关幻觉输出

6. 源码说明

6.1 关键目录与文件

video-agent/
├── desktop_main.py          # 桌面应用入口:启动服务 + pywebview 窗口
├── api/server.py            # FastAPI 路由、设置、静态 UI 挂载
├── src/
│   ├── video_processor.py   # ASR 转写 + 分段
│   ├── embeddings.py        # sentence-transformers 懒加载封装
│   ├── vector_store.py      # Chroma 向量库封装 + videos.json 记录表
│   ├── agent.py             # Agent 编排(intent → 检索 → 摘要/时间戳)
│   ├── summarizer.py        # LLM 摘要(失败自动降级抽取式)
│   ├── session_memory.py    # 轻量会话记忆(多轮追问补全)
│   └── llm_registry.py      # 模型实例与密钥治理(Keychain/加密回退)
├── ui/                      # 原生前端:index.html / app.js / style.css
├── config/config.yaml       # 默认配置(ASR/Embedding/Chunk/检索等)
└── config/local_settings.json # 运行时生成:代理、模型实例等(不建议提交)

6.2 推荐安装环境版本

为了保证在桌面端稳定运行(尤其是 macOS 本地推理链路),建议优先使用以下环境组合:

  • Python:3.12(推荐),可选 3.9–3.12;不建议使用 3.13(部分依赖在某些平台缺少可用 wheel,容易触发安装/解析失败)
  • NumPy:1.26.4(稳定锚点,避免与部分本地推理依赖的 ABI 兼容问题)
  • SciPy:1.13.1(与 NumPy 1.26.x 组合更稳)
  • scikit-learn:1.5.2(与 NumPy 1.26.x 组合更稳)
  • typing-extensions:>= 4.16.0(保证 FastAPI/Pydantic 相关依赖兼容)

建议使用 venv 或 conda 创建隔离环境后再安装 requirements.txt,避免污染系统 Python。

6.3 安装与运行命令(conda,macOS / Linux)

conda create -n video-agent python=3.12 -y
conda activate video-agent

python -m pip install --upgrade pip
pip install -r requirements.txt

python desktop_main.py

6.4 安装与运行命令(conda,Windows)

在 Windows 上建议使用 Anaconda Prompt / PowerShell 执行:

conda create -n video-agent python=3.12 -y
conda activate video-agent

python -m pip install --upgrade pip
pip install -r requirements.txt

python desktop_main.py

6.5 安装与运行命令(venv / python,macOS / Linux)

macOS 如果系统里没有合适版本的 Python,可以先安装 Python 3.12:

brew install python@3.12

然后在项目目录创建 venv 并安装依赖:

python3.12 -m venv .venv
source .venv/bin/activate

python -m pip install --upgrade pip
pip install -r requirements.txt

python desktop_main.py

6.6 安装与运行命令(venv / python,Windows)

在 Windows 上建议优先用 conda;若使用 venv,可在 PowerShell 执行:

py -3.12 -m venv .venv
.venv\Scripts\activate

python -m pip install --upgrade pip
pip install -r requirements.txt

python desktop_main.py

6.7 运行方式

python desktop_main.py

后台服务监听 127.0.0.1:8765,桌面窗口加载同地址。首次运行可能需要下载 Whisper/Embedding 模型;若网络受限,建议先在“设置 → 网络代理”中配置代理后再进行视频处理。


结语

这套实现的核心价值不在“堆叠更多大模型能力”,而在于把视频问答拆成可控、可解释、可演示的工程闭环:ASR 给出结构化时间戳,Chunk/Embedding/向量库保证检索确定性,Agent 编排把“找时间点”和“生成摘要”做成稳定流程,代理与密钥治理则确保能在真实环境中跑起来。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐