Skip to content

Commit 4d6ca5b

Browse files
committed
add Agent Builder A2A with Agent Framework example app
1 parent 3da1672 commit 4d6ca5b

File tree

6 files changed

+247
-0
lines changed

6 files changed

+247
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM python:3.11-slim
2+
3+
WORKDIR /app
4+
5+
COPY elastic_agent_builder_a2a.py test_elastic_agent_builder_a2a.py ./
6+
7+
RUN pip install agent-framework
8+
RUN pip install python-dotenv
9+
10+
CMD ["python", "-m", "unittest", "test_elastic_agent_builder_a2a.py"]
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Elastic Agent Builder A2A App
2+
3+
**Getting started with Agent Builder and A2A using Microsoft Agent Framework**
4+
5+
This is a example Python console app that demonstrates how to connect and utilize an [Elastic Agent Builder](https://www.elastic.co/elasticsearch/agent-builder) agent via the Agent2Agent (A2A) Protocol orchestrated with the [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview).
6+
7+
## Prerequisites
8+
9+
1. An Elasticsearch deployment running in [Elastic Cloud](https://cloud.elastic.co/registration).
10+
* Requires Elasticsearch Serverless (or for hosted deployments at least Elasticsearch version 9.2.0).
11+
2. An integrated development environment (IDE) like [Visual Studio Code](https://code.visualstudio.com/download) running on your local computer.
12+
3. [Python version 3.10 or greater](https://www.python.org/downloads/) installed on your local computer.
13+
14+
## Setup your Elasticsearch deployment
15+
16+
1. Create an index named `my-docs` in your Elasticsearch deployment by running the following command in Elastic Developer Tools:
17+
18+
PUT /my-docs
19+
{
20+
"mappings": {
21+
"properties": {
22+
"title": { "type": "text" },
23+
"content": {
24+
"type": "semantic_text"
25+
},
26+
"filename": { "type": "keyword" },
27+
"last_modified": { "type": "date" }
28+
}
29+
}
30+
}
31+
2. Insert a document into your index named `greetings.md` by running the following command in Elastic Developer Tools:
32+
33+
PUT /my-docs/_doc/greetings-md
34+
{
35+
"title": "Greetings",
36+
"content": "
37+
# Greetings
38+
## Basic Greeting
39+
Hello!
40+
41+
## Helloworld Greeting
42+
Hello World! 🌎
43+
44+
## Not Greeting
45+
I'm only a greeting agent. 🤷
46+
47+
",
48+
"filename": "greetings.md",
49+
"last_modified": "2025-11-04T12:00:00Z"
50+
}
51+
52+
3. In Elastic Agent Builder, create a **tool** with the following values:
53+
* **Type**: `ES|QL`
54+
* **Tool ID**: `example.get_greetings`
55+
* **Description**: `Get greetings doc from Elasticsearch my\docs index.`
56+
* **ES|QL**:
57+
58+
FROM my-docs | WHERE filename == "greetings.md"
59+
60+
4. In Elastic Agent Builder, create an **agent** with the following values:
61+
* **Agent ID**: `helloworld_agent`
62+
* **Custom Instructions**:
63+
64+
If the prompt contains greeting text like "Hi" or "Hello" then respond with only the Basic Hello text from your documents.
65+
66+
If the prompt contains the text “Hello World” then respond with only the Hello World text from your documents.
67+
68+
In all other cases where the prompt does not contain greeting words, then respond with only the Not Greeting text from your documents.
69+
70+
* **Display Name**: `HelloWorld Agent`
71+
* **Display Description**: `An agent that responds to greetings.`
72+
73+
74+
75+
## Running the example app
76+
77+
1. Open Visual Studio Code and open a new terminal within the Visual Studio Code editor.
78+
2. In the open terminal, clone the Search Labs source code repository which contains the Elastic Agent Builder A2A App example.
79+
80+
git clone https://github.com/elastic/elasticsearch-labs
81+
82+
3. `cd` to change directory to the example code located in the `example-apps/agent-builder-a2a-agent-framework` subdirectory.
83+
84+
cd elasticsearch-labs/example-apps/agent-builder-a2a-agent-framework
85+
86+
4. Replace placeholder values in `elastic_agent_builder_a2a.py` with values copied from your Elastic deployment.
87+
1. Open the file `elastic_agent_builder_a2a.py` in the Visual Studio editor.
88+
2. Replace <YOUR-ELASTIC-AGENT-BUILDER-URL\>
89+
1. In your Elastic deployment, go to the Elastic Agent Builder - Tools page. Click the **MCP Server** dropdown at the top of the Tools page. Select **Copy MCP Server URL.**
90+
2. In Visual Studio add the **MCP Server URL** value to the `elastic-agent-builder-a2a.py` file.
91+
* Find where the placeholder text “**\<YOUR-ELASTIC-AGENT-BUILDER-URL\>**” appears and paste in the copied **MCP Server URL** to replace the placeholder text. Now edit the pasted **MCP Server URL**. Delete the text “mcp” at the end of the URL and replace it with the text “a2a”. The edited URL should look something like this
92+
93+
`https://example-project-a123.kb.westus2.azure.elastic.cloud/api/agent_builder/a2a`
94+
95+
3. Replace \<YOUR-ELASTIC-API-KEY\>
96+
1. In your Elastic deployment, click **Elasticsearch** in the navigation menu to go to your deployment’s home page.
97+
2. Click **Create API key** to create a new API key.
98+
3. After the API key is created, copy the API Key value.
99+
4. In Visual Studio add the API Key value to the `elastic-agent-builder-a2a.pys` file.
100+
* Find where the placeholder text “**\<YOUR-ELASTIC-API-KEY\>**” appears and paste in the copied API Key value to replace the placeholder text.
101+
102+
4. Confirm the **relative_card_path** is set correctly in the `elastic-agent-builder-a2a.py` file by finding the code line that starts with the text “agent_card”. Confirm that the **relative_card_path** matches the Agent ID you specified when you created the agent in Elastic Agent Builder. If your Agent ID is “helloworld_agent” then the **relative_card_path** should be set to `/helloworld_agent.json`
103+
5. Save the `elastic_agent_builder_a2a.py` file in the Visual Studio editor.
104+
105+
5. Create a Python virtual environment by running the following code in the Visual Studio Code terminal.
106+
107+
python \-m venv .venv
108+
109+
6. Activate the Python virtual environment.
110+
* If you’re running MacOS, the command to activate the virtual environment is:
111+
112+
source .venv/bin/activate
113+
114+
* If you’re on Windows, the command to activate the virtual environment is:
115+
116+
.venv\Scripts\activate
117+
118+
7. Install the Microsoft Agent Framework with the following `pip` command:
119+
120+
pip install agent-framework
121+
122+
8. Run the example code by entering the following command into the terminal:
123+
124+
python elastic-agent-builder-a2a.py
125+
126+
## Running the example test
127+
128+
1. Setup the environment variables.
129+
1. Make a copy of the file `env.example` and name the new file `.env `
130+
2. Edit the `.env` file to replace the placeholder text with actual values from your Elastic deployment. See instructions on where to get these values in the [Running the example app](#running-the-example-app) section of this `README.md` file.
131+
* Replace **YOUR-ELASTIC-AGENT-BUILDER-URL**
132+
* Replace **YOUR-ELASTIC-API-KEY**
133+
2. Run the test directly with Python.
134+
135+
python test_elastic_agent_builder_a2a.py
136+
137+
3. Run the test with Docker.
138+
139+
docker compose up
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: '3.8'
2+
3+
services:
4+
elastic-agent-builder-a2a-test:
5+
build:
6+
context: .
7+
dockerfile: Dockerfile
8+
container_name: elastic-agent-builder-a2a-test
9+
environment:
10+
- ES_AGENT_URL=${ES_AGENT_URL}
11+
- ES_API_KEY=${ES_API_KEY}
12+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import asyncio
2+
import httpx
3+
from a2a.client import A2ACardResolver
4+
from agent_framework.a2a import A2AAgent
5+
6+
async def main():
7+
a2a_agent_host = "<YOUR-ELASTIC-AGENT-BUILDER-URL>"
8+
9+
print(f"Connection to Elastic A2A agent at: {a2a_agent_host}")
10+
11+
custom_headers = {
12+
"Authorization": "ApiKey <YOUR-ELASTIC-API-KEY>"
13+
}
14+
15+
async with httpx.AsyncClient(timeout=60.0, headers=custom_headers) as http_client:
16+
# Resolve the A2A Agent Card
17+
resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_agent_host)
18+
agent_card = await resolver.get_agent_card(relative_card_path="/helloworld_agent.json")
19+
print(f"Found Agent: {agent_card.name} - {agent_card.description}")
20+
21+
# Use the Agent
22+
agent = A2AAgent(
23+
name=agent_card.name,
24+
description=agent_card.description,
25+
agent_card=agent_card,
26+
url=a2a_agent_host,
27+
http_client=http_client
28+
)
29+
prompt = input("Enter Greeting >>> ")
30+
print("\nSending message to Elastic A2A agent...")
31+
response = await agent.run(prompt)
32+
print("\nAgent Response:")
33+
for message in response.messages:
34+
print(message.text)
35+
36+
if __name__ == "__main__":
37+
asyncio.run(main())
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ES_AGENT_URL=YOUR-ELASTIC-AGENT-BUILDER-URL
2+
ES_API_KEY=YOUR-ELASTIC-API-KEY
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import unittest
2+
from unittest.mock import patch
3+
from dotenv import load_dotenv
4+
import os
5+
import asyncio
6+
from pathlib import Path
7+
import importlib.util
8+
9+
class test_main_function(unittest.TestCase):
10+
def setUp(self):
11+
# Replace placeholder values with .env values
12+
load_dotenv()
13+
14+
self.test_file = Path("elastic_agent_builder_a2a.py")
15+
self.backup_file = Path("elastic_agent_builder_a2a.py.backup")
16+
17+
if self.test_file.exists():
18+
self.original_content = self.test_file.read_text()
19+
self.backup_file.write_text(self.original_content)
20+
21+
self.es_agent_url = os.getenv("ES_AGENT_URL")
22+
self.es_api_key = os.getenv("ES_API_KEY")
23+
content = self.test_file.read_text()
24+
modified_content = content.replace("<YOUR-ELASTIC-AGENT-BUILDER-URL>", self.es_agent_url)
25+
modified_content = modified_content.replace("<YOUR-ELASTIC-API-KEY>", self.es_api_key)
26+
self.test_file.write_text(modified_content)
27+
28+
# Import the modified module
29+
spec = importlib.util.spec_from_file_location("elastic_agent_builder_a2a", self.test_file)
30+
self.user_input_module = importlib.util.module_from_spec(spec)
31+
spec.loader.exec_module(self.user_input_module)
32+
33+
def tearDown(self):
34+
if self.backup_file.exists():
35+
self.test_file.write_text(self.backup_file.read_text())
36+
self.backup_file.unlink()
37+
38+
39+
@patch('builtins.input', return_value='hello world')
40+
@patch('builtins.print')
41+
def test_main_input(self, mock_print, mock_input):
42+
# Run test
43+
asyncio.run(self.user_input_module.main())
44+
mock_print.assert_called_with("Hello World! 🌎")
45+
46+
if __name__ == "__main__":
47+
unittest.main()

0 commit comments

Comments
 (0)