☰
本地管线挖掘剧情:从语音转写到实体关联的文本分析
2026/9/26 8:13:52 网站建设 项目流程

“在最终章,海兰德铁道学院在干什么?”——如果只看原文,这个问题往往要翻几十章内容,把对话、行动、组织决策一点点拼起来。但换个角度,这个问题本质上是一个非结构化文本分析任务:把“学院最终都在做什么”变成可查询、可验证、可批量处理的数据问题。这篇文章不猜剧情,也不下剧情结论,而是用一整套本地可跑的工程管线,把最终章里与“海兰德铁道学院”相关的行为线索抽出来:视频转写、OCR 截图、全文检索、实体关联、本地 LLM 问答。跑通之后,你不仅能回答这个标题,还能把它复用到任何长篇作品、项目文档、会议记录的分析场景。

这套管线完全离线可用,核心组件包括 faster-whisper、PaddleOCR、SQLite、jieba 和 Ollama。它支持 CPU 推理,也支持 GPU 加速;支持单文件测试,也支持批量任务;支持本地接口 API,方便接到自己的工具链里。整条链路不依赖云端服务,不限制输入格式,不绑定具体显卡品牌。对只是想梳理剧情线的内容读者来说,它是把“人肉翻书”变成“代码检索”的辅助工具;对做知识库、文档分析、视频内容归档的开发者来说,它就是一个可以直接改造成业务代码的通用模板。

本文会带读者完成以下内容:先搭好 Python 环境和本地模型服务,再把一本小说、一份字幕或一批视频截图转成纯文本,接着把文本导入 SQLite 做检索,用分词和实体共现判断“学院”与哪些角色、地点、事件高度关联,最后用 Ollama 本地大模型做行为抽取与摘要问答。整个流程会给出可复制的代码示例、接口调用示例和批量任务脚本。

1. 剧情分析的本地管线核心能力速览

在动手之前,先把这套“剧情考据管线”的能力边界说清楚。它不是一个已经训练好的剧情问答模型,而是一组开源工具的组合。下面的表格列出了每个环节使用的工具、输入和输出。

能力项说明
项目类型本地剧情/长文本文本挖掘管线,由多个开源组件组合
文本来源视频/音频字幕、图片截图、TXT/EPUB/PDF 文本、SRT 字幕文件
主要功能语音转写、OCR 识别、全文检索、实体共现、本地 LLM 问答、批量任务
默认组件faster-whisper、PaddleOCR、SQLite、jieba、Ollama
推理设备支持 CPU,也支持 NVIDIA GPU;具体显存占用按模型版本实测
启动方式命令行 + Python 脚本 + Ollama 本地服务
是否支持 API支持,Ollama 提供 HTTP 接口,可被 Python/curl 调用
是否支持批量任务支持,可遍历输入目录逐条处理,建议加日志和失败重试
是否需要联网首次下载模型需要联网,后续推理可完全离线
适合场景长篇小说剧情梳理、游戏最终章分析、网文角色行为追踪、视频字幕归档
不适合场景对官方剧情结论做自动判定、大规模爬取未授权文本

这套方案的重点不是“智能”,而是“可查”。把最终章里所有和“海兰德铁道学院”有关的内容抽出来之后,你会得到一本带时间戳、带来源路径、带上下文引用的小型数据库。后面的分析是基于数据,而不是凭印象。

2. 适用场景与使用边界

先说适合谁。

适合内容研究者、剧情考据爱好者和知识库开发者。假设你要分析一部几十万字的网文或一部几十集的动画,想搞清楚某个学院在最后阶段调动了哪些人物、提出了什么方案、最终结果如何。手动搜索关键词可以得到零散片段,但很难形成时间线。更麻烦的是,同一个学院在不同章节可能有不同称呼,比如“海兰德铁道学院”“海兰德学院”“铁道学院”同时在文本中出现。只用 Ctrl+F 会漏掉很多上下文。

