如何让 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 |
title | semantic 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 时,需要确认以下配置:
- Text Split skill 的输出路径与 projection source context 一致。
- 每个 chunk 都写入一条独立的索引记录。
- 标题、Blob URL、页码等父文档信息同步到每个 chunk。
- 重新运行 indexer 后,检查
chunk字段是否有值。 - 不要将相同的整篇文本同时写入
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 主要用于检查原始搜索响应,不会自动按这种格式排版。带生成式问答能力的示例应用可以修改提示词和结果模板;普通搜索页面则需要增加一层后端编排。
排查顺序
可以按以下顺序检查当前配置:
- 在 Search explorer 中使用 semantic query,并加入
answers和captions。 - 使用
select排除完整content,确认查询结果不再直接返回长文本。 - 检查
metadata_title、keywords、merged_content和split_text为空的原因。 - 将索引改为每个 chunk 一条记录,并保存文档 URL、页码和章节标题。
- 让 semantic configuration 优先使用
chunk,避免重复加入整篇正文。 - 如果只需要抽取式短答案,直接显示
@search.answers和@search.captions。 - 如果需要严格控制为两行、重新组织多个事实或统一语言,再接入大语言模型,并保留 chunk 级引用。
备注:内容仅供参考。
