Skip to main content

Azure Cognitive Search

Azure Cognitive Search(以前称为Azure Search)是一种云搜索服务,为开发人员提供基础设施、API和工具,用于在Web、移动和企业应用程序中构建丰富的搜索体验,以处理私有、异构内容。

安装 Azure Cognitive Search SDK

pip install azure-search-documents==11.4.0b6
pip install azure-identity

导入所需的库 (Import required libraries)

import openai
import os
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores.azuresearch import AzureSearch

配置OpenAI设置 (Configure OpenAI settings)

配置OpenAI设置以使用Azure OpenAI或OpenAI

os.environ["OPENAI_API_TYPE"] = "azure"
os.environ["OPENAI_API_BASE"] = "YOUR_OPENAI_ENDPOINT"
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
os.environ["OPENAI_API_VERSION"] = "2023-05-15"
model: str = "text-embedding-ada-002"

配置向量存储设置 (Configure vector store settings)

使用环境变量设置向量存储设置:

vector_store_address: str = "YOUR_AZURE_SEARCH_ENDPOINT"
vector_store_password: str = "YOUR_AZURE_SEARCH_ADMIN_KEY"

创建嵌入和向量存储实例 (Create embeddings and vector store instances)

创建 OpenAIEmbeddings 和 AzureSearch 类的实例:

embeddings: OpenAIEmbeddings = OpenAIEmbeddings(deployment=model, chunk_size=1)
index_name: str = "langchain-vector-demo"
vector_store: AzureSearch = AzureSearch(
azure_search_endpoint=vector_store_address,
azure_search_key=vector_store_password,
index_name=index_name,
embedding_function=embeddings.embed_query,
)

将文本和嵌入插入向量存储

将JSON数据中的文本和元数据添加到向量存储中:

from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter

loader = TextLoader("../../../state_of_the_union.txt", encoding="utf-8")

documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

vector_store.add_documents(documents=docs)

Additionally, after translating the primary (or first-level) title, add the corresponding English text.

此外,在翻译主标题(或一级标题)后,添加相应的英文文本。

使用 similarity_search() 方法执行纯向量相似性搜索:

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

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

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

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

使用 search_typehybrid_search() 方法执行混合搜索:

# 执行混合搜索
docs = vector_store.similarity_search(
query="What did the president say about Ketanji Brown Jackson",
k=3,
search_type="hybrid"
)
print(docs[0].page_content)
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。顺便说一下,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

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

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

我在4天前做到了这一点,当时我提名了巡回上诉法院法官凯坦吉·布朗·杰克逊。她是我们国家最顶尖的法律智慧之一,将继续布雷耶法官的卓越传统。
# 执行混合搜索
docs = vector_store.hybrid_search(
query="What did the president say about Ketanji Brown Jackson",
k=3
)
print(docs[0].page_content)
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。顺便说一下,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

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

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

我在4天前做到了这一点,当时我提名了巡回上诉法院法官凯坦吉·布朗·杰克逊。她是我们国家最顶尖的法律智慧之一,将继续布雷耶法官的卓越传统。

使用自定义可过滤字段创建新索引 (Create a new index with custom filterable fields)

from azure.search.documents.indexes.models import (
SearchableField,
SearchField,
SearchFieldDataType,
SimpleField,
ScoringProfile,
TextWeights,
)

embeddings: OpenAIEmbeddings = OpenAIEmbeddings(deployment=model, chunk_size=1)
embedding_function = embeddings.embed_query

fields = [
SimpleField(
name="id",
type=SearchFieldDataType.String,
key=True,
filterable=True,
),
SearchableField(
name="content",
type=SearchFieldDataType.String,
searchable=True,
),
SearchField(
name="content_vector",
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
searchable=True,
vector_search_dimensions=len(embedding_function("Text")),
vector_search_configuration="default",
),
SearchableField(
name="metadata",
type=SearchFieldDataType.String,
searchable=True,
),
# 用于存储标题的附加字段
SearchableField(
name="title",
type=SearchFieldDataType.String,
searchable=True,
),
# 用于根据文档来源进行过滤的附加字段
SimpleField(
name="source",
type=SearchFieldDataType.String,
filterable=True,
),
]

index_name: str = "langchain-vector-demo-custom"

vector_store: AzureSearch = AzureSearch(
azure_search_endpoint=vector_store_address,
azure_search_key=vector_store_password,
index_name=index_name,
embedding_function=embedding_function,
fields=fields,
)

使用自定义过滤器执行查询 (Perform a query with a custom filter)

