Skip to content

在 AI 超级智能体项目中,你为什么要设计批处理优化策略?它解决了什么问题? ​

  • 标签:后端、编程导航

🏛 业务架构师 ​

批处理优化的动因是一次真实的成本事故:知识库初建时逐条调用 Embedding API,3 万文档片段跑了几个小时还触发限流——串行 + 单条请求,时间和金钱双输。

我的改造:

  • 批量请求:Embedding 从逐条改为每批 50~100 条(API 支持数组输入)——请求次数降两个数量级,网络往返摊薄,吞吐翻数十倍;
  • 并发 pipeline:多批次并行(可控线程池 + 限速),对齐供应商的 RPM 配额——并发度不是越大越好,要贴着配额上限跑;
  • 失败批重试:批级失败重试 + 死信记录,部分失败不阻断全量;
  • 幂等入库:按内容 hash 去重——重复文档、重复片段不再重复付费。

它解决的业务问题账:

  1. 时间:全量重建从 4 小时压到 10 分钟——知识库重建频率从"季度"变"随时",知识时效性直接提升(更新越勤,答案越准,用户越信);
  2. 金钱:请求次数与 token 计费的双重节约(批量接口单价比单条低),成本降约 60%;
  3. 稳健:重建任务放进大促/低峰窗口执行,不再阻塞运营。

架构启示:AI 应用与传统应用最大的差异是——计算即成本,每次调用都明码标价。批处理、缓存(同一片段的向量化结果复用)、分级模型(低价值内容用便宜 Embedding)这三个手段构成 AI 应用的"成本工程学"——不懂成本工程的 AI 团队,产品做得越好亏损越大。

🏗 系统架构师 ​

批处理优化解决的是入库与查询的吞吐、成本、稳定性三个问题:

它解决的问题:

  1. 成本:Embedding API 按 token 计费且按调用次数限流——10 万 chunk 逐条调用 = 10 万次请求(钱 ×N、限流墙);攒批(如 64 条/批)一次调用,成本与请求数双降;
  2. 吞吐:建库是重 IO/重计算任务,逐条同步处理一小时起步;批处理 + 并行(线程池分片)时间降一个量级;
  3. 稳定性:逐条失败重试粒度太碎;批处理按批记录状态(成功/失败/offset),断点续传,故障后从失败批继续,不用全量重跑。

我的实现(Spring AI 下的三层批处理):

  • 文档级并行:多个文档的 ETL 任务线程池并行(按文档隔离,互不影响);
  • chunk 级攒批:切分后的 chunk 按 64/批聚合调 embeddingModel.embed(batch),再 vectorStore.add(batch) 批量入库(pgvector COPY/JDBC batch);
  • 失败隔离:批失败落任务表(doc_id、批号、错误信息),独立重试任务扫描补跑,指数退避。

效果数字:10 万 chunk 建库从逐条的 ~2 小时压到 ~8 分钟;embedding 调用数降 64 倍;中途故障可续传。

架构视角:这是典型的"小请求合并"模式——与 Kafka 批量发送、JDBC batch insert、Redis pipeline 同源。LLM 应用工程化的大部分优化,本质都是把互联网高并发的老经验搬到"token 流量"上:批处理、缓存、分级路由、限流。

💻 全栈开发专家 ​

背景问题:批量构建知识库时,逐条处理有两宗罪——

  1. 慢:1000 个 chunk 逐条调 embedding API(每次一个 HTTP 往返),网络 RTT 占了 90% 耗时;
  2. 贵且易限流:调用次数多,触发厂商 RPM 限流,还会被按次计费的项目多扣费。

我的批处理实现:

java
// 1. embedding 批量化:Spring AI 的 EmbeddingModel 本身支持 List 输入
List<float[]> vectors = embeddingModel.embed(texts);   // 一次请求批量返回

// 2. 分批 + 并行(批 20~50 条,线程池并发 4)
List<List<Document>> batches = Lists.partition(chunks, 20);
ExecutorService pool = Executors.newFixedThreadPool(4);
List<CompletableFuture<Void>> futures = batches.stream()
    .map(batch -> CompletableFuture.runAsync(() -> {
        vectorStore.add(batch);                      // 批量入库
    }, pool)).toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

效果数据:1 万 chunk 的知识库构建从 40 分钟降到 4 分钟,embedding API 调用次数从 10000 降到 500。

工程细节(坑):

  1. 批大小要试:厂商上限不一(OpenAI 单请求 2048 条,但每条有长度限制),我保守用 20;
  2. 失败重试以批为单位,批内失败要拆分定位坏数据(个别超长文本截断处理);
  3. 入库用批量 SQL(PGVector 的 INSERT ... VALUES 多行),别循环单条 insert;
  4. 并发度对限流敏感,遇到 429 指数退避 + 降并发。