- 后端
- AI 应用
- NLP
【免费下载链接】xberg
Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with CLI, REST API, and MCP server.
本指南以 xberg 仓库中的chunking-embeddings技能文档为核心骨架,系统讲解 xberg 的文本分块(Chunking)与向量嵌入(Embedding)两大能力:如何通过ExtractionConfig.chunking驱动分块、ChunkerType四类分块策略的差异、预设(Preset)如何同时决定分块大小与嵌入模型,以及embeddings/static-embeddings/embedding-presets三个特性(feature)在构建与运行时的行为边界。读完本文,你将掌握在配置文件与 Rust 代码中正确配置分块与嵌入、规避 serde 字段名与特性门控的常见陷阱、并把分块结果接入稠密/稀疏检索管线的完整实战方案。
一、概览:分块与嵌入在 xberg 中的位置
xberg 的分块与嵌入实现分别位于 crates/xberg/src/chunking/(27 个 Rust 文件)与 crates/xberg/src/embeddings/(含 ONNX 推理引擎engine与纯 Rust 的静态static_engine)。这两个目录是完整模块,而非单文件。
分块由ExtractionConfig.chunking: Option<ChunkingConfig>驱动,即分块总是发生在文本提取(Extraction)之后、向量化之前;嵌入向量按chunk附加,而不是按 document 附加(见下文"关键规则")。
二、分块入口:两个公开 API 与数据结构
2.1 两个独立入口
- 通用入口:
chunking::chunk_text(text, &ChunkingConfig, page_boundaries) -> Result<ChunkingResult>,定义于 chunking/core.rs。它是分块的主要公开 API,支持纯文本与 Markdown,可传可选的分页边界(page_boundaries)用于把块映射到页号。函数内部首先调用config.resolve_preset()解析预设,再进入chunk_text_with_heading_source;空文本直接返回空结果,非空文本会先通过validate_utf8_boundaries校验 UTF-8 边界。 - RAG 专用入口:
chunking::rag::chunk_for_rag(text, &ChunkingConfig),定义于 chunking/rag.rs。它是一个轻量组合器:委托chunk_text完成切分,但把默认的ChunkerType::Text自动升级为Markdown(除非调用方已显式指定其他 chunker),从而让分块感知标题层级,并随后为每个 chunk 填充heading_path面包屑(breadcrumb)。
2.2 返回结构
ChunkingResult定义于 chunking/config.rs:
pub struct ChunkingResult { pub chunks: Vec<crate::types::Chunk>, pub chunk_count: usize, }单个Chunk定义于 types/extraction.rs,携带:
content:块文本内容;chunk_type:由启发式分类器(chunking/classifier.rs)基于内容模式与标题上下文给出的语义结构分类,默认Unknown;metadata:ChunkMetadata(位置、页范围、标题路径等,见下);- 三个可选向量字段:
embedding(稠密向量)、sparse_embedding(SPLADE 稀疏向量)、late_interaction(ColBERT 式多向量),三者仅在对应配置与特性开启时填充,且 serde 序列化时均skip_serializing_if = "Option::is_none"。
ChunkMetadata定义于 types/extraction.rs,关键字段:
| 字段 | 含义 |
|---|---|
byte_start/byte_end | 块在原文中的字节区间(UTF-8 合法边界) |
token_count | 块内 token 数(启用嵌入时由模型 tokenizer 计算) |
chunk_index/total_chunks | 块序号与总数 |
first_page/last_page | 块跨越的页号(启用页追踪时填充,1 起始) |
heading_context | 使用 Markdown chunker 时的嵌套标题层级 |
heading_path | 扁平化的标题路径(根到本块的标题文本序列),RAG 友好的面包屑 |
image_indices | 本块覆盖页面上图片在顶层images集合中的索引 |
三、ChunkerType:只有这四种策略
ChunkerType是唯一的切分策略枚举,定义于 core/config/processing.rs,serde 使用 lowercase 命名:
| 变体 | 行为 |
|---|---|
Text(默认) | 通用切分器,按空白与标点切分,不感知结构 |
Markdown | 感知 Markdown 结构,保留标题与代码块边界 |
Yaml | 按 YAML 顶层键切分,每个顶层键生成一个块 |
Semantic | 主题感知切分,见下文专节 |
分块调度逻辑在 chunking/core.rs:Yaml转入yaml_section::chunk_yaml_by_sections,Semantic转入semantic::chunk_semantic,其余走text_splitter(含TextSplitter与MarkdownSplitter)。
3.1 Semantic 切分:嵌入感知与结构回退
Semantic是唯一的"理解内容"的切分器,实现在 chunking/semantic/mod.rs。其流程分两阶段:
- 先将文本按固定
SEGMENT_SIZE = 200字符切成细粒度片段(有 Markdown 标题时用MarkdownSplitter,否则TextSplitter); - 检测主题边界并合并成块。有
EmbeddingConfig时,使用嵌入向量间的余弦相似度检测主题迁移,阈值由topic_threshold控制(默认0.75,取值范围0.0..=1.0,值越低切出的块越多越小);没有嵌入配置时,回退到纯结构启发式(detect_plain_text_boundaries:全大写标题、编号章节、空行段落),并把片段按max_characters(默认 1000)合并成组——此时topic_threshold完全不生效(源码在warn_if_fallback_path中会给出警告)。因此要发挥 Semantic 切分的最佳效果,务必配对嵌入模型。
四、ChunkingConfig:字段、serde 线上名称与默认值
ChunkingConfig定义于 core/config/processing.rs,其核心字段与配置文件(wire)名称对照如下(原技能文档的字段表,经过源码核实):
| 字段 | 配置文件中的 wire 名称 | 默认值 |
|---|---|---|
max_characters | max_chars(别名max_characters) | 1000 |
overlap | max_overlap(别名overlap) | 200 |
trim | trim | true |
chunker_type | chunker_type | Text |
preset | preset | 无 |
重命名是承重(load-bearing)的:配置文件里写max_characters只有通过 alias 才能生效;而拼写错误的键会被deny_unknown_fields之外的 serde 规则静默忽略(详见 config-loading-precedence)。此外还有几个源码新增、原文档未详列的字段:
embedding: Option<EmbeddingConfig>:为每个 chunk 生成稠密向量;sparse_embedding: Option<SparseEmbeddingConfig>:SPLADE 稀疏向量,需sparse-embeddings特性(配置文件中才有此键,无 CLI flag 与环境变量);late_interaction: Option<LateInteractionConfig>:ColBERT 多向量,需late-interaction特性(同上);sizing: ChunkSizing:块大小计量方式,默认Characters(Unicode 字符数);开启chunking-tokenizers特性后可选用Tokenizer { model, cache_dir },其中model可以是 HuggingFace tokenizer 模型 ID(如Xenova/gpt-4o、bert-base-uncased)或通过register_tokenizer_backend注册的后端名(注册名优先);topic_threshold: Option<f32>:见 Semantic 一节;table_chunking: TableChunkingMode:仅对Markdownchunker 生效,默认Split(超限表格按行切分,续块无表头);可选RepeatHeader(每个续块重复预置表头与分隔行,保证块自包含,利于提取/搜索/LLM 消费)。
一个取自仓库契约 fixture 的真实配置示例(fixtures/contract/chunking_config_and_output.json):
{ "chunking": { "max_characters": 300, "overlap": 40, "trim": true, "chunker_type": "text" } }注意此处用的是 wire 名称chunker_type: "text"(lowercase serde 名)而非 Rust 枚举名。
五、预设(Preset):同时决定块大小与嵌入模型
ChunkingConfig.preset通过resolve_preset()解析,实现在 core/config/processing.rs。它被#[cfg(feature = "embeddings")]门控:没有embeddings特性时该函数被编译为 no-op,preset 名字什么都不做(仅记录一条Chunking presets require the 'embeddings' feature警告,见 processing.rs)。有特性时,预设会覆盖max_characters与overlap,并且若调用方没有显式提供EmbeddingConfig,会顺带选定嵌入模型。
预设真值来源是 embeddings/mod.rs 中的EMBEDDING_PRESETS静态表(EmbeddingPreset结构还携带pooling、model_file、description、backend、additional_files、query_prefix等元数据)。完整预设表:
| 预设 | chunk_size | overlap | 维度 | 后端 |
|---|---|---|---|---|
fast | 512 | 50 | 384 | ONNX |
balanced | 1024 | 100 | 768 | ONNX |
quality | 2000 | 200 | 1024 | ONNX |
multilingual | 1024 | 100 | 768 | ONNX |
gte-modernbert-base | 1024 | 100 | 768 | ONNX |
lightweight | 512 | 50 | 256 | static(model2vec) |
arctic-embed-m-v2.0 | 1024 | 100 | 768 | ONNX |
qwen3-embedding-0.6b | 2000 | 200 | 1024 | ONNX |
源码补充了每个预设的底层细节(均在EMBEDDING_PRESETS内):
- 所有 ONNX 预设的模型仓库均为
xberg-io/embedding-models,并固定在EMBEDDING_MODEL_REVISION提交(4b127809f88a5aa1569d1238032b5ff40e5879bc),下载时按EMBEDDING_SHA256_MANIFEST做 SHA-256 校验; fast实为all-MiniLM-L6-v2的量化版(~22M 参数,mean 池化),适合快速原型与资源受限环境;balanced实为bge-base-en-v1.5(~109M 参数,cls 池化),面向通用 RAG 与生产部署;quality实为bge-large-en-v1.5(~335M 参数,cls 池化);multilingual实为multilingual-e5-base(100+ 语言,mean 池化);gte-modernbert-base为 2026 代 GTE ModernBERT base,8192 长上下文,cls 池化;lightweight实为potion-base-8m(~7.5M 参数),走纯 Rust 的 model2vec 静态引擎(EmbeddingBackend::Static),是 WASM/Android 等no-ort-target上唯一可用的稠密嵌入;arctic-embed-m-v2.0是 Snowflake Arctic-Embed-M v2.0,非对称检索模型:查询侧需预置"query: "前缀(query_prefix),文档侧不做前缀;其权重大文件存储在外部model.onnx.data(additional_files);qwen3-embedding-0.6b是 decoder 式 last-token 池化的多语模型,32k 上下文,同样带外部数据文件。
若 preset 名未识别,resolve_preset()会记录Unknown chunking preset ...警告并原样返回配置,不会报错中断。
六、Embeddings:模型选择与"两个默认值不一致"的坑
6.1 不要写那些不存在的 API
原技能文档明确告诫:xberg没有TextEmbeddingManager、没有embed_chunks()、没有ChunkWithEmbedding、没有RagDocument,也没有 fastembed 依赖。不要针对这些名字编写代码。
6.2 EmbeddingModelType:四种模型来源
模型选择由EmbeddingModelType(tagged enum,core/config/processing.rs)承载:
Preset { name }(推荐):直接引用上文预设表,例如"balanced";Custom { model_id, dimensions }:任意 HuggingFace ONNX 仓库(如BAAI/bge-small-en-v1.5),模型文件固定为model.onnx,mean 池化;Llm { llm: Box<LlmConfig> }:由 liter-llm 走 HTTP 提供商的托管嵌入(如openai/text-embedding-3-small),无本地模型下载;Plugin { name }:进程内注册的嵌入后端(通过register_embedding_backend),宿主语言负责模型生命周期,无下载、无 ONNX Runtime 依赖;此模式下仅normalize与max_embed_duration_secs生效,batch_size/cache_dir/show_download_progress/acceleration均被忽略,且 Semantic 切分在该模式下回退到max_characters作上限。
6.3 两个默认值不一致,且两者都真实生效
这是最容易踩的坑之一:
EmbeddingModelType::default()返回gte-modernbert-base预设——语言绑定(bindings)与#[serde(default)]拿到的就是这个;EmbeddingConfig::default()通过default_balanced_embedding_model()返回balanced预设(processing.rs)。
两者在 processing.rs 中相邻定义却指向不同模型。因此在写代码前,务必确认自己实际走的是哪个构造函数,再判断最终运行的是哪个模型。
6.4 EmbeddingConfig 默认值
EmbeddingConfig(processing.rs)的默认值如下:
| 字段 | 默认值 |
|---|---|
normalize | true(L2 归一化,为余弦相似度准备) |
batch_size | 32 |
max_embed_duration_secs | Some(60)(插件路径的调度超时,防宿主后端挂死) |
max_sequence_length | None(回退到 512,且最终被模型自身model_max_length封顶;可设为长上下文模型值如 8192 让长块完整嵌入) |
另有两个源码级扩展字段:show_download_progress(默认 false,开启后模型/tokenizer/config 下载进度以info级日志输出到xberg::model_download目标)与acceleration(可选AccelerationConfig,控制 CPU/CUDA/CoreML/TensorRT 执行提供者)。
6.5 推理引擎与缓存
ONNX 路径由 embeddings/engine.rs 提供:get_or_init_engine按"仓库 + 模型文件 + 附加文件 + 修订 + 池化 + 最大序列长度 + 缓存根 + 加速配置"构造缓存键(EmbeddingEngineCacheKey),模型文件与 tokenizer 首次下载后经ENGINE_CACHE复用(embeddings/mod.rs)。全局信号量限制并发 ONNX 推理调用,防止大量异步调用耗尽资源;Llm与Plugin变体在到达信号量前短路,不占用本地推理资源池。
七、特性门控与构建注意事项
7.1 三个关键特性
embeddings特性的依赖组合(源码 embeddings/mod.rs 顶部注释与 Cargo 配置一致):
embeddings = ["onnx-runtime", "dep:ndarray", "chunking", "tokio-runtime", "embedding-presets"]ort-bundled(默认 ORT 链接方式)会在构建时自动下载 ONNX Runtime——无需系统安装,也不需要ORT_DYLIB_PATH。ORT_DYLIB_PATH仅在ort-dynamic链接方式下有意义(动态加载系统 ORT 库)。static-embeddings:纯 Rust 的 model2vec 路径(embeddings/static_engine.rs),不依赖任何原生 ONNX 库,是no-ort-target(WASM、Android x86_64 模拟器)上唯一的稠密嵌入后端;lightweight预设走的就是它。embedding-presets:只携带预设元数据(名称/尺寸/描述等),WASM 安全。
7.2 行为边界:降级而非失败
没有 ORT 的构建(如 WASM 目标、未开static-embeddings)应当跳过嵌入而非报错:相关嵌入字段保持None,分块照常进行。这要求特性组合可预测——preset在无embeddings特性时是惰性的,语义切分在无嵌入时回退结构启发式,都属于同一设计原则。
八、heading_path 与三种检索臂的正确用法
chunk_for_rag始终填充heading_path,但分块器绝不把面包屑预置进chunk.content:content永远是源文档[byte_start, byte_end)的精确字节区间(见 rag.rs 的 "Breadcrumb placement" 一节)。面包屑的渲染是消费方在索引时的决定,三种检索消费者需要三种不同的视图:
- 稠密/嵌入检索:把
render_heading_breadcrumb渲染出的"# Guide > ## Setup\n\n"前缀拼进content再嵌入,让段落自包含结构上下文,嵌入质量更高; - 词法检索(BM25/TF-IDF):面包屑有害——同一小节的所有块会重复相同的标题 token,标题词的文档频率趋近块数,IDF 塌缩到零。直接索引
chunk.content原样即可(或把heading_path作为单独的低权重字段),无需剥离(因为从未预置); - 稀疏学习检索(SPLADE):比 BM25 更糟。SPLADE 的 term 权重来自通用语料训练的编码器,看不到本集合统计,无法自我纠偏;且 term 展开会让标题词的整个学习邻域(如
Authentication→auth、login、credential、oauth…)注入该节每个块,整片语义区域判别力退化,重索引也无法修复(展开是预训练编码器的属性)。永远不要喂面包屑给它。
因此:BM25 与 SPLADE 无需任何特殊处理(按返回的 chunk 直接索引),只有稠密臂需要显式多一步render_heading_breadcrumb。
九、关键规则(来自技能文档,附源码印证)
- 先分块,再嵌入——向量按 chunk 附加(
Chunk.embedding),而非按 document;单个文档的多个 chunk 各自携带向量。 - 无
embeddings特性的 preset 是惰性的——resolve_preset()被编译掉(processing.rs)。 - 配置文件里写 serde wire 名称——
max_chars/max_overlap(或它们的别名max_characters/overlap),而不是 Rust 字段名;拼错键会被静默忽略。 - 降级,不要失败——无 ORT 的构建应跳过嵌入(字段留
None),而不是报错。 - 为余弦相似度归一化——
normalize默认true,保持开启。
十、测试与契约佐证
仓库测试直接验证了上述行为,可作为实现事实的锚点:
- tests/config_behavioral.rs:
test_chunking_max_chars_limits_chunk_size用max_characters: 100, overlap: 20分块 500 词文本,断言每个 chunk 长度<= 100 + 20; - 同文件
test_chunking_overlap_creates_overlap(config_behavioral.rs)验证相邻块存在非空白重叠文本; - tests/config_loading_tests.rs 验证
max_chars等 wire 名称从 TOML/JSON 配置正确加载; - tests/contract_mcp.rs 验证 MCP 请求中
max_chars: 500正确映射到max_characters; - fixtures/contract/chunking_config_and_output.json 给出端到端契约:URI 输入 + chunking 配置 →
results[0].chunks至少 2 块且首个块内容长度 ≥ 9; - crates/xberg/tests/chunking_tokenizer_plugin.rs 覆盖
Text/Markdown两种 chunker 与 tokenizer 插件路径。
十一、Rust 实战示例
将分块与嵌入接入提取管线的完整示例(来自 embeddings/mod.rs 的文档示例):
use xberg::{extract, ChunkingConfig, EmbeddingConfig, ExtractInput, ExtractionConfig}; let config = ExtractionConfig { chunking: Some(ChunkingConfig { preset: Some("balanced".to_string()), embedding: Some(EmbeddingConfig::default()), ..Default::default() }), ..Default::default() }; let output = extract(ExtractInput::from_uri("document.pdf"), &config).await?; let result = output.results.into_iter().next().expect("one input yields one result"); for chunk in result.chunks.unwrap() { if let Some(embedding) = chunk.embedding { println!("Chunk has {} dimension embedding", embedding.len()); } }注意:EmbeddingConfig::default()的 model 实际是balanced预设(见 6.3 节),与preset: "balanced"一致;若希望使用与EmbeddingModelType::default()相同的gte-modernbert-base,需显式指定model。独立分块(不经过提取管线)则直接调用:
use xberg::chunking::{chunk_for_rag, ChunkingConfig, ChunkerType}; let markdown = "# Introduction\n\nWelcome.\n\n## Details\n\nMore text here."; let config = ChunkingConfig { max_characters: 512, overlap: 50, chunker_type: ChunkerType::Markdown, ..Default::default() }; let result = chunk_for_rag(markdown, &config)?; for chunk in &result.chunks { println!("{:?} -> {:?}", chunk.metadata.heading_path, chunk.content); }十二、关联技能
分块与嵌入并不是孤岛,它与以下仓库技能文档紧密联动,遇到具体问题时建议对照阅读:
- extraction-pipeline-patterns——分块之前的文本提取管线模式;
- config-loading-precedence——
ChunkingConfig的解析顺序与拼写错误静默忽略的原因; - feature-flag-policy——
embeddings与static-embeddings、embedding-presets三者的边界与选择策略。
- 后端
- AI 应用
- NLP
【免费下载链接】xberg
Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with CLI, REST API, and MCP server.
相关推荐
embedding-strategies 实战指南:为 RAG 与语义搜索选型、分块与评估嵌入模型
embedding strategies 实战指南:为 RAG 与语义搜索选型、分块与评估嵌入模型 本指南以 llm application dev 插件的 e
AI 插件AI 技能开发工具claude-skills RAG Architect 实战:Embedding 模型选型、微调与生产级嵌入流水线指南
claude skills RAG Architect 实战:Embedding 模型选型、微调与生产级嵌入流水线指南 Embedding(嵌入向量)是 RAG
AI 技能AI 插件后端前端DevOpsLate Chunking 实战指南:先嵌入整篇文档再切块的上下文保持型 RAG 策略(all-rag-strategies 项目)
Late Chunking 实战指南:先嵌入整篇文档再切块的上下文保持型 RAG 策略(all rag strategies 项目) 导读 Late Chunk
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考