摘要
本文记录第七周在 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 接口链顺序(逻辑不变,环境可变)

本周实现的主路径为:

  1. x/web-interface/view?bvid= → 读取 aidcid(分页场景取第一页 cid);
  2. x/player/v2?aid=&cid=&bvid= → 在 subtitle.subtitles[] 中寻找可用字幕资源;
  3. 优先 中文相关 lan(如 zh-CNzh-Hans),其次取首个非空 subtitle_url
  4. 下载字幕 JSON → 解析根节点下的 body 数组,拼接各条 content

这与前一周的核心代码路径一致,因此清洗模块无需重写接口,只要把「数据源不确定性」隔离在 Service 层。

3.2 降级(fallback)

任一步异常或 URL 为空时,走与(一)相同风格的 模拟字幕 JSON,并通过 SubtitleFetchResult 标记 fallbackMock=true


四、清洗层:TextCleanUtil 的迭代

上周实战(一)已经用预编译正则解决了「性能对不对」的问题;本周更多处理「语义对不对」:

  • 避免宽匹配误伤:少用「全局删除某个单字语气词」这类规则,多采用 短语级赘词(例如逗号引导的口头禅),并结合 句首填充音的保守替换。
  • 空白策略升级:不把全文压成「零空格」,而是折叠为 单空格,减轻下游英文术语(如 Spring Boot)粘连风险,也方便切片阶段按标点回溯。
维度 实战(一) 实战(二)

目标

证明清洗 够快、链路 成立

在真实噪声分布下 尽量少误伤

数据源

主要内存模拟

真接口优先 + 降级

测试策略

main 毫秒级验证

保留 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 任务」;Reduce 仍是单次全局调用,避免并行 reduce 带来的语义分裂。


七、多 Agent 提示词与 LLM 客户端

本周在后端硬编码不少于三套系统提示:内容提炼、结构排版、复习出题(对应任务书中的多 Agent 思路)。工程上以 OpenAI 兼容 chat/completions 调用为准(LlmChatClient),并配套 指数退避重试(ExponentialBackoffExecutor),应对网络抖动。


八、AiWorkbenchController 的职责边界

我把 AI 调试接口挂在 /api/ai/**,并与项目现有的 JWT 拦截策略一致(需登录,与 /api/user/login 等排除项分离)。这组接口的定位是:

  • 给前端/后端并联调:快速验证清洗、切片、字幕、摘要;
  • 业务上的「笔记落库、目录树、SSE 长连接」仍由组员主责模块承接,我提供的是 稳定输入与稳定输出契约。

九、小结

第七周的工作,是在实战(一)已经跑通的清洗基座上补齐三件事:真实世界的不确定性(降级)、长文本的工程化切块、多线程 Map + 单阶段 Reduce 的摘要主链;并通过 /api/ai 把可演示、可量化、可截图的接口面交给团队。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