掘金 人工智能 04月02日
浅谈用Azure AI Search实现RAG (1)---基本概念
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了基于Azure AI Search的RAG(检索增强生成)技术,重点介绍了其核心组成部分:数据源、Azure AI Search索引创建与检索、以及Azure Open AI的结合应用。文章详细阐述了索引创建的步骤,包括数据源连接、索引字段定义、矢量索引(Vector Index)的配置与应用,以及如何通过Indexer管理数据,构建完整的RAG解决方案。作者还分享了对RAG方案的理解与实践经验,为读者提供了宝贵的参考。

🔍 RAG技术的核心在于检索(Retrieval)和生成(Generation),前者从外部源检索相关信息,后者利用检索到的信息增强生成回复。

🗂️ Azure AI Search的索引(Index)类似于数据库中的表,包含支持传统全文搜索和矢量搜索的字段,矢量索引用于语义搜索,将文本转化为高维向量进行比对。

⚙️ 创建Azure AI Search Index需要连接数据源、定义索引字段,并使用Indexer来管理数据。Indexer包含数据分块(Chunk)、嵌入(Embedding)、内容扩充(Content Enrichment)和索引映射(Index Mapping)等步骤,构建数据处理的Pipeline。

🧩 数据分块(Chunk)策略对RAG效果至关重要,推荐使用“Document Layout”Skill,根据语义分割文本。Content Enrichment通过Skill扩充索引字段,比如提取邮箱或识别图片文字。

💡 Indexer是管理Azure Search Index数据的枢纽,它整合了数据源连接、索引字段定义、Skill Set和Index Mapping等配置,并提供执行结果的查看。

序言

最近在学习Azure AI Search(其实我好几年前接触过它,我记得2021年它还叫Azure Search,但那个时候还是传统search的路数,现在真的变化很大),想归纳整理一下我自己的理解。抛砖引玉,欢迎大家指教!

RAG

首先说一下RAG吧。

RAG(Retrieval Augmented Generation,检索增强生成)是现在非常火的AI技术,让LLM模型依据咱自己的知识库进行回答。RAG有两个核心步骤

    检索(Retrieval)​:首先它从数据库、文档或 Web 等外部源检索相关信息。​生成(Generation)​:然后收集到相关信息后,就会将其用于指导和增强生成的回复

截止2025年的微软的技术体系里,其实有好几种方法来实现RAG(可能不止我说的这几种)

    Copilot Studio 这个我玩过,可以用public web site/sharepoint pages and documents/dataverse table/Azure AI Search来作为知识库,dataverse的数据它还能进行简单的聚合计算SharePoint Agent 用SharePoint站点内容创建的应答机器人,要是有Copilot 365license那就是免费的。去年出来的,我没有试过。。。Power BI Copilot这个好像要那个5000美刀一个月的PowerBI Premium Capacity license才行,实在没实力接触。。。Power BI的data model现在都叫“Semantic model"了(语义模型),这个功能应该很牛逼。最后就是本文要说的Azure AI Search+Azure Open AI了. 检索(Retrieval) 交给Azure AI Search,生成(Generation) 交给Azure Open AI. 这个方案应该是最支持扩展的。

基于Azure AI Search的RAG Solution

以下是我自己的理解,可能有偏颇,欢迎大家指正。(这个是微软官方的资料: learn.microsoft.com/en-us/azure… ,术语有点多,建议中文英文切换着看)

总的来说,Azure AI Search的RAG solution的后台部分主要由三大部分组成

    数据源, 如果是文件的话一般是 Azure Blob Storage(大家可以理解成网盘),图中以下service都能成为Azure AI Search的数据源

    Azure AI Search,用来创建索引以及检索Azure Open AI,大语言模型,用以生成回复,在对源数据进行矢量化(vectorization)的时候也需要Azure Open AI参与

创建Azure AI Search Index(索引)

Index

