想象一下,我们如何搜索如下的一个问题:
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 Agent。
步骤一:写入数据
我们需要按照文章 "Elasticsearch:智能搜索的 MCP" 写入文档到 Elasticsearch 中。
步骤一:创建 workflow
我们创建一个如下的 workflow:
name: real_estate_esql_workflow
enabled: true
description: Advanced ES|QL search with Geocoding, WKT construction, and clean console output
consts:
geocoding_api_url: https://maps.googleapis.com/maps/api/geocode/json?key=<Your API key>&
triggers:
- type: manual
inputs:
- name: user_query
type: string
required: true
description: Natural language real estate search request
steps:
# Step 1: Extract structured parameters from user query
- name: extract_parameters
type: ai.prompt
with:
temperature: 0.2
outputSchema:
type: object
properties:
query: { type: string }
bathrooms: { type: integer }
bedrooms: { type: integer }
home_price_max: { type: number, minimum: 0 }
property_features: { type: string }
location: { type: string }
distance: { type: string }
additionalProperties: false
prompt: |
Extract structured real estate parameters from: {{ inputs.user_query }}
# Step 2: Geocode the location (Restored)
- name: get_coordinate
type: http
with:
url: "{{ consts.geocoding_api_url }}address={{ steps.extract_parameters.output.content.location | url_encode }}"
method: GET
# Step 3: Combine parameters and normalize fields
- name: combine_with_coordinates
type: console
with:
message: |-
{%- assign lat = steps.get_coordinate.output.data.results[0].geometry.location.lat | default: 0 -%}
{%- assign lon = steps.get_coordinate.output.data.results[0].geometry.location.lng | default: 0 -%}
{%- assign dist_raw = steps.extract_parameters.output.content.distance | default: "0" | replace: ' miles', '' | plus: 0 -%}
{
"query": "{{ steps.extract_parameters.output.content.query | replace: '"', '\"' | strip }}",
"bathrooms": "{{ steps.extract_parameters.output.content.bathrooms }}",
"bedrooms": "{{ steps.extract_parameters.output.content.bedrooms }}",
"home_price_max": "{{ steps.extract_parameters.output.content.home_price_max }}",
"property_features": "{{ steps.extract_parameters.output.content.property_features | replace: '"', '\"' | strip }}",
"longitude": {{ lon }},
"latitude": {{ lat }},
"distance_meters": {{ dist_raw | times: 1609.34 }}
}
# Step 4: Build Dynamic ES|QL Query
- name: build_esql_query
type: console
with:
message: |-
{%- assign v = steps.combine_with_coordinates.output | json_parse -%}
{%- assign q = 'FROM properties METADATA _score | EVAL pt = TO_GEOPOINT(CONCAT("POINT(", "' | append: v.longitude | append: '", " ", "' | append: v.latitude | append: '", ")")) | EVAL distance = ST_DISTANCE(location, pt)' -%}
{%- if v.bathrooms != "" -%}{%- assign q = q | append: " | WHERE bathrooms >= " | append: v.bathrooms -%}{%- endif -%}
{%- if v.bedrooms != "" -%}{%- assign q = q | append: " | WHERE bedrooms >= " | append: v.bedrooms -%}{%- endif -%}
{%- if v.home_price_max != "" -%}{%- assign q = q | append: " | WHERE home_price <= " | append: v.home_price_max -%}{%- endif -%}
{%- if v.property_features != "" -%}
{%- assign q = q | append: ' | WHERE MATCH(property_features, "' | append: v.property_features | append: '")' -%}
{%- endif -%}
{%- if v.distance_meters > 0 -%}
{%- assign q = q | append: " | WHERE distance <= " | append: v.distance_meters -%}{%- endif -%}
{%- if v.query != "" -%}
{%- assign q = q | append: ' | WHERE MATCH(body_content_semantic_text, "' | append: v.query | append: '")' -%}{%- endif -%}
{%- assign q = q | append: " | SORT _score DESC, distance ASC | LIMIT 10 | KEEP title, bedrooms, bathrooms, home_price, property_features, distance" -%}
{{ q | strip }}
# Step 5: Run Query
- name: esql_run
type: elasticsearch.esql.query
with:
format: json
query: "{{ steps.build_esql_query.output | strip }}"
# Step 6: Display Top 3 Results Nicely
- name: display_top_results
type: console
with:
message: |-
{%- assign results = steps.esql_run.output.values -%}
{%- if results.size == 0 -%}
No properties found matching your criteria.
{%- else -%}
{%- for row in results limit:3 -%}
Title: {{ row[0] }}
Bathrooms: {{ row[2] }}
Bedrooms: {{ row[1] }}
Price: ${{ row[3] }}
Features: {{ row[4] }}
Distance: {{ row[5] | divided_by: 1609.34 | round: 2 }} miles
{% endfor -%}
{%- endif -%}
注意:上面的版本是在 9.3 上测试的。针对 9.4,我们需要把 outoutSchema 改为 schema 才可以工作。
在上面,你需要填入自己的 google API key。
测试文档如下:
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.
我们运行的结果如下:








从上面的测试中,我们可以看到我们的查询是成功的。
步骤三:创建一个 tool

步骤四:创建一个 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.

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.

我们完美地避开了 Python 代码的创建。
Hooray!