智汇笔记项目实战(二):从模拟链路到可降级抓取——语义切片与多线程 Map-Reduce 摘要编排
摘要
本文记录第七周在 AI 中台侧的三层推进:(1)抓取层由纯模拟扩展为 HTTP 接口链 + 可控降级;(2)清洗层在保持 KPI 思路的前提下迭代规则,减少误伤与英文粘连;(3)计算层实现 语义切片 与 Map(线程池并行)→ Reduce(全局归并) 的摘要主链路,并暴露 /api/ai 供组员联调。
一、与实战(一)的关系
在实战(一)中,我论证了两条结论:
第一,B 站字幕以 独立 JSON 存在,适合用 RestTemplate + Jackson 解析 body → content 再拼成长文本;
第二,面向「2 秒/万字」量级,应用 Pattern 预编译 取代循环内临时正则,并把纯算法验证放在 main 或轻量测试中,避免为 Util 拉起整套 Spring——这是合理的解耦。
第七周要解决的新矛盾是:验收与真实环境不能只依赖内存里的假 JSON。典型现实包括:稿件 无 CC、接口返回 subtitle_url 为空、部分场景 需登录 或风控导致链路中断。若此时系统直接报错退出,就与「全媒体知识提炼」的产品叙事不符。因此本周的目标可以概括成一句话:
在不去伪造线上结果的前提下,保证「永远有一条可演示、可截图、可对日志」的主路径。
二、流水线与模块边界
从工程视角,我把 AI 中台拆成三层:

说明:
- 抓取层只负责「拿到尽可能真实的原始字符串」;清洗之前不做业务假设。
- 清洗层输出的是「适合喂给切片与模型的文本」,与是否调用 LLM 无关。
- 计算层才引入 Token 预算、overlap、并发 Map、Reduce,属于成本与延迟敏感区。
三、抓取层:BilibiliFetchService 的真实链路与降级策略
3.1 接口链顺序(逻辑不变,环境可变)
本周实现的主路径为:
x/web-interface/view?bvid=→ 读取aid、cid(分页场景取第一页cid);x/player/v2?aid=&cid=&bvid=→ 在subtitle.subtitles[]中寻找可用字幕资源;- 优先 中文相关
lan(如zh-CN、zh-Hans),其次取首个非空subtitle_url; - 下载字幕 JSON → 解析根节点下的
body数组,拼接各条content。
这与前一周的核心代码路径一致,因此清洗模块无需重写接口,只要把「数据源不确定性」隔离在 Service 层。
3.2 降级(fallback)
任一步异常或 URL 为空时,走与(一)相同风格的 模拟字幕 JSON,并通过 SubtitleFetchResult 标记 fallbackMock=true。

四、清洗层:TextCleanUtil 的迭代
上周实战(一)已经用预编译正则解决了「性能对不对」的问题;本周更多处理「语义对不对」:
- 避免宽匹配误伤:少用「全局删除某个单字语气词」这类规则,多采用 短语级赘词(例如逗号引导的口头禅),并结合 句首填充音的保守替换。
- 空白策略升级:不把全文压成「零空格」,而是折叠为 单空格,减轻下游英文术语(如 Spring Boot)粘连风险,也方便切片阶段按标点回溯。
| 维度 | 实战(一) | 实战(二) |
|---|---|---|
|
目标 |
证明清洗 够快、链路 成立 |
在真实噪声分布下 尽量少误伤 |
|
数据源 |
主要内存模拟 |
真接口优先 + 降级 |
|
测试策略 |
|
保留 Util 可独立测 + Spring 下切片单测 |
|
下游 |
单一输出字符串 |
接入 切片与 Map-Reduce |

五、计算层(一):SemanticChunkService 语义切片
直接进入 LLM 会遇到上下文上限;任务书也要求超过近似 Token 阈值时触发切片,并设置 overlap。
5.1 设计取舍
- 先段落、再标点:在
[windowStart, windowEnd)内优先\n\n,其次单换行,再退到。!?.!?等,减少「句子被腰斩」。 - overlap:相邻块之间重叠一小段,缓解「切块缝」上的信息丢失;具体字数来自配置而非魔法数字,便于中期答辩时解释「为什么取 200」。
5.2 与「字符」和「Token」的关系
课堂 KPI 往往写 Token,但 Java 侧通常先做 字符预算的近似(通过 chars-per-token-estimate 配置)。这是工程近似而不是 tokenizer 级精确,精确计费应由网关或模型侧为准。

六、计算层(二):MapReduceSummarizeService 与多线程 Map
- 单块文本:直接一次「提炼型」System Prompt 调用,无需切块。
- 多块文本:
- Map 阶段:每块独立摘要;为缩短_wall-clock_等待时间,使用 固定线程池 并行发起 LLM 请求(并发度
mapper-threads可配,且不超过分块数)。 - Reduce 阶段:将各块摘要拼成材料,再触发一次 全局归并 Prompt,得到最终结构化 Markdown。
- Map 阶段:每块独立摘要;为缩短_wall-clock_等待时间,使用 固定线程池 并行发起 LLM 请求(并发度
这样叙事与代码一致:并行的是「多个 Map 任务」;Reduce 仍是单次全局调用,避免并行 reduce 带来的语义分裂。

七、多 Agent 提示词与 LLM 客户端
本周在后端硬编码不少于三套系统提示:内容提炼、结构排版、复习出题(对应任务书中的多 Agent 思路)。工程上以 OpenAI 兼容 chat/completions 调用为准(LlmChatClient),并配套 指数退避重试(ExponentialBackoffExecutor),应对网络抖动。
八、AiWorkbenchController 的职责边界
我把 AI 调试接口挂在 /api/ai/**,并与项目现有的 JWT 拦截策略一致(需登录,与 /api/user/login 等排除项分离)。这组接口的定位是:
- 给前端/后端并联调:快速验证清洗、切片、字幕、摘要;
- 业务上的「笔记落库、目录树、SSE 长连接」仍由组员主责模块承接,我提供的是 稳定输入与稳定输出契约。
九、小结
第七周的工作,是在实战(一)已经跑通的清洗基座上补齐三件事:真实世界的不确定性(降级)、长文本的工程化切块、多线程 Map + 单阶段 Reduce 的摘要主链;并通过 /api/ai 把可演示、可量化、可截图的接口面交给团队。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)