创建Azure AI Search Index第一步就是连接数据源,然后创建index。这个章节讲的“index”其实是一个专门的术语,如果你把Azure AI Search想象成一个数据库,search这个行为想象成对数据库进行查询,那么“index”就类似数据库里的表,“index field”是表里的字段,具体的索引文件类似表里的每个具体的record。"index"就好比数据库里表的定义(类似SharePoint的Search Schema),如何填充这张表是“Indexer”的事情,index只关心这张表本身。

选择数据源后,Azure AI Search的向导会根据数据源的schema自动生成一些字段,你也可以自己做一些调整。

Azure AI Search Index里的字段其实可以分成两类,一种是支持传统full text search那种字段,字段有一堆是否searchable/facetable/retriveable/sortable的属性,和SharePoint Search非常相似。

你可以用这些属性来进一步缩小搜索范围,比如learn.microsoft.com/zh-cn/azure… 这个例子里就拿价格做过滤,然后按照地理位置远近排序

POST /indexes/hotels/docs/search?api-version=2024-07-01 { "search": "Spacious, air-condition* +\"Ocean view\"", "searchFields": "description, title", "searchMode": "any", "filter": "price ge 60 and price lt 300", "orderby": "geo.distance(location, geography'POINT(-159.476235 22.227659)')","queryType": "full" }

还有一种矢量索引字段 (Vector Index)。

Vector这个词我感觉我们国内通常翻译成“向量”,高等数学,线性代数好像都用“向量”,但是微软官网翻译成“矢量”,咱们这里就沿用官方的御用翻译吧。

体验过传统search(现在各大主流搜索引擎好像都加了语义识别来精炼出关键词,现在体验又不一样了)与基于大语言模型的AI的话,会发现他们的查询输入方式完全不一样。传统search实际上是关键词的search,把最主要的关键词放到查询里即可。你要是多加了一些次要的词的话很有可能什么都匹配不到了。传统search一般也不会让你打很多字上去。

大语言模型AI则是你说得越多,AI越是能理解你到底要什么,给出的回复质量越是高。它会判断你的具体意图到底是什么,如果你言简意赅的话还会帮你脑补补完你的意图(这点DeepSeek上体现得很明显,特别是你要选了“深度思考”的话,它会脑补出好多好多东西)

然后把分析出来的你的具体意图转化成高维空间向量(这个过程叫做“Embedding(嵌入)”)

比如我输入“什么是人工智能”,LLM的嵌入模型(例如text-embedding-3-large)会把它转换为一个高维向量(例如:[0.23, -0.45, 0.89, ..., 0.12])。这个“Embedding”可能有点抽象,我(一个没怎么系统学过机器学习的学渣 )个人理解是这个向量体现对应事物的特征(想起很久很久以前学校学的主成分分析)。特征相似(语义接近)的事物在这个向量空间的位置也比较接近,这样就可以做比对查找了。这种搜索就是矢量搜索(Vector Search).

万事万物皆可“Embedding”,传统search只能根据图片的标签来搜索,矢量索引就可以直接对比图片本身。

下图"text_vector"这个field就是一个矢量字段,它有3072个维度。某个index document里具体的vector数据(可以看到右边缩略图里有很长的数据)

Vector Index有很多设置(比如哪些字段和vector index相关,embedding模型选哪种,向量化用的是哪种算法,如何压缩索引),需要配置相应的Vector Profile,概念还蛮多的。

创建包含矢量索引的Search Indexer的大致步骤

这是微软的原文:learn.microsoft.com/en-us/azure… ,我讲讲自己的理解。

有了数据库的表(index)之后,我们还得导入数据进去,这个活是由Indexer来干的。

一个比较典型的含有Vector的Indexer一般会包含以下步骤

    连接数据源Document Trunk(把文件分成小块,不然模型一口吞不下)Embedding(嵌入)Content Enrichment(不知道怎么翻译成中文最好,”内容扩充“?这一步会产生一些额外的metadata)最后一步叫“index mapping”,把上述步骤产生的output给组装成json文件,最后塞到index(塞到数据库)里去。“index mapping"本身还分index projection/field mapping/output field mapping等好几种。

