作者:来自 Elastic Sunile Manjee 及 Justin Castilla

通过将 Elastic 的智能查询层与 MCP 集成来构建智能搜索系统,以增强 LLMs 的生成效果。
Elasticsearch 充满了新功能,可以帮助你为你的用例构建最佳的搜索解决方案。深入我们的示例 notebooks 了解更多,开始免费的 cloud 试用,或立即在本地机器上尝试 Elastic。
从数据库中检索相关文档时,查询结构至关重要。即使是对搜索结构的小调整,也可能显著影响结果的准确性和返回速度。在与 AI agents 一起工作时尤其如此,因为 Large Language Models (LLMs) 在生成响应时本质上是不可预测的。
不幸的是,如果你仅仅依赖 LLM 来创建数据库搜索中最关键的部分,那你基本上是在一个不稳定的基础上构建。这就是 Intelligent Query Layer 发挥作用的地方,它在早期的博客中已经介绍过。通过将 LLMs 的推理能力与 Elasticsearch 的精确搜索能力结合起来,我们可以创建兼具两者优势的查询,同时实现近乎完美的召回率。
我们现在已经将这种经过验证的方法调整为适用于 MCP 框架,并且改进非常显著。在这篇博客文章中,我们将通过一个实际示例进行讲解:使用一个 Python MCP 服务器,结合 MCP 与 Elasticsearch 搜索模板,由 LLM 协调,实现高效的房产搜索。
演示
这个 MCP 服务器可以通过 Claude Desktop 查询,使用生成的搜索模板来搜索房产。

