使用 Elasticsearch 提升 Copilot 能力

发布于:2025-06-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

作者:来自 Elastic Jeffrey Rengifo

了解如何将 Elasticsearch 与 Microsoft 365 Copilot Chat 和 Microsoft Teams 中的 Copilot 搭配使用。

Elasticsearch 原生集成了业界领先的生成式 AI 工具和提供商。查看我们的网络研讨会,了解超越 RAG 基础的内容,或构建可投入生产的应用程序 Elastic Vector Database

为了为你的使用场景构建最佳的搜索解决方案,现在可以开始免费云试用,或者在本地机器上尝试 Elastic。


本文将指导你如何将 Elasticsearch 与 Microsoft Copilot 集成。我们将构建一个能够从你的 Elasticsearch 文档中检索并使用数据的自定义代理。

什么是 Microsoft Copilot

Microsoft Copilot 是一个 AI 助手,帮助你在 Microsoft 生态系统产品中完成各类任务,提供类似于 ChatGPT 的功能,满足各种生产力需求。

通过使用 Microsoft Copilot Studio 和图形界面,你可以构建自定义 agent,并为它们配置 actions。这些 actions 会连接到你的 API,以获取数据并响应用户查询。

action 是 agent 可用来帮助你完成任务的工具。这些 actions 可以连接到平台内置的连接器,或连接到 API。OpenAPI JSON 规范为描述你的 API 提供了一种标准格式,使 Copilot 能理解它的功能、可用的端点,以及如何正确构造查询,包括自动填充参数。

我们将构建一个 agent,用于提供存储在 Elasticsearch 中的发票信息,通过 API 连接。这个 agent 将运行在 Microsoft Teams 上,作为个人助手使用。

本教程将引导你完成以下步骤:

  • 构建 API

  • 设置 Elasticsearch 数据

  • 配置 Copilot 和 agent

  • 使用 agent

本文中引用的所有代码和文件都可以在这个 GitHub 仓库中找到。

构建 API

我们将创建一个查询 Elasticsearch 的简单 API。我们会在 Jupyter notebook 中使用 FastAPI 来构建它。

%pip install fastapi pyngrok uvicorn nest-asyncio elasticsearch==9 -q

在导入这些工具之前,让我们先回顾一下已安装的工具:

  • fastapi, uvicorn:生成一个用于访问 Elasticsearch 的 HTTP 服务器。
  • pyngrok:为本地服务器创建一个安全的公共 IP 地址(隧道),使 Copilot agent 能通过互联网访问它。这个和花生壳有点类似。
  • elasticsearch:Elasticsearch 的 Python 客户端。
  • nest-asyncio:在 Notebook 环境中创建服务器所需。
import os
from datetime import datetime

import nest_asyncio
import uvicorn

from fastapi import FastAPI, Query
from pyngrok import conf, ngrok

from elasticsearch.helpers import bulk
from elasticsearch import Elasticsearch

要在 Copilot agent 中使用这个 API,我们需要让它可以通过互联网访问。因此,我们将使用 ngrok 创建一个隧道,把 API 连接到互联网。在这里创建一个 ngrok 账户并获取一个 auth token。

让我们来配置环境变量:

os.environ["ELASTICSEARCH_ENDPOINT"] = (
    "Elasticsearch_endpoint"
)
os.environ["ELASTICSEARCH_API_KEY"] = (
    "Elasticsearch_api_key"
)
os.environ["NGROK_AUTH_TOKEN"] = "ngrok-token"
INDEX_NAME = "invoices"

现在,实例化 Elasticsearch 客户端:

_client = Elasticsearch(
    os.environ["ELASTICSEARCH_ENDPOINT"],
    api_key=os.environ["ELASTICSEARCH_API_KEY"],
)

让我们启动服务器并创建以下端点:

/search/semantic

根据用户的查询执行语义搜索

/search/by-date

根据日期范围执行搜索。

在这两种情况下,Elasticsearch 都会返回每个查询的一组文档。

app = FastAPI()

@app.get("/search/semantic")
async def search_semantic(query: str = Query(None)):
    # ... full code in the GitHub repository ...


@app.get("/search/by-date")
async def search_by_date(from_date: str = Query(None), to_date: str = Query(None)):
    # ... full code in the GitHub repository ...

让我们配置隧道并运行服务器。

conf.get_default().auth_token = os.environ["NGROK_AUTH_TOKEN"]
ngrok_tunnel = ngrok.connect(8000)

print("Public URL:", ngrok_tunnel.public_url)

nest_asyncio.apply()
uvicorn.run(app, port=8000)

你获得的 Public URL 将提供给我们的 Microsoft Copilot agent。

Public URL: https://81c1-181-237-140-155.ngrok-free.app

INFO:     Started server process [77882]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

设置 Elasticsearch 数据

映射

我们将索引一系列发票,其中包含所提供服务的描述、日期、参考 URL、总金额和描述。

我们还会将需要用于语义搜索的属性复制到一个名为 semantic_field 的字段类型中。

_client.indices.create(
        index=INDEX_NAME,
        body={
            "mappings": {
                "properties": {
                    "id": {"type": "keyword"},
                    "file_url": {"type": "keyword"},
                    "issue_date": {"type": "date"},
                    "description": {"type": "text", "copy_to": "semantic_field"},
                    "services": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "text",
                                "copy_to": "semantic_field",
                            },
                            "price": {"type": "float"},
                        },
                    },
                    "total_amount": {
                        "type": "float",
                    },
                    "semantic_field": {"type": "semantic_text"},
                }
            }
        },
    )

