跳转到内容

7.2 与 LLM Framework 的集成

Chroma 是引擎,LLM Framework 是方向盘——两者结合才能开动 RAG 这辆车


这一节在讲什么?

在前面的章节中,我们一直是直接使用 Chroma 的 Python API 来构建 RAG 系统——手动切分文档、手动检索、手动组装 prompt、手动调用 LLM。这种方式灵活但繁琐,特别是当你需要添加查询改写、Re-ranking、对话记忆等高级功能时,代码量会急剧膨胀。LLM Framework(如 LangChain、LlamaIndex、Haystack)就是为了解决这个问题而生的——它们提供了预构建的 RAG 组件和编排能力,让你用更少的代码实现更完整的功能。这一节我们要讲清楚 Chroma 如何与三大主流 LLM Framework 集成,各自的优劣势,以及什么时候该用框架、什么时候该用原生 API。


LangChain + Chroma

LangChain 是目前最流行的 LLM 应用开发框架,它的 Chroma 集成是最成熟的。

基础用法:一行入库

python
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import SentenceTransformerEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 配置 embedding
embeddings = SentenceTransformerEmbeddings(
    model_name="paraphrase-multilingual-MiniLM-L12-v2"
)

# 方式 1:从文本直接创建向量库
vectorstore = Chroma.from_texts(
    texts=["退款政策:购买后7天内可无条件退款", "安装指南:下载安装包并运行"],
    embedding=embeddings,
    collection_name="langchain_demo",
    persist_directory="./langchain_chroma"
)

# 方式 2:从文档加载器创建
loader = TextLoader("./data/document.txt")
docs = loader.load()

splitter = RecursiveCharacterTextSplitter(
    chunk_size=600,
    chunk_overlap=80
)
chunks = splitter.split_documents(docs)

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    collection_name="langchain_docs",
    persist_directory="./langchain_chroma"
)

# 检索
results = vectorstore.similarity_search(
    query="如何退款",
    k=3
)
for doc in results:
    print(f"  {doc.page_content[:60]}... | {doc.metadata}")

与 RetrievalQA Chain 集成

LangChain 的 RetrievalQA Chain 把"检索 → 组装 prompt → LLM 生成"封装成一个调用:

python
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import SentenceTransformerEmbeddings
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI

# 初始化
embeddings = SentenceTransformerEmbeddings(
    model_name="paraphrase-multilingual-MiniLM-L12-v2"
)

vectorstore = Chroma(
    collection_name="langchain_demo",
    embedding_function=embeddings,
    persist_directory="./langchain_chroma"
)

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1)

# 创建 QA Chain
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",  # "stuff" = 把所有检索结果塞进一个 prompt
    retriever=vectorstore.as_retriever(
        search_type="similarity",
        search_kwargs={"k": 3}
    ),
    return_source_documents=True
)

# 一行完成 RAG
result = qa_chain.invoke({"query": "退款政策是什么?"})
print(f"回答: {result['result']}")
print(f"来源: {[doc.metadata for doc in result['source_documents']]}")

ConversationalRetrievalChain:带对话记忆的 RAG

python
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain

memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

conv_chain = ConversationalRetrievalChain.from_llm(
    llm=llm,
    retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
    memory=memory
)

# 多轮对话
result1 = conv_chain.invoke({"question": "退款政策是什么?"})
print(f"回答1: {result1['answer']}")

result2 = conv_chain.invoke({"question": "它有时间限制吗?"})  # "它"指代"退款政策"
print(f"回答2: {result2['answer']}")

LlamaIndex + Chroma

LlamaIndex 专注于"数据索引"这一层,它的 Chroma 集成更注重细粒度的节点管理和索引策略。

基础用法

python
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.vector_stores.chroma import ChromaVectorStore
import chromadb

# 初始化 Chroma
chroma_client = chromadb.Client(settings=chromadb.Settings(
    is_persistent=True,
    persist_directory="./llamaindex_chroma"
))
chroma_collection = chroma_client.get_or_create_collection("llamaindex_demo")

# 创建 LlamaIndex 的 VectorStore 包装
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)

# 加载文档
documents = SimpleDirectoryReader("./data").load_data()

# 创建索引(自动切分 + embedding + 入库)
index = VectorStoreIndex.from_documents(
    documents,
    storage_context=storage_context
)

# 查询
query_engine = index.as_query_engine(similarity_top_k=3)
response = query_engine.query("退款政策是什么?")
print(f"回答: {response}")
print(f"来源: {[n.metadata for n in response.source_nodes]}")

LlamaIndex 的优势:节点级别的细粒度控制

LlamaIndex 允许你对每个节点(chunk)做精细的配置——不同的切分策略、不同的 embedding 模型、不同的 metadata:

python
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.extractors import SummaryExtractor

# 自定义切分器
splitter = SentenceSplitter(
    chunk_size=600,
    chunk_overlap=80
)

# 自定义 metadata 提取器
extractors = [
    SummaryExtractor()  # 自动为每个 chunk 生成摘要
]

# 构建索引
nodes = splitter.get_nodes_from_documents(documents)
index = VectorStoreIndex(
    nodes,
    storage_context=storage_context
)

Haystack + Chroma

Haystack 是 deepset 开发的 RAG 框架,它的设计哲学是"管道(Pipeline)"——每个处理步骤是一个独立的组件,通过管道串联起来。