适合需要把视频/音频内容转成文字的人。很多剧情关键信息藏在配音、字幕和闪回画面里。faster-whisper 可以把视频轨里的语音转成带时间戳的文本,PaddleOCR 可以把截图里的台词、公告、信件识别出来。两者合在一起,基本能把最终章的场景文案全部落盘。

适合需要批量处理多个章节或多个作品的人。这个管线支持遍历 input 目录下的所有文件,自动输出到 output 目录。第一次跑通后,后续喂入新文件就行。

再说不适合什么。

不适合用来直接生成“官方剧情答案”。本地模型会根据上下文生成摘要,但摘要带有模型自身理解,和官方设定不是一回事。它只是辅助人判断,不能替代最终的人工复核。

不适合处理未授权的版权内容。如果文本、视频、截图来自平台独占的付费内容,请只用于个人学习笔记,不要公开传播。涉及人脸、声音、实名信息的素材,在使用前必须确认授权范围。这篇文章只讨论技术方法,不鼓励任何绕过版权边界的操作。

3. 本地部署环境准备与前置条件

这套方案的核心运行环境是 Python 3.10+,外加一个 Ollama 本地模型服务。操作系统方面,Windows、Linux、macOS 都可以跑,但语音转写和 OCR 在 Linux 上的 CUDA 环境通常更好配置。

3.1 硬件要求

  • CPU:可以跑,建议至少 8 核;在 CPU 上做语音转写会比较慢,但能出结果。
  • GPU:有 NVIDIA 显卡且显存足够时,推荐用 GPU 推理;显存占用由模型版本、视频长度、并发数决定,需以本机实测为准。
  • 内存:至少 16GB 更稳妥,转写长视频时内存占用会显著上升。
  • 磁盘:预留 10GB 以上空间。模型文件、虚拟环境、输出文本都会占空间。

3.2 软件依赖

需要安装:

  • Python 3.10 或更高版本
  • ffmpeg,用于处理视频/音频流
  • SQLite,一般 Python 自带
  • Ollama,用于本地 LLM 服务
  • Tesseract 或 PaddleOCR,二选一,中文场景建议 PaddleOCR

3.3 创建项目目录和解压环境

建议把输入素材、输出结果、数据库、脚本分成独立目录。

mkdir -p analysis/input analysis/output analysis/db analysis/scripts cd analysis

这样做的目的是让每个环节互不干扰。视频文件放在 input,脚本输出统一写在 output,数据库单独放 db,后续做批量任务时不会把中间文件混在一起。

3.4 安装 Python 依赖

python -m venv .venv # Windows 激活方式 .venv\Scripts\activate # Linux/macOS 激活方式 source .venv/bin/activate pip install --upgrade pip pip install faster-whisper paddleocr paddlepaddle jieba requests

如果是在 macOS 上使用 MPS 或纯 CPU,PaddlePaddle 的安装命令可能需要根据官方文档调整。这里给出的是通用安装方式,具体版本以本机环境为准。

3.5 安装并启动 Ollama

Ollama 是一个本地模型运行工具,安装完成后先启动服务,再拉取一个适合中文摘要的模型。

# 拉取一个 7B 级别的中文指令模型,网络时间取决于带宽 ollama pull qwen2.5:7b-instruct # 启动服务,默认监听 127.0.0.1:11434 ollama serve

如果ollama serve已经作为后台服务运行,终端会出现端口占用的提示,这也是正常的。可以通过下面命令验证服务是否可用:

curl http://127.0.0.1:11434/api/tags

能返回 JSON 列表,说明 Ollama 服务已经就绪。

4. 安装部署与启动验证

依赖装完,不要急着直接跑完整分析。应该先做三个门槛检查:语音转写能否跑通、OCR 能否识别一张截图、Ollama 能否返回一句话。这三个小实验通过,说明环境本身没问题。

4.1 验证 faster-whisper 语音转写

新建一个scripts/test_whisper.py脚本,读取 input 目录下的一段短视频或音频文件,输出带时间戳的文本。

