Skip to main content
Azure Cosmos DB for NoSQL 支持查询具有灵活模式的项,并原生支持 JSON。它现在提供向量索引和搜索功能。此功能旨在处理高维向量,实现高效、准确的向量搜索,且可扩展至任意规模。您现在可以直接将向量与数据一起存储在文档中。数据库中的每个文档不仅可以包含传统的无模式数据,还可以包含作为文档其他属性的高维向量。
请参阅此页面了解如何利用 Azure Cosmos DB for NoSQL 的向量搜索功能。如果您没有 Azure 账户,可以创建一个免费账户开始使用。

设置

您首先需要安装 @langchain/azure-cosmosdb 包:
npm
npm install @langchain/azure-cosmosdb @langchain/core
您还需要运行一个 Azure Cosmos DB for NoSQL 实例。您可以按照此指南在 Azure 门户上免费部署一个版本。 实例运行后,请确保您拥有连接字符串。您可以在 Azure 门户中,在实例的“设置 / 密钥”部分找到它们。然后,您需要设置以下环境变量:
.env example
# 使用连接字符串进行身份验证
AZURE_COSMOSDB_NOSQL_CONNECTION_STRING=

# 使用托管标识进行身份验证
AZURE_COSMOSDB_NOSQL_ENDPOINT=

使用 Azure 托管标识

如果您使用 Azure 托管标识,可以像这样配置凭据:
import { AzureCosmosDBNoSQLVectorStore } from "@langchain/azure-cosmosdb";
import { OpenAIEmbeddings } from "@langchain/openai";

// 创建 Azure Cosmos DB 向量存储
const store = new AzureCosmosDBNoSQLVectorStore(new OpenAIEmbeddings(), {
  // 或使用环境变量 AZURE_COSMOSDB_NOSQL_ENDPOINT
  endpoint: "https://my-cosmosdb.documents.azure.com:443/",

  // 数据库和容器必须已存在
  databaseName: "my-database",
  containerName: "my-container",
});
使用 Azure 托管标识和基于角色的访问控制时,必须确保数据库和容器已预先创建。RBAC 不提供创建数据库和容器的权限。您可以在 Azure Cosmos DB 文档中获取有关权限模型的更多信息。

使用过滤器时的安全注意事项

如果数据未正确清理,使用用户提供的输入进行过滤可能存在安全风险。请遵循以下建议以防止潜在的安全问题。
允许将原始用户输入连接到类似 SQL 的子句中(例如 WHERE ${userFilter})会引入 SQL 注入攻击的关键风险,可能导致暴露非预期数据或损害系统完整性。为缓解此问题,请始终使用 Azure Cosmos DB 的参数化查询机制,传入 @param 占位符,从而将查询逻辑与用户提供的输入清晰分离。 以下是不安全代码的示例:
import { AzureCosmosDBNoSQLVectorStore } from "@langchain/azure-cosmosdb";

const store = new AzureCosmosDBNoSQLVectorStore(embeddings, {});

// 不安全:用户控制的输入被注入到查询中
const userId = req.query.userId; // 例如 "123' OR 1=1"
const unsafeQuerySpec = {
  query: `SELECT * FROM c WHERE c.metadata.userId = '${userId}'`,
};

await store.delete({ filter: unsafeQuerySpec });
如果攻击者提供 123 OR 1=1,则查询变为 SELECT * FROM c WHERE c.metadata.userId = '123' OR 1=1,这会强制条件始终为真,导致绕过预期过滤器并删除所有文档。 为防止此注入风险,您可以定义一个占位符(如 @userId),Cosmos DB 会将用户输入作为参数单独绑定,确保其严格被视为数据而非可执行查询逻辑,如下所示。
import { SqlQuerySpec } from "@azure/cosmos";

const safeQuerySpec: SqlQuerySpec = {
  query: "SELECT * FROM c WHERE c.metadata.userId = @userId",
  parameters: [{ name: "@userId", value: userId }],
};

await store.delete({ filter: safeQuerySpec });
现在,如果攻击者输入 123 OR 1=1,该输入将被视为要匹配的字面字符串值,而不是查询结构的一部分。 请参阅官方文档 Azure Cosmos DB for NoSQL 中的参数化查询 以获取更多使用示例和最佳实践。

