侧边栏壁纸
博主头像
程序员进阶之路

技术之道,不可浅尝辄止;架构之路,须当支持以恒!

  • 累计撰写 18 篇文章
  • 累计创建 3 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录
LLM

Chroma 向量数据库入门与实战:从选型、原理到 Java 集成

云端行笔
2026-06-15 / 0 评论 / 0 点赞 / 9 阅读 / 0 字

摘要:Chroma 是一个面向 AI 应用的开源向量数据库,常用于 RAG、知识库问答、文档检索、Agent 记忆和语义搜索。相比 Milvus、Qdrant、pgvector 这类方案,Chroma 的优势是上手快、API 简单、适合本地开发和初学者理解向量检索完整流程。本文从向量数据库选型讲起,系统介绍 Chroma 的适用场景、实现原理、安装使用、常用语法、metadata 过滤,以及如何在 Java 项目中通过 Spring AI 集成 Chroma 构建一个基础 RAG 检索链路。

写在前面:为什么很多人第一次做 RAG 会选 Chroma

如果你第一次做 RAG,最容易卡住的地方往往不是大模型,而是“知识怎么存、怎么搜”。

一个最小 RAG 系统至少要做几件事:文档 -> 切块 -> Embedding -> 向量存储 -> 相似度检索 -> Prompt -> 大模型回答。 这里的“向量存储”和“相似度检索”,就是向量数据库的主要工作。

Chroma 的定位很清楚:它不是最复杂、最重型的向量数据库,而是一个非常适合 AI 应用开发者快速上手的开源向量数据库。官方文档也把它描述为面向 AI 的开源数据基础设施,内置了启动 RAG 原型需要的基本能力,可以在本机运行,也可以用 client-server 模式连接独立服务。

这正是它适合初学者的原因:

  • 安装简单,pip install chromadb 就能开始。

  • API 直观,核心概念主要是 client、collection、document、embedding、metadata。

  • 本地开发友好,可以先用内存模式或本地持久化模式。

  • 可以自动生成 embedding,也可以接入自己生成好的 embedding。

  • 支持 metadata 过滤,能做基础权限、类型、版本过滤。

  • 生态里有 LangChain、LlamaIndex、Spring AI 等常见框架集成。

如果你只是想理解 RAG 的完整链路,Chroma 比上来就部署一套复杂集群更合适。它能让你把注意力放在文档切块、Embedding、召回质量和 Prompt 组装上,而不是一开始就陷进分片、压缩、扩容和运维细节。

当然,适合入门不等于只能做玩具项目。Chroma 也支持独立服务模式和 Chroma Cloud。但在选型时,仍然要根据数据规模、团队技术栈、线上稳定性、权限隔离、运维能力和成本做判断。

一、向量数据库怎么选:别一上来就问“哪个最好”

向量数据库没有绝对最好,只有适不适合当前阶段。选型时可以先问五个问题:

第一,数据规模有多大?

如果只是几千到几十万条 chunk,Chroma、pgvector、本地向量库都可以。如果是千万级、亿级向量,并且对召回延迟和扩展性有硬要求,就要认真评估 Milvus、Qdrant、Weaviate、Pinecone、Elasticsearch/OpenSearch 向量检索等更偏生产规模的方案。

第二,团队是不是已经有数据库基础设施?

如果公司已经大量使用 PostgreSQL,且向量数据规模不大,pgvector 可能更容易落地。如果团队已经有 Elasticsearch/OpenSearch,并且关键词检索是强需求,可以考虑在搜索引擎里做向量和关键词混合检索。

第三,RAG 系统是不是还在 Demo 阶段?

如果还在验证产品方向,Chroma 很合适。它能快速跑通“写入文档、向量检索、metadata 过滤、返回结果”这条链路,试错成本低。

第四,线上是否需要复杂权限和多租户隔离?

企业知识库通常要按租户、部门、角色、文档密级、版本、生效时间过滤。向量数据库不仅要能做相似度检索,还要能做可靠 metadata filter。Chroma 支持 metadata 过滤,但复杂权限模型最好在应用层和数据层一起设计,不要只靠 Prompt 约束。

第五,谁来运维?