from faster_whisper import WhisperModel # 模型大小可选 tiny/base/small/medium/large-v3 # 第一次运行会自动下载模型 model = WhisperModel("base", device="auto", compute_type="int8") segments, info = model.transcribe( "input/final_chapter_demo.mp4", language="zh", word_timestamps=True, vad_filter=True, ) print(f"检测语言: {info.language}, 概率: {info.language_probability:.2f}") for segment in segments: print(f"[{segment.start:.2f} -> {segment.end:.2f}] {segment.text.strip()}")

运行方式:

python scripts/test_whisper.py

如果看到带时间戳的句子输出,说明转写链路可用。没有输出时,先检查文件路径、ffmpeg 是否安装、模型是否下载完整。

4.2 验证 PaddleOCR 文字识别

再建一个scripts/test_ocr.py脚本,读取 input 目录下的一张截图,输出图上文字。

from paddleocr import PaddleOCR ocr = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False) result = ocr.ocr("input/screenshot_01.png", cls=True) if result: for page in result: if page: for line in page: print(line[1][0]) else: print("未识别到文字")

运行方式:

python scripts/test_ocr.py

识别结果包含中文、数字、标点时,说明 OCR 环境正常。识别乱码时,可以先检查图片分辨率,再检查 PaddleOCR 模型文件是否完整。

4.3 验证 Ollama 文本生成

新建一个scripts/test_ollama.py脚本,直接调用 Ollama 的本地接口,让模型返回一句简单回答。

import requests url = "http://127.0.0.1:11434/api/generate" payload = { "model": "qwen2.5:7b-instruct", "prompt": "用一句话解释:什么是剧情时间线梳理?", "stream": False, "options": { "temperature": 0.7, "num_predict": 200 } } resp = requests.post(url, json=payload, timeout=300) resp.raise_for_status() print(resp.json()["response"])

运行方式:

python scripts/test_ollama.py

能返回一句中文内容,说明本地 LLM 服务可用。如果请求超时,先确认 Ollama 服务进程是否还在,再看端口是否被防火墙拦截。

5. 功能测试与效果验证:把最终章变成可查询文本

三个小实验通过后,进入完整流程。这里按“文本采集 -> 入库检索 -> 实体关联 -> LLM 行为抽取”的顺序展开。

5.1 语音转写:处理最终章的视频与音频

假设最终章素材是一段约 20 分钟的视频,包含了片头解说、角色对话和旁白。把这个视频放进input/final_chapter.mp4,然后运行批量转写脚本。

import os import glob from faster_whisper import WhisperModel model = WhisperModel("base", device="auto", compute_type="int8") files = sorted(glob.glob("input/*.mp4")) print(f"共发现 {len(files)} 个视频文件") for idx, path in enumerate(files, 1): base = os.path.splitext(os.path.basename(path))[0] out_txt = os.path.join("output", base + ".txt") out_srt = os.path.join("output", base + ".srt") try: segments, _ = model.transcribe( path, language="zh", vad_filter=True, ) with open(out_txt, "w", encoding="utf-8") as f: for seg in segments: f.write(seg.text.strip() + "\n") print(f"[{idx}/{len(files)}] 转写完成: {path}") except Exception as exc: print(f"[{idx}/{len(files)}] 转写失败: {path} -> {exc}")

转写成功后,output/final_chapter.txt就是最终章字幕的纯文本版本。判断标准:文件里出现完整句子,且能够搜到“海兰德铁道学院”等实体词。如果出现大量空行,说明vad_filter=True可能过滤掉了部分弱语音,可以改成vad_filter=False再试。

5.2 OCR 截图识别:处理画面中的文字

很多关键信息并不出现在语音里,而是出现在画面中的公告、报纸、指示牌上。把最终章的截图放在input/screenshots/目录下,运行 OCR 脚本。

import os import glob from paddleocr import PaddleOCR ocr = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False) files = sorted(glob.glob("input/screenshots/*.png")) for path in files: base = os.path.splitext(os.path.basename(path))[0] out_path = os.path.join("output", base + "_ocr.txt") result = ocr.ocr(path, cls=True) lines = [] for page in result or []: for line in page or []: lines.append(line[1][0]) with open(out_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) print(f"{base}: 识别到 {len(lines)} 行文字")

