浅谈用Azure AI Search实现RAG (1)---基本概念

序言

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

RAG

首先说一下RAG吧。

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

  1. 检索(Retrieval)​:首先它从数据库、文档或 Web 等外部源检索相关信息。
  2. 生成(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参与

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... 这个例子里就拿价格做过滤,然后按照地理位置远近排序

sql 复制代码
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一般会包含以下步骤

  1. 连接数据源
  2. Document Trunk(把文件分成小块,不然模型一口吞不下)
  3. Embedding(嵌入)
  4. Content Enrichment(不知道怎么翻译成中文最好,"内容扩充"?这一步会产生一些额外的metadata)
  5. 最后一步叫"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字段,Child index也有parent id字段,这样把他们联系起来。

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

Embedding

提到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可以直接用。

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的实例

json 复制代码
{
  "@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

json 复制代码
"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产生一对多的父子关系。

json 复制代码
  "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生成的答案。

相关推荐
x-cmd4 小时前
[250401] OpenAI 向免费用户开放 GPT-4o 图像生成功能 | Neovim 0.11 新特性解读
人工智能·gpt·文生图·openai·命令行·neovim
鸿蒙布道师17 小时前
OpenAI战略转向:开源推理模型背后的行业博弈与技术趋势
人工智能·深度学习·神经网络·opencv·自然语言处理·openai·deepseek
ssshooter1 天前
2025 最新 AI 模型深度对比:ChatGPT、Claude、Gemini到底选谁?
程序员·aigc·openai
新智元1 天前
美国 CS 专业卷上天,满分学霸惨遭藤校全拒!父亲大受震撼引爆热议
人工智能·openai
新智元1 天前
美国奥数题撕碎 AI 数学神话,顶级模型现场翻车!最高得分 5%,DeepSeek 唯一逆袭
人工智能·openai
新智元2 天前
吉卜力太火,奥特曼求饶!GPT-4o 免费生图登王座,设计师直呼天塌了
人工智能·openai
新智元2 天前
OpenAI 要 Open 了!奥特曼开源首个推理模型,ChatGPT 一小时暴增百万用户
人工智能·openai
新智元2 天前
DeepSeek-V3 击败 R1 开源登顶!杭州黑马撼动硅谷 AI 霸主,抹去 1 万亿市值神话
人工智能·openai
桜吹雪2 天前
手把手教你在浏览器中处理流式传输(Event Stream/SSE)
前端·langchain·openai