本地 Chroma 很轻,但生产环境仍然要考虑备份、恢复、监控、访问控制、版本升级、数据迁移。如果团队没有运维资源,托管服务可能比自建更合适。

可以简单总结:

所以 Chroma 的最佳位置是:学习成本低、原型速度快、适合把 RAG 基础链路讲清楚,也适合中小规模知识库的早期实现。

二、Chroma 的核心概念和实现原理

Chroma 的核心概念不多。

最外层是 Client。

Client 用来连接 Chroma。它可以是内存客户端、本地持久化客户端,也可以是连接远程 Chroma 服务的 HTTP Client。

第二层是 Collection。

Collection 可以理解为一组向量数据的集合,类似关系型数据库里的表,但它面向的是 embedding、document 和 metadata。官方文档里也强调,Collection 用来存储 embeddings、documents 和附加 metadata,并支持检索和过滤。

第三层是 Record。

一条典型记录可以理解为:

  {
    "id": "refund-policy#chunk-001",
    "document": "订单进入结算流程后,不支持原路退款,需要走人工退款流程。",
    "embedding": [0.013, -0.082, 0.194],
    "metadata": {
      "doc_type": "policy",
      "product": "payment",
      "version": "2026",
      "permission": "support"
    }
  }

Chroma 的检索过程大致是:

这里有几个点需要初学者特别注意。

第一,Chroma 可以帮你自动生成 embedding,也可以接收你提前算好的 embedding。

如果你只传 documents,Chroma 会使用 collection 配置的 embedding function 生成向量;如果你已经在外部用自己的模型算好了向量,也可以把 embeddings 一起传进去。官方文档明确说明,添加数据时至少要提供 documentsembeddings 二者之一。

第二,id 必须唯一。

Chroma 的 add 要求每条记录有唯一字符串 ID。如果你重复添加同一个 ID,行为和更新不是一回事。工程里更推荐把文档 ID、版本号、chunk 序号拼进 ID,例如:

  doc_2026_refund_policy:v1:chunk_0001

第三,metadata 很重要。

向量检索只能判断“语义像不像”,不能判断“这个用户能不能看”“这份文档是否过期”“是不是当前产品线”。这些问题要靠 metadata 和业务权限系统解决。

第四,Chroma 不等于完整 RAG 系统。

Chroma 解决的是向量存储和检索问题。文档解析、切块、Embedding 模型选择、Rerank、Prompt 组装、引用生成、效果评估,仍然需要应用层处理。

三、Chroma 安装与启动:本地模式、持久化模式和服务模式

Chroma 的 Python 安装很直接:(依赖python环境,推荐Python 3.10)

  pip install chromadb

验证:(进入phthon环境)

  python
  import chromadb
  
  print(chromadb.__version__)

安装后,需要进入进入python控制台,通过Python脚本中操作数据库。

  • 方式一:内存客户端 (EphemeralClient):数据仅在内存中,程序退出即销毁。

  import chromadb
  
  client = chromadb.Client()
  collection = client.get_or_create_collection(name="rag_demo")

这种方式适合快速试 API,但数据会随着进程结束丢失。官方 getting started 文档也提醒,示例里的 in-memory client 主要用于简单入门,如果需要持久化,应使用 persistent client 或 client-server 模式。

  • 方式二:持久化客户端 (PersistentClient):数据保存在磁盘,程序退出后不丢失,是最推荐的本地开发和生产模式

  import chromadb
  
  client = chromadb.PersistentClient(path="./chroma_data")
  collection = client.get_or_create_collection(name="rag_demo")

这样数据会落到本地目录,适合个人 Demo、小工具、离线实验。

  • 方式三:HTTP API Server (生产部署):启动 server,让多个应用连接同一个 Chroma 服务。

  chroma run --path ./chroma_db

然后在 Python 里用 HTTP Client 连接:

  import chromadb
  
  client = chromadb.HttpClient(host="localhost", port=8000)
  collection = client.get_or_create_collection(name="rag_demo")

如果你更习惯 Docker,也可以参考 Spring AI 文档中的本地启动方式:

  docker run -it --rm --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:1.0.0