使用示例

以下示例演示了如何将文件中的文档索引到 Azure Cosmos DB for NoSQL 中,运行向量搜索查询,最后使用链根据检索到的文档以自然语言回答问题。
import { AzureCosmosDBNoSQLVectorStore } from "@langchain/azure-cosmosdb";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { createStuffDocumentsChain } from "@langchain/classic/chains/combine_documents";
import { createRetrievalChain } from "@langchain/classic/chains/retrieval";
import { TextLoader } from "@langchain/classic/document_loaders/fs/text";
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

// 从文件加载文档
const loader = new TextLoader("./state_of_the_union.txt");
const rawDocuments = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 0,
});
const documents = await splitter.splitDocuments(rawDocuments);

// 创建 Azure Cosmos DB 向量存储
const store = await AzureCosmosDBNoSQLVectorStore.fromDocuments(
  documents,
  new OpenAIEmbeddings(),
  {
    databaseName: "langchain",
    containerName: "documents",
  }
);

// 执行相似性搜索
const resultDocuments = await store.similaritySearch(
  "What did the president say about Ketanji Brown Jackson?"
);

console.log("Similarity search results:");
console.log(resultDocuments[0].pageContent);
/*
  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.

  Tonight, 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.

  One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.

  And 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.
*/

// 将存储用作链的一部分
const model = new ChatOpenAI({ model: "gpt-3.5-turbo-1106" });
const questionAnsweringPrompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    "Answer the user's questions based on the below context:\n\n{context}",
  ],
  ["human", "{input}"],
]);

const combineDocsChain = await createStuffDocumentsChain({
  llm: model,
  prompt: questionAnsweringPrompt,
});

const chain = await createRetrievalChain({
  retriever: store.asRetriever(),
  combineDocsChain,
});

const res = await chain.invoke({
  input: "What is the president's top priority regarding prices?",
});

console.log("Chain response:");
console.log(res.answer);
/*
  The president's top priority is getting prices under control.
*/

// 清理
await store.delete();

高级搜索选项

所有搜索类型都通过统一的 similaritySearchsimilaritySearchWithScore 方法访问,使用过滤器选项中的 searchType 参数。搜索类型可作为常量从 AzureCosmosDBNoSQLSearchType 获取: 可用搜索类型:
  • AzureCosmosDBNoSQLSearchType.Vector(默认):标准向量相似性搜索
  • AzureCosmosDBNoSQLSearchType.VectorScoreThreshold:带最小分数过滤器的向量搜索
  • AzureCosmosDBNoSQLSearchType.FullTextSearch:使用 FullTextContains 的全文搜索(预览版)
  • AzureCosmosDBNoSQLSearchType.FullTextRanking:使用 BM25 排名的全文搜索(预览版)
  • AzureCosmosDBNoSQLSearchType.Hybrid:使用 RRF 的混合向量 + 全文搜索(预览版)
  • AzureCosmosDBNoSQLSearchType.HybridScoreThreshold:带分数阈值的混合搜索(预览版)
您还可以在创建存储时使用 defaultSearchType 配置选项设置默认搜索类型,这样就不必在每次查询中指定它:
import {
  AzureCosmosDBNoSQLVectorStore,
  AzureCosmosDBNoSQLSearchType,
} from "@langchain/azure-cosmosdb";
import { OpenAIEmbeddings } from "@langchain/openai";

const store = new AzureCosmosDBNoSQLVectorStore(new OpenAIEmbeddings(), {
  databaseName: "langchain",
  containerName: "documents",
  defaultSearchType: AzureCosmosDBNoSQLSearchType.VectorScoreThreshold,
});

带分数阈值的向量搜索

根据最小相似性分数过滤结果:
import {
  AzureCosmosDBNoSQLVectorStore,
  AzureCosmosDBNoSQLSearchType,
} from "@langchain/azure-cosmosdb";
import { OpenAIEmbeddings } from "@langchain/openai";

const store = new AzureCosmosDBNoSQLVectorStore(new OpenAIEmbeddings(), {
  databaseName: "langchain",
  containerName: "documents",
});

