滑动窗口:优雅解决 API 批量处理限制

滑动窗口:优雅解决 API 批量处理限制

_

在 AI 应用开发或者对接第三方服务时,我们经常会遇到一个非常现实的“拦路虎”:API 的调用限制

比如最近我在做 RAG(检索增强生成)知识库时,需要把本地的 Markdown 文档向量化并存入 PostgreSQL。我使用的 DashScope(通义千问)嵌入模型虽然效果很棒,但它有一个硬性规定:单次请求最多只能处理 10 个文档

如果我手里有 1000 篇文档,难道要发 1000 次网络请求吗?显然不行,效率太低且容易触发限流。这时候,经典的“滑动窗口”(或者叫分批处理)算法就派上用场了。今天就来聊聊如何用短短几行代码,优雅地解决这个问题。

🎯 问题的核心:化整为零

假设我们有一个包含 N 个文档的大列表,但外部接口每次只允许接收 batchSize(比如 10)个元素。我们的目标就是把这个大列表,“切”成一个个符合规格的小批次,然后循环发送。

这就好比我们要用一辆载重有限的小货车去运一堆砖头,一次只能拉 10 块,我们必须一趟一趟地往返搬运,直到把所有砖头都运完。

💻 核心代码实现

下面是我用 Java 实现的通用分批处理逻辑(这段代码不仅适用于 AI 向量入库,也适用于任何需要批量调用的场景):

// 假设 documents 是你从数据库或文件中读取到的全量数据列表
List<Document> documents = loadAllDocuments(); 

if (documents == null || documents.isEmpty()) {
    return; // 没数据就直接下班
}

int batchSize = 10; // 设定每批次的“容量”,适配 API 的限制

// 使用带步长的 for 循环来实现滑动窗口
for (int i = 0; i < documents.size(); i += batchSize) {
    // 计算当前窗口的结束位置
    int endIndex = Math.min(i + batchSize, documents.size());
    
    // 截取当前这一小批数据 [i, endIndex)
    List<Document> batch = documents.subList(i, endIndex);

    System.out.println("正在处理第 " + (i / batchSize + 1) + 
            " 批,本批包含 " + batch.size() + " 个文档");

    // 核心业务:将这一批数据发送给 API 或存入数据库
    processBatch(batch); 
}

🔍 为什么这样写最稳?

这段看似简单的代码,其实包含了三个非常关键的工程细节:

  1. 精准的步长控制 (i += batchSize)
    普通的 for 循环是 i++(每次走一步),而这里我们是 i += 10(每次跨一个批次)。这就像滑动窗口一样,每次处理完一批,窗口就向后“滑”过这 10 个元素,直接定位到下一批的起点。

  2. 完美的边界保护 (Math.min)
    这是最容易写出 Bug 的地方!假设你总共有 25 个文档,前两批分别是 [0-9][10-19],都很完美。但当处理最后一批时,i 是 20,如果直接取 i + 10 就会变成 30,导致数组越界崩溃。
    使用 Math.min(i + batchSize, documents.size()) 能确保:即使最后一批凑不够 10 个(只剩 5 个),程序也能安全地截取剩下的所有数据,不会报错。

  3. 高效的内存切片 (subList)
    subList 方法并没有在内存中复制一份新的数据,而是返回了原列表的一个“视图”。这意味着无论你的原始数据有多大,这个分批过程几乎不消耗额外的内存,对系统非常友好。

🚀 这种写法的好处

  • 规避限制:完美绕过第三方 API 的单次数量上限。

  • 防止内存溢出 (OOM):在处理海量数据时,分批加载和处理能保证内存占用始终维持在低位。

  • 提升容错率:如果网络波动导致某一批失败了,你只需要重试这一小批即可,不需要从头再来。

📌 总结

在编程的世界里,越是复杂的业务,往往越依赖这些基础且扎实的算法思想。一个简单的“滑动窗口”,就能帮我们解决大模型时代最常见的批量处理难题。

如果你也在做 AI 应用开发,或者经常需要对接各种受限的第三方接口,不妨把这个代码片段收藏起来,它绝对能成为你工具箱里的得力助手!

Spring AI 消息持久化避坑指南:利用 Kryo 解决 Message 序列化难题 2026-05-11

评论区