判断标准:输出文件中包含与剧情相关的关键词。截图模糊、文字过小、旋转角度过大时,识别效果会下降。可以先对截图做放大和灰度处理,再进入 OCR。

5.3 全文检索:定位“海兰德铁道学院”的每一次出现

文本采集完成后,把所有 txt 文件导入 SQLite。这里使用最简单的方式,以行为单位存储文本,方便后续检索。

import sqlite3 import glob conn = sqlite3.connect("db/plot_analysis.db") conn.execute("CREATE TABLE IF NOT EXISTS lines (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT, line_index INTEGER, content TEXT)") for txt_path in sorted(glob.glob("output/*.txt")): source = txt_path.split("/")[-1] with open(txt_path, "r", encoding="utf-8") as f: lines = f.readlines() for idx, line in enumerate(lines): line = line.strip() if line: conn.execute( "INSERT INTO lines (source, line_index, content) VALUES (?, ?, ?)", (source, idx, line), ) conn.commit() print("入库完成")

随后检索“海兰德铁道学院”出现的上下文:

import sqlite3 conn = sqlite3.connect("db/plot_analysis.db") rows = conn.execute( """ SELECT source, line_index, content FROM lines WHERE content LIKE '%海兰德铁道学院%' ORDER BY source, line_index LIMIT 30 """ ).fetchall() for source, idx, content in rows: print(f"{source} 第{idx}行: {content}")

判断标准:检索结果能覆盖多集、多时间段,而不是只出现在某一两个段落里。如果结果过少,说明需要补充同义词,比如“海兰德学院”“铁道学院”,再查一次。

5.4 实体关联:用 jieba 寻找高频共现实体

要回答“学院在干什么”,不能只看学院自己的句子,还要看它和谁一起出现。用 jieba 分词,把一句话中的实体词抽取出来,统计与“海兰德铁道学院”出现在同一个句子中的其他实体。

import jieba import sqlite3 from collections import Counter # 添加自定义词典 jieba.add_word("海兰德铁道学院") jieba.add_word("海兰德学院") jieba.add_word("铁道学院") conn = sqlite3.connect("db/plot_analysis.db") rows = conn.execute("SELECT source, content FROM lines WHERE content LIKE '%海兰德铁道学院%'").fetchall() co_occur = Counter() for source, content in rows: words = jieba.lcut(content) for w in words: w = w.strip() if len(w) >= 2 and w != "海兰德铁道学院": co_occur[w] += 1 for word, count in co_occur.most_common(20): print(word, count)

判断标准:高频共现词里应该能看到几个角色名、地名或事件名。这些词就是学院行动的“上下文锚点”。如果高频词全是“我们”“他们”“已经”等无意义词,需要加入停用词表,或者把候选词长度提高到 3 个字以上。

5.5 本地 LLM 行为抽取:让模型回答“学院在干什么”

检索完上下文后,把包含“海兰德铁道学院”的句子拼成一段上下文,交给 Ollama 做行为抽取。

import requests import sqlite3 conn = sqlite3.connect("db/plot_analysis.db") rows = conn.execute( """ SELECT content FROM lines WHERE content LIKE '%海兰德铁道学院%' ORDER BY source, line_index """ ).fetchall() context = "\n".join([r[0] for r in rows]) if len(context) > 3000: context = context[:3000] + "\n……" prompt = f""" 下面是从剧情文本中检索到的,与“海兰德铁道学院”相关的上下文: {context} 请基于以上上下文,用 3 个要点概括:在最终章,海兰德铁道学院在干什么? 要求: 1. 只能基于上下文,不要虚构事实。 2. 每个要点不超过 50 字。 3. 如果上下文不足,请直接说“信息不足”。 """ resp = requests.post( "http://127.0.0.1:11434/api/generate", json={ "model": "qwen2.5:7b-instruct", "prompt": prompt, "stream": False, "options": {"temperature": 0.2, "num_predict": 500}, }, timeout=300, ) resp.raise_for_status() print(resp.json()["response"])