实际开发时可以按阶段选择:

  • 学 API:chromadb.Client()

  • 本地 Demo:PersistentClient

  • Java / 多服务集成:chroma run 或 Docker server

  • 生产托管:Chroma Cloud 或其他托管方案

四、Chroma 常用语法:Collection、Add、Query、Filter、Update

Chroma 的 API 很适合初学者理解向量数据库的基本动作。

1. 创建 Collection

  import chromadb
  
  client = chromadb.PersistentClient(path="./chroma_data")
  collection = client.get_or_create_collection(name="knowledge_base")

get_or_create_collectioncreate_collection 更适合脚本反复执行,不会因为 collection 已存在就失败。

2. 添加文档

  collection.add(
      ids=["refund-001", "refund-002"],
      documents=[
          "订单进入结算流程后,不支持原路退款。",
          "人工退款需要运营提交申请,财务审批后处理。"
      ],
      metadatas=[
          {"doc_type": "policy", "product": "payment", "version": "2026"},
          {"doc_type": "sop", "product": "payment", "version": "2026"}
      ]
  )

只传 documents 时,Chroma 会根据 collection 的 embedding function 处理向量(注:默认 embedding 会去联网下载/调用模型,可能超时)。如果你已经有 embedding,可以直接传:

  collection.add(
      ids=["refund-003"],
      embeddings=[[0.12, 0.08, 0.33]],
      documents=["退款失败时,请先检查订单状态和支付渠道。"],
      metadatas=[{"doc_type": "faq", "product": "payment"}]
  )

实际项目里,更常见的是应用层统一调用 Embedding 模型,然后把 embedding、document、metadata 一起写入。这样更容易控制模型版本和向量维度。

Chroma 默认 embedding function / all-MiniLM-L6-v2

3. 查询相似内容

  results = collection.query(
      query_texts=["订单结算后还能退款吗?"],
      n_results=3
  )
  
  print(results["documents"])
  print(results["metadatas"])
  print(results["distances"])

返回结果通常包括:

  • ids

  • documents

  • metadatas

  • distances

  • embeddings,默认通常不返回

n_results 控制返回多少条相似结果。如果不传,Chroma 默认返回 10 条结果。

4. metadata 过滤

Chroma 的 where 参数用于 metadata filter。例如只查支付产品线:

  results = collection.query(
      query_texts=["订单结算后还能退款吗?"],
      n_results=3,
      where={"product": "payment"}
  )

查版本为 2026 且产品为 payment:

  results = collection.query(
      query_texts=["退款失败怎么办?"],
      n_results=5,
      where={
          "$and": [
              {"product": {"$eq": "payment"}},
              {"version": {"$eq": "2026"}}
          ]
      }
  )

查某些文档类型:

  results = collection.get(
      where={
          "doc_type": {
              "$in": ["policy", "faq"]
          }
      }
  )

Chroma 支持 $eq、比较、$and$or$in$nin 等过滤方式,也支持数组 metadata 的包含判断。对 RAG 来说,metadata filter 是权限、版本和业务范围控制的基础。

5. 更新和覆盖

如果脚本可能重复执行,通常用 upsert 更省心:

  collection.upsert(
      ids=["refund-001"],
      documents=["订单进入结算流程后,不支持原路退款,需要走人工退款流程。"],
      metadatas=[{"doc_type": "policy", "product": "payment", "version": "2026"}]
  )

工程里建议把 addupsert 的使用边界分清楚:

  • 首次导入:可以用 add

  • 增量同步:更适合 upsert

  • 删除过期文档:按 doc_idversionstatus 做批量治理

五、Chroma 适合初学者,但生产落地要注意这些边界

Chroma 很适合入门,但不是说任何规模、任何场景都应该默认选它。

适合 Chroma 的场景:

  • RAG 学习和课程演示。

  • 本地知识库 Demo。

  • 小团队内部文档问答。

  • Agent 记忆原型。

  • 轻量语义搜索。

  • 需要快速验证 Embedding 和 chunk 策略的项目。

需要谨慎评估的场景:

  • 亿级向量检索。

  • 高并发线上服务。

  • 强多租户隔离。

  • 复杂权限模型。

  • 强 SLA 和跨地域容灾。

  • 大规模混合检索和复杂排序。