要复制这个环境,你必须先启动 MCP 服务器。你可以在 GitHub 上的 elastic-property-mcp 仓库中找到服务器的源代码和设置说明。
设置和配置
这个示例包含一个简单的 MCP 服务器(在这个仓库里),它与 Claude Desktop 交互。
要运行它,请确保满足以下前提条件:
-
Python 3.12 或更高版本
-
一个 Elasticsearch 实例(推荐使用 Serverless),带有 endpoint 和 API key
初始化项目和 API keys 的步骤在项目 README 的安装部分中列出。
数据摄取
数据由佛罗里达州的房产列表组成,格式为 JSON。每个房产包含一个文本描述、卧室和浴室数量、平方英尺、地址和价格。下面是一个示例条目:
bash
`
1. {
2. "listing-agent-info": "FIRST HOME REALTY, INC. , SEFMLS : A11699330",
3. "title": "26601 SW 124th Ave, Homestead, FL 33032 Property for sale",
4. "geo_point": {
5. "lon": -80.39215,
6. "lat": 25.51954
7. },
8. "bedrooms": 4.0,
9. "bathrooms": 2.0,
10. "square-footage": 1081.0,
11. "tax": 5435.0,
12. "home-price": 575000,
13. "body_content": "Motivated Seller!!! This stunning home is perfect for a small family or investor looking for a high-potential property. Featuring a beautifully renovated kitchen with quartz countertops, stainless steel appliances, and new electrical and lighting throughout the home, this property has been thoughtfully updated. The bathrooms have also been fully renovated, and the home is equipped with...",
14. "url": "https://www.corcoran.com/listing/for-sale/26601-sw-124th-ave-homestead-fl-33032",
15. "property-features": "Central air Dryer Washer Attached Garage Garage Room For Pool Aluminum Roof Ceiling Fans Cooling Central Air Cooling Central Heating Driveway Fence Heating Heating Security High Impact Doors Shingle Roof Tile Flooring",
17. },
`AI写代码
ingest_properties.py 脚本作为数据管道,用于准备并加载房产列表到 Elasticsearch。它通过以下命令执行:
go
`python ingest_properties.py`AI写代码
当执行该文件时完成的不同任务的分解将在后续部分讨论。
索引配置与创建
在加载任何数据之前,脚本会创建一个名为 properties 的 Elasticsearch 索引,并带有特定字段映射。可以在 properties_index_mappings.json 文件中查看这些映射。
下面是索引映射的一个示例:
bash
`
1. {
2. "mappings": {
3. "properties": {
4. "geo_point": {"type": "geo_point"},
5. "bedrooms": {"type": "float"},
6. "bathrooms": {"type": "float"},
7. "home-price": {"type": "long"},
8. "body_content": {"type": "text",
9. "copy_to": "body_content_semantic_text"},
10. "body_content_semantic_text": {"type": "semantic_text"}
11. }
12. }
`AI写代码
搜索模板
search_temlpate.mustache 文件包含用于查询房产的主要搜索结构。
Elasticsearch 搜索模板是预定义的搜索查询,它利用 Mustache 模板语言来实现动态搜索参数。它们允许应用程序运行复杂的搜索,而无需理解或直接操作完整的 Elasticsearch 查询语法。这些模板存储在 Elasticsearch 实例中,可以通过提供模板 ID 以及任何必要的变量来执行。
搜索模板促进了在应用程序的不同部分或各种用户界面中重用常见的搜索模式。对底层搜索逻辑的更改可以在模板内部完成,而无需修改应用程序的代码。Mustache 模板中的变量(例如 {{house_price}})允许在运行时传递动态值,从而基于用户输入或其他条件实现灵活且可定制的搜索。
在本次演示中,LLM 被赋予权力,将其认为必要的变量传递到搜索模板并执行生成的查询。这使得 LLM 能够执行极其精确且召回率极高的搜索操作。
语义文本增强
摄取过程的一个关键特性是通过简单地添加一个字段类型来创建语义搜索(semantic search)功能。对于每条房产记录,脚本会将 body_content 的内容复制到一个 semantic_text 类型的字段中。这样会自动将文本嵌入到向量中以进行语义搜索。
在这个字段上进行语义上下文查询就像运行任何普通文本查询一样简单 ------ 语义搜索会在后台自动执行,而开发者只需要创建普通的文本查询即可。
json
`
1. "body_content": {
2. "type": "text",
3. "copy_to": [
4. "body_content_semantic_text"
5. ]
6. },
7. "body_content_semantic_text": {
8. "type": "semantic_text"
9. },
`AI写代码
在这里,你可以看到 body_content 被复制到 body_content_semantic_text,这会自动将文本嵌入到向量中。
json
`
1. "query": {
2. "semantic": {
3. "field": "body_content_semantic_text",
4. "query": "{{query}}"
5. }
6. },
`AI写代码
这是一个使用普通文本查询 body_content_semantic_text 字段的示例。查询会被转换为查询向量,并执行相似性搜索,无需额外配置或工具。
批量索引操作
为了优化性能,脚本使用 Elasticsearch 的并行 bulk API 同时索引多个文档:
ini
`
1. def generate_actions():
2. for line in data_set:
3. yield {"_index": INDEX_NAME, "_source": line}
5. helpers.parallel_bulk(
6. es,
7. actions=generate_actions(),
8. thread_count=4,
9. chunk_size=50,
10. ):
`AI写代码
这显著加快了大量文档插入 Elasticsearch 的速度。
系统架构
智能搜索系统背后的分层架构使用 FastMCP 框架进行交互和编排,如下图所示。

在最上层,Claude 管理 LLM chains 的执行,这些链包括工具、提示函数和外部 API 调用。
由 Elastic Python MCP 提供支持的编排层协调工具调用、API 访问和响应格式化。该层充当用户查询与下游服务之间的桥梁。
MCP 提供 LLM 工具以随时执行。当调用这些工具时,会触发 Elasticsearch 查询或执行地理位置搜索,以用地理坐标增强搜索模板。
结果返回给 LLM,LLM 可以生成自然语言响应。
这种模块化设计使系统能够将精确搜索能力与深度推理相结合,从而处理自然语言查询并返回高度相关且结构化的响应。
深入架构,每个组件提供以下功能:
Claude Desktop
Claude Desktop 作为这个 MCP 服务器项目的主要用户界面和交互中心。它处理实时消息流,确保流畅、响应迅速的通信。同时管理整个对话过程中的用户会话状态,保持多个交互的上下文和连续性。应用程序以用户友好的格式显示房产结果及相关图片,并支持自然对话,使用户能够通过对话而非传统表单界面无缝地提问、请求澄清和浏览房产数据。
Elasticsearch 搜索模板
Elasticsearch 搜索模板定义了可重用的查询结构,用于不同的搜索场景,将用户输入中检测到的实体映射到特定搜索参数。系统生成实时 Elasticsearch 查询,处理布尔逻辑,结合价格、特征和位置的过滤,同时支持基于半径的地理空间查询和距离计算。
Elastic Python MCP 服务器
MCP 服务器协调所有系统组件之间的通信,管理工具调用和响应,同时处理与 Elasticsearch 和 Google Maps API 的集成。它处理这些服务的搜索结果,并将响应格式化以便在用户界面显示,作为中央协调层,确保整个应用的数据流无缝。
数据流架构
在这个示例中,智能解析、语义理解和模块化编排结合在一起,提供精准且用户友好的搜索体验。用户提交的所有自然语言查询都会经过多个处理阶段以生成有意义的响应,如下所示:

