讨论

设计基于LLM的算法,将字幕整理为段落和章节

来自 Wikiprompt,自由的提示词百科全书

宝玉
贡献者宝玉X来源

2024年12月19日

设计基于LLM的算法,将字幕整理为段落和章节 一个详细的提示,要求使用LLM设计一种算法,将字幕片段组织成段落和章节,并包含关于上下文窗口、分页、成本和输出格式的具体约束。

提示词内容收藏

🌐
好的,我将为您设计一个基于大语言模型(LLM)的字幕分段与分章算法,并附上代码说明。 --- ## 算法设计思路 ### 核心目标 - 将长字幕文件按语义划分为段落和章节。 - 控制LLM调用次数与传输数据量。 - 避免因分页导致章节或段落被切断。 - 不依赖LLM输出精确位置,而是通过结构化返回结果进行后处理。 --- ### 总体流程 1. **预处理**:将字幕按时间顺序切分为多个“页”(Page),每页包含若干Segment,确保每页内容量适中(例如不超过3000字符)。 2. **页内分段**:对每一页,调用LLM,要求其返回该页内的段落划分(每个段落包含哪些Segment的索引或文本片段)。 3. **页间合并**:将相邻页的段落进行合并,形成全局段落。 4. **章节划分**:再次调用LLM,基于全局段落列表,返回章节划分(每个章节包含哪些段落)。 5. **后处理**:根据LLM返回的段落/章节文本,映射回原始Segment,生成最终结构。 --- ### 关键设计点 - **分页策略**:按字符数或Segment数分页,并确保每页边界不切断一个完整的句子(即Segment)。 - **LLM返回格式**:要求LLM返回JSON数组,每个元素包含该页内段落所包含的Segment的`start`时间戳(或文本片段),便于映射。 - **合并策略**:利用时间戳或文本重叠,将相邻页的段落合并。 - **章节划分**:基于段落的时间顺序,要求LLM返回章节标题及包含的段落时间范围。 --- ### 代码示例(TypeScript) ```typescript type SubtitleSegment = { start: number; end: number; text: string; words: { word: string; start: number; end: number }[]; }; type Subtitle = { segments: SubtitleSegment[]; }; type Paragraph = { start: number; end: number; text: string; segments: SubtitleSegment[]; }; type Chapter = { title: string; paragraphs: Paragraph[]; }; type Transcription = { chapters: Chapter[]; }; // 分页策略:按字符数分页,每页最多 maxChars 字符,且不切断Segment function paginateSegments(segments: SubtitleSegment[], maxChars = 3000): SubtitleSegment[][] { const pages: SubtitleSegment[][] = []; let currentPage: SubtitleSegment[] = []; let currentChars = 0; for (const seg of segments) { const segChars = seg.text.length; if (currentChars + segChars > maxChars && currentPage.length > 0) { pages.push(currentPage); currentPage = []; currentChars = 0; } currentPage.push(seg); currentChars += segChars; } if (currentPage.length > 0) pages.push(currentPage); return pages; } // 调用LLM,返回该页的段落划分(每个段落包含的Segment索引范围或文本) async function callLLMForParagraphs(pageSegments: SubtitleSegment[]): Promise<{ start: number; end: number }[]> { // 构造prompt,要求返回JSON数组,每个元素包含start和end时间戳 const prompt = `请将以下字幕片段划分为若干段落,每个段落应语义完整。返回JSON数组,每个元素包含"start"和"end"(对应字幕片段的起始和结束时间戳)。\n\n字幕片段:\n${pageSegments.map(s => `[${s.start}-${s.end}] ${s.text}`).join('\n')}`; // 调用LLM(此处为伪代码) const response = await callLLM(prompt); // 解析JSON,返回时间戳数组 return JSON.parse(response); } // 将LLM返回的时间戳映射回Segment索引 function mapTimestampsToSegments(pageSegments: SubtitleSegment[], ranges: { start: number; end: number }[]): SubtitleSegment[][] { const paragraphs: SubtitleSegment[][] = []; for (const range of ranges) { const segs = pageSegments.filter(s => s.start >= range.start && s.end <= range.end); if (segs.length > 0) paragraphs.push(segs); } return paragraphs; } // 合并相邻页的段落(基于时间连续性) function mergeParagraphsAcrossPages(pagesParagraphs: SubtitleSegment[][]): SubtitleSegment[][] { const merged: SubtitleSegment[][] = []; for (const para of pagesParagraphs) { if (merged.length === 0) { merged.push(para); } else { const last = merged[merged.length - 1]; const lastEnd = last[last.length - 1].end; const firstStart = para[0].start; // 如果时间间隔小于阈值(如2秒),则合并 if (firstStart - lastEnd < 2) { merged[merged.length - 1] = [...last, ...para]; } else { merged.push(para); } } } return merged; } // 调用LLM进行章节划分 async function callLLMForChapters(paragraphs: Paragraph[]): Promise<{ title: string; start: number; end: number }[]> { const prompt = `请将以下段落划分为若干章节,每个章节应主题一致。返回JSON数组,每个元素包含"title"、"start"和"end"(对应段落的起始和结束时间戳)。\n\n段落列表:\n${paragraphs.map(p => `[${p.start}-${p.end}] ${p.text}`).join('\n')}`; const response = await callLLM(prompt); return JSON.parse(response); } // 主算法 async function transcribeToChapters(subtitle: Subtitle): Promise<Transcription> { const pages = paginateSegments(subtitle.segments); // 1. 每页内分段 const allPageParagraphs: SubtitleSegment[][] = []; for (const page of pages) { const ranges = await callLLMForParagraphs(page); const pageParagraphs = mapTimestampsToSegments(page, ranges); allPageParagraphs.push(...pageParagraphs); } // 2. 合并跨页段落 const mergedParagraphs = mergeParagraphsAcrossPages(allPageParagraphs); // 3. 构建Paragraph对象 const paragraphs: Paragraph[] = mergedParagraphs.map(segs => ({ start: segs[0].start, end: segs[segs.length - 1].end, text: segs.map(s => s.text).join(' '), segments: segs, })); // 4. 章节划分 const chapterRanges = await callLLMForChapters(paragraphs); // 5. 构建Chapter对象 const chapters: Chapter[] = chapterRanges.map(range => { const chapterParagraphs = paragraphs.filter(p => p.start >= range.start && p.end <= range.end); return { title: range.title, paragraphs: chapterParagraphs, }; }); return { chapters }; } ``` --- ### 优化与注意事项 - **分页大小**:根据LLM上下文窗口调整,建议每页不超过3000字符。 - **时间戳映射**:LLM返回的时间戳可能不精确,可通过容错匹配(如允许±0.5秒)提高鲁棒性。 - **合并阈值**:根据字幕语速调整,一般2秒内视为连续。 - **成本控制**:每页调用一次LLM,章节划分再调用一次,总调用次数约为页数+1,可接受。 - **错误处理**:若LLM返回格式错误,可重试或降级为按时间均匀分段。 --- 这个算法平衡了语义质量、上下文长度和调用成本,适合长字幕处理。您可以根据实际需求调整参数和LLM提示词。

登录以查看完整提示词

继续使用:

登录即表示你同意我们的 使用条款 和 隐私政策

用法

此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。

为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。

参考资料

分类:coding| twitter| llm-algorithm| subtitle-processing

讨论