基础用法

python
from haystack import Pipeline
from haystack.components.converters import PyPDFToDocument
from haystack.components.writers import DocumentWriter
from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
from haystack.document_stores.types import DuplicatePolicy
from haystack_integrations.document_stores.chroma import ChromaDocumentStore
from haystack_integrations.components.retrievers.chroma import ChromaEmbeddingRetriever

# 初始化 Chroma Document Store
document_store = ChromaDocumentStore(
    collection_name="haystack_demo",
    persist_path="./haystack_chroma"
)

# 构建入库 Pipeline
indexing_pipeline = Pipeline()
indexing_pipeline.add_component("converter", PyPDFToDocument())
indexing_pipeline.add_component(
    "embedder",
    SentenceTransformersDocumentEmbedder(model="paraphrase-multilingual-MiniLM-L12-v2")
)
indexing_pipeline.add_component(
    "writer",
    DocumentWriter(document_store=document_store, policy=DuplicatePolicy.OVERWRITE)
)

indexing_pipeline.connect("converter", "embedder")
indexing_pipeline.connect("embedder", "writer")

# 运行入库
indexing_pipeline.run({"converter": {"sources": ["./data/document.pdf"]}})

# 构建查询 Pipeline
query_pipeline = Pipeline()
query_pipeline.add_component(
    "text_embedder",
    SentenceTransformersTextEmbedder(model="paraphrase-multilingual-MiniLM-L12-v2")
)
query_pipeline.add_component(
    "retriever",
    ChromaEmbeddingRetriever(document_store=document_store)
)

query_pipeline.connect("text_embedder.embedding", "retriever.query_embedding")

# 查询
result = query_pipeline.run({"text_embedder": {"text": "退款政策是什么?"}})
for doc in result["retriever"]["documents"]:
    print(f"  [{doc.score:.4f}] {doc.content[:60]}...")

三大框架对比

维度LangChainLlamaIndexHaystack
设计哲学链式编排数据索引优先管道组件化
Chroma 集成成熟度⭐⭐⭐ 最成熟⭐⭐ 良好⭐⭐ 良好
上手难度⭐⭐ 中等⭐⭐ 中等⭐⭐⭐ 较高
灵活性⭐⭐⭐ 最高⭐⭐ 中等⭐⭐ 中等
RAG 开箱即用✅ RetrievalQA✅ as_query_engine✅ Pipeline
对话记忆✅ 内置✅ 内置✅ 需组装
适合场景快速原型、灵活定制文档密集型应用生产级 Pipeline
社区规模最大中等

什么时候用框架,什么时候用原生 API

┌─────────────────────────────────────────────────────────────────┐
│  框架 vs 原生 API 的选择指南                                     │
│                                                                 │
│  用框架(LangChain/LlamaIndex/Haystack)当:                     │
│  ✅ 需要快速搭建端到端 RAG 系统                                  │
│  ✅ 需要对话记忆、查询改写等高级功能                              │
│  ✅ 团队中有人已经熟悉某个框架                                   │
│  ✅ 需要与多种 LLM/工具集成(不只是 Chroma)                     │
│                                                                 │
│  用原生 Chroma API 当:                                          │
│  ✅ 只需要向量检索能力,不需要完整的 RAG 编排                     │
│  ✅ 需要极致的性能控制(框架有抽象开销)                          │
│  ✅ 需要深度定制检索逻辑(框架的抽象可能不够灵活)                │
│  ✅ 项目简单,不想引入框架的依赖和复杂度                          │
│  ✅ 想要理解底层原理(框架隐藏了太多细节)                        │
└─────────────────────────────────────────────────────────────────┘

常见误区

误区 1:用了框架就不需要理解 Chroma 的原理

框架封装了 Chroma 的 API,但没有封装 Chroma 的原理。如果你不理解 distance metric、metadata 过滤、embedding 维度这些底层概念,用框架也会踩同样的坑——只是踩坑的方式从"API 调用错误"变成了"配置参数错误"。

误区 2:LangChain 是唯一选择

LangChain 虽然最流行,但不是唯一选择。LlamaIndex 在文档索引方面更专业,Haystack 在 Pipeline 编排方面更灵活。根据你的具体需求选择最合适的框架。

误区 3:框架一定比原生 API 慢

框架的抽象开销通常很小(< 1ms),对大多数应用来说可以忽略。真正影响性能的是 embedding 计算、HNSW 搜索和 LLM 推理,这些无论用不用框架都一样。


本章小结

LLM Framework 与 Chroma 的集成让 RAG 开发更高效。核心要点回顾:第一,LangChain 的 Chroma 集成最成熟,Chroma.from_texts() 一行入库,RetrievalQA 一行完成 RAG;第二,LlamaIndex 注重节点级别的细粒度控制,适合文档密集型应用;第三,Haystack 的管道设计适合构建生产级 RAG Pipeline;第四,框架适合快速搭建和需要高级功能的场景,原生 API 适合需要极致控制和深度定制的场景;第五,用框架不代表不需要理解 Chroma 的底层原理——框架封装的是 API,不是知识。

下一节也是本教程的最后一节,我们将讨论 Chroma 的局限性及替代方案——什么时候该继续用 Chroma,什么时候该考虑迁移。

基于 MIT 许可发布