加载器与 Node

LLM 看不到「磁盘上的文件」,只看得到你送进上下文的文本。LlamaIndex 把摄入拆成:Load(读成 Document)→ Transform(切成 Node、抽元数据、Embedding)→ Index / Store。概念上对应本站 RAG 教程 的切块与元数据,只是类名不同。


Document 与 Node

对象是什么典型来源
Document一篇完整材料:正文 + metadata(路径、页码、来源)Reader / 手写 Document(text=...)
Node可检索的小块;Document 在类型上是 Node 的子类SentenceSplitterfrom_documents 内部切分

切块太碎:检索碎片化、答案丢上下文。切块太大:噪声进 Prompt、超窗口。先从 chunk_size=512、少量 overlap 试起,用真实问题看 source_nodes

手写文档:

from llama_index.core import Document

doc = Document(
    text="垦荒学园 LlamaIndex 课覆盖 RAG 与 FunctionAgent。",
    metadata={"filename": "intro.md", "lang": "zh"},
)

SimpleDirectoryReader

入门 Reader,能读目录里常见的 Markdown、PDF、Word、PPT、部分音视频。默认只扫顶层;子目录要 recursive=True

from llama_index.core import SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()

reader = SimpleDirectoryReader(
    input_dir="data",
    recursive=True,
    required_exts=[".md", ".txt", ".pdf"],
)
documents = reader.load_data()

也可 input_files=["./data/a.pdf"] 只喂指定文件。生产上往往改用 LlamaHub 上的专用 Reader(数据库、Notion、S3 等),而不是把所有源都塞进一个目录扫描。

from llama_index.readers.database import DatabaseReader

reader = DatabaseReader(
    scheme="postgresql",
    host="localhost",
    port=5432,
    user="postgres",
    password="...",
    dbname="app",
)
documents = reader.load_data(query="SELECT id, body FROM articles")

具体包名以 LlamaHub 为准(例如 llama-index-readers-database)。


切分成 Node

VectorStoreIndex.from_documents(documents) 会按全局 Settings(或传入的 transformations)切块。要控制粒度:

from llama_index.core import Settings, VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter

splitter = SentenceSplitter(chunk_size=512, chunk_overlap=50)
Settings.text_splitter = splitter

index = VectorStoreIndex.from_documents(
    documents,
    transformations=[splitter],
)

也可以声明式 IngestionPipeline(切分、抽标题、Embedding 串成流水线):

from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.openai import OpenAIEmbedding

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=512, chunk_overlap=50),
        OpenAIEmbedding(model="text-embedding-3-small"),
    ]
)
nodes = pipeline.run(documents=documents)

直接建 Node 再索引(跳过 Document 切分):

from llama_index.core.schema import TextNode
from llama_index.core import VectorStoreIndex

nodes = [
    TextNode(text="第一块", id_="n1", metadata={"source": "manual"}),
    TextNode(text="第二块", id_="n2"),
]
index = VectorStoreIndex(nodes)

元数据为什么重要

检索后常按部门、日期、文件类型过滤。摄入时就把字段写进 metadata,查询阶段用 metadata filter(见 检索)。自动抽取可用 TitleExtractor 等,但会额外消耗 LLM 调用,适合离线批处理,不适合每次用户提问都跑一遍。

实践要点:

  • 中文文档优先按句 / 段切,避免在字中间切开
  • PDF 扫描件需要 OCR 或 LlamaParse 一类解析,否则 Reader 只能得到乱码
  • 同一语料的 index 与 query 必须用同一 embedding 模型

下一步

评论