跳到主要内容

适配器生态与 Rust 引擎

本章讲两件让 Chonkie「轻而广」的事:(1) 所有可换后端(tokenizer、嵌入、LLM、向量库)都走同一套适配器模式,按需懒加载;(2) 性能热点统一下沉到 Rustchonkie-core

1. AutoTokenizer:一个 __new__ 决定后端

Chonkie 支持字符/词/字节内置 token,也支持 HF tokenizers、OpenAI tiktoken、HF transformers,还能直接传函数当 token 计数器。怎么用一个类名 AutoTokenizer("...") 统一这些?

答案在 AutoTokenizer.__new__(src/chonkie/tokenizer.py:515-549)——工厂模式:__new__ 根据你传进来的东西返回不同的适配器子类实例:

# 真实源码 tokenizer.py:525-547(节选)
if isinstance(tokenizer, Tokenizer): return ChonkieAutoTokenizer(tokenizer)
if isinstance(tokenizer, str): return _create_auto_tokenizer_from_string(tokenizer)
# 看类型名里有没有 transformers / tokenizers / tiktoken …
for backend_name, adapter_class in supported_backends:
if backend_name in str(type(tokenizer)): return adapter_class(tokenizer)
if callable(tokenizer): return CallableAutoTokenizer(tokenizer)

每个适配器(ChonkieAutoTokenizerTiktokenAutoTokenizer…)都暴露同一组方法 encode/decode/count_tokens/encode_batch/...(tokenizer.py:557-585),内部转调各自后端。上层切块器只认这套统一接口,完全不知道背后是 tiktoken 还是字符计数。

巧妙处:能力探测式降级

count_tokens / count_tokens_batch 会先看后端有没有原生批量计数,没有才退回逐条 encode(tokenizer.py:565-585):

# 真实源码 tokenizer.py:580-585
def count_tokens_batch(self, texts):
if hasattr(self.tokenizer, "count_tokens_batch"):
return self.tokenizer.count_tokens_batch(texts) # 后端原生,快
return [self.count_tokens(text) for text in texts] # 没有就逐条

2. 同一个模式,复制到四类后端

这套「统一抽象基类 + AutoXxx 自动选后端 + 懒加载可选依赖」的模式,在 Chonkie 里重复了四遍:

抽象基类自动加载入口后端举例
TokenizerTokenizer / TokenizerProtocolAutoTokenizercharacter、tiktoken、HF
EmbeddingsBaseEmbeddings(embeddings/base.py:10)AutoEmbeddingsOpenAI、Cohere、Jina、catsu…
LLM(Genie)BaseGenie(genie/base.py:8)Gemini、OpenAI、Groq、Cerebras
向量库(Handshake)BaseHandshake(handshakes/base.py:19)Chroma、Qdrant、pgvector…

它们的共性:

  • 统一接口:嵌入都实现 embed/embed_batch/similarity(embeddings/base.py:32-87);LLM 都实现 generate/generate_json(genie/base.py:43-75);向量库都实现 write(handshakes/base.py:51)。
  • 可选依赖懒加载:每个后端的第三方库(openaichromadb…)只在真正用到时import,装不装由 pip install chonkie[xxx] 决定。这是 Chonkie 核心包能压到 ~50MB 的关键。

巧妙处:Handshake 的元数据扁平化

不同向量库对 metadata 的容忍度不同——有的只收原始类型(str/int/float/bool)。BaseHandshake 备了通用工具:_coerce_flat_metadata 把复杂值 JSON 序列化成字符串(handshakes/base.py:33-44),_generate_iduuid5 从文本算确定性 ID(同文本→同 ID,天然去重,handshakes/base.py:46-49)。

3. Refinery:切完之后的后处理

Refinery 在切块和入库之间做增强。两个内置:

OverlapRefinery —— 给块加重叠

相邻块完全不重叠时,跨边界的句子检索容易丢上下文。OverlapRefinery(refinery/overlap.py:16)给每块补一段邻块的「上下文」。它支持 token 或递归两种粒度、suffix/prefix/justified 三种方位(overlap.py:24-71),并对 tokenize 结果套 lru_cache(maxsize=8192) 复用(overlap.py:73-78)。

context_size 既可是整数(绝对 token 数),也可是 0~1 浮点(相对比例),构造时校验区间(overlap.py:50-53)。

EmbeddingsRefinery —— 顺手算嵌入

把嵌入计算挪到 refinery 阶段,这样 Pipeline 末尾 .refine_with("embeddings", ...) 就能给每块填上 Chunk.embedding,直接供下游 handshake 入库。

4. chonkie-core:那层 Rust 引擎

chonkie-core(pyproject.toml:52 的依赖 chonkie-core>=0.10.2)是一个独立的 Rust 扩展包,源码不在本 clone 内。Python 侧在多处 import chonkie_core 调它。从调用点能看出它承担的全是性能热点:

Rust 函数干什么调用点
split_offsets简单分隔符切,返回字节偏移chunker/base.py:56
split_pattern_offsets复杂/多字符分隔符切chunker/base.py:48
merge_splits把短碎片贪心合并到 chunk_sizechunker/recursive.py:187
find_local_minima_interpolatedSavitzky-Golay 平滑 + 找极小值chunker/semantic.py:244
filter_split_indices按阈值/最小句数过滤切点chunker/semantic.py:256
windowed_cross_similarity窗口交叉相似度chunker/semantic.py:306

设计哲学一句话:Python 负责「编排和决策」,Rust 负责「逐字节/逐数值的重活」。 字符串切分、装箱合并、数值滤波这些 O(n) 紧循环全在 Rust;Python 只做高层流程控制。这既保住了 Python 的易用与可扩展(装饰器、签名反射),又拿到了接近原生的吞吐(README 自称 token 切块比最慢竞品快 33×)。

所有 Rust 函数的具体实现本 clone 看不到(它们的行为基于调用签名与上下文推断,标 inferred)。要核实细节需另读 chonkie-core 仓库。

5. 边界与局限

  • 不做检索/重排/生成:Chonkie 只覆盖 RAG 的「ingestion(摄入)」半边——切块到入库。检索那半边交给向量库和你自己的编排。
  • 依赖 Rust 扩展:核心切块路径硬依赖 chonkie_core;纯 Python 环境跑不起来(它不是可选依赖)。
  • 广度靠可选依赖:32+ 集成大多要 pip install chonkie[xxx],不装则对应后端不可用——这是「轻」的代价,换来按需付费的安装体积。

6. 代码地图

主题文件符号
tokenizer 工厂src/chonkie/tokenizer.pyAutoTokenizerAutoTokenizer.__new__
tokenizer 协议src/chonkie/tokenizer.pyTokenizerProtocolTokenizer
嵌入抽象src/chonkie/embeddings/base.pyBaseEmbeddings
LLM 抽象src/chonkie/genie/base.pyBaseGenie
向量库抽象 + 元数据工具src/chonkie/handshakes/base.pyBaseHandshake_coerce_flat_metadata_generate_id
重叠精炼src/chonkie/refinery/overlap.pyOverlapRefinery
各向量库实现src/chonkie/handshakes/ChromaHandshakeQdrantHandshake
各嵌入实现src/chonkie/embeddings/OpenAIEmbeddingsJinaEmbeddings
Rust 引擎(外部)pyproject.toml依赖 chonkie-core