查询由 MCP 服务器处理,通过搜索模板和 LLM 提取结构化参数。get_properties_template_params 函数确定哪些字段(例如卧室数量、浴室数量、位置)可以作为过滤条件。LLM 对用户查询进行语义解析,以检测匹配的候选项 ------ 例如,将 "2 bed"、"two bedrooms" 或 "needs a couple of rooms" 理解为 number_of_bedrooms = 2 的过滤条件。
如果查询中包含位置,系统会调用地理编码服务(在此示例中为 Google Maps API)将其转换为坐标。然后,将结构化参数和地理坐标丰富后的完整查询执行到 Elasticsearch。这是原始查询 ------ 作为语义搜索执行 ------ 与基于传入参数的过滤相结合,以减少需要搜索的结果。检索到的结果随后被格式化并返回给用户。
真实示例
考虑这个查询示例:"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." 让我们来看看系统如何处理这个查询。
实体检测与映射
MCP 应用提供了多个工具,供 LLM 用于识别和验证关键实体:
1)位置检测与地理编码:
-
验证位置的存在(例如 Miami, Florida)。
-
将距离测量(例如 "10 miles")转换为可搜索的半径。
-
将位置转换为 Elasticsearch geo-point 坐标。
python
`1. @mcp.tool()
2. async def geocode_location(location: str) -> Dict[str, Any]:
3. """Geocode a location string into a geo_point."""
4. try:
6. base_url = "https://maps.googleapis.com/maps/api/geocode/json"
7. params = {
8. 'address': location,
9. 'region': 'us',
10. 'key': google_maps_api_key
11. }
13. logger.info(f"Attempting to geocode: '{location}'")
14. response = requests.get(base_url, params=params)
15. data = response.json()
18. result = data.get('results', [{}])[0]
19. geo_point = {
20. "latitude": result['geometry']['location']['lat'],
21. "longitude": result['geometry']['location']['lng']
22. }
24. logger.info(f"Successfully geocoded to: {json.dumps(geo_point)}")
25. return {
26. "content": [
27. {"type": "text", "text": f"Geocoded '{location}' to: {json.dumps(geo_point)}"}
28. ],
29. "data": geo_point
30. }`AI写代码
2)房产需求:
-
验证设施(例如 "pool")的可搜索性。
-
将术语如 "bed" 规范化为 "bedrooms" 并提取数值。
-
将术语如 "bath" 规范化为 "bathrooms" 并提取数值。
-
将缩写价格格式(例如 "300k")转换为数值(例如 300,000)。
python
`1. @mcp.tool()
2. async def get_properties_template_params() -> Dict[str, Any]:
3. """Get the required parameters for the properties search template."""
4. try:
5. template_id = SEARCH_TEMPLATE_ID
6. source = get_template_script(template_id)
8. return {
9. "content": [
10. {"type": "text", "text": "Required parameters for properties search template:"},
11. {"type": "text", "text": ", ".join(parameters)},
12. {"type": "text", "text": "Parameter descriptions:"},
13. {"type": "text", "text": """- query: Main search query (mandatory)
14. - latitude: Geographic latitude coordinate
15. - longitude: Geographic longitude coordinate
16. - distance: Distance from the latitude/longitude in miles. If not provided, default to 25
17. - bathrooms: Number of bathrooms
18. - tax: Real estate tax amount
19. - maintenance: Maintenance fee amount
20. - 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.
21. - square_footage_max: Maximum property square footage
22. - 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.
23. - home_price_max: Maximum home price
24. - features: Home features such as AC, pool, updated kitches, etc should be listed as a single string For example features such as pool and updated kitchen should be formated as pool updated kitchen"""}
25. ],
26. "data": {"parameters": parameters}
27. }`AI写代码
智能动态查询生成
用户开始与应用程序的交互式会话,具体目标是寻找新房。该交互使用自然语言,使用户能够以对话方式表达他们的偏好、需求和问题,就像与另一个人交流一样。这种对话格式促进了动态且响应迅速的搜索过程,聊天机器人可以澄清模糊查询、根据提供的信息提供建议,并在对话进行过程中优化结果。其目的是利用 MCP 的功能简化和优化房屋搜索体验,使其对用户更直观、高效。

