恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
使用 Semantic Kernel Python 全流程操作 Azure AI Search:酒店数据集索引构建、向量/混合检索与 Agent 插件化实战
首页
资讯中心
/
使用 Semantic Kernel Python 全流程操作 Azure AI Search:酒店数据集索引构建、向量/混合检索与 Agent 插件化实战
使用 Semantic Kernel Python 全流程操作 Azure AI Search:酒店数据集索引构建、向量/混合检索与 Agent 插件化实战
发布时间:2026/9/13 8:51:34
使用 Semantic Kernel Python 全流程操作 Azure AI Search酒店数据集索引构建、向量/混合检索与 Agent 插件化实战【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel导读本文围绕 Semantic KernelPython仓库中的azure_ai_search_hotel_samples示例完整讲解如何纯代码化地在 Azure AI Search 上完成「定义数据模型 → 下载酒店样例数据 → 创建索引 → 写入Upsert数据 → 文本/向量/混合检索 → 清理索引」的整条链路全程无需在 Azure Portal 上手动操作。在此基础上文章还会深入AzureAISearchCollection连接器源码讲清索引字段映射、HNSW 向量配置、OData 过滤 Lambda 解析等底层机制并带你实现两个进阶场景把酒店检索封装成 Kernel Plugin 交给 ChatCompletionAgent 自动调用以及切换到 Azure AI Search 内置的集成向量化Integrated Vectorization以省去客户端嵌入生成。一、示例总览四个 Python 脚本各司其职示例位于 python/samples/concepts/memory/azure_ai_search_hotel_samples共包含三个可执行脚本与一个公共数据模型模块文件作用data_model.py定义酒店数据模型HotelSampleClass、索引 Schemacustom_index和数据加载函数load_records()被另外两个脚本引用无需手动执行1_interact_with_the_collection.py主示例创建集合、Upsert 数据、取前 5 条记录、执行向量检索与混合检索并打印结果2_use_as_a_plugin.py进阶示例将检索封装为 Kernel Plugin供一个旅行客服 Agent 自动调用整套流程可概括为定义酒店数据模型与索引 Schema含中英双语描述字段及对应的两套 1536 维向量字段从 Azure 官方样例仓库下载hotels.json酒店数据并解析为模型对象通过AzureAISearchCollection.ensure_collection_exists()创建索引若不存在通过upsert()将数据批量写入索引使用search()/hybrid_search()运行文本、向量与混合检索。二、前置条件与环境配置运行示例前需要准备Azure AI Search 服务实例示例会通过代码在该服务上创建与删除索引因此账号需要有对应的写权限OpenAI 资源用于生成向量嵌入OpenAITextEmbedding同样可替换为 Azure OpenAI Embeddings正确的环境变量与凭据确保 Azure 凭据与 Endpoint 已在环境中正确配置。从连接器源码 azure_ai_search.py 可以看到AzureAISearchSettings的env_prefix为AZURE_AI_SEARCH_支持以下环境变量环境变量含义AZURE_AI_SEARCH_API_KEYAzure AI Search 的 API Key也可以传入AzureKeyCredential或AsyncTokenCredential令牌凭据AZURE_AI_SEARCH_ENDPOINTAzure AI Search 服务 Endpoint类型为HttpsUrl必填AZURE_AI_SEARCH_INDEX_NAME索引名可选优先级低于构造函数显式传入的collection_name此外由于示例使用OpenAITextEmbedding()与OpenAIChatCompletion()还需要配置对应的 OpenAI API Key或替换为 Azure OpenAI 连接器。凭据解析逻辑见源码中的_resolve_credential()azure_ai_search.py优先使用显式传入的azure_credential/token_credential其次读取api_key环境变量全部缺失时抛出ServiceInitializationError。三、数据模型与索引创建data_model.py 深度解析3.1 用 Pydantic 装饰器声明向量存储数据模型data_model.py通过vectorstoremodel(collection_namehotel-index)装饰器把普通 Pydantic 模型声明为 Semantic Kernel 的向量存储模型并用VectorStoreField注解描述每个字段在索引中的角色vectorstoremodel(collection_namehotel-index) class HotelSampleClass(BaseModel): HotelId: Annotated[str, VectorStoreField(key)] HotelName: Annotated[str | None, VectorStoreField(data)] None Description: Annotated[str, VectorStoreField(data, is_full_text_indexedTrue)] DescriptionVector: Annotated[list[float] | str | None, VectorStoreField(vector, dimensions1536)] None Description_fr: Annotated[str, VectorStoreField(data, is_full_text_indexedTrue)] DescriptionFrVector: Annotated[list[float] | str | None, VectorStoreField(vector, dimensions1536)] None Category: Annotated[str, VectorStoreField(data)] Tags: Annotated[list[str], VectorStoreField(data, is_indexedTrue)] ParkingIncluded: Annotated[bool | None, VectorStoreField(data)] None LastRenovationDate: Annotated[str | None, VectorStoreField(data, typeSearchFieldDataType.DateTimeOffset)] None Rating: Annotated[float, VectorStoreField(data)] Location: Annotated[dict[str, Any], VectorStoreField(data, typeSearchFieldDataType.GeographyPoint)] Address: Annotated[Address, VectorStoreField(data)] Rooms: Annotated[list[Rooms], VectorStoreField(data)] model_config ConfigDict(extraignore) def model_post_init(self, context: Any) - None: if self.DescriptionVector is None: self.DescriptionVector self.Description if self.DescriptionFrVector is None: self.DescriptionFrVector self.Description_fr要点解读字段角色VectorStoreField第一参数key声明主键字段data声明普通数据字段vector声明向量字段并通过dimensions1536指定向量维度与 OpenAItext-embedding-ada-002等模型的输出维度一致。全文索引is_full_text_indexedTrue使Description/Description_fr可被混合检索的关键词部分命中。类型显式映射LastRenovationDate映射为DateTimeOffset、Location映射为GeographyPoint对应 data_model.py 中通过type参数声明。复杂类型AddressAddress模型与RoomsRooms模型列表是嵌套复杂结构。由于内置连接器无法妥善处理这类复杂数据类型示例手工定义了一个custom_index来显式声明嵌套字段——这是本示例之所以自定义索引而非直接依赖模型自动生成索引的核心原因见 data_model.py 的注释说明。向量兜底生成model_post_init保证DescriptionVector缺失时用Description文本自身作为生成向量的输入OpenAITextEmbedding会为其生成 1536 维向量避免数据写入时因向量为空而失败。3.2 自定义索引 Schema字段、复杂类型与 HNSW 向量搜索配置custom_index是一个azure.search.documents.indexes.models.SearchIndex对象完整声明了索引名hotel-index及全部字段。其中向量检索相关的关键配置位于 data_model.pyvector_searchVectorSearch( profiles[VectorSearchProfile(namehnsw, algorithm_configuration_namehnsw)], algorithms[HnswAlgorithmConfiguration(namehnsw)], vectorizers[], )两个向量字段DescriptionVector、DescriptionFrVector都声明了vector_search_dimensions1536与vector_search_profile_namehnsw共同挂到名为hnsw的向量搜索 Profile 上算法采用HNSWHierarchical Navigable Small World近似最近邻检索这是 Azure AI Search 默认且最常用的向量索引算法vectorizers[]表示当前不使用服务端集成向量化——向量由客户端OpenAITextEmbedding生成后随文档写入。若想切换为服务端向量化需要按本文第七节改造此处。此外custom_index中还通过ComplexField显式声明了两个复杂类型字段AddresscollectionFalse包含StreetAddress、City、StateProvince、PostalCode、Country五个子字段均可过滤、可搜索其中城市/州/邮编还可分面facetableRoomscollectionTrue包含Description、Type、BaseRate、BedOptions、SleepsCount、SmokingAllowed、Tags等子字段的集合法语描述字段Description_fr使用了analyzer_namefr.microsoft法语分析器。这样的索引声明方式让后续混合检索中的过滤器例如lambda x: x.Address.City Seattle能够直接作用于Address/City这样的嵌套字段路径。3.3 数据下载与解析load_records()data_model.py默认从 Azure 官方样例仓库下载hotels.json也可通过url参数指定自定义数据源随后用HotelSampleClass.model_validate(record)逐条校验解析为模型对象列表返回。该仓库版本的数据经过改造字段名采用 Python 风格命名且移除了原始文件中的DescriptionEmbedding字段。四、运行主示例从建索引到混合检索4.1 启动命令python 1_interact_with_the_collection.py脚本执行后将依次完成创建索引如不存在加载并 Upsert 酒店数据取出前 5 条记录核对写入结果执行向量检索与混合检索并打印结果在脚本末尾删除索引。4.2 主流程逐行拆解主函数位于 1_interact_with_the_collection.pyasync def main(query: str): records load_records() # 创建 Azure AI Search 集合 async with AzureAISearchCollectionstr, HotelSampleClass ) as collection: # 检查集合是否存在不存在则创建 await collection.ensure_collection_exists(indexcustom_index) await collection.upsert(records) # 取前五条记录核对 Upsert 是否成功 results await collection.get(order_byHotelName, top5) ... # 向量检索 results await collection.search( query, vector_property_nameDescriptionVector, ) ... # 混合检索 results await collection.hybrid_search( query, vector_property_nameDescriptionVector, additional_property_nameDescription, ) ... await collection.ensure_collection_deleted()关键点说明AzureAISearchCollection[str, HotelSampleClass]泛型参数依次为 Key 类型与记录类型主键被限定为str连接器源码supported_key_types {str}见 azure_ai_search.py。集合名按「构造函数 → 数据模型装饰器 → search_client → 环境变量」的优先级解析源码注释见 azure_ai_search.py这里取自vectorstoremodel(collection_namehotel-index)。embedding_generatorOpenAITextEmbedding()检索与写入时自动把查询文本/描述文本转成向量若不传则检索时会退化为服务端VectorizableTextQuery或直接报错。ensure_collection_exists(indexcustom_index)传入手工构造的SearchIndex底层调用SearchIndexClient.create_index()见 azure_ai_search.py。upsert(records)底层调用search_client.merge_or_upload_documents()见 azure_ai_search.py即按HotelId主键「存在则合并、不存在则上传」。get(order_byHotelName, top5)等价于以search_text*全量扫描并排序分页见连接器_inner_get实现azure_ai_search.py。向量检索search(query, vector_property_nameDescriptionVector)连接器先由embedding_generator把query文本生成向量再构造VectorizedQuery提交源码分支见 azure_ai_search.py。混合检索hybrid_search(query, vector_property_nameDescriptionVector, additional_property_nameDescription)search_text作用于Description字段做关键词检索同时向量查询作用于DescriptionVector两者融合打分排序源码见 azure_ai_search.py。ensure_collection_deleted()脚本结束前删除整个索引用于保证演示环境的干净可重复如需保留索引继续实验注释掉这一行即可。4.3 底层检索原理补充从连接器源码可以看到两个值得了解的机制检索类型与能力矩阵AzureAISearchCollection.supported_search_types {SearchType.VECTOR, SearchType.KEYWORD_HYBRID}azure_ai_search.py即当前连接器支持纯向量检索与关键词向量混合检索两种模式。Lambda 过滤器转 OData_lambda_parser()azure_ai_search.py会把形如lambda x: x.Address.City Seattle的 Python Lambda 表达式解析为 Azure AI Search 的 OData 过滤语法Address/City eq Seattle支持/!//等比较符、in/not in子句转换为search.ismatch()、以及and/or/not逻辑组合。混合检索时这些过滤器会作为$filter参数下发。五、示例输出解读以默认查询swimming pool and good internet connection为例脚本输出大致如下Get first five records: 31 (in Nashville, USA): All of the suites feature full-sized kitchens stocked with cookware, separate living and sleeping areas and sofa beds. Some of the larger rooms have fireplaces and patios or balconies. ... 23 (in Kirkland, USA): Mix and mingle in the heart of the city. ... 3 (in Atlanta, USA): The Gastronomic Hotel stands out for its culinary excellence ... 20 (in Albuquerque, USA): The Best Gaming Resort in the area. ... 45 (in Seattle, USA): The largest year-round resort in the area ... Search results using vector: 6 (in San Francisco, USA): Newest kid on the downtown block. ... (score: 0.6350645) 27 (in Aventura, USA): Complimentary Airport Shuttle WiFi. ... (score: 0.62773544) 25 (in Metairie, USA): Newly Redesigned Rooms airport shuttle. ... (score: 0.6193533) Search results using hybrid: 25 (in Metairie, USA): Newly Redesigned Rooms airport shuttle. ... (score: 0.03279569745063782) 27 (in Aventura, USA): Complimentary Airport Shuttle WiFi. ... (score: 0.032786883413791656) 36 (in Memphis, USA): Stunning Downtown Hotel with indoor Pool. ... (score: 0.0317460335791111)解读要点前五条记录按HotelName字典序返回用于验证 Upsert 数据完整ID、城市、描述均正确向量检索的score为 0~1 的余弦相似度分数越接近 1 表示语义越相关例如命中游泳池、WiFi、健身房等描述与查询语义高度吻合的记录混合检索的score是融合了关键词 BM25 相关性与向量相似度的综合分数其量级与纯向量分数不同此处约 0.03不能直接跨模式比较绝对值。六、自定义检索修改查询与更多控制修改 1_interact_with_the_collection.py 末尾的query变量即可更换检索内容if __name__ __main__: query swimming pool and good internet connection asyncio.run(main(queryquery))该query会同时作用于向量检索生成查询向量与混合检索search_text 查询向量。若想进一步控制结果数量、过滤条件或排序可参考连接器_inner_search支持的VectorSearchOptions能力例如通过filterlambda x: x.Rating 4之类的 Lambda 过滤或调整top/skip分页参数。七、进阶一将酒店检索封装为 Agent 插件2_use_as_a_plugin.py 演示了把同一套 Azure AI Search 集合包装成两个 Kernel 函数并交给 Agent 自动调用的完整方案可独立运行。核心思路是基于同一个集合创建多个用途不同、带固定过滤和定制输出的检索函数再由 Agent 按用户意图自主选择调用。7.1 基于集合快速创建两个检索函数search_plugin KernelPlugin( nameazure_ai_search, descriptionA plugin that allows you to search for hotels in Azure AI Search., functions[ collection.create_search_function( descriptionA hotel search engine, allows searching for hotels in specific cities, you do not have to specify that you are searching for hotels, for all, use *., search_typekeyword_hybrid, filterlambda x: x.Address.Country USA, parameters[ KernelParameterMetadata(namequery, descriptionWhat to search for., typestr, is_requiredTrue, type_objectstr), KernelParameterMetadata(namecity, descriptionfThe city that you want to search for a hotel in, values are: {, .join(cities)}, typestr, type_objectstr), KernelParameterMetadata(nametop, descriptionNumber of results to return., typeint, default_value5, type_objectint), ], filter_update_functionfilter_update, string_mapperlambda x: f(hotel_id :{x.record.HotelId}) {x.record.HotelName} (rating {x.record.Rating}) - {x.record.Description}. Address: {x.record.Address.StreetAddress}, {x.record.Address.City}, {x.record.Address.StateProvince}, {x.record.Address.Country}. Number of room types: {len(x.record.Rooms)}. Last renovated: {x.record.LastRenovationDate}., ), collection.create_search_function( function_nameget_details, descriptionGet details about a hotel, by ID, use the generic search function to get the ID., top1, parameters[ KernelParameterMetadata(nameHotelId, descriptionThe hotel ID to get details for., typestr, is_requiredTrue, type_objectstr), ], ), ], )要点create_search_function参数见 text_search.pyfunction_name、description、output_type默认str、parameters、filter、top/skip、include_total_count、filter_update_function、string_mapper。其中description、parameters会作为函数调用Tool Calling元数据序列化给 LLM属于 Prompt 设计的一部分值得反复打磨固定过滤器filterlambda x: x.Address.Country USA表示该函数永远只返回美国酒店string_mapper把VectorSearchResult[HotelSampleClass]记录映射成给 LLM 看的紧凑字符串含 hotel_id、评分、描述、地址、房型数、翻新日期等第二个函数get_details按HotelId精确取详情top1只返回一条与通用搜索函数形成「先搜列表、再查详情」的分工。7.2 动态过滤器把city参数翻译成嵌套字段过滤默认的filter_update_function会把除query、top、skip之外的参数含默认值自动转换成等值过滤器追加到搜索选项上。但本例中索引里的城市字段技术名是Address/City为了让 LLM 只面对友好的city参数名示例自定义了filter_update2_use_as_a_plugin.pydef filter_update( filter: OptionalOneOrList[Callable | str] | None None, parameters: list[KernelParameterMetadata] | None None, **kwargs: Any, ) - OptionalOneOrList[Callable | str] | None: if city in kwargs: city kwargs[city] if city not in cities: raise ValueError(fCity {city} is not in the list of cities: {, .join(cities)}) # 需要真实值而非命名参数否则解析器无法定位字段 new_filter flambda x: x.Address.City {city} ... return filter该函数遵循DynamicFilterFunction签名filter、parameters两个具名参数加**kwargs会把city参数拼装为字符串形式的 Lambda 过滤器并追加到已有过滤器列表。其中cities集合在脚本开头从load_records()的 USA 记录中提取2_use_as_a_plugin.pycity参数的描述里也把可选城市列表直接注入方便 LLM 生成合法取值传入非法城市会抛出带合法城市列表的ValueError。7.3 创建 Agent 并启用自动函数调用travel_agent ChatCompletionAgent( nameTravelAgent, descriptionA travel agent that helps you find a hotel., serviceOpenAIChatCompletion(), instructionsinstructions, function_choice_behaviorFunctionChoiceBehavior.Auto(), plugins[search_plugin], )instructions定义了一个名为 Mosscap 的旅行客服人设并要求回答中必须包含hotel_id方便用户后续追问详情FunctionChoiceBehavior.Auto()让 Agent 在需要时自动决策并调用azure_ai_search插件中的两个函数。7.4 调试过滤器观察每次检索的实参脚本还通过函数调用过滤器打印每次插件调用的实际参数2_use_as_a_plugin.pytravel_agent.kernel.filter(filter_typeFilterTypes.FUNCTION_INVOCATION) async def log_search_filter(context, next): print(fCalling Azure AI Search ({context.function.name}) with arguments:) for arg in context.arguments: if arg chat_history: continue print(f {arg}: {context.arguments[arg]}) await next(context)这为调试检索体验、微调参数描述提供了直观手段。7.5 交互式对话主循环chat()中以async with collection:打开集合首次运行时若集合为空则 Upsert 数据用get(top1)判断随后进入input()交互循环用户输入exit退出退出后可选择是否删除集合2_use_as_a_plugin.py。八、进阶二切换到 Azure AI Search 集成向量化默认方案中向量由客户端OpenAITextEmbedding生成后随文档写入。若希望把向量生成下沉到 Azure AI Search 服务端集成向量化Integrated Vectorization需要在现有步骤之外做两处改动改造custom_indexdata_model.py在vectorizers列表与profiles列表中加入对应的向量化器Vectorizer定义。例如把当前VectorSearch(profiles[...], algorithms[...], vectorizers[])中的vectorizers[]替换为指向 OpenAI/Azure OpenAI 模型的 Vectorizer 配置并让profiles引用该 Vectorizer使服务端在写入时自动为文档生成向量移除embedding_generator参数在两个脚本的AzureAISearchCollection构造处1_interact_with_the_collection.py 与 2_use_as_a_plugin.py删除embedding_generatorOpenAITextEmbedding()。移除后即表示向量化由服务端完成——此时检索请求会走VectorizableTextQuery文本可向量化查询路径让 Azure AI Search 在查询时用同一个向量化器把查询文本转为向量对应连接器源码中的VectorizableTextQuery分支见 azure_ai_search.py。九、常见问题与注意事项凭据配置遇到初始化或鉴权失败优先检查AZURE_AI_SEARCH_ENDPOINT、AZURE_AI_SEARCH_API_KEY或令牌凭据以及 OpenAI Key 是否在环境中正确配置。凭据缺失会抛出ServiceInitializationError见 azure_ai_search.py。索引清理1_interact_with_the_collection.py在结尾会删除整个hotel-index想保留索引继续实验请注释ensure_collection_deleted()这一行。custom_index的角色因为酒店模型含Address、Rooms等复杂嵌套类型内置连接器无法直接生成正确的索引所以必须使用手工声明的custom_index。若你的业务模型全部是简单扁平字段可以省略该参数连接器会根据模型定义自动构建索引_definition_to_azure_ai_search_index见 azure_ai_search.py。适用边界本连接器当前标记为release_candidate阶段支持str主键、float/int向量类型以及纯向量与关键词混合两种检索模式azure_ai_search.py。结语通过本文你已经掌握了在 Semantic KernelPython中用纯代码完成 Azure AI Search 酒店数据集「模型定义 → 建索引 → 灌数据 → 三种检索 → 插件化 → 服务端向量化」的全链路技能。从data_model.py的手工索引 Schema到AzureAISearchCollection底层的merge_or_upload_documents、VectorizedQuery与 Lambda 过滤器转 OData 的实现细节再到create_search_function的插件封装这套模式可以直接迁移到自己的业务数据上只需替换数据模型、索引 Schema 与load_records数据源即可快速构建一个「可被 Agent 自主检索」的向量搜索服务。【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考