判断标准:模型能输出与原始上下文一致的要点,而不是编造出不存在的剧情。此时得到的要点只是“辅助结论”,需要再回到原始文本里核对一遍。

6. 接口 API 与批量任务设计

整套管线可以只用命令行运行,但如果要接到自己的知识库、定时任务或网页服务上,就需要把关键环节接口化。Ollama 本身就提供 HTTP API,faster-whisper 和 PaddleOCR 也可以在 Python 里封装成函数。

6.1 Ollama API 调用示例

Ollama 默认监听127.0.0.1:11434,常用路径是/api/generate。一个完整请求体如下:

{ "model": "qwen2.5:7b-instruct", "prompt": "请回答:海兰德铁道学院在最终章做了哪些事?", "stream": false, "options": { "temperature": 0.2, "num_predict": 300 } }

Python 调用方式:

import requests url = "http://127.0.0.1:11434/api/generate" payload = { "model": "qwen2.5:7b-instruct", "prompt": "请回答:海兰德铁道学院在最终章做了哪些事?", "stream": False, "options": { "temperature": 0.2, "num_predict": 300 } } resp = requests.post(url, json=payload, timeout=300) resp.raise_for_status() print(resp.json()["response"])

6.2 批量任务目录设计

批量任务的关键是输入输出隔离。建议目录结构如下:

analysis/ ├── input/ │ ├── final_chapter.mp4 │ ├── episode_23.mp4 │ └── screenshots/ ├── output/ │ ├── final_chapter.txt │ └── episode_23.txt ├── db/ │ └── plot_analysis.db └── scripts/ ├── transcribe_all.py ├── ocr_all.py ├── build_db.py └── ask_ollama.py

批量转写脚本要加日志和失败重试逻辑。最简单的做法是写一个process_one函数,内部捕获异常;外部循环里记录成功和失败数量。

import os import glob import time from faster_whisper import WhisperModel model = WhisperModel("base", device="auto", compute_type="int8") def transcribe_one(path): segments, _ = model.transcribe(path, language="zh", vad_filter=True) base = os.path.splitext(os.path.basename(path))[0] out_path = os.path.join("output", base + ".txt") with open(out_path, "w", encoding="utf-8") as f: for seg in segments: f.write(seg.text.strip() + "\n") return out_path files = sorted(glob.glob("input/*.mp4")) ok = 0 fail = 0 for p in files: try: out = transcribe_one(p) print(f"成功: {p} -> {out}") ok += 1 except Exception as exc: print(f"失败: {p} -> {exc}") fail += 1 time.sleep(1) print(f"完成,成功 {ok} 个,失败 {fail} 个")

批量任务最容易踩的坑是:模型第一次加载耗时较长,但每个文件都重新加载模型。解决办法是让模型常驻内存,在脚本开头只加载一次。同理,OCR 模型也建议全局只初始化一次。

7. 资源占用与性能观察方法

这套管线不是单一程序,资源占用要分模块看。

7.1 怎么观察资源占用

  • GPU 显存:终端执行nvidia-smi,或者在 Python 里使用torch.cuda.memory_allocated()观察。
  • CPU 和内存:Linux 用top,Windows 用任务管理器。
  • Ollama 服务:Ollama 会在模型加载后常驻内存,具体占用取决于模型大小和量化版本。

需要特别说明的是,显存占用会随着模型版本、输入视频长度、并发任务数变化。一个 7B 模型在量化后的占用和一个 13B 模型完全不同。更稳妥的做法是先跑一个 30 秒的短视频,观察峰值显存和内存,再决定是否用更大的模型。

7.2 不同参数对性能的影响

  • 语音转写模型大小:tiny最快,但准确率较低;large-v3更准,但占用资源和时间明显上升。
  • OCR 图片分辨率:原图过大时,可以先压缩再识别;原图过小时,先放大再识别。
  • LLM 输出长度:num_predict控制生成的 token 数,输出越长,等待时间越久。
  • 并发数:Ollama 默认可以并发处理请求,但并发过高会导致显存溢出。批量任务建议先并发数设为 1,跑通后再逐步调大。

