PAYATHON 2026

如何让 Azure AI Search 返回两行内答案及引用

技术老齐

结论

实现方式取决于答案要求:

  • 如果只需从原文抽取一段较短的答案,可以直接使用 Azure AI Search 的 semantic answers 和 captions。
  • 如果答案必须控制在两行内,还要整理多个百分比并附上引用,则需要在应用层接入 Azure OpenAI 或其他大语言模型。Azure AI Search 检索相关文本,模型再生成短答案。

当前返回内容过长,是因为查询结果包含了可检索的完整 content 字段,并非 semantic search 生成了长答案。使用 select 排除该字段,再读取 @search.answers 和 @search.captions,即可缩短返回内容。

修正示例中的数据含义

原文中的百分比按经济部门划分 GHG emissions:

  • Electricity & Heat Production:31%
  • Agriculture, Forestry & Other Land Use:20%
  • Industry:18%
  • Transportation:16%
  • Other Energy:9%
  • Buildings:6%

31% 指“电力和供热”,不能解释成“汽车”;20% 指“农业、林业及其他土地利用”,不能解释成“工厂”。按原文生成的简短回答可以写成:

电力和供热占31%,农业、林业及其他土地利用占20%,工业占18%,交通运输占16%,其他能源占9%,建筑占6%。

引用可以显示为:

Global Greenhouse Gas (GHG) Emissions by Economic Sector: Electricity & Heat Production 31%, Agriculture, Forestry & Other Land Use 20%, Industry 18%, Transportation 16%, Other Energy 9%, Buildings 6%.

直接使用 semantic answers 和 captions

查询时需启用 semantic query,并指定已经创建的 docs-test semantic configuration。

POST https://<search-service>.search.windows.net/indexes/<index-name>/docs/search?api-version=<supported-api-version>
Content-Type: application/json
api-key: <query-api-key>
{
  "search": "What are the percentages of GHG emissions by economic sector?",
  "queryType": "semantic",
  "semanticConfiguration": "docs-test",
  "answers": "extractive|count-1",
  "captions": "extractive|highlight-false",
  "select": "metadata_title,metadata_storage_path",
  "top": 3
}

不要在 select 中包含完整的 content。如果省略 select,所有设置为 retrievable 的字段都可能出现在结果中。

返回结果通常分为两部分:

{
  "@search.answers": [
    {
      "key": "document-or-chunk-id",
      "text": "Electricity and heat production account for 31% and transportation accounts for 16% of GHG emissions.",
      "highlights": null,
      "score": 0.98
    }
  ],
  "value": [
    {
      "metadata_title": "2019 Tesla Impact Report",
      "metadata_storage_path": "...",
      "@search.captions": [
        {
          "text": "Global Greenhouse Gas emissions by economic sector: Electricity and heat production 31%, agriculture and land use 20%, industry 18%, transportation 16%, other energy 9%, and buildings 6%.",
          "highlights": null
        }
      ]
    }
  ]
}

应用可以将 @search.answers[0].text 作为答案,并将排名最高结果中的 @search.captions[0].text 作为引用。

semantic answers 是抽取式答案,只会从索引内容中选择合适的原文片段,无法稳定完成改写、归纳或严格的两行长度控制。如果找不到置信度足够的片段,@search.answers 也可能为空。

调整索引结构

目前一个搜索结果似乎对应整份 PDF,因此 content 中包含了几十页文字。虽然已经配置 Text Split skill,但返回结果中的 split_text 是空数组。这通常表示拆分结果没有正确写入索引,或索引仍采用“一个源文件对应一个搜索文档”的保存方式。

问答检索更适合采用“一条索引记录对应一个文本块”的结构:

{
  "id": "unique-chunk-id",
  "parent_id": "source-document-id",
  "chunk": "该段落或页面附近的正文",
  "title": "2019 Tesla Impact Report",
  "section_title": "Global Greenhouse Gas Emissions by Economic Sector",
  "source_uri": "https://example.blob.core.windows.net/reports/impact-report.pdf",
  "page_number": 4
}

各字段可以这样设置:

字段建议用途
id索引主键
parent_id关联原始文档,建议设为 filterable
chunk主要检索内容,设为 searchable、retrievable
titlesemantic configuration 的 title field
section_title可放入 content fields 或 keywords fields
source_uri页面引用链接,设为 retrievable
page_number显示页码,设为 retrievable、filterable
content_vector使用 vector 或 hybrid search 时保存 embedding