# metadata 字典中具有与索引中对应字段的数据将被添加到索引中
# 在此示例中,metadata 字典包含一个标题、一个来源和一个随机字段
# 标题和来源将作为单独的字段添加到索引中,但随机字段不会(因为它在字段列表中未定义)
# 随机字段将仅存储在 metadata 字段中
vector_store.add_texts(
["Test 1", "Test 2", "Test 3"],
[
{"title": "Title 1", "source": "A", "random": "10290"},
{"title": "Title 2", "source": "A", "random": "48392"},
{"title": "Title 3", "source": "B", "random": "32893"},
],
)
res = vector_store.similarity_search(query="Test 3 source1", k=3, search_type="hybrid")
res
    [Document(page_content='Test 3', metadata={'title': 'Title 3', 'source': 'B', 'random': '32893'}),
Document(page_content='Test 1', metadata={'title': 'Title 1', 'source': 'A', 'random': '10290'}),
Document(page_content='Test 2', metadata={'title': 'Title 2', 'source': 'A', 'random': '48392'})]
res = vector_store.similarity_search(query="Test 3 source1", k=3, search_type="hybrid", filters="source eq 'A'")
res
    [Document(page_content='Test 1', metadata={'title': 'Title 1', 'source': 'A', 'random': '10290'}),
Document(page_content='Test 2', metadata={'title': 'Title 2', 'source': 'A', 'random': '48392'})]

使用评分配置创建新索引 (Create a new index with a Scoring Profile)

from azure.search.documents.indexes.models import (
SearchableField,
SearchField,
SearchFieldDataType,
SimpleField,
ScoringProfile,
TextWeights,
ScoringFunction,
FreshnessScoringFunction,
FreshnessScoringParameters
)

embeddings: OpenAIEmbeddings = OpenAIEmbeddings(deployment=model, chunk_size=1)
embedding_function = embeddings.embed_query

fields = [
SimpleField(
name="id",
type=SearchFieldDataType.String,
key=True,
filterable=True,
),
SearchableField(
name="content",
type=SearchFieldDataType.String,
searchable=True,
),
SearchField(
name="content_vector",
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
searchable=True,
vector_search_dimensions=len(embedding_function("Text")),
vector_search_configuration="default",
),
SearchableField(
name="metadata",
type=SearchFieldDataType.String,
searchable=True,
),
# 用于存储标题的附加字段
SearchableField(
name="title",
type=SearchFieldDataType.String,
searchable=True,
),
# 用于根据文档来源进行过滤的附加字段
SimpleField(
name="source",
type=SearchFieldDataType.String,
filterable=True,
),
# 用于最后一次文档更新的附加数据字段
SimpleField(
name="last_update",
type=SearchFieldDataType.DateTimeOffset,
searchable=True,
filterable=True
)
]
# 添加具有新的鲜度函数的自定义评分配置
sc_name = "scoring_profile"
sc = ScoringProfile(
name=sc_name,
text_weights=TextWeights(weights={"title": 5}),
function_aggregation="sum",
functions=[
FreshnessScoringFunction(
field_name="last_update",
boost=100,
parameters=FreshnessScoringParameters(boosting_duration="P2D"),
interpolation="linear"
)
]
)

index_name = "langchain-vector-demo-custom-scoring-profile"

vector_store: AzureSearch = AzureSearch(
azure_search_endpoint=vector_store_address,
azure_search_key=vector_store_password,
index_name=index_name,
embedding_function=embeddings.embed_query,
fields=fields,
scoring_profiles = [sc],
default_scoring_profile = sc_name
)
# 添加具有不同 last_update 的相同数据以展示评分配置的效果
from datetime import datetime, timedelta

today = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S-00:00')
yesterday = (datetime.utcnow() - timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%S-00:00')
one_month_ago = (datetime.utcnow() - timedelta(days=30)).strftime('%Y-%m-%dT%H:%M:%S-00:00')

vector_store.add_texts(
["Test 1", "Test 1", "Test 1"],
[
{"title": "Title 1", "source": "source1", "random": "10290", "last_update": today},
{"title": "Title 1", "source": "source1", "random": "48392", "last_update": yesterday},
{"title": "Title 1", "source": "source1", "random": "32893", "last_update": one_month_ago},
],
)
    ['NjQyNTI5ZmMtNmVkYS00Njg5LTk2ZDgtMjM3OTY4NTJkYzFj',
'M2M0MGExZjAtMjhiZC00ZDkwLThmMTgtODNlN2Y2ZDVkMTMw',
'ZmFhMDE1NzMtMjZjNS00MTFiLTk0MTEtNGRkYjgwYWQwOTI0']
res = vector_store.similarity_search(query="Test 1", k=3, search_type="hybrid")
res
    [Document(page_content='Test 1', metadata={'title': 'Title 1', 'source': 'source1', 'random': '10290', 'last_update': '2023-07-13T10:47:39-00:00'}),
Document(page_content='Test 1', metadata={'title': 'Title 1', 'source': 'source1', 'random': '48392', 'last_update': '2023-07-12T10:47:39-00:00'}),
Document(page_content='Test 1', metadata={'title': 'Title 1', 'source': 'source1', 'random': '32893', 'last_update': '2023-06-13T10:47:39-00:00'})]