这不是 Chroma 的缺点,而是所有向量数据库选型都要面对的现实:原型阶段看 API 是否顺手,生产阶段看稳定性、扩展性、可观测性、运维能力和成本。

如果把 Chroma 用在企业 RAG 项目里,建议从第一天就做好这些设计:

  1. 每个 chunk 都有稳定 ID。

  2. metadata 至少包含 doc_idtitleversionsourcepermissionstatus

  3. Embedding 模型名称和版本要记录。

  4. 不同 embedding 维度不要混在同一个 collection。

  5. 文档删除和版本更新要有同步策略。

  6. 对召回结果做日志记录,方便 bad case 归因。

  7. 不要把权限控制交给 Prompt。

Chroma 能帮你把向量检索跑起来,但 RAG 系统能不能答准,还取决于文档治理、切块、检索策略、Rerank 和评估闭环。

六、Java 集成:用 Spring AI 连接 Chroma 做 RAG 检索

Java 生态里可以通过 Spring AI 集成 Chroma。这里选择 Spring AI,是因为它的 VectorStore 抽象比较适合 Spring Boot 项目:上层业务不需要直接关心 Chroma 的底层 REST API,只需要注入 VectorStore,然后执行 addsimilaritySearch

1. 添加依赖

Spring AI 官方文档中,Chroma Vector Store 的 starter 是:

  <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-vector-store-chroma</artifactId>
  </dependency>

如果你还需要 OpenAI Embedding,可以再引入对应模型 starter。实际项目里也可以替换成其他 EmbeddingModel,只要最终提供 Spring AI 的 EmbeddingModel Bean。

2. 配置 Chroma 连接

本地 Chroma 默认可以这样配置:

  spring.ai.vectorstore.chroma.client.host=http://localhost
  spring.ai.vectorstore.chroma.client.port=8000
  spring.ai.vectorstore.chroma.collection-name=knowledge_base
  spring.ai.vectorstore.chroma.initialize-schema=true
  
  spring.ai.openai.api.key=${OPENAI_API_KEY}

如果使用 Chroma Cloud,还需要配置 API key、tenant name 和 database name。Spring AI 文档中也说明,Chroma Cloud 的 host 通常是 api.trychroma.com,端口为 443,并且需要 key-token、tenant、database 等信息。

3. 注入 VectorStore 写入文档

  import org.springframework.ai.document.Document;
  import org.springframework.ai.vectorstore.VectorStore;
  import org.springframework.stereotype.Service;
  
  import java.util.List;
  import java.util.Map;
  
  @Service
  public class KnowledgeIngestService {
  
      private final VectorStore vectorStore;
  
      public KnowledgeIngestService(VectorStore vectorStore) {
          this.vectorStore = vectorStore;
      }
  
      public void ingest() {
          List<Document> documents = List.of(
              new Document(
                  "订单进入结算流程后,不支持原路退款,需要走人工退款流程。",
                  Map.of(
                      "doc_type", "policy",
                      "product", "payment",
                      "version", "2026"
                  )
              ),
              new Document(
                  "人工退款需要运营提交申请,财务审批通过后处理。",
                  Map.of(
                      "doc_type", "sop",
                      "product", "payment",
                      "version", "2026"
                  )
              )
          );
  
          vectorStore.add(documents);
      }
  }

这里的 vectorStore.add(documents) 会通过配置好的 EmbeddingModel 计算 embedding,并写入 Chroma。

4. 相似度检索

  import org.springframework.ai.document.Document;
  import org.springframework.ai.vectorstore.SearchRequest;
  import org.springframework.ai.vectorstore.VectorStore;
  import org.springframework.stereotype.Service;
  
  import java.util.List;
  
  @Service
  public class KnowledgeSearchService {
  
      private final VectorStore vectorStore;
  
      public KnowledgeSearchService(VectorStore vectorStore) {
          this.vectorStore = vectorStore;
      }
  
      public List<Document> search(String question) {
          return vectorStore.similaritySearch(
              SearchRequest.builder()
                  .query(question)
                  .topK(5)
                  .similarityThreshold(0.65)
                  .build()
          );
      }
  }

