想象一下,我们如何搜索如下的一个问题:
Find a home within 10 miles of Miami, Florida that has 2 bedrooms, 2 bathrooms, central air, and tile floors, with a budget up to $300,000.这类问题存在于很多的电子商务网站搜索中。它也是一种非常实用的搜索方式之一。那么要实现这样的搜索方式,我们有如下的几种方式来实现:
- 使用 Python 代码实现工具,并让 LLM 来进行调用。我们需要调用 LLM 来提取我们搜索的参数。为了精准搜索,我们可以使用 template 来下继续搜索。详细的情况,可以参考文章 “统一 Elastic 向量数据库与 LLM 功能,实现智能查询”
- 我们可以为这个搜索用 Python 创建一个定制的 MCP 服务器,然后在客户端里进行调用。我们可以参考文章 “Elasticsearch:智能搜索的 MCP”
- 我们使用 AI Builder 及 Workflow 来实现。在 workflow 里实现类似于在 DSL 中的模版搜索从而达到精确搜索的目的。详细的使用说明,请参考文章 “Elasticsearch:智能搜索 - AI Builder 及 Workflow”。
在如上的三种方案里,第三种方案的实现最为简捷,因为它不需要另外单独的编程。我们只需要在 Kibana 里创建 agent 及 workflow 来完成即可。维护起来也非常简单直接。那么我们有没有更为方便的方法呢?答案是肯定的。在即将推出的 Elastic Stack 9.4 中(目前在 Elastic Serverless cloud 中可用),我们可以使用 skill 来更进一步简化的目的。
步骤一:写入数据
我们需要按照文章 “Elasticsearch:智能搜索的 MCP” 写入文档到 Elasticsearch 中。
步骤二:创建 geocoding worflow 及相应的工具
在我们的实现里,我们需要根据位置信息来得到一个精确的经纬度,以便实现相应的搜索。我们可以仿照之前的文章 “Elasticsearch:创建 geocoding workflow,并在 agent 中使用它进行位置搜索”。
步骤三:创建 DSL search template
在目前的情况下,在 agent 里,我们只能创建如下的几种 tools:
也就是说,如果我们想创建一个类似于 DSL search template 的搜索,在当前的 agent 设计中,DSL 是不被支持的。那么我们该如何实现这个呢?
答案是,我们可以为 agent 设计一个 skill。这个 skill 可以为我们的 agent 提供额外的能力。我们按照如下的步骤来创建一个这样的 elasticsearch_dsl_template skill:
它的设置如下:
- ID:elasticsearch_dsl_template
- Name:Elasticsearch DSL search template
- Description:Construct Elasticsearch using DSL search template.
- Instructions:
The details for implementing a search template can be found at https://www.elastic.co/docs/solutions/search/search-templates. For our search template, we need to use the following search template to do the DSL search: { "_source": false, "size": 5, "fields": ["title", "tax", "maintenance_fee", "bathrooms", "bedrooms", "square_footage", "home_price", "property_features"], "retriever": { "standard": { "query": { "semantic": { "field": "body_content_semantic_text", "query": "{{query}}" } }, "filter": { "bool": { "must": [ {{#distance}}{ "geo_distance": { "distance": "{{distance}}", "location": { "lat": {{latitude}}, "lon": {{longitude}} } } }{{/distance}} {{#bedrooms}}{{#distance}},{{/distance}}{ "range": { "bedrooms": { "gte": {{bedrooms}} } } }{{/bedrooms}} {{#bathrooms}}{{#distance}}{{^bedrooms}},{{/bedrooms}}{{/distance}}{{#bedrooms}},{{/bedrooms}}{ "range": { "bathrooms": { "gte": {{bathrooms}} } } }{{/bathrooms}} {{#tax}}{{#distance}}{{^bedrooms}}{{^bathrooms}},{{/bathrooms}}{{/bedrooms}}{{/distance}}{{#bedrooms}}{{^bathrooms}},{{/bathrooms}}{{/bedrooms}}{{#bathrooms}},{{/bathrooms}}{ "range": { "tax": { "lte": {{tax}} } } }{{/tax}} {{#maintenance}}{{#distance}}{{^bedrooms}}{{^bathrooms}}{{^tax}},{{/tax}}{{/bathrooms}}{{/bedrooms}}{{/distance}}{{#bedrooms}}{{^bathrooms}}{{^tax}},{{/tax}}{{/bathrooms}}{{/bedrooms}}{{#bathrooms}}{{^tax}},{{/tax}}{{/bathrooms}}{{#tax}},{{/tax}}{ "range": { "maintenance_fee": { "lte": {{maintenance}} } } }{{/maintenance}} {{#square_footage_max}}{{#distance}}{{^bedrooms}}{{^bathrooms}}{{^tax}}{{^maintenance}},{{/maintenance}}{{/tax}}{{/bathrooms}}{{/bedrooms}}{{/distance}}{{#bedrooms}}{{^bathrooms}}{{^tax}}{{^maintenance}},{{/maintenance}}{{/tax}}{{/bathrooms}}{{/bedrooms}}{{#bathrooms}}{{^tax}}{{^maintenance}},{{/maintenance}}{{/tax}}{{/bathrooms}}{{#tax}}{{^maintenance}},{{/maintenance}}{{/tax}}{{#maintenance}},{{/maintenance}}{ "range": { "square_footage": { "gte": {{#square_footage_min}}{{square_footage_min}}{{/square_footage_min}}{{^square_footage_min}}0{{/square_footage_min}}, "lte": {{square_footage_max}} } } }{{/square_footage_max}} {{#home_price_max}}{{#distance}}{{^bedrooms}}{{^bathrooms}}{{^tax}}{{^maintenance}}{{^square_footage}},{{/square_footage}}{{/maintenance}}{{/tax}}{{/bathrooms}}{{/bedrooms}}{{/distance}}{{#bedrooms}}{{^bathrooms}}{{^tax}}{{^maintenance}}{{^square_footage}},{{/square_footage}}{{/maintenance}}{{/tax}}{{/bathrooms}}{{/bedrooms}}{{#bathrooms}}{{^tax}}{{^maintenance}}{{^square_footage}},{{/square_footage}}{{/maintenance}}{{/tax}}{{/bathrooms}}{{#tax}}{{^maintenance}}{{^square_footage}},{{/square_footage}}{{/maintenance}}{{/tax}}{{#maintenance}}{{^square_footage}},{{/square_footage}}{{/maintenance}}{{#square_footage}},{{/square_footage}}{ "range": { "home_price": { "gte": {{#home_price_min}}{{home_price_min}}{{/home_price_min}}{{^home_price_min}}0{{/home_price_min}}, "lte": {{home_price_max}} } } }{{/home_price_max}} {{#feature}},{ "bool": { "should": [ { "match": { "property_features": { "query": "{{feature}}", "operator": "or" } } } ], "minimum_should_match": 1 } }{{/feature}} ] } } } } } We need to use "properties" index to do the search. **please do see the range searches for bedrooms and bathrooms“. We want to have bigger or equal matches. For the price, we need to have equal or smaller matches我们保存好上面的 skill。
步骤四:创建 Property search agent
我们按照如下的步骤创建 Property search agent:
其中的设置:
- Agent ID:property_search
- Custom Instructions:
This agent is used to search for properties: # Step 1: You are an information extraction assistant. Extract real estate search parameters from the user query. Parameter descriptions: - bathrooms: Number of bathrooms - bedrooms: Number of bedrooms - tax: Real estate tax amount - maintenance: Maintenance fee amount - square_footage_min: Minimum property square footage. If only a max square footage is provided, set this to 0. Otherwise set this to the minimum square footage specified by the user. - square_footage_max: Maximum property square footage - home_price_min: Minimum home price. If only a max home price is provided, set this to 0. Otherwise set this to the minimum home price specified by the user. - home_price_max: Maximum home price - property_features: Home features such as AC, pool, updated kitchens, etc should be listed as a single string. - location: City, state, or full address if present. Rules: - Only include parameters explicitly mentioned. - property_features must be a single space-separated string. - Return ONLY a JSON object (not a string, no quotes, no extra text, no explanations). - Do not include explanations. Example JSON: { "query": "Find a home within 10 miles of Miami, Florida that has 2 bedrooms, 2 bathrooms, central air, and tile floors, with a budget up to $300,000." "bathrooms": 2, "bedrooms": 2, "home_price_min": 0, "home_price_max": 300000, "property_features": "central air tile floors", "location": "Miami, Florida" } # Step 2: Based on the last extracted location, please use the get_coordinate_by_location tool to get the location, and finally get its coordinate info. The final data format is like: Example JSON: { "query": "Find a home within 10 miles of Miami, Florida that has 2 bedrooms, 2 bathrooms, central air, and tile floors, with a budget up to $300,000.", "bathrooms": "2", "bedrooms": "2", "home_price_max": "300000", "property_features": "central air, tile floors", "longitude": -80.1917902, "latitude": 25.7616798, "distance_meters": 16093.4 } # Step 3: Use the above constructed JSON format, and do a DSL template search. Please print out the search template used for search, and then print out the top **4 results** for viewing.- Display name:Property search
- Display description:Search for property
为 agent 添加所需要的 geocoding 工具:
为 agent 添加 skill
这样我们的 agent 就设计好了。
测试
我们使用如下的搜索例子来做测试:
Find a home within 10 miles of Miami, Florida that has 2 bedrooms, 2 bathrooms, central air, and tile floors, with a budget up to $300,000.上面显示了我们所需要的结果。和我们之前的搜索结果是一样的。我们可以查看一下它的思索过程:
很显然,它使用我们提供的 skill 来完成这个搜索。我们可以看到 Calling tool filestore.read。它使用了我们的 skill。更加有意思的是,它理解我们的意图,并使用 ES|QL 生成相应的 ES|QL 查询:
FROM properties | WHERE ST_DISTANCE(location, TO_GEOPOINT("POINT(-80.1917902 25.7616798)")) <= 16093.44 AND bedrooms >= 2 AND bathrooms >= 2 AND home_price >= 0 AND home_price <= 300000 AND MATCH(property_features, "central air tile floors") | KEEP title, tax, maintenance_fee, bathrooms, bedrooms, square_footage, home_price, property_features | LIMIT 4很显然,这个就是我们所需要的类似于 DSL template 的查询。根据含有的字段来做相应的搜索,虽然它使用的不是 DSL template 查询。
我们使用另外一个例子来展示:
Find a home within 10 miles of DeBary, Florida with 5 bedrooms, at least 2 bathrooms, central air, and a garage, with a budget up to $600,000.结论
Skill 是在即将发布的 Elastic Stack 9.4 里一个非常重要的功能。它极大地方便了我们对 agent 的设计。在很多的设计中,我们甚至不需要使用任何的代码实现就可以在 Kibana 中完成我们所需要的功能。
祝大家学习愉快!