// 仅返回相似性分数 >= 0.8 的结果
const results = await store.similaritySearchWithScore(
  "What is the capital of France?",
  10,
  {
    searchType: AzureCosmosDBNoSQLSearchType.VectorScoreThreshold,
    threshold: 0.8,
  }
);

for (const [doc, score] of results) {
  console.log(`Score: ${score}, Content: ${doc.pageContent}`);
}

最大边际相关性 (MMR) 搜索

MMR 搜索在结果的相关性和多样性之间取得平衡:
import { AzureCosmosDBNoSQLVectorStore } from "@langchain/azure-cosmosdb";
import { OpenAIEmbeddings } from "@langchain/openai";

const store = new AzureCosmosDBNoSQLVectorStore(new OpenAIEmbeddings(), {
  databaseName: "langchain",
  containerName: "documents",
});

const results = await store.maxMarginalRelevanceSearch("machine learning", {
  k: 5, // 要返回的结果数量
  fetchK: 20, // 要考虑的候选数量
  lambda: 0.5, // 0 = 最大多样性,1 = 最大相关性
});

全文和混合搜索(预览版)

全文和混合搜索是 Azure Cosmos DB 中的预览功能。您需要使用全文策略和适当的索引配置容器才能使用这些功能。请参阅 Azure Cosmos DB 文档 获取设置说明。
要使用全文或混合搜索,请在创建存储时启用它:
import {
  AzureCosmosDBNoSQLVectorStore,
  AzureCosmosDBNoSQLSearchType,
} from "@langchain/azure-cosmosdb";
import { OpenAIEmbeddings } from "@langchain/openai";

const store = new AzureCosmosDBNoSQLVectorStore(new OpenAIEmbeddings(), {
  databaseName: "langchain",
  containerName: "documents",
  fullTextSearchEnabled: true,
  fullTextPolicy: {
    defaultLanguage: "en-US",
    fullTextPaths: [{ path: "/text", language: "en-US" }],
  },
  indexingPolicy: {
    indexingMode: "consistent",
    automatic: true,
    includedPaths: [{ path: "/*" }],
    excludedPaths: [{ path: "/_etag/?" }],
    vectorIndexes: [{ path: "/vector", type: "quantizedFlat" }],
    fullTextIndexes: [{ path: "/text" }],
  },
});

全文搜索

// 在筛选子句中使用 FullTextContains 进行全文搜索
const fullTextResults = await store.similaritySearch("", 10, {
  searchType: AzureCosmosDBNoSQLSearchType.FullTextSearch,
  filterClause: "WHERE FullTextContains(c.text, 'artificial intelligence')",
});

全文排名

// 使用 BM25 评分进行全文排名
const rankingResults = await store.similaritySearch("", 10, {
  searchType: AzureCosmosDBNoSQLSearchType.FullTextRanking,
  fullTextRankFilter: [
    { searchField: "text", searchText: "artificial intelligence" },
  ],
});

混合搜索

混合搜索结合向量相似性和全文搜索,使用互惠排名融合 (RRF):
// 结合向量和全文结果的混合搜索
const hybridResults = await store.similaritySearchWithScore(
  "machine learning",
  10,
  {
    searchType: AzureCosmosDBNoSQLSearchType.Hybrid,
    fullTextRankFilter: [
      { searchField: "text", searchText: "machine learning" },
    ],
  }
);

// 带分数阈值的混合搜索
const filteredResults = await store.similaritySearchWithScore(
  "machine learning",
  10,
  {
    searchType: AzureCosmosDBNoSQLSearchType.HybridScoreThreshold,
    fullTextRankFilter: [
      { searchField: "text", searchText: "machine learning" },
    ],
    threshold: 0.5,
  }
);

实用方法

删除文档

// 按 ID 删除特定文档
await store.delete({ ids: ["document-id-123"] });

// 删除匹配过滤器的文档
await store.delete({
  filter: {
    query: "SELECT * FROM c WHERE c.metadata.category = @category",
    parameters: [{ name: "@category", value: "old" }],
  },
});

// 删除所有文档
await store.delete();

访问底层容器

// 获取 Cosmos DB 容器的直接访问权限以进行高级操作
const container = store.getContainer();
const { resources } = await container.items
  .query("SELECT * FROM c WHERE c.metadata.category = 'tech'")
  .fetchAll();

相关内容