把 1124 场 AI Engineer 演讲整理成一个可搜索、带时间戳的索引,这事儿一开始听起来像是个“用脚本跑一遍就行”的小工具,但真正动手做的时候,光是数据清洗就折腾掉我两个周末。我最初的需求很朴素:每次听技术演讲,听到一半想回头找某个具体的观点或代码片段,就得在 YouTube 时间轴上反复拖动,运气好能一次命中,运气不好基本等于把视频重看了一遍。我需要的不是一份“有哪些视频”的目录,而是一个能让我直接跳到“讲某话题的第几秒”的检索系统。
所以这个项目的目标很明确:把散落在各个平台、各个会议里的 AI Engineer 演讲全部收进来,做完数据清洗和去重之后,给每一场演讲生成带时间戳的段落索引,让用户输入关键词就能定位到具体的那一场演讲,甚至是那一段话。最终做出来的索引覆盖了 1124 场演讲,包含标题、演讲人、会议/频道来源、时长、上传时间、以及按字幕对齐生成的分钟级时间戳。适合谁用?如果你是 AI 工程师、技术内容策展人、想系统学习某一方向但不知道从哪看起的学习者,这个索引能帮你把几百小时视频变成一本可以翻目录的书。
1. 项目动机:为什么我觉得“能搜到视频”远远不够
1.1 AI 工程内容供给爆炸,但检索能力还停留在“浏览”
AI Engineer 这个方向这两年的内容增速非常夸张。各大会议开始增设 AI Engineer 专题,一些以 AI 为主题的独立会议也冒了出来,更别提那些每周更新的技术频道。我在收集过程中统计过,光是我接触到的来源里,2023 年之后的演讲数量占了总量的七成以上,而且这个比例还在涨。
问题在于,内容的增量并没有带来检索的便利。今天你想找一个“RAG 评估指标怎么设计”的演讲,最常规的做法是在 YouTube 上搜关键词,然后从搜索结果里一个个点进去看简介,再手动拖视频进度条找相关内容。运气好的话,演讲者会在简介里贴时间轴;运气不好,你只能在评论区找其他观众的“好人一生平安”。这种体验本质上是在做手工索引,人力成本极高,而且换一个关键词就要重新来一遍。
1.2 视频本身是“不可搜索”的,需要中间层
视频这个载体天然有信息盲区。搜索引擎能索引标题、描述、标签,但索引不了画面里的每一句话。语音识别技术早就能把语音转成文字,可转出来的文字如果没有跟时间轴做对齐,你也只能看到整段文本,却不知道这句话出现在视频的第几分钟。我做这个索引的核心思路,就是在“视频内容”和“用户检索词”之间加一层结构化的中间数据:字幕文本 + 时间戳 + 元信息。有了这一层,搜索就能从“搜视频标题”升级成“搜演讲内容”,能力完全不一样了。
1.3 市面上的现成工具为什么不满足需求
说实话,做之前我也尝试过用一些现成的工具组合。比如直接用 YouTube 搜索,或者用某些会议官网自带的议程检索。但它们各有各的问题。YouTube 搜索本质是标题和描述的全文匹配,精度非常差,而且没有跨会议的聚合能力。会议官网的议程页倒是结构化,但只能覆盖自家内容,我想跨会议对比同一主题的不同观点时,就完全没辙了。还有一些第三方课程平台,做了人工精选,但覆盖量太小,并且更新慢。这些都促使我下定决心,自己搭一套能持续更新的索引系统。
2. 数据收集:1124 场演讲是怎么筛出来的
2.1 演讲来源与采集策略
我一开始定了一条原则:只收“演讲”,不收“教程视频”和“播客节目”。这两者区别是什么?演讲一般有明确的主题边界、通常在某个会议或技术社区活动中发布、时长大多在 20 到 60 分钟;教程视频则往往是系列化、分 P 的(比如“XX 框架入门到精通 01”),播客又是完全不同的内容形式,没有画面的时序结构。这条原则帮我过滤掉了很多噪音。
来源大致可以分为以下几类:
| 来源类型 | 示例 | 在最终索引中的占比 |
|---|---|---|
| 大型行业会议 | 各类 AI/ML 技术大会 | 约 40% |
| 语言/框架社区会议 | PyCon、JSConf 等 | 约 20% |
| 技术公司官方频道 | 大厂研究院、工程博客配套视频 | 约 25% |
| 独立技术社区 | 线上 meetup、社区月度分享 | 约 15% |
采集端主要是写了一个 Python 爬虫,通过 YouTube Data API 按频道和播放列表拉取视频列表,再按关键词过滤(比如标题或描述里包含 AI Engineer、LLM、AI Agent、RAG 等词)。这里要注意,API 只返回视频的标题、描述、时长这些基础信息,字幕文件要走另一个接口,而且有些字幕是自动生成的,有些是人工精校的,质量差距很大。
2.2 去重与清洗:3000 多场到 1124 场的过滤过程
原始抓取量大概是 3000 多场,最终只留下 1124 场。很多人会问,为什么砍掉这么多?因为数据脏的程度超出我的预期。第一层去重是视频级别的重复,同一个演讲被多个频道搬运、剪辑成不同版本,这在 AI 内容领域特别常见。比如一个 40 分钟的完整演讲,有人截了其中 5 分钟单独上传,标题还写得特别吸引人,如果不做重复检测,你的索引里就会出现两个“不同”的条目,但实际内容是同一场。
第二层过滤是标题规范性。有些视频标题写得很随意,比如“chat with 李雷 about AI”。这种标题在自动抓取时很难判断它是不是一次合格的演讲,我最后采用了一个综合评分规则:标题里有明确主题词加分,有演讲人姓名加分,描述里有议程信息加分,时长在 25 到 70 分钟区间加分,低于 5 分钟直接排除。这套规则虽然没法做到百分百精确,但在数据量级下已经足够实用。
2.3 元信息的统一与规范化
清洗完之后,还需要把每一场演讲的元信息结构化。这里容易踩的坑是同一个人名有多种写法,比如“Zhang Wei”和“Wei Zhang”,在原始数据里可能是两个人,其实是同一个人。我做了一个简单的归一化处理:把拼音名统一成“姓在前”的形式,西文名统一按“名+姓”来存储,另外保留一个别名表用来关联花名和常用缩略称呼。规范化的好处体现在后续搜索上,用户搜“大模型对齐”的时候,系统能同时匹配到中英文表述和不同的演讲人拼音写法,查全率明显提高。
3. 搜索与时间戳:把视频变成可检索的语料库
3.1 时间戳从哪里来:字幕对齐的三条路径
这是整个项目里技术含量最高的部分。要做“带时间戳的搜索”,核心是拿到每一场演讲的逐句字幕时间轴。我的方案分三条路径,按优先级依次尝试:
第一优先是 YouTube 自带字幕。平台提供的字幕文件里其实包含每个句子出现的时间点(以秒为单位),直接把.vtt文件解析出来就能拿到结构化数据。问题在于并不是每场演讲都有字幕,很多搬运视频连自动字幕都没开。
第二优先是视频描述或评论区里的章节标记。不少演讲者会在简介里写“00:00 Intro, 05:30 Main Talk, 30:00 Q&A”,这种数据虽然粒度粗,但胜在干净。我写了解析器去识别这类时间轴格式,提取出来之后存成一个粗粒度索引。
第三是兜底方案,用 Whisper 做本地语音转写。这个方案最费时间,尤其是时长接近一个小时的演讲,转写一次可能需要几分钟到十几分钟,取决于 GPU 资源。但它是唯一能保证所有演讲都能被“内容化”的方案。我会先判断前两条路径是否命中,只有全部失败才走 Whisper。
字幕拿到之后,我会按每分钟做一个内容分片,每个分片对应一个时间戳。搜索的粒度就是“某一分钟到下一分钟之间的内容”,用户点击搜索结果时,直接跳转到该时间点的视频位置。
3.2 搜索索引的字段设计与排序规则
搜索不是简单地存进去就能查。我用数据库建了一个索引表,每个演讲的字段包含:演讲标题、演讲人、会议/频道、内容分片文本、时间戳范围、上传日期、视频时长。搜索时,这些字段的权重是不一样的。
| 字段 | 权重 | 说明 |
|---|---|---|
| 标题 | 高 | 精准命中最重要 |
| 演讲人 | 高 | 用户找特定讲者时优先匹配 |
| 分片文本 | 中 | 命中说明是正文级别的相关性 |
| 会议/频道名 | 低 | 仅作为降级匹配 |
排序逻辑是:先算关键词在标题和演讲人上的命中分数,再算分片文本上的命中分数。两者都命中的内容排最前;只有标题命中的其次;只有内容命中的再次。另外,上传日期越新,在同等分数下排名越靠前,这符合 AI 领域内容迭代快的特点,老演讲的观点可能已经过时了。
3.3 一个最简单的搜索流程示例
我用一个实际的搜索场景演示这个过程:假设你想找“AI Agent 的 planning 模块怎么设计”。
- 输入关键词 agent planning。
- 搜索层把这两个词拆成独立 token,在数据库里做全文检索。
- 先看标题字段,找到标题含 agent 和 planning 的演讲。
- 没有完全命中的情况下,再查内容分片文本。
- 返回结果时,每条结果带上“命中位置所在的时间戳”,比如第 12 分钟。
- 点击后,前端把视频播放器跳转到第 12 分钟并开始播放。
整个过程在用户侧看是一步操作,但背后的时间戳对齐、权重排序和分片命中逻辑,决定了返回结果是不是真的有用。
4. 技术选型与实现:整套索引系统我用了什么
4.1 数据管道:采集、清洗、索引、服务
整个系统分成四个环节。采集端用 Python + YouTube Data API 每天跑一次增量任务,把新增视频拉进原始库。清洗端负责去重、标题规范化、人名归一等脏活,我用的是 Pandas 做批处理,跑完输出一份干净的 JSON。索引端把 JSON 写入数据库,同时对字幕文件分片并生成全文索引。服务端用一个轻量 API 提供搜索接口,前端页面把 API 包装成一个看起来像搜索引擎的界面。
这一套架构没有用什么花哨的技术,核心原则是每个环节都能独立重跑。比如脏数据清洗脚本,我改了一版之后能直接对全量数据重放,不用人工介入。这一点在项目后期帮了大忙,因为每次调整清洗规则,我都需要知道历史数据有哪些会被“新规则”重新归类。
4.2 存储选型:SQLite 起步,Postgres 收尾
最开始我图省事用了 SQLite,数据量到几千条记录的时候完全没问题。但等索引表加上全文索引,数据增长到几十万行级别时,SQLite 的并发写入和查询性能开始吃紧。最后我把核心数据迁移到了 PostgreSQL,用了它自带的全文检索能力。其实有两种方案:
| 方案 | 优势 | 不足 |
|---|---|---|
| PostgreSQL 全文检索 | 数据不用同步到第三方,SQL 一个查询搞定 | 中文分词需要额外插件 |
| Meilisearch / Typesense | 开箱即用的搜索体验,支持模糊匹配 | 多一套服务,部署和维护成本高 |
考虑到这个项目的核心是英文技术演讲,Postgres 内置的tsvector和tsquery已经够用,而且少维护一个搜索引擎组件,所以最终选了 Postgres。如果你的索引数据里中文内容占比很高,建议直接上 Meilisearch,中文分词体验会好很多。
4.3 时间戳对齐的代码实现思路
时间戳对齐的核心逻辑其实不复杂:从字幕文件里拿到每个句子的开始时间和结束时间,然后按“分钟”聚合。每一分钟的文本内容就是该分钟的字幕片段,整个演讲的分片文本构成了一个时间轴。
import webvtt def parse_vtt_to_segments(vtt_path): segments = [] for caption in webvtt.read(vtt_path): start_sec = caption.start_in_seconds end_sec = caption.end_in_seconds text = " ".join(caption.text.split()) segments.append({ "start": start_sec, "end": end_sec, "text": text }) return segments拿到 segments 之后,再按整分钟聚合文本。比如一个 40 分钟的演讲,最终生成 40 个分片,每个分片包含该分钟内讲到的内容片段。这样查询就可以通过“时间戳索引表”直接定位到具体分钟,不需要实时扫描整个演讲稿。
4.4 前端展示:搜索框之外,还做了什么
前端部分我坚持了极简原则:一个搜索框、一个结果列表、一个展开详情区域。结果列表里,每一条显示演讲标题、演讲人、会议名称、日期,以及命中内容所在的时间段。点击之后,右侧会拉起来一个内嵌播放器,直接定位到对应时间戳。
额外加的细节是“关键词高亮”。搜索词在结果描述里会用黄色底色标出来,这样用户能快速判断这条结果是否真的命中了需求。高亮逻辑放在后端,因为前端做会泄露所有内容分片的数据,不必要的传输会拖慢加载速度。
5. 踩坑记录与排查实录:我做索引时遇到的 7 个典型问题
5.1 大量视频没有字幕,怎么兜底
我最初以为 YouTube 字幕覆盖率至少有八成,结果实际跑下来,只有六成左右。很多会议的官方录像是真的没有字幕,只有自动生成的英文 CC,但自动字幕质量参差不齐,断句错乱严重,很多专有名词完全识别错误。最终的选择是:自动字幕照单全收,但对专有名词做了一次全局纠错,比如把常见的模型名、框架名的识别错误批量替换掉。这个纠错词表是人工整理的,大概花了一个下午,但效果立竿见影,搜索命中率提升了不少。
5.2 时间戳偏移:视频片头导致所有索引错位
这是一个很隐蔽的坑。不少会议视频都有 1 到 2 分钟的片头动画和主持人开场,这段时间里演讲者并没有开讲。字幕文件的时间轴是以整个视频为单位计算的,所以第 2 分钟的字幕可能是片头音乐,而不是演讲内容。我最初的索引直接拿字幕时间戳建,结果用户点击第 15 分钟,看到的往往还是主持人铺垫,真正的内容从第 16 分半才开始。
解决方法是,检测字幕文本里的“关键词起点”。如果字幕前几行没有出现演讲者名字或者会议名首字母这类标记,就往后找第一个包含技术关键词的句子,把它所在的时间点设为“实际内容起点”,后续所有时间戳按这个起点做偏移。这个方案不完美,但在大部分场景下能把偏差缩小到 30 秒之内。
5.3 重复视频的“标题变体”骗过了去重
原始数据里的重复,除了完全一样的 URL,还有不少是标题里加了一堆前后缀的变体。比如“Keynote: Building AI Agent”和“Building AI Agent | Keynote 2024”。字符串比对基本无效,只能用语义相似度。我对标题做了向量化处理,用嵌入模型算标题相似度,超过阈值就标记为重复候选,再人工确认。这个方法把重复检测的召回率拉高了很多,也顺便帮我识别出了一批“同一演讲、不同语言字幕”的版本。
5.4 全文检索的中文分词问题
虽然是英文语料为主,但 AI Engineer 圈子里很多内容会夹杂中文、日文、韩文的专有词。Postgres 默认的英文分词器遇到中文内容,会把一整个句子当成一个 token,导致搜索几乎失效。我的临时方案是只对中文字段做特殊处理:在写入索引前,按字符 n-gram 拆开,比如拆成 2-gram 的 token 子串。这样至少保证中文关键词也能被部分匹配到。如果后续要做全面中文支持,我还是建议上一个专用的搜索引擎。
5.5 搜索“太精确”导致结果太少
早期的搜索结果,我按单词精确匹配,输入“agent planning”就只能返回同时包含两个词的结果。真实用户的搜索词经常是“AI agent 怎么设计规划”,这里的“规划”和“planning”是不同语言、不同词形,精确匹配完全没办法。后来我引入了一个简单的词形还原和同义词扩展层,比如“planning”匹配“plan”、“planner”、“规划”、“计划”等变体。不需要多复杂,一个同义词表就能提升不少体验。
5.6 增量更新的幂等性
爬虫每天跑增量任务,最怕的是同一条数据被重复写入。我的解决方式是给每个视频规定一个唯一业务 ID,以它作为主键。每次写入前检查是否存在,存在则更新元信息,不存在则新增。这个逻辑很基础,但我一开始偷懒没做,导致数据库里出现了几千条重复记录,清洗脚本又得重跑一遍,白白浪费了一个下午。
5.7 无法按“话题脉络”浏览的缺口
搜索做得再好,也只能解决“我知道要查什么”的需求。还有一类用户,他们不知道要查什么,只是想按时间顺序看看这个月 AI Engineer 圈子里大家讲了什么。目前索引虽然可以按上传日期倒序排列,但缺少“话题聚类”能力,同一主题的多场演讲没有关联起来。这个我打算后面用主题建模做轻量聚类,把“Agent”、“RAG”、“Inference Optimization”这样的主题标签加到每条记录上,让用户能按脉络浏览。
6. 实操心得:如果你的索引要覆盖 5000 场,我建议这样设计
中间有一段时间,我尝试过把收集范围扩大到所有 ML 相关演讲,结果数据量很快冲到 5000 多场。这时候我发现几个设计上的瓶颈值得提前规划:一是字幕分片表的存储占用增长很快,建议提前做分区表,按月或者按会议 ID 分区;二是全文索引的构建时间会随着数据量非线性增长,最好用增量索引而不是全量重建;三是去重逻辑必须考虑“批量搬运”这种内容农场行为,我在后期用聚类方式把同一来源的批量上传视频做分组,再按组内相似度去重,效率高了很多。
另一个心得很重要:元数据质量直接决定索引价值。一个字段缺失的演讲,即使内容再好,也会在搜索里沉底。我建议在做数据清洗时,把“元数据完整度”作为核心指标来监控。如果某个来源的演讲,有超过 20% 缺少文字表述、缺失演讲人信息,就应该考虑把该来源降权甚至剔除。
最后说一点工具层面的经验。整个项目我大部分时间花在数据清洗和字幕处理上,真正写搜索逻辑的时间反而不多。前端用的是一个很轻量的静态页面,后端是 FastAPI 暴露的搜索接口,部署在一台低配云主机上,跑得很稳。整套系统加起来不到 3000 行代码,但数据管道里的每一环节都经历过手工检查和规则调优。这也是我最想强调的:这类索引项目的难度不在技术有多深,而在于你愿不愿意花时间把脏数据一点点擦干净。