浅谈用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生成的答案。

相关推荐
x007xyz1 天前
🚀🚀🚀前端的无限可能-纯Web实现的字幕视频工具 FlyCut Caption
前端·openai·音视频开发
机器之心2 天前
首个代码世界模型引爆AI圈,能让智能体学会「真推理」,Meta开源
人工智能·openai
安思派Anspire2 天前
这不是炒作——Claude Code证明未来已然到来
aigc·openai
机器之心3 天前
大模型七连发,外国人馋透了!阿里云栖大会全栈升级够狠
人工智能·openai
机器之心3 天前
Sam Altman发文,透露OpenAI正在干的大事业
人工智能·openai
鸽芷咕3 天前
告别Excel熬夜!基于LazyLLM框架打造财报分析Agent 副本
openai·agent
新智元3 天前
奥特曼刚刚发文,10GW 核爆级算力!每周一座核电站,五座新城官宣
人工智能·openai
机器之心8 天前
英伟达50亿美元入股英特尔,将发布CPU+GPU合体芯片,大结局来了?
人工智能·openai
新智元8 天前
芯片大地震,黄仁勋355亿入股!英特尔要为老黄造CPU,股价狂飙30%
人工智能·openai
新智元9 天前
阿里王牌 Agent 横扫 SOTA,全栈开源力压 OpenAI!博士级难题一键搞定
人工智能·openai