主题
本指南演示如何用 Split API,按内容类别把一份拼接 PDF 自动切分成逻辑上的文档小节。
使用场景
当一份大 PDF 里混着多篇彼此独立的文档或章节(例如一篇篇研究论文的合订、一批报告的合集)时,你往往需要把它们拆成独立片段。Split API 用 AI 来:
- 分析每一页的内容
- 把页面分类到用户定义的类别
- 判断一份文档在哪里结束、下一份从哪里开始(相邻的段可以共用同一类别)
示例文档
我们用的 PDF 拼接了三份文档:
- Alan Turing 的文章 “Intelligent Machinery, A Heretical Theory”(一篇文章)
- ImageNet 论文(一篇研究论文)
- “Attention is All You Need” 论文(一篇研究论文)
我们会把它切成 essay 与 research_paper 两类段。
把示例 PDF 下载到 data/:
终端窗口
curl -fL --create-dirs -o "data/turing+imagenet+attention.pdf" https://raw.githubusercontent.com/run-llama/llama-cloud-py/main/examples/example_files/turing%2Bimagenet%2Battention.pdf设置
安装所需的包:
- Python
- TypeScript
- Go
- Java
- CLI
终端窗口
pip install "llama-cloud>=2.8"配置好环境(或稍后在代码里直接传入你的 API key):
终端窗口
export LLAMA_CLOUD_API_KEY="your_api_key_here"终端窗口
npm install @llamaindex/llama-cloud配置好环境(或稍后在代码里直接传入你的 API key):
终端窗口
export LLAMA_CLOUD_API_KEY="your_api_key_here"终端窗口
go get github.com/run-llama/llama-parse-go配置好环境(或稍后在代码里直接传入你的 API key):
终端窗口
export LLAMA_CLOUD_API_KEY="your_api_key_here"implementation("ai.llamaindex:llama-cloud:1.3.0")配置好环境(或稍后在代码里直接传入你的 API key):
终端窗口
export LLAMA_CLOUD_API_KEY="your_api_key_here"终端窗口
go install github.com/run-llama/llama-parse-cli/cmd/llp@latest配置好环境(或稍后在代码里直接传入你的 API key):
终端窗口
export LLAMA_CLOUD_API_KEY="your_api_key_here"第 1 步:上传 PDF
用 llama-cloud SDK 把这份拼接 PDF 上传到 LlamaCloud:
- Python
- TypeScript
- Go
- Java
- CLI
from llama_cloud import LlamaCloud
client = LlamaCloud()
pdf_path = "./data/turing+imagenet+attention.pdf"
uploaded_file = client.files.create(file=pdf_path, purpose="split")
file_id = uploaded_file.id
print(f"✅ File uploaded: {uploaded_file.id}")import fs from "fs";
import { LlamaCloud } from "@llamaindex/llama-cloud";
const client = new LlamaCloud();
const pdfPath = "./data/turing+imagenet+attention.pdf";
const uploadedFile = await client.files.create({
file: fs.createReadStream(pdfPath),
purpose: "split",
});
const fileId = uploadedFile.id;
console.log(`✅ File uploaded: ${uploadedFile.id}`);package main
import (
"context"
"fmt"
"log"
"os"
llamacloud "github.com/run-llama/llama-parse-go"
)
func main() {
client := llamacloud.NewClient()
f, err := os.Open("./data/turing+imagenet+attention.pdf")
if err != nil {
log.Fatal(err)
}
defer f.Close()
uploadedFile, err := client.Files.New(context.TODO(), llamacloud.FileNewParams{
File: f,
Purpose: "split",
})
if err != nil {
log.Fatal(err)
}
fileID := uploadedFile.ID
fmt.Printf("✅ File uploaded: %s\n", fileID)
}import ai.llamaindex.llamacloud.client.LlamaCloudClient;
import ai.llamaindex.llamacloud.client.okhttp.LlamaCloudOkHttpClient;
import ai.llamaindex.llamacloud.models.files.FileCreateParams;
import ai.llamaindex.llamacloud.models.files.FileCreateResponse;
import java.nio.file.Paths;
LlamaCloudClient client = LlamaCloudOkHttpClient.fromEnv();
FileCreateResponse uploadedFile = client.files().create(
FileCreateParams.builder()
.file(Paths.get("./data/turing+imagenet+attention.pdf"))
.purpose("split")
.build()
);
String fileId = uploadedFile.id();
System.out.println("✅ File uploaded: " + uploadedFile.id());终端窗口
FILE_ID=$(llp files create \
--file ./data/turing+imagenet+attention.pdf \
--purpose split | jq -r '.id')
echo "✅ File uploaded: $FILE_ID"第 2 步:创建 Split 任务
用类别定义创建一个 split 任务。每个类别需要一个 name 与一段 description,用来帮 AI 理解哪些内容属于这个类别:
- Python
- TypeScript
- Go
- Java
- CLI
job = client.beta.split.create(
document_input={
"type": "file_id",
"value": file_id,
},
configuration={
"categories": [
{
"name": "essay",
"description": "A philosophical or reflective piece of writing that presents personal viewpoints, arguments, or thoughts on a topic without strict formal structure",
},
{
"name": "research_paper",
"description": "A formal academic document presenting original research, methodology, experiments, results, and conclusions with citations and references",
},
],
},
)
print(f"✅ Split job created: {job.id}")
print(f" Status: {job.status}")const job = await client.beta.split.create({
document_input: {
type: "file_id",
value: fileId,
},
configuration: {
categories: [
{
name: "essay",
description:
"A philosophical or reflective piece of writing that presents personal viewpoints, arguments, or thoughts on a topic without strict formal structure",
},
{
name: "research_paper",
description:
"A formal academic document presenting original research, methodology, experiments, results, and conclusions with citations and references",
},
],
},
});
console.log(`✅ Split job created: ${job.id}`);
console.log(` Status: ${job.status}`);job, err := client.Beta.Split.New(context.TODO(), llamacloud.BetaSplitNewParams{
DocumentInput: llamacloud.SplitDocumentInputParam{
Type: "file_id",
Value: fileID,
},
Configuration: llamacloud.BetaSplitNewParamsConfiguration{
Categories: []llamacloud.SplitCategoryParam{
{
Name: "essay",
Description: llamacloud.String("A philosophical or reflective piece of writing that presents personal viewpoints, arguments, or thoughts on a topic without strict formal structure"),
},
{
Name: "research_paper",
Description: llamacloud.String("A formal academic document presenting original research, methodology, experiments, results, and conclusions with citations and references"),
},
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("✅ Split job created: %s\n", job.ID)
fmt.Printf(" Status: %s\n", job.Status)import ai.llamaindex.llamacloud.models.beta.split.SplitCategory;
import ai.llamaindex.llamacloud.models.beta.split.SplitCreateParams;
import ai.llamaindex.llamacloud.models.beta.split.SplitCreateResponse;
import ai.llamaindex.llamacloud.models.beta.split.SplitDocumentInput;
SplitCreateResponse job = client.beta().split().create(
SplitCreateParams.builder()
.documentInput(
SplitDocumentInput.builder()
.type("file_id")
.value(fileId)
.build())
.configuration(
SplitCreateParams.Configuration.builder()
.addCategory(
SplitCategory.builder()
.name("essay")
.description("A philosophical or reflective piece of writing that presents personal viewpoints, arguments, or thoughts on a topic without strict formal structure")
.build())
.addCategory(
SplitCategory.builder()
.name("research_paper")
.description("A formal academic document presenting original research, methodology, experiments, results, and conclusions with citations and references")
.build())
.build())
.build()
);
System.out.println("✅ Split job created: " + job.id());
System.out.println(" Status: " + job.status());终端窗口
JOB_ID=$(llp beta:split create \
--document-input "{type: file_id, value: $FILE_ID}" \
--configuration '{categories: [{name: essay, description: "A philosophical or reflective piece of writing that presents personal viewpoints, arguments, or thoughts on a topic without strict formal structure"}, {name: research_paper, description: "A formal academic document presenting original research, methodology, experiments, results, and conclusions with citations and references"}]}' | jq -r '.id')
echo "✅ Split job created: $JOB_ID"第 3 步:轮询直到完成
split 任务是异步运行的。轮询直到它完成:
- Python
- TypeScript
- Go
- Java
- CLI
completed_job = client.beta.split.wait_for_completion(job.id, polling_interval=2.0)const completedJob = await client.beta.split.waitForCompletion(job.id, { pollingInterval: 2.0 });Go SDK 没有等待辅助函数,因此要轮询 Get 直到任务进入终态:
completedJob, err := client.Beta.Split.Get(context.TODO(), job.ID, llamacloud.BetaSplitGetParams{})
if err != nil {
log.Fatal(err)
}
for completedJob.Status != "completed" &&
completedJob.Status != "failed" &&
completedJob.Status != "cancelled" {
time.Sleep(2 * time.Second)
completedJob, err = client.Beta.Split.Get(context.TODO(), job.ID, llamacloud.BetaSplitGetParams{})
if err != nil {
log.Fatal(err)
}
}Java SDK 没有等待辅助函数,因此要轮询 get 直到任务进入终态:
import ai.llamaindex.llamacloud.models.beta.split.SplitGetResponse;
SplitGetResponse completedJob = client.beta().split().get(job.id());
while (!completedJob.status().equals("completed")
&& !completedJob.status().equals("failed")
&& !completedJob.status().equals("cancelled")) {
Thread.sleep(2000);
completedJob = client.beta().split().get(job.id());
}CLI 没有等待辅助函数,因此要轮询 get 直到任务进入终态:
终端窗口
while true; do
STATUS=$(llp beta:split get "$JOB_ID" | jq -er '.status') || break
case "$STATUS" in
completed|failed|cancelled) break ;;
esac
sleep 2
done第 4 步:分析结果
查看切分结果:
- Python
- TypeScript
- Go
- Java
- CLI
segments = completed_job.result.segments if completed_job.result else []
print(f"📊 Total segments found: {len(segments)}")
for i, segment in enumerate(segments, 1):
category = segment.category
pages = segment.pages
confidence = segment.confidence_category
if len(pages) == 1:
page_range = f"Page {pages[0]}"
else:
page_range = f"Pages {min(pages)}-{max(pages)}"
print(f"\nSegment {i}:")
print(f" Category: {category}")
print(f" {page_range} ({len(pages)} pages)")
print(f" Confidence: {confidence}")const segments = completedJob.result?.segments || [];
console.log(`📊 Total segments found: ${segments.length}`);
segments.forEach((segment, index) => {
const category = segment.category;
const pages = segment.pages;
const confidence = segment.confidence_category;
const pageRange =
pages.length === 1
? `Page ${pages[0]}`
: `Pages ${Math.min(...pages)}-${Math.max(...pages)}`;
console.log(`\nSegment ${index + 1}:`);
console.log(` Category: ${category}`);
console.log(` ${pageRange} (${pages.length} pages)`);
console.log(` Confidence: ${confidence}`);
});segments := completedJob.Result.Segments
fmt.Printf("📊 Total segments found: %d\n", len(segments))
for i, segment := range segments {
pages := segment.Pages
var pageRange string
if len(pages) == 1 {
pageRange = fmt.Sprintf("Page %d", pages[0])
} else if len(pages) > 0 {
pageRange = fmt.Sprintf("Pages %d-%d", pages[0], pages[len(pages)-1])
}
fmt.Printf("\nSegment %d:\n", i+1)
fmt.Printf(" Category: %s\n", segment.Category)
fmt.Printf(" %s (%d pages)\n", pageRange, len(pages))
fmt.Printf(" Confidence: %s\n", segment.ConfidenceCategory)
}import ai.llamaindex.llamacloud.models.beta.split.SplitResultResponse;
import ai.llamaindex.llamacloud.models.beta.split.SplitSegmentResponse;
import java.util.Collections;
import java.util.List;
List<SplitSegmentResponse> segments = completedJob.result()
.map(SplitResultResponse::segments)
.orElse(Collections.emptyList());
System.out.println("📊 Total segments found: " + segments.size());
for (int i = 0; i < segments.size(); i++) {
SplitSegmentResponse segment = segments.get(i);
List<Long> pages = segment.pages();
String pageRange = pages.size() == 1
? "Page " + pages.get(0)
: !pages.isEmpty()
? "Pages " + pages.get(0) + "-" + pages.get(pages.size() - 1)
: "";
System.out.println("\nSegment " + (i + 1) + ":");
System.out.println(" Category: " + segment.category());
System.out.println(" " + pageRange + " (" + pages.size() + " pages)");
System.out.println(" Confidence: " + segment.confidenceCategory());
}终端窗口
llp beta:split get "$JOB_ID" \
| jq -r 'if .status == "completed" then .result.segments[] | "Category: \(.category) | Pages: \(.pages | join(",")) | Confidence: \(.confidence_category)"
else "Split job failed: \(.error_message // .)" end'预期输出
📊 Total segments found: 3
Segment 1:
Category: essay
Pages 1-4 (4 pages)
Confidence: high
Segment 2:
Category: research_paper
Pages 5-13 (9 pages)
Confidence: high
Segment 3:
Category: research_paper
Pages 14-24 (11 pages)
Confidence: highSplit API 正确识别出了:
- 1 个 essay 段:Alan Turing 的 “Intelligent Machinery, A Heretical Theory”
- 2 个 research paper 段:ImageNet 论文与 “Attention is All You Need”
控制未分类页
默认情况下,没有命中任何已定义类别的页会被归为 uncategorized。你可以用 allow_uncategorized 控制:
"include"(默认)—— 未命中任何类别的页会被归为uncategorized并包含在结果里"forbid"—— 所有页都必须被分到已定义的类别中"omit"—— 未命中任何类别的页不会出现在结果里
- Python
- TypeScript
- Go
- Java
- CLI
# Exclude uncategorized pages from results
job = client.beta.split.create(
...,
configuration={"categories": [...], "splitting_strategy": {"allow_uncategorized": "omit"}},
)
# Force all pages into defined categories
job = client.beta.split.create(
...,
configuration={"categories": [...], "splitting_strategy": {"allow_uncategorized": "forbid"}},
)// Exclude uncategorized pages from results
const job = await client.beta.split.create({
...,
configuration: { categories: [...], splitting_strategy: { allow_uncategorized: "omit" } },
});
// Force all pages into defined categories
const job = await client.beta.split.create({
...,
configuration: { categories: [...], splitting_strategy: { allow_uncategorized: "forbid" } },
});// Exclude uncategorized pages from results
omitJob, err := client.Beta.Split.New(context.TODO(), llamacloud.BetaSplitNewParams{
// ...
Configuration: llamacloud.BetaSplitNewParamsConfiguration{
// ...
SplittingStrategy: llamacloud.BetaSplitNewParamsConfigurationSplittingStrategy{
AllowUncategorized: "omit",
},
},
})
// Force all pages into defined categories
forbidJob, err := client.Beta.Split.New(context.TODO(), llamacloud.BetaSplitNewParams{
// ...
Configuration: llamacloud.BetaSplitNewParamsConfiguration{
// ...
SplittingStrategy: llamacloud.BetaSplitNewParamsConfigurationSplittingStrategy{
AllowUncategorized: "forbid",
},
},
})// Exclude uncategorized pages from results
SplitCreateResponse omitJob = client.beta().split().create(
SplitCreateParams.builder()
// ...
.configuration(
SplitCreateParams.Configuration.builder()
// ...
.splittingStrategy(
SplitCreateParams.Configuration.SplittingStrategy.builder()
.allowUncategorized(
SplitCreateParams.Configuration.SplittingStrategy.AllowUncategorized.OMIT)
.build())
.build())
.build()
);
// Force all pages into defined categories
SplitCreateResponse forbidJob = client.beta().split().create(
SplitCreateParams.builder()
// ...
.configuration(
SplitCreateParams.Configuration.builder()
// ...
.splittingStrategy(
SplitCreateParams.Configuration.SplittingStrategy.builder()
.allowUncategorized(
SplitCreateParams.Configuration.SplittingStrategy.AllowUncategorized.FORBID)
.build())
.build())
.build()
);终端窗口
# Exclude uncategorized pages from results
llp beta:split create \
--document-input "{type: file_id, value: $FILE_ID}" \
--configuration '{categories: [...], splitting_strategy: {allow_uncategorized: omit}}'
# Force all pages into defined categories
llp beta:split create \
--document-input "{type: file_id, value: $FILE_ID}" \
--configuration '{categories: [...], splitting_strategy: {allow_uncategorized: forbid}}'下一步
- 查阅 REST API 参考 了解全部可用选项
- 把 Split 与 LlamaExtract 组合,对每个段做定向抽取
- 用 LlamaParse 以优化过的设置解析单个段