你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
本教程介绍如何使用 Azure AI 内容理解创建检索扩充生成 (RAG) 解决方案。 它介绍了构建强大的 RAG 系统的关键步骤,提供了改进相关性和准确性的提示,并演示如何与其他 Azure 服务连接。 最后,可以使用内容理解来处理多模式数据、改进检索并帮助 AI 模型提供准确而有意义的响应。
本教程中包含的练习
- 创建分析器。 了解如何创建可重用的分析器,以使用内容提取从多模式数据中提取结构化内容。
- 使用字段提取生成目标元数据。 了解如何使用 AI 生成进一步的元数据,例如摘要或关键主题,以扩充提取的内容。
- 预处理提取的内容。 探索将提取的内容转换为矢量嵌入的方法,以便进行语义搜索和检索。
- 设计统一索引。 开发统一的 Azure AI 搜索索引,用于集成和组织多模式数据,以便高效检索。
- 语义区块检索。 提取上下文相关信息,以便为用户查询提供更精确的有意义的答案。
- 使用聊天模型与数据交互 使用 Azure OpenAI 聊天模型与索引数据互动,实现对话搜索、查询和答案。
先决条件
首先需要一个有效的 Azure 订阅。 如果没有 Azure 订阅,可以创建一个免费订阅。
拥有 Azure 订阅后,请在 Azure 门户中创建 Azure AI Foundry 资源 。 此多服务资源允许使用一组凭据访问多个 Azure AI 服务。
此资源在门户中的AI Foundry>AI Foundry类别下列出。
重要
Azure 为 Azure AI 服务提供多个资源类型。 请确保选择“AI Foundry”>“AI Foundry”下面列出的那个选项,如下图所示。 有关详细信息,请参阅 创建 Azure AI Foundry 资源。
Azure AI 搜索资源: 设置 Azure AI 搜索资源 以启用多模式数据的索引和检索。
Azure OpenAI 聊天模型部署: 部署支持对话交互的 Azure OpenAI 聊天模型 。
嵌入模型部署: 确保已部署嵌入模型,以生成语义搜索的矢量表示形式。
API 版本: 本教程使用最新的预览 版 API 版本:
2024-12-01-preview
。Python 环境: 安装 Python 3.11 以执行提供的代码示例和脚本。
本教程遵循此示例代码可在 Python 笔记本中找到。 按照 自述文件 创建基本资源,授予资源适当的访问控制(IAM)角色,并安装本教程所需的所有包。
本教程中使用的多模式数据包括文档、图像、音频和视频。 它们旨在指导你完成使用 Azure AI 内容理解构建可靠的 RAG 解决方案的过程。
提取数据
检索扩充生成(RAG*)是一种通过集成外部知识源中的数据增强大型语言模型(LLM)功能的方法。 构建可靠的多模式 RAG 解决方案首先从各种内容类型中提取和构建数据。 Azure AI 内容理解提供了三个关键组件来促进此过程: 内容提取、 字段提取和分析 器。 这些组件共同构成了为 RAG 工作流创建统一、可重用和增强的数据管道的基础。
实施步骤
若要在内容理解中实现数据提取,请执行以下步骤:
创建分析器: 使用 REST API 或 Python 代码示例定义分析器。
执行内容提取: 使用分析器处理文件和提取结构化内容。
(可选) 使用字段提取增强: (可选)指定 AI 生成的字段,以使用添加的元数据来扩充提取的内容。
创建分析器
分析器是内容理解中的可重用组件,可简化数据提取过程。 创建分析器后,可以重复使用它来处理文件,并根据预定义的架构提取内容或字段。 分析器充当如何处理数据的蓝图,确保跨多个文件和内容类型的一致性和效率。
以下代码示例演示如何为每个形式创建分析器,并指定要提取的结构化数据,例如关键字段、摘要或分类。 这些分析器是提取和扩充 RAG 解决方案中内容的基础。
从 Langchain 加载所有环境变量和必要的库
import os
from dotenv import load_dotenv
load_dotenv()
# Load and validate Azure AI Services configs
AZURE_AI_SERVICE_ENDPOINT = os.getenv("AZURE_AI_SERVICE_ENDPOINT")
AZURE_AI_SERVICE_API_VERSION = os.getenv("AZURE_AI_SERVICE_API_VERSION") or "2024-12-01-preview"
AZURE_DOCUMENT_INTELLIGENCE_API_VERSION = os.getenv("AZURE_DOCUMENT_INTELLIGENCE_API_VERSION") or "2024-11-30"
# Load and validate Azure OpenAI configs
AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME")
AZURE_OPENAI_CHAT_API_VERSION = os.getenv("AZURE_OPENAI_CHAT_API_VERSION") or "2024-08-01-preview"
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME")
AZURE_OPENAI_EMBEDDING_API_VERSION = os.getenv("AZURE_OPENAI_EMBEDDING_API_VERSION") or "2023-05-15"
# Load and validate Azure Search Services configs
AZURE_SEARCH_ENDPOINT = os.getenv("AZURE_SEARCH_ENDPOINT")
AZURE_SEARCH_INDEX_NAME = os.getenv("AZURE_SEARCH_INDEX_NAME") or "sample-doc-index"
# Import libraries from Langchain
from langchain import hub
from langchain_openai import AzureChatOpenAI
from langchain_openai import AzureOpenAIEmbeddings
from langchain.schema import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain.text_splitter import MarkdownHeaderTextSplitter
from langchain.vectorstores.azuresearch import AzureSearch
from langchain_core.prompts import ChatPromptTemplate
from langchain.schema import Document
import requests
import json
import sys
import uuid
from pathlib import Path
from dotenv import find_dotenv, load_dotenv
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
# Add the parent directory to the path to use shared modules
parent_dir = Path(Path.cwd()).parent
sys.path.append(str(parent_dir))
代码示例:创建分析器
from pathlib import Path
from python.content_understanding_client import AzureContentUnderstandingClient
credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default")
#set analyzer configs
analyzer_configs = [
{
"id": "doc-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/content_document.json",
"location": Path("../data/sample_layout.pdf"),
},
{
"id": "image-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/image_chart_diagram_understanding.json",
"location": Path("../data/sample_report.pdf"),
},
{
"id": "audio-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/call_recording_analytics.json",
"location": Path("../data/callCenterRecording.mp3"),
},
{
"id": "video-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/video_content_understanding.json",
"location": Path("../data/FlightSimulator.mp4"),
},
]
# Create Content Understanding client
content_understanding_client = AzureContentUnderstandingClient(
endpoint=AZURE_AI_SERVICE_ENDPOINT,
api_version=AZURE_AI_SERVICE_API_VERSION,
token_provider=token_provider,
x_ms_useragent="azure-ai-content-understanding-python/content_extraction", # This header is used for sample usage telemetry, please comment out this line if you want to opt out.
)
# Iterate through each config and create an analyzer
for analyzer in analyzer_configs:
analyzer_id = analyzer["id"]
template_path = analyzer["template_path"]
try:
# Create the analyzer using the content understanding client
response = content_understanding_client.begin_create_analyzer(
analyzer_id=analyzer_id,
analyzer_template_path=template_path
)
result = content_understanding_client.poll_result(response)
print(f"Successfully created analyzer: {analyzer_id}")
except Exception as e:
print(f"Failed to create analyzer: {analyzer_id}")
print(f"Error: {e}")
注意: 字段提取架构是可选的,不需要执行内容提取。 若要在不定义字段架构的情况下执行内容提取和创建分析器,只需提供分析器 ID 和要分析的文件。
本教程使用了架构。 下面是架构定义的示例
在以下示例中,我们定义了一个架构,用于从发票文档中提取基本信息。
{
"description": "Sample invoice analyzer",
"scenario": "document",
"config": {
"returnDetails": true
},
"fieldSchema": {
"fields": {
"VendorName": {
"type": "string",
"method": "extract",
"description": "Vendor issuing the invoice"
},
"Items": {
"type": "array",
"method": "extract",
"items": {
"type": "object",
"properties": {
"Description": {
"type": "string",
"method": "extract",
"description": "Description of the item"
},
"Amount": {
"type": "number",
"method": "extract",
"description": "Amount of the item"
}
}
}
}
}
}
}
内容和字段提取
内容提取 是 RAG 实现过程中的第一步。 它将原始多模式数据转换为结构化的可搜索格式。 此基本步骤可确保内容已组织和准备好编制索引和检索。 虽然内容提取提供了索引和检索的基线,但它可能无法完全满足特定于域的需求或提供更深入的上下文见解。 详细了解 每种形式的内容提取功能。
字段提取 基于内容提取,使用 AI 生成扩充知识库的进一步元数据。 此步骤允许你定义针对特定用例定制的自定义字段,从而实现更精确的检索和增强的搜索相关性。 字段提取通过添加深度和上下文来补充内容提取,使数据对 RAG 方案更具可作性。 详细了解 每种形式的字段提取功能。
通过为每个形式创建的分析器,我们现在可以处理文件,以基于定义的架构提取结构化内容和 AI 生成的元数据。 本部分演示如何使用分析器分析多模式数据,并提供 API 返回的结果示例。 这些结果展示了将原始数据转换为可作的见解,从而形成索引、检索和 RAG 工作流的基础。
分析文件
#Iterate through each analyzer created and analyze content for each modality
analyzer_results =[]
extracted_markdown = []
analyzer_content = []
for analyzer in analyzer_configs:
analyzer_id = analyzer["id"]
template_path = analyzer["template_path"]
file_location = analyzer["location"]
try:
# Analyze content
response = content_understanding_client.begin_analyze(analyzer_id, file_location)
result = content_understanding_client.poll_result(response)
analyzer_results.append({"id":analyzer_id, "result": result["result"]})
analyzer_content.append({"id": analyzer_id, "content": result["result"]["contents"]})
except Exception as e:
print(e)
print("Error in creating analyzer. Please double-check your analysis settings.\nIf there is a conflict, you can delete the analyzer and then recreate it, or move to the next cell and use the existing analyzer.")
print("Analyzer Results:")
for analyzer_result in analyzer_results:
print(f"Analyzer ID: {analyzer_result['id']}")
print(json.dumps(analyzer_result["result"], indent=2))
# Delete the analyzer if it is no longer needed
#content_understanding_client.delete_analyzer(ANALYZER_ID)
提取结果
以下代码示例演示如何使用 Azure AI 内容理解输出内容和字段提取。 JSON 响应包含多个字段,每个字段在表示提取的数据时提供特定用途。
Markdown 字段:该
markdown
字段提供提取内容的简化且可读的表示形式。 这对于快速预览或将提取的数据集成到需要结构化文本的应用程序(如知识库或搜索界面)尤其有用。 例如,对于文档,markdown
该字段可能包含标题、段落和其他格式的结构元素,以便易于阅读。JSON 输出:完整的 JSON 输出提供提取数据的综合表示形式,包括提取过程中生成的内容和元数据,包括以下属性:
- 领域: AI 生成的元数据(如摘要、关键主题或分类)根据分析器中定义的特定架构进行定制。
- 置信度分数: 提取数据的可靠性指标。
- 范围:有关源文件中已提取内容的位置的信息。
- 其他元数据: 页码、维度和其他上下文信息等详细信息。
结果显示提取标头、段落、表和其他结构元素,同时维护内容的逻辑组织。 此外,它还展示了提取关键字段的能力,对冗长材料进行简洁提取。
{
"id": "bcf8c7c7-03ab-4204-b22c-2b34203ef5db",
"status": "Succeeded",
"result": {
"analyzerId": "training_document_analyzer",
"apiVersion": "2024-12-01-preview",
"createdAt": "2024-11-13T07:15:46Z",
"warnings": [],
"contents": [
{
"markdown": "CONTOSO LTD.\n\n\n# Contoso Training Topics\n\nContoso Headquarters...",
"fields": {
"ChapterTitle": {
"type": "string",
"valueString": "Risks and Compliance regulations",
"spans": [ { "offset": 0, "length": 12 } ],
"confidence": 0.941,
"source": "D(1,0.5729,0.6582,2.3353,0.6582,2.3353,0.8957,0.5729,0.8957)"
},
"ChapterAuthor": {
"type": "string",
"valueString": "John Smith",
"spans": [ { "offset": 0, "length": 12 } ],
"confidence": 0.941,
"source": "D(1,0.5729,0.6582,2.3353,0.6582,2.3353,0.8957,0.5729,0.8957)"
},
"ChapterPublishDate": {
"type": "Date",
"valueString": "04-11-2017",
"spans": [ { "offset": 0, "length": 12 } ],
"confidence": 0.941,
"source": "D(1,0.5729,0.6582,2.3353,0.6582,2.3353,0.8957,0.5729,0.8957)"
},
},
"kind": "document",
"startPageNumber": 1,
"endPageNumber": 1,
"unit": "inch",
"pages": [
{
"pageNumber": 1,
"angle": -0.0039,
"width": 8.5,
"height": 11,
"spans": [ { "offset": 0, "length": 1650 } ],
"words": [
{
....
},
],
"lines": [
{
...
},
]
}
],
}
]
}
}
内容理解的预处理输出
使用 Azure AI 内容理解提取数据后,下一步是准备分析输出以嵌入搜索系统。 预处理输出可确保提取的内容转换为适合索引和检索的格式。 此步骤涉及将分析器中的 JSON 输出转换为结构化字符串,同时保留内容和元数据,以便无缝集成到下游工作流。
以下示例演示如何预处理分析器中的输出数据,包括文档、图像、音频和视频。 将每个 JSON 输出转换为结构化字符串的过程为将数据嵌入基于矢量的搜索系统奠定了基础,可实现高效的检索和增强的 RAG 工作流。
def convert_values_to_strings(json_obj):
return [str(value) for value in json_obj]
#process all content and convert to string
def process_allJSON_content(all_content):
# Initialize empty list to store string of all content
output = []
document_splits = [
"This is a json string representing a document with text and metadata for the file located in "+str(analyzer_configs[0]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[0]["content"])
]
docs = [Document(page_content=v) for v in document_splits]
output += docs
#convert image json object to string and append file metadata to the string
image_splits = [
"This is a json string representing an image verbalization and OCR extraction for the file located in "+str(analyzer_configs[1]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[1]["content"])
]
image = [Document(page_content=v) for v in image_splits]
output+=image
#convert audio json object to string and append file metadata to the string
audio_splits = [
"This is a json string representing an audio segment with transcription for the file located in "+str(analyzer_configs[2]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[2]["content"])
]
audio = [Document(page_content=v) for v in audio_splits]
output += audio
#convert video json object to string and append file metadata to the string
video_splits = [
"The following is a json string representing a video segment with scene description and transcript for the file located in "+str(analyzer_configs[3]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[3]["content"])
]
video = [Document(page_content=v) for v in video_splits]
output+=video
return output
all_splits = process_allJSON_content(analyzer_content)
print("There are " + str(len(all_splits)) + " documents.")
# Print the content of all doc splits
for doc in all_splits:
print(f"doc content", doc.page_content)
嵌入和索引提取的内容
在完成从 Azure AI 内容理解中预处理提取的数据后,下一步是嵌入和编制内容索引,以便高效检索。 此步骤涉及使用嵌入模型将结构化字符串转换为矢量嵌入,并将其存储在 Azure AI 搜索系统中。 通过嵌入内容,可以启用语义搜索功能,使系统能够基于含义而不是确切的关键字匹配来检索最相关的信息。 此步骤对于构建可靠的 RAG 解决方案至关重要,因为它可确保提取的内容针对高级搜索和检索工作流进行优化。
# Embed the splitted documents and insert into Azure Search vector store
def embed_and_index_chunks(docs):
aoai_embeddings = AzureOpenAIEmbeddings(
azure_deployment=AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME,
openai_api_version=AZURE_OPENAI_EMBEDDING_API_VERSION, # e.g., "2023-12-01-preview"
azure_endpoint=AZURE_OPENAI_ENDPOINT,
azure_ad_token_provider=token_provider
)
vector_store: AzureSearch = AzureSearch(
azure_search_endpoint=AZURE_SEARCH_ENDPOINT,
azure_search_key=None,
index_name=AZURE_SEARCH_INDEX_NAME,
embedding_function=aoai_embeddings.embed_query
)
vector_store.add_documents(documents=docs)
return vector_store
# embed and index the docs:
vector_store = embed_and_index_chunks(all_splits)
语义区块检索
嵌入并编制索引提取的内容后,下一步是使用相似性和矢量搜索的强大功能来检索最相关的信息区块。 本部分演示如何执行相似性和混合搜索,使系统能够基于语义含义而不是完全匹配关键字来显示内容。 通过检索上下文相关的区块,可以增强 RAG 工作流的精度,并为用户查询提供更准确、更有意义的响应。
# Set your query
query = "japan"
# Perform a similarity search
docs = vector_store.similarity_search(
query=query,
k=3,
search_type="similarity",
)
for doc in docs:
print(doc.page_content)
# Perform a hybrid search using the search_type parameter
docs = vector_store.hybrid_search(query=query, k=3)
for doc in docs:
print(doc.page_content)
使用 OpenAI 与数据交互
嵌入并编制索引提取的内容后,构建可靠的 RAG 解决方案的最后一步是使用 OpenAI 聊天模型实现对话交互。 本部分演示如何查询索引数据并应用 OpenAI 聊天模型,以提供简洁、上下文丰富的答案。 通过将对话式 AI 集成,可以将 RAG 解决方案转换为交互式系统,从而提供有意义的见解并增强用户参与度。 以下示例指导你设置检索增强型聊天流,确保数据与 OpenAI 聊天模型之间的无缝集成。
# Setup rag chain
prompt_str = """You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:"""
def setup_rag_chain(vector_store):
retriever = vector_store.as_retriever(search_type="similarity", k=3)
prompt = ChatPromptTemplate.from_template(prompt_str)
llm = AzureChatOpenAI(
openai_api_version=AZURE_OPENAI_CHAT_API_VERSION,
azure_deployment=AZURE_OPENAI_CHAT_DEPLOYMENT_NAME,
azure_ad_token_provider=token_provider,
temperature=0.7,
)
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
return rag_chain
# Setup conversational search
def conversational_search(rag_chain, query):
print(rag_chain.invoke(query))
rag_chain = setup_rag_chain(vector_store)
while True:
query = input("Enter your query: ")
if query=="":
break
conversational_search(rag_chain, query)