Skip to main content

Chroma(色度)

Chroma 是一个以人工智能为基础的开源向量数据库,专注于开发者的生产力和幸福感。Chroma 使用 Apache 2.0 许可证。

使用以下命令安装 Chroma:

pip install chromadb

Chroma 可以以多种模式运行。请参阅下面的示例,了解每种模式与 LangChain 集成的方式。

  • in-memory - 在 Python 脚本或 Jupyter Notebook 中
  • in-memory with persistance - 在脚本或 Notebook 中保存/加载到磁盘
  • in a docker container - 作为在本地机器或云中运行的服务器

与任何其他数据库一样,您可以执行以下操作:

  • .add - 添加数据
  • .get - 获取数据
  • .update - 更新数据
  • .upsert - 更新或插入数据
  • .delete - 删除数据
  • .peek - 查看数据
  • .query - 运行相似性搜索。

查看完整文档,请访问 docs。要直接访问这些方法,可以使用 ._collection_.method()

基本示例 (Basic Example)

在这个基本示例中,我们将最近的国情咨文演讲分割成块,使用开源嵌入模型进行嵌入,加载到Chroma中,然后进行查询。

# 导入模块
from langchain.embeddings.sentence_transformer import SentenceTransformerEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.document_loaders import TextLoader

# 加载文档并将其分割成块
loader = TextLoader("../../../state_of_the_union.txt")
documents = loader.load()

# 将文档分割成块
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

# 创建开源嵌入函数
embedding_function = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")

# 加载到Chroma中
db = Chroma.from_documents(docs, embedding_function)

# 查询
query = "What did the president say about Ketanji Brown Jackson"
docs = db.similarity_search(query)

# 打印结果
print(docs[0].page_content)
    /Users/jeff/.pyenv/versions/3.10.10/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm


今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一句,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人致敬:司法部长斯蒂芬·布雷耶——一位退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

总统最重要的宪法责任之一就是提名人选担任美国最高法院法官。

我在4天前提名了联邦上诉法院法官凯坦吉·布朗·杰克逊。她是我们国家顶尖的法律专家,将继续布雷耶法官的卓越传统。

基本示例(包括保存到磁盘)

扩展前面的示例,如果您想要保存到磁盘,只需初始化Chroma客户端并传递要保存数据的目录。

注意:Chroma会尽力自动将数据保存到磁盘,但是多个内存中的客户端可能会互相覆盖对方的工作。作为最佳实践,在任何给定时间只运行一个路径上的客户端。

# 保存到磁盘
db2 = Chroma.from_documents(docs, embedding_function, persist_directory="./chroma_db")
docs = db2.similarity_search(query)

# 从磁盘加载
db3 = Chroma(persist_directory="./chroma_db", embedding_function=embedding_function)
docs = db3.similarity_search(query)
print(docs[0].page_content)
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一句,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

总统最重要的宪法责任之一是提名人选担任美国最高法院法官。

我在4天前提名了联邦上诉法院法官凯坦吉·布朗·杰克逊。她是我们国家顶尖的法律专家之一,将继续布雷耶法官的卓越传统。

将Chroma客户端传递给Langchain (Passing a Chroma Client into Langchain)

您还可以创建一个Chroma客户端并将其传递给LangChain。如果您想更轻松地访问底层数据库,这将非常有用。

您还可以指定您希望LangChain使用的集合名称。

import chromadb

persistent_client = chromadb.PersistentClient()
collection = persistent_client.get_or_create_collection("collection_name")
collection.add(ids=["1", "2", "3"], documents=["a", "b", "c"])

langchain_chroma = Chroma(
client=persistent_client,
collection_name="collection_name",
embedding_function=embedding_function,
)

print("There are", langchain_chroma._collection.count(), "in the collection")
    Add of existing embedding ID: 1
Add of existing embedding ID: 2
Add of existing embedding ID: 3
Add of existing embedding ID: 1
Add of existing embedding ID: 2
Add of existing embedding ID: 3
Add of existing embedding ID: 1
Insert of existing embedding ID: 1
Add of existing embedding ID: 2
Insert of existing embedding ID: 2
Add of existing embedding ID: 3
Insert of existing embedding ID: 3


There are 3 in the collection

基本示例(使用Docker容器)

您还可以在单独的Docker容器中运行Chroma服务器,创建一个客户端连接到它,然后将其传递给LangChain。

Chroma有处理多个文档集合(Collections)的能力,但是LangChain接口只接受一个集合,因此我们需要指定集合名称。LangChain使用的默认集合名称是“langchain”。

以下是克隆、构建和运行Docker镜像的方法:

git clone git@github.com:chroma-core/chroma.git

编辑docker-compose.yml文件,在environment下添加ALLOW_RESET=TRUE

    ...
command: uvicorn chromadb.app:app --reload --workers 1 --host 0.0.0.0 --port 8000 --log-config log_config.yml
environment:
- IS_PERSISTENT=TRUE
- ALLOW_RESET=TRUE
ports:
- 8000:8000
...

然后运行docker-compose up -d --build

# 创建Chroma客户端
import chromadb
import uuid
from chromadb.config import Settings