5. metadata 过滤

企业知识库里,检索时通常要加业务过滤条件。Spring AI 支持 portable metadata filter,并会转换成 Chroma 的 where 表达式。比如只查 payment 产品线、指定文档类型:

  public List<Document> searchPaymentDocs(String question) {
      return vectorStore.similaritySearch(
          SearchRequest.builder()
              .query(question)
              .topK(5)
              .filterExpression("product == 'payment' && doc_type in ['policy', 'sop']")
              .build()
      );
  }

这样写的好处是业务代码不直接绑定 Chroma 的 JSON filter 格式,后续如果切换向量数据库,迁移成本会低一些。

6. 一个最小 RAG Controller

  import org.springframework.ai.document.Document;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.RequestParam;
  import org.springframework.web.bind.annotation.RestController;
  
  import java.util.List;
  
  @RestController
  public class RagController {
  
      private final KnowledgeSearchService searchService;
  
      public RagController(KnowledgeSearchService searchService) {
          this.searchService = searchService;
      }
  
      @GetMapping("/rag/search")
      public List<String> search(@RequestParam String q) {
          return searchService.search(q).stream()
              .map(Document::getText)
              .toList();
      }
  }

这个例子还没有接大模型,只是把 Chroma 检索结果返回出来。实际 RAG 应用会继续做:

建议初学者先把检索结果调准,再接大模型。否则模型生成的语言会掩盖召回问题,让你误以为系统已经可用。

七、Chroma 实战建议:别只跑通 API,要关注召回质量

Chroma 的 API 很容易跑通,但 RAG 项目真正难的是“找得准”。以下是总结的几点经验:

第一,先做高质量切块。

不要直接把整篇 PDF 或整页 HTML 扔进 Chroma。先去掉页眉、页脚、导航、广告、重复声明,再按标题、段落、列表、表格切成 chunk。

第二,metadata 要从一开始就设计。

至少包括:

  doc_id
  chunk_id
  title
  source
  version
  status
  permission
  created_at

后期再补 metadata 会很痛苦,因为你需要重新同步和重建索引。

第三,记录每次检索日志。

至少记录:

  query
  topK
  where filter
  returned ids
  distances
  metadatas
  最终是否被用户采纳

RAG 调优不是玄学,bad case 要能追到具体 chunk。

第四,不要迷信向量检索。

Chroma 支持相似度检索,但错误码、字段名、订单号、配置项、接口名这类问题,关键词检索往往更可靠。实际生产里可以考虑 Chroma + 搜索引擎,或者在应用层做 Hybrid Search。

第五,Rerank 仍然有价值。

Chroma 返回的是初始召回结果,不代表最终最适合进入 Prompt。对于复杂问题,可以先召回 top20 或 top50,再用 rerank 模型选 top5。

第六,定期清理旧版本。

企业知识库里最怕新旧制度同时被召回。建议用 versionstatuseffective_at 控制文档生命周期,不要让过期 chunk 长期混在主 collection 里。

结语:Chroma 是理解 RAG 的好入口,但不是 RAG 的全部

Chroma 的价值,在于它把向量数据库这件事做得足够轻。

你可以很快完成:

  • 安装 Chroma

  • 创建 collection

  • 写入 documents

  • 自动或手动生成 embedding

  • 执行 query

  • 使用 metadata 过滤

  • 接入 Java / Spring AI

对初学者来说,这比一开始研究复杂集群和索引参数更重要。先把 RAG 链路跑通,理解 chunk、embedding、metadata、query、distance、filter、topK 这些基础概念,后面再谈扩容和生产治理会顺很多。

但也要清楚:Chroma 只是 RAG 系统的一部分。一个真正稳定的知识库问答系统,还需要文档清洗、切块策略、Embedding 版本治理、权限控制、Hybrid Search、Rerank、Prompt 约束、引用追踪、评估集和线上反馈。

如果你是第一次做 RAG,我的建议是:先用 Chroma 跑通,再用 bad case 逼出真正的工程需求。当你的问题从“怎么把文档存进去”变成“为什么这个问题召回错了”,你就已经开始真正理解向量数据库了。

0

评论区