MCP 是智能搜索的核心,通过分析查询特征并将其匹配到搜索模板来处理查询。它与 Google Maps API 集成,对查询中的位置进行地理编码,提高搜索相关性。所有 MCP 处理过程都会被记录并实时显示,为用户提供透明的审计轨迹。这种复杂性将搜索输入转化为精炼且具有上下文的检索操作,使 MCP 成为智能搜索的重要组成部分。

使用的方法
- geocode_location :
- 如果检测到地理查询(例如 "10 miles from Miami, Florida"),则调用 Google API。
- search_template :
- 一旦查询参数与可用搜索模板输入匹配,就使用 Elasticsearch 模板发起搜索查询。
房产搜索模板
使用已识别的查询参数,MCP 发起对 Elasticsearch 搜索模板的调用。例如,用户查询 "Home within 10 miles of Miami, Florida with 2 beds 2 baths with central air and tile floors, up to 300k" 会被转换为特定的调用。
bash
`
1. POST /_render/template
2. {
3. "id": "properties-search-template",
4. "params": {
5. "query": "Home within 10 miles of Miami, Florida with a 2 beds 2 baths with central air and tile floors, up to 300k",
6. "latitude": 28.9516604,
7. "longitude": -95.2875474,
8. "distance": "10mi",
9. "bedrooms": 2,
10. "bathrooms": 2,
11. "home_price": 300000,
12. "feature": "central air tile floors"
13. }
14. }
`AI写代码
该模板生成的查询如下:
bash
`
1. {
2. "template_output": {
3. "_source": false,
4. "size": 5,
5. "fields": [
6. "title",
7. "tax",
8. "maintenance",
9. "bathrooms",
10. "bedrooms",
11. "square_footage",
12. "home_price",
13. "property_features"
14. ],
15. "retriever": {
16. "standard": {
17. "query": {
18. "semantic": {
19. "field": "body_content_semantic",
20. "query": "Home within 10 miles of Miami Florida with 2 beds 2 baths with central air and tile floors, up to 300k"
21. }
22. },
23. "filter": {
24. "bool": {
25. "must": [
26. {
27. "geo_distance": {
28. "distance": "10mi",
29. "location": {
30. "lat": 28.9516604,
31. "lon": -95.2875474
32. }
33. }
34. },
35. {
36. "range": {
37. "bedrooms": {
38. "gte": 2
39. }
40. }
41. },
42. {
43. "range": {
44. "bathrooms": {
45. "gte": 2
46. }
47. }
48. },
49. {
50. "range": {
51. "home_price": {
52. "lte": 300000
53. }
54. }
55. }
56. ],
57. "should": [
58. {
59. "match": {
60. "property_features": {
61. "query": "central air tile floors",
62. "operator": "or"
63. }
64. }
65. }
66. ],
67. "minimum_should_match": 1
68. }
69. }
70. }
71. }
72. }
73. }
`AI写代码
注意,带有 "match " 的 "should " 条款使用了 "or" 操作符。设置 "operator": "or" 和 "minimum_should_match": 1 后,查询将:
-
匹配包含任意特征的文档("central air" OR "tile floors" OR 两者都有)。
-
因为 Elasticsearch 的自然 TF-IDF 评分,匹配特征更多的文档得分更高。
部分匹配的文档也符合条件,但包含所有特征的文档获得更高的相关性评分。
将数据返回给 LLM 后,LLM 可以进一步增强自然语言交互,例如在自定义地图网站上投影房产,如下所示。

结论
"MCP for Intelligent Search" 系统通过集成 Elasticsearch 提升了 LLM 的生成能力。它使用智能查询层和 Elastic Python MCP 服务器。LLM 负责实体检测,而 Elasticsearch 搜索模板生成动态查询。
通过房产搜索示例,我们展示了自然语言查询是如何被处理的,突出了 LLM 和 Elasticsearch 在高效数据检索与响应中的协作。文中提供了实际的设置细节和系统架构,为你的实现提供了清晰的基础。