client = chromadb.HttpClient(settings=Settings(allow_reset=True))
client.reset() # 重置数据库
collection = client.create_collection("my_collection")
for doc in docs:
collection.add(
ids=[str(uuid.uuid1())], metadatas=doc.metadata, documents=doc.page_content
)

# 告诉LangChain使用我们的客户端和集合名称
db4 = Chroma(client=client, collection_name="my_collection", embedding_function=embedding_function)
query = "What did the president say about Ketanji Brown Jackson"
docs = db4.similarity_search(query)
print(docs[0].page_content)
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一句,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

总统最重要的宪法责任之一是提名人选担任美国最高法院法官。

我在4天前提名了联邦上诉法院法官Ketanji Brown Jackson。她是我们国家顶尖的法律专家,将继续延续布雷耶法官的卓越传统。

更新和删除

在构建真实应用程序时,您希望不仅添加数据,还要更新和删除数据。

Chroma要求用户提供ids以简化这里的簿记工作。ids可以是文件名,也可以是组合哈希值,例如filename_paragraphNumber等。

Chroma支持所有这些操作-尽管其中一些操作仍在通过LangChain接口进行集成。将很快添加其他工作流改进。

下面是一个基本示例,展示了如何执行各种操作:

# 创建简单的ids
ids = [str(i) for i in range(1, len(docs) + 1)]

# 添加数据
example_db = Chroma.from_documents(docs, embedding_function, ids=ids)
docs = example_db.similarity_search(query)
print(docs[0].metadata)

# 更新文档的元数据
docs[0].metadata = {
"source": "../../../state_of_the_union.txt",
"new_value": "hello world",
}
example_db.update_document(ids[0], docs[0])
print(example_db._collection.get(ids=[ids[0]]))

# 删除最后一个文档
print("删除前的数量", example_db._collection.count())
example_db._collection.delete(ids=[ids[-1]])
print("删除后的数量", example_db._collection.count())
    {'source': '../../../state_of_the_union.txt'}
{'ids': ['1'], 'embeddings': None, 'metadatas': [{'new_value': 'hello world', 'source': '../../../state_of_the_union.txt'}], 'documents': ['Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. \n\nTonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. \n\nOne of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n\nAnd I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.']}
删除前的数量 46
删除后的数量 45

使用OpenAI Embeddings

许多人喜欢使用OpenAI Embeddings,以下是设置方法。

# 获取一个令牌:https://platform.openai.com/account/api-keys

from getpass import getpass
from langchain.embeddings.openai import OpenAIEmbeddings

OPENAI_API_KEY = getpass()
import os

os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
embeddings = OpenAIEmbeddings()
new_client = chromadb.EphemeralClient()
openai_lc_client = Chroma.from_documents(
docs, embeddings, client=new_client, collection_name="openai_collection"
)

query = "总统对Ketanji Brown Jackson说了什么"
docs = openai_lc_client.similarity_search(query)
print(docs[0].page_content)
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一下,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

总统最重要的宪法责任之一是提名人选担任美国最高法院法官。

我在4天前提名了联邦上诉法院法官Ketanji Brown Jackson。她是我们国家顶尖的法律专家之一,将继续延续布雷耶法官的卓越传统。

其他信息 (Other Information)

相似度搜索与分数

返回的距离分数是余弦距离。因此,分数越低越好。

docs = db.similarity_search_with_score(query)
docs[0]
    (Document(page_content='今晚。我呼吁参议院:通过《自由选举法案》。通过《约翰·刘易斯选举权法案》。而且,趁机通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。\n\n今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。\n\n总统拥有的最重要的宪法责任之一就是提名人选担任美国最高法院的职位。\n\n4天前,我提名了巡回上诉法院法官凯坦吉·布朗·杰克逊。她是我们国家最顶尖的法律智慧之一,将继续延续布雷耶法官的卓越传统。', metadata={'source': '../../../state_of_the_union.txt'}),
1.1972057819366455)

检索器选项 (Retriever options)

这一部分介绍了如何使用Chroma作为检索器的不同选项。

### 检索器选项 (Retriever options)

这一部分介绍了如何使用Chroma作为检索器的不同选项。

一级标题 (Primary Title)

MMR (最大边缘相关性)

除了在检索器对象中使用相似性搜索之外,您还可以使用 mmr

retriever = db.as_retriever(search_type="mmr")
retriever.get_relevant_documents(query)[0]
    Document(page_content='今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。\n\n今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。\n\n总统拥有的最重要的宪法责任之一就是提名人选担任美国最高法院的职位。\n\n4天前,我提名了巡回上诉法院法官凯坦吉·布朗·杰克逊。她是我们国家顶级的法律智慧之一,将继续布雷耶法官的卓越传统。', metadata={'source': '../../../state_of_the_union.txt'})

元数据过滤

在处理集合之前,缩小范围可能会很有帮助。

例如,可以使用get方法对元数据进行过滤。

# 过滤更新的来源集合
example_db.get(where={"source": "some_other_source"})
    {'ids': [], 'embeddings': None, 'metadatas': [], 'documents': []}