(这个是一个我项目里debug索引的截图,我感觉很适合拿来讲解创建索引的流程)

下面我们分别来介绍一下。

对文件进行分块(Chunk)

我们需要用嵌入模型(embedding model)来对文件向量化。大家知道,模型是有最大token的输入限制的(比如text-embedding-ada-002 model的最大token是8191,一个token对应4个character,8191个token大致是五六千英文单词),太大的文件不拆分肯定没法塞进去。所以第一步要对文件进行分块(Chunk)。

Chunk Document里详细介绍了文件分块的策略以及注意事项。Chunk通常是整个RAG解决方案的第一步,Chunk策略选择得当与否对整个RAG的效果有关键影响。要是Chunk做得不理想搞不好就“断章取义”了。

微软现在比较推荐用一种叫“Document Layout”的“Skill(技能)”来对文件进行Chunk。这种技能可以

根据段落或句子表示的语义上连贯的片段对内容进行分块。 然后,这些片段可以独立处理,并重新组合为语义表征,而不会丢失信息、解释或语义相关性。 文本的固有含义会用作分块过程的指导。

现在这个Skill还处于preview阶段,大家可以自己试试。

Chunk把一个大文件拆分成好几个小块,这样就会产生一对多索引(one to many index)以及父子层级关系.一般要求子级的索引也能追溯父节点的相关属性,例如文件名,作者,修改时间等,这就涉及了索引投影(index projection)以及创建父子索引对(简单地说就是Parend Index有个parent id字段,Childindex也有parent id字段,这样把他们联系起来。

详情可以看learn.microsoft.com/zh-cn/azure…

提到Embedding首先要选Embedding Model,Azure AI Search自己不提供,你得去Azure Open AI上创建embedding model

注意:Azure Open AI Service和Azure AI Search必须在同一个region(区域),不然他们之间没法互相调用

Content Enrichment

简单的说,Content Enrichment就是对原始数据进行处理,扩充索引字段,比如说用Entity Extract把内容里的邮箱地址给抓取出来,或者用OCR把图片里的文字给识别出来。从广义上来说,文件向量化这件事本身也是一种Content Enrichment。

每一种Content Enrichment的处理方式都是一个"Skill".微软有很多现成的Skill可以直接用。

Azure AI Search Index Pipeline的每一步操作都要用skill来实现,比如上文说的“Chunk",你要选具体的skill来实现(前面说的"Document Layout"就是一个用于Chunk的skill)。skill设置里会有context(上下文),input(输入),output(输出)

一群skill组成一个Skill Set(技能集合),某些skill的output会是另一些skill的input,由此形成它们之间的依赖关系,成为整个一个pipeline。

一个skill set的实例

{  "@odata.etag": "\"0x8DD65BCE8953FA9\"",  "name": "vector-index-nasa-skillset",  "description": "Skillset to chunk documents and generate embeddings",  "skills": [    {      "@odata.type": "#Microsoft.Skills.Text.SplitSkill",      "name": "#1",      "description": "Split skill to chunk documents",      "context": "/document",      "defaultLanguageCode": "en",      "textSplitMode": "pages",      "maximumPageLength": 2000,      "pageOverlapLength": 500,      "maximumPagesToTake": 0,      "unit": "characters",      "inputs": [        {          "name": "text",          "source": "/document/content",          "inputs": []        }      ],      "outputs": [        {          "name": "textItems",          "targetName": "pages"        }      ]    },    {      "@odata.type": "#Microsoft.Skills.Text.AzureOpenAIEmbeddingSkill",      "name": "#2",      "context": "/document/pages/*",      "resourceUri": "https://xxx-ai-service-east-us.openai.azure.com",      "apiKey": "<redacted>",      "deploymentId": "text-embedding-3-large-3",      "dimensions": 3072,      "modelName": "text-embedding-3-large",      "inputs": [        {          "name": "text",          "source": "/document/pages/*",          "inputs": []        }      ],      "outputs": [        {          "name": "embedding",          "targetName": "text_vector"        }      ]    },    {      "@odata.type": "#Microsoft.Skills.Text.V3.EntityRecognitionSkill",      "name": "#3",      "context": "/document/pages/*",      "categories": [        "Location"      ],      "defaultLanguageCode": "en",      "inputs": [        {          "name": "text",          "source": "/document/pages/*",          "inputs": []        }      ],      "outputs": [        {          "name": "locations",          "targetName": "locations"        }      ]    }  ],  "cognitiveServices": {    "@odata.type": "#Microsoft.Azure.Search.AIServicesByKey",    "subdomainUrl": "https://xxx-ai-service-us-east.cognitiveservices.azure.com/"  },  "indexProjections": {    "selectors": [      {        "targetIndexName": "vector-index-nasa",        "parentKeyFieldName": "parent_id",        "sourceContext": "/document/pages/*",        "mappings": [          {            "name": "text_vector",            "source": "/document/pages/*/text_vector",            "inputs": []          },          {            "name": "chunk",            "source": "/document/pages/*",            "inputs": []          },          {            "name": "title",            "source": "/document/title",            "inputs": []          },          {            "name": "locations",            "source": "/document/pages/*/locations",            "inputs": []          }        ]      }    ],    "parameters": {      "projectionMode": "skipIndexingParentDocuments"    }  }}

我个人感觉Skill很像Power Automate里的action,但是它的UI很弱,大多数时候你设置Skill都是直接编辑json文件,很多时候也不知道自己设得对不对。。。搞不清楚的话,在Debug Session里可以对Skill Set进行调试.

如果原生的Skill满足不了你,你还可以创建自定义的Skill,按照要求整一个Web API就行,具体例子可以看learn.microsoft.com/en-us/azure…顺便说一句,这个扩展的方法和SharePoint Search (on-Premise)的Content Enrichment Web Service很像(我十多年前就搞过这个。。。)

Index Mapping

这是创建index pipeline的最后一步,把源数据或者之前产生的中间结果给mapping到之前定义好的search index字段里,最后生成index文件(azure ai search的索引的每一条记录都是一个json document)

它实际上有好几种情况,一种是对数据源本身的字段做mapping,比如说数据源是一个cosmos db,它的json schema有*_city这个字段,我们要把它mapping到city*这个index字段。这个叫Field Mapping

"fieldMappings": [  {    "sourceFieldName": "_city",    "targetFieldName": "city",    "mappingFunction": null  }]

output field mapping用来mapping content enrichment新产生的数据。但是我感觉index projection也能处理同样的事情(至少在nasa earth book那个教程里,我没用到output field mapping),我对这块不是很了解。

前两个mapping都是配置在indexer(中文好像翻译成索引器,下文会介绍)里,但是index projection是配置在skill set里。官网说index projection的典型应用是处理Chunk Document产生一对多的父子关系。

  "indexProjections": {    "selectors": [      {        "targetIndexName": "vector-index-nasa",        "parentKeyFieldName": "parent_id",        "sourceContext": "/document/pages/*",        "mappings": [          {            "name": "text_vector",            "source": "/document/pages/*/text_vector",            "inputs": []          },          {            "name": "chunk",            "source": "/document/pages/*",            "inputs": []          },          {            "name": "title",            "source": "/document/title",            "inputs": []          },          {            "name": "locations",            "source": "/document/pages/*/locations",            "inputs": []          }        ]      }    ],    "parameters": {      "projectionMode": "skipIndexingParentDocuments"    }  }

Indexer

上述所有步骤/配置(连接数据源,创建index字段,Skill Set,index mapping等等)会组成Indexer。Indexer是管理Azure Search Index数据的枢纽,你可以看到这个index的具体配置信息

以及执行的结果

待续

我本来想先把基本概念一次性讲完的,结果写search index就断断续续花了一周时间。下篇会讲Azure AI Search如何查询数据,以及如何用检索出来的数据来增强LLM生成的答案。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Azure AI Search RAG 矢量搜索 索引 AI
相关文章