Text Split skill 需要将拆分后的每个 chunk 投影为独立的搜索文档。如果只创建 split_text 集合字段,索引中保存的仍是整份文件,检索粒度不会得到充分改善。使用 index projections 时,需要确认以下配置:

  1. Text Split skill 的输出路径与 projection source context 一致。
  2. 每个 chunk 都写入一条独立的索引记录。
  3. 标题、Blob URL、页码等父文档信息同步到每个 chunk。
  4. 重新运行 indexer 后,检查 chunk 字段是否有值。
  5. 不要将相同的整篇文本同时写入 content 和 merged_content。

文本块不宜过大,具体大小需要结合文档结构测试。一个块最好包含完整的段落、表格或小节。必要时可以保留少量重叠,防止句子在块边界被截断。

调整 semantic configuration

改为 chunk 索引后,可以按以下方式配置 semantic configuration:

{
  "name": "docs-test",
  "prioritizedFields": {
    "titleField": {
      "fieldName": "title"
    },
    "prioritizedContentFields": [
      {
        "fieldName": "chunk"
      }
    ],
    "prioritizedKeywordsFields": [
      {
        "fieldName": "section_title"
      },
      {
        "fieldName": "keywords"
      }
    ]
  }
}

当前配置中的 metadata_title、merged_content 和 keywords 大量为 null,无法为 semantic ranking 提供有效信息。先检查 skillset 的 output field mappings 和 indexer field mappings,确保这些字段已写入数据。

semantic configuration 还应优先使用最有可能回答问题的字段。对于分块索引,正文块 chunk 通常比完整文档 content 更合适。

将答案严格限制为两行

semantic answers 无法保证字符数或显示行数。实际行数还会受到页面宽度、字体和终端的影响,因此应用通常需要同时限制内容长度和界面高度。

可以采用以下流程:

用户问题
  -> Azure AI Search semantic/hybrid query
  -> 取排名最高的若干 chunk
  -> 大语言模型根据 chunk 生成简短答案
  -> 返回答案、引用原文、文档 URL 和页码

模型提示词可以写成:

请只根据提供的资料回答问题。

要求:
1. 答案不超过80个中文字符,最多两句话。
2. 保留资料中的所有相关百分比及其准确类别。
3. 不得把“交通运输”改写成“汽车”,也不得把“工业”改写成“工厂”,除非原文明确这样表述。
4. 资料不足时返回“现有资料无法确定”。
5. 返回 JSON,不要输出其他内容。

输出格式:
{
  "answer": "简短答案",
  "citation_ids": ["chunk-id"]
}

应用层根据 citation_ids 找到相应的 chunk,再显示引用:

def build_response(search_response, model_result):
    chunks = {
        item["id"]: item
        for item in search_response["value"]
    }

    citations = []
    for chunk_id in model_result["citation_ids"]:
        chunk = chunks.get(chunk_id)
        if not chunk:
            continue

        citations.append({
            "text": chunk["chunk"],
            "title": chunk.get("title"),
            "url": chunk.get("source_uri"),
            "page": chunk.get("page_number")
        })

    return {
        "answer": model_result["answer"],
        "citations": citations
    }

引用文本和 URL 应直接取自 Search 返回的原始 chunk,不要让模型自行编写,以免答案与引用不一致。

前端也可以限制显示高度:

.answer {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

CSS 只能控制显示效果,不能保证语义完整。内容长度仍需通过生成提示、字符数校验和必要的重试来控制。

Basic tier 和演示应用

Basic tier 不会直接决定答案长短。能否使用 semantic ranker,还取决于服务所在区域、当前功能可用性和服务配置。部署前应在 Azure 门户中确认 semantic ranker 已启用,并核对当前计费和配额。

Azure AI Search 的 demo application 能否直接显示“两行答案加引用”,取决于生成 demo 时所用的模板。通用 Search explorer 主要用于检查原始搜索响应,不会自动按这种格式排版。带生成式问答能力的示例应用可以修改提示词和结果模板;普通搜索页面则需要增加一层后端编排。

排查顺序

可以按以下顺序检查当前配置:

  1. 在 Search explorer 中使用 semantic query,并加入 answers 和 captions。
  2. 使用 select 排除完整 content,确认查询结果不再直接返回长文本。
  3. 检查 metadata_title、keywords、merged_content 和 split_text 为空的原因。
  4. 将索引改为每个 chunk 一条记录,并保存文档 URL、页码和章节标题。
  5. 让 semantic configuration 优先使用 chunk,避免重复加入整篇正文。
  6. 如果只需要抽取式短答案,直接显示 @search.answers 和 @search.captions。
  7. 如果需要严格控制为两行、重新组织多个事实或统一语言,再接入大语言模型,并保留 chunk 级引用。

备注:内容仅供参考。