7.3 降低资源占用的方法

  • 语音转写使用compute_type="int8",占用显存显著低于 float16。
  • OCR 和 whisper 不要在同一个进程里同时初始化大模型,分步骤执行。
  • LLM 使用量化版本,比如qwen2.5:7b-instruct-q4_K_M,比原版占用更小。
  • 长视频先切片,再分批转写,避免一次性加载太长的音频。

8. 常见问题与排查方法

问题现象可能原因排查方式解决方案
faster-whisper 转写结果为空ffmpeg 未安装或视频文件损坏检查 ffmpeg 是否可用,用播放器打开视频安装 ffmpeg,重新下载素材文件
OCR 识别乱码图片模糊、旋转角度过大、文字过小查看原图,放大截图对图片做灰度化、放大、矫正后再识别
SQLite 查不到关键词文本库未导入,或关键词用词不一致检查 output 目录文件是否存在,使用同义词再查重新执行入库脚本,补充同义词检索
Ollama 请求超时模型未加载完成或生成的 token 数过长查看 Ollama 日志,缩短 prompt首次请求前先发送一个短测试请求,降低 num_predict
显存不足模型过大,或并发请求过多运行 nvidia-smi 查看显存换成更小的模型,减少并发数
批量任务中途失败某个文件格式不被支持查看脚本日志,定位失败文件对异常文件单独处理,添加失败重试逻辑
端口 11434 被占用Ollama 服务重复启动或端口被第三方程序占用执行lsof -i :11434或netstat -ano停掉冲突进程,或修改 Ollama 监听端口

上面这些问题是本地部署最常见的,大部分都能通过日志定位。建议在所有脚本里统一打印时间、文件名和错误信息,排查效率会提高很多。

9. 最佳实践与使用建议

第一次做剧情分析,不要直接对整个长篇作品跑完整流程。先用最终章的 1 集视频或 10 页文本做小样本验证,确认转写、OCR、检索、问答四个环节都能跑通,再扩展规模。

目录管理要严格。input、output、db、scripts 四个目录从开始就分开,避免模型文件、中间文本、数据库混在一起。后续清理或重新处理时,只需要清空 output 目录。

自定义词典要维护。分析“海兰德铁道学院”这类专有名词时,jieba 默认分词可能把它切成“海兰德 / 铁道 / 学院”。在脚本里加入jieba.add_word("海兰德铁道学院"),检索结果会更完整。

批量任务一定要加日志。一个文件失败不应该中断整个队列。推荐的策略是:先顺序执行,每个文件都 try/except 捕获异常;跑通之后再改成多线程或异步。日志里至少记录文件名、成功/失败、耗时和输出路径。

使用 Ollama 接口时,不要把服务暴露到公网。本地服务默认只绑定127.0.0.1,如果在服务器上使用,建议通过反向代理加上鉴权,避免未授权访问。

合规方面要特别注意:如果文本、图片、视频来自商业作品或他人创作,只能用于个人分析,不要公开传播完整原文;如果素材包含真实人物、声音或私人信息,使用前必须确认授权。不要用公共 API 上传敏感或未授权数据。

10. 总结与下一步

回到最初的问题:“在最终章,海兰德铁道学院在干什么?”

用这套管线跑完后,你会得到一份可追溯的答案:哪个时间点、哪个场景、哪些角色、哪些行为与学院相关。最终判断还是要回到原始文本里核对,但检索和梳理过程已经做完,不需要再翻几十章。

最先要验证的功能是语音转写和 OCR 识别,这两步决定了后续文本维度的完整性;最容易踩的坑是模型首次加载时间过长和 OCR 图片质量不佳,建议先用小文件测试。

后续还可以继续扩展:把输出结果导入 RAG 框架,做带引用的问答系统;用 NetworkX 建立角色共现图,观察学院与各阵营的关系变化;或者对每一章的摘要做时间线排序,生成事件演化报告。工具链已经具备,剩下的就是给素材建好输入目录,然后跑起来试试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询