索引数据

我们将上传一组不同的发票。数据集可以在这里找到。

让我们创建一个函数,遍历发票数组,并使用 bulk API 将其索引到 Elasticsearch 中。

def build_data():
    for doc in invoices:
        yield {"_index": INDEX_NAME, "_source": doc}


try:
    success, errors = bulk(_client, build_data())
    print(f"{success} documents indexed successfully")

    if errors:
        print("Errors during indexing:", errors)

except Exception as e:
    print(f"Error: {str(e)}, please wait some seconds and try again.")

Copilot 和代理配置

使用 Copilot chat 需要一个 Office 365 账户。如果你没有,可以在这里创建试用账户。

创建代理和操作

创建账户后,访问主 Copilot Studio 网站,按照以下说明操作:

  1. 按照微软的步骤创建一个代理(create an agent)。

  2. 从 REST API 添加操作。

    一个 action 是一个 API 端点。这个例子里,你需要添加两个:一个用于语义文本查询,另一个用于基于日期的查询。

    为此,你需要先生成你刚创建的 API 的 OpenAPI JSON 参考。你可以在专门为本文准备的 GitHub 仓库里找到 OpenAPI 规范文件。在 OpenAPI 配置中,把已有的 'host' 值替换成 ngrok 生成的 URL。注意去掉 'https://' 前缀,只保留主机名和端口。

  3. 点击 Settings 并启用 Generative AI,以便使用 GenAI 处理对话。根据 Copilot 在对话中检测到的内容,选择 Actions、Knowledge 和 Topics。

  4. 点击 Overview,然后向下滚动,你会找到 Knowledge 部分。关闭选项 Allow the AI to use its own general knowledge。这样可以防止代理使用其训练中的通用信息进行回答,因为我们希望它们专注于我们的 Elasticsearch 文档。

  5. 如果你使用的是 ngrok 免费层,必须设置请求头 'ngrok-skip-browser-warning',其值可以是任意内容。这样可以绕过 ngrok 要求的浏览器验证步骤,因为免费通道可能会被滥用。为此,前往 “Actions > [Action] > Inputs > Ngrok Skip Browser Warning”,点击 “How will the agent fill this input?”,选择 “Set as value”。在 Value 中填写任意值后点击 “Save”,这样你就可以通过 ngrok 通道使用这些端点了。

让代理在 Copilot 中可见

要在 Microsoft 365 或 Microsoft Teams 中使用该代理,你必须按照以下步骤操作:

  1. 在 Copilot Studio 中,进入 Channels,然后点击 Teams + Microsoft 365:

  2. 取消选择 “Make agent available in Microsoft 365 Copilot Chat” 选项。这个选项只适用于你想在 Microsoft 365 Copilot 中使用代理的情况,而我们是要在 Microsoft Teams 中使用它。

  3. 现在点击 “Add channel”。

  4. 将代理添加到频道后,点击 “See agent in Teams”,然后直接将其添加到 Teams 中。

添加完成后,你就可以在 Copilot 中通过输入 @ 并从选项中选择该代理来开始使用它。

使用代理

首次使用 actions 时,Copilot 会提示权限问题,并在需要你授权每个 action 时显示一个框,点击 Connect 即可授予权限。每个 action 只需授权一次。

你现在已经可以开始使用代理了。

要测试语义搜索,我们可以向代理询问有关餐饮费用的发票:

Which invoices have billed items related to food comsumption?

让我们来看一下回答结果:

得益于语义搜索功能,Copilot 能够找到那些虽然没有包含 “food” 一词,但包含像 “dinner” 和 “lunch” 这类词的发票。

在 API 日志中,我们可以验证被调用的端点和 Copilot 生成的查询。在这个例子里,查询是 “food consumption”。

这个文件中找到 JSON 格式的查询内容,以及 Elasticsearch 针对前一个查询返回的原始命中结果。

现在,让我们通过查询特定时间范围内开具的发票,来测试日期范围搜索:

invoices between 20 of April and 22 of April

以下是结果:

  • Invoice ID: INV-0011
    • Description: Business lunch with client
    • Services: Lunch at La Terraza Bistro - $85.00
    • Total Amount: $85.00
    • Issue Date: 2025-04-20
    • View Invoice
  • Invoice ID: INV-0012
    • Description: Hotel accommodation during client visit
    • Services: 3-night stay at Hotel Central - $450.00
    • Total Amount: $450.00
    • Issue Date: 2025-04-21
    • View Invoice
  • Invoice ID: INV-0013
    • Description: Team-building activity
    • Services: Escape room experience for team - $200.00
    • Total Amount: $200.00
    • Issue Date: 2025-04-22
    • View Invoice

通过查看 API 请求,我们可以确认 Copilot 正确地解析了数据和所选的端点:

范围查询如下所示,你还可以看到 Elasticsearch 返回的数据。

结论

本文中,我们成功地将 Elasticsearch 与 Microsoft Copilot 集成,创建了一个能够访问我们索引的个人助理。

通过使用带有清晰描述的 API OpenAI 规范,代理能够有效地选择并调用合适的端点。

这种架构类似于 Model Context Protocol(MCP),在未来的文章中,我们将探讨 Microsoft Copilot 与 MCP 以及 Elasticsearch MCP 服务器的新集成。

原文:Improving Copilot capabilities using Elasticsearch - Elasticsearch Labs


网站公告

今日签到

点亮在社区的每一天
去签到