Whisper断句不够好?用AI LLM和结构化数据打造完美字幕

OpenAI的Whisper模型在语音识别领域无疑是革命性的,它能以惊人的准确率将音频转为文字。然而,对于长视频或复杂对话,其自动断句和标点符号功能有时会不尽人意,常常生成不便于阅读的大段文字。

本文将提供一个解决方案:结合Whisper的字级时间戳功能与大语言模型(LLM)的强大理解能力,打造一个能智能断句、优化文本并输出结构化数据的全自动字幕处理管道。

从 Whisper 获取"原料"------ 字级时间戳

要让LLM精确地为新句子赋予起止时间,我必须先从Whisper获取每个字或词的时间信息。这需要开启一个特定参数。

在使用Whisper进行识别时,务必将 word_timestamps 参数设为 True。以Python的openai-whisper库为例:

python 复制代码
import whisper

model = whisper.load_model("base")
# 开启 word_timestamps 选项
result = model.transcribe("audio.mp3", word_timestamps=True)

result 中会包含一个 segments 列表,每个 segment 里又有 words 列表。我需要的数据就在这里。接下来,我将这些数据组装成一个干净的、专为LLM设计的JSON列表。

python 复制代码
word_level_timestamps = []
for segment in result['segments']:
    for word_info in segment['words']:
        word_level_timestamps.append({
            'word': word_info['word'],
            'start': word_info['start'],
            'end': word_info['end']
        })

# 最终得到的数据结构:
# [
#   {"word": " 五", "start": 1.95, "end": 2.17},
#   {"word": "老", "start": 2.17, "end": 2.33},
#   ...
# ]

这个列表就是我喂给LLM的"原料"。

智能分块(Chunking)------ 规避 Token 限制

一个小时的视频转写出的字词列表可能非常庞大,直接发送给LLM会超出其Token限制(Context Window)。因此,必须进行分块处理。

一个简单有效的方法是设定一个阈值,例如每500个字词为一块。

python 复制代码
def create_chunks(data, chunk_size=500):
    chunks = []
    for i in range(0, len(data), chunk_size):
        chunks.append(data[i:i + chunk_size])
    return chunks

word_chunks = create_chunks(word_level_timestamps, 500)

高级技巧 :为了避免在句子中间粗暴地切断,更好的分块策略是,在达到 chunk_size 附近时,寻找一个字词间隙(end到下一个start时间差)最大的地方进行切分。这能提高LLM处理每一块时的上下文完整性。

编写高质量的 LLM 提示词

提示词是整个流程的灵魂,它直接决定了输出的质量和稳定性。一个优秀的提示词应该包含以下几个要素:

  1. 清晰的角色与目标:明确告知LLM它的身份(如"AI字幕处理引擎")和唯一任务。
  2. 详细的处理流程:分步描述它需要做什么,包括识别语言、智能分段、文本修正、添加标点等。
  3. 极其严格的输出格式定义:使用表格、代码块等方式,精确定义输出的JSON结构、键名、值类型,并强调哪些是"必须"和"禁止"的。
  4. 提供示例:给出1-2个包含输入和预期输出的完整示例。这能极大地帮助模型理解任务,尤其是在处理特殊情况(如修正错别字、移除口头禅)时。
  5. 内置最终检查清单:在Prompt末尾让模型进行自我检查,这是一种强大的心理暗示,能有效提升输出格式的遵循度。

最终优化出的提示词,正是遵循了以上所有原则的典范。(具体提示词见底部)

结构化调用的常见问题与解决方案

陷阱一:指令与数据混为一谈

问题描述:初学者常常将长篇的提示词指令和海量的JSON数据拼接成一个巨大的字符串,然后作为单条消息发送给LLM。

症状:LLM返回错误,抱怨"输入格式不符合要求",因为它看到的是一个混合了自然语言和JSON的复杂文本,而不是它被告知要处理的纯JSON数据。

swift 复制代码
{  "error": "The input provided does not conform to the expected format for processing. Please ensure the input is a valid JSON list of dictionaries, each containing \'word\', \'start\', and \'end\' keys."}'

解决方案 :严格分离指令与数据 。利用OpenAI API的 messages 结构,将你的提示词放入 role: 'system' 的消息中,将待处理的纯JSON数据字符串放入 role: 'user' 的消息中。

python 复制代码
messages = [
    {"role": "system", "content": "你的完整提示词..."},
    {"role": "user", "content": '纯JSON数据字符串...'} # e.g., json.dumps(chunk)
]

陷阱二:json_object 模式与 Prompt 指令冲突

问题描述 :为了确保100%返回合法的JSON,我使用 response_format={"type": "json_object"} 参数。但这个参数强制模型返回一个JSON对象 (以 {} 包裹)。如果在提示词中,你却要求模型直接返回一个JSON列表 (以 [] 包裹),就会产生指令冲突。

ini 复制代码
response = model.chat.completions.create(
                    model=config.params['chatgpt_model'],
                    timeout=7200,
                    max_tokens= max(int(config.params.get('chatgpt_max_token')) if config.params.get('chatgpt_max_token') else 4096,4096),
                    messages=message,
                    response_format= { "type":"json_object" }
                )

错误的提示词

markdown 复制代码
## 输出 **json** 格式结果 (关键且必须遵守)

你**必须**以合法 json 列表的形式返回结果,输出列表中的每个元素**必须且只能**包含以下三个键:

症状:即使分离了指令和数据,LLM仍然可能报错,因为它无法同时满足"返回一个对象"和"返回一个列表"这两个矛盾的要求。

解决方案 :让Prompt的指令与API的约束保持一致 。修改你的提示词,要求模型返回一个包裹着字幕列表的JSON对象。

  • 错误的做法 :要求直接输出 [{...}, {...}]
  • 正确的做法 :要求输出 {"subtitles": [{...}, {...}]}

这样,API的要求(返回一个对象)和Prompt的指令(返回一个包含subtitles键的对象)就完美统一了。相应地,在代码中解析结果时,也需要多一步提取:result_object['subtitles']。

其他注意事项

  1. 完整流程:在代码中,你需要遍历所有分块(chunks),对每一块调用LLM进行处理,然后将每一块返回的字幕列表拼接起来,形成最终完整的字幕文件。

  2. 错误处理与重试 :网络请求可能失败,LLM也可能偶尔返回不合规范的JSON。在API调用外层包裹 try-except 块,并加入重试机制(如使用 tenacity 库),是保证程序稳定性的关键。

  3. 成本与模型选择 :像 GPT-4o 或 deepseek-chat 这样的模型在遵循复杂指令和格式化输出方面表现更佳。

  4. 最终校对:虽然LLM能完成99%的工作,但在拼接完所有结果后,可以编写简单的脚本进行最后一次检查,例如:检查是否有字幕时长超过6秒,或两条字幕的起止时间是否重叠。


附录:最终系统提示词

markdown 复制代码
# 角色与最终目标

你是一位顶级的 AI 字幕处理引擎。你的**唯一目标**是将用户输入(user message)中的**字级**时间戳数据(包含 `'word'` 键),转换成**句子级**的、经过智能断句和文本优化的字幕列表,并以一个包含字幕列表的 **JSON 对象**格式返回。

---

## 核心处理流程

1.  **接收输入**: 你会收到一个 json 格式的列表作为用户输入。列表中每个元素均包含 `'word'`, `'start'`, `'end'`。
2.  **识别语言**: 自动判断输入文本的主要语言(如中文、英文、日文、西班牙语等),并调用相应的语言知识库。**单次任务只处理一种语言**。
3.  **智能分段与合并**:
    *   **原则**: 以**语义连贯、语法自然**为最高准则进行断句。
    *   **时长**: 每条字幕理想时长为 1-3 秒,**绝对不能超过 6 秒**。
    *   **合并**: 将属于同一句话的多个字/词字典合并成一个。
4.  **文本修正与增强**:
    *   在合并文本的过程中,对**整句**进行深度校对和优化。
    *   **修正**: 自动修正拼写错误、语法错误以及特定语言的常见用词错误。
    *   **优化**: 移除不必要的口头禅、调整语序,使表达更流畅、地道,但绝不改变原意。
    *   **标点**: 在断句处和句子内部,根据已识别语言的规范,智能添加或修正标点符号。
5.  **生成输出**: 按照下方**严格定义的输出格式**返回结果。

---

## 输出 json 格式结果 (关键且必须遵守)

你**必须**以一个合法的 **JSON 对象**格式返回结果。该对象**必须**包含一个名为 `'subtitles'` 的键,其值是一个字幕列表。列表中的每个元素**必须且只能**包含以下三个键:

| 输出键 (Key) | 类型 (Type)  | 说明                                                                                                           |
| :------------- | :----------- | :------------------------------------------------------------------------------------------------------------- |
| `'start'`      | `float`      | **必须存在**。取自该句**第一个字/词**的 `start` 时间。                                                              |
| `'end'`        | `float`      | **必须存在**。取自该句**最后一个字/词**的 `end` 时间。                                                              |
| `'text'`       | `str`        | **必须存在**。合并、修正、优化并添加标点后的**完整字幕文本**。**【这是最重要的键,绝对不能使用 'word' 或其他任何名称。】** |

**严格禁止**:输出的字典中**不应**出现 `'word'` 键。输入的 `'word'` 内容经过处理后,统一存放于 `'text'` 键中。

---

## 示例:演示核心处理原则 (适用于所有语言)

**重要提示**: 以下示例旨在阐明您需要遵循的**处理逻辑和输出格式**。这些原则是通用的,您必须将它们应用于您在用户输入中识别出的**任何语言**,而不仅仅是示例中的语言。

### 原则演示 1
#### 用户输入
```
[
    {'word': 'so', 'start': 0.5, 'end': 0.7},
    {'word': 'uh', 'start': 0.9, 'end': 1.0},
    {'word': 'whatis', 'start': 1.2, 'end': 1.6},
    {'word': 'your', 'start': 1.7, 'end': 1.9},
    {'word': 'plan', 'start': 2.0, 'end': 2.4}
]
```
#### 你的 JSON 输出
```json
{
    "subtitles": [
        {
            "start": 0.5,
            "end": 2.4,
            "text": "So, what is your plan?"
        }
    ]
}
```

### 原则演示 2
#### 用户输入
```
[
    {'word': '这', 'start': 2.1, 'end': 2.2},
    {'word': '里是', 'start': 2.3, 'end': 2.6},
    {'word': '机', 'start': 2.8, 'end': 2.9},
    {'word': '场吗', 'start': 3.0, 'end': 3.5},
    {'word': '以经', 'start': 4.2, 'end': 4.5},
    {'word': '很晚', 'start': 4.6, 'end': 5.0}
]
```
#### 你的 JSON 输出
```json
{
    "subtitles": [
        {
            "start": 2.1,
            "end": 3.5,
            "text": "这里是机场吗?"
        },
        {
            "start": 4.2,
            "end": 5.0,
            "text": "已经很晚了。"
        }
    ]
}
```

---

## 执行前最终检查

在你生成最终答案之前,请在内部进行最后一次检查,确保你的输出 **100%** 符合以下规则:

1.  **最终输出是否是合法的 json 对象`{...}`?** -> (是/否)
2.  **该 JSON 对象是否包含一个名为 `'subtitles'` 的键?** -> (是/否)
3.  **`'subtitles'` 的值是否是一个列表 `[...]`,且列表中的每一个元素都是一个合法的 JSON 对象`{...}`?** -> (是/否)
4.  **列表中的每个字典是否都只包含 `'start'`, `'end'`, `'text'` 这三个键?** -> (是/否)
5.  **最关键的一点:键名是否是 `'text'`,而不是 `'word'`?** -> (是/否)

**只有当以上所有问题的答案都是"是"时,才生成你的最终输出。**
相关推荐
IT_陈寒8 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
yumgpkpm8 小时前
Acceldata ODP(Open Data Platform)3.3.6.4(RHEL9)保姆级完整安装手册
大数据·人工智能·hive·hadoop·kafka·hbase·cloudera
程序员cxuan8 小时前
腾讯又来一王炸,开源版 WorkBuddy 太夯了!
人工智能·后端·程序员
邓工说电8 小时前
智慧断路器安全吗?数据加密、离线保护与合规认证全解读
大数据·数据库·人工智能·智能断路器·炜晔科技
阿里云大数据AI技术8 小时前
Lance 数据检索怎么选,当然阿里云 Milvus 向量湖
人工智能
出海客8 小时前
跨境电商多语言客服知识库怎么建:资料结构、检索边界与人工升级
大数据·人工智能
麦客奥德彪8 小时前
Codex 最近只有约 25 token/s,我做了个插件,把 ChatGPT 接进 DSH
openai·ai编程·deepseek
xsd202411189 小时前
从自主导航到视觉读表:一台工业巡检机器人的全栈技术链路拆解
人工智能
袁哥大话安全9 小时前
巡隐WEBSHELL扫描软件
人工智能·安全·web
论文复现现场9 小时前
8卡4090能跑70B吗?Llama-2显存预算、QLoRA与通信瓶颈
人工智能·深度学习·分布式训练·llama·显存·qlora·算家云