diff --git a/gemini-research-agent/.env.example b/gemini-research-agent/.env.example new file mode 100644 index 0000000..42141e3 --- /dev/null +++ b/gemini-research-agent/.env.example @@ -0,0 +1,2 @@ +# Add your Google Gemini API key here +GEMINI_API_KEY=your_api_key_here \ No newline at end of file diff --git a/gemini-research-agent/.gitignore b/gemini-research-agent/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/gemini-research-agent/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/gemini-research-agent/README.md b/gemini-research-agent/README.md new file mode 100644 index 0000000..a7f9549 --- /dev/null +++ b/gemini-research-agent/README.md @@ -0,0 +1,43 @@ +# Gemin# Gemini-Powered Research Agent + +This example demonstrates how to integrate Google's Gemini API (`google-genai`) within the Fetch.ai `uagents` framework to create an autonomous research and summarization agent. + +## Architecture +This example utilizes two agents: + +1. **Gemini Agent (`gemini_agent.py`)**: Acts as the backend processor. It listens for `ResearchRequest` messages, queries the Gemini 2.5 Flash model, and returns a structured `ResearchResponse`. +2. **User Agent (`user_agent.py`)**: Acts as the client. It triggers the request on startup and logs the AI-generated summary upon receipt. + +## Prerequisites +Ensure you have Python 3.10+ installed. + +Install the required dependencies: +```bash +python -m pip install uagents google-genai python-dotenv +``` + +## Setup Instructions + +### 1. API Key Setup +Create a `.env` file in this directory and add your Google Gemini API key: +```text +GEMINI_API_KEY=your_api_key_here +``` + +### 2. Start the Gemini Agent +Open a terminal and run the Gemini agent: +```bash +python gemini_agent.py +``` +*Note: Look at the startup logs and copy the Agent address (it starts with `agent1...`).* + +### 3. Configure the User Agent +Open `user_agent.py` and replace the `TARGET_AGENT_ADDRESS` variable with the address you just copied. + +### 4. Trigger the Research +Open a second terminal and run the user agent: +```bash +python user_agent.py +``` + +You will see the asynchronous message transfer and the final AI-generated summary printed in your terminal! \ No newline at end of file diff --git a/gemini-research-agent/assets/demo.png b/gemini-research-agent/assets/demo.png new file mode 100644 index 0000000..bae8dd9 Binary files /dev/null and b/gemini-research-agent/assets/demo.png differ diff --git a/gemini-research-agent/gemini_agent.py b/gemini-research-agent/gemini_agent.py new file mode 100644 index 0000000..0e2a8fb --- /dev/null +++ b/gemini-research-agent/gemini_agent.py @@ -0,0 +1,88 @@ +from datetime import datetime +from uuid import uuid4 +from uagents import Agent, Context, Protocol +from uagents_core.contrib.protocols.chat import ( + ChatMessage, + ChatAcknowledgement, + TextContent, + chat_protocol_spec, +) +from google import genai +from dotenv import load_dotenv + +# Load environment variables (ensure GEMINI_API_KEY is in your .env) +load_dotenv() + +# --- 1. Initialize the Agent --- +research_agent = Agent( + name="gemini_researcher", + port=8000, + seed="gemini_researcher_secret_seed", + endpoint=["http://127.0.0.1:8000/submit"], +) + +# Initialize the chat protocol +chat_proto = Protocol(spec=chat_protocol_spec) + +# Initialize the Gemini Client +gemini_client = genai.Client() + +# --- 2. Define the Message Handlers --- +@chat_proto.on_message(ChatMessage) +async def handle_research_request(ctx: Context, sender: str, msg: ChatMessage): + # Send an immediate acknowledgment that the message was received + ack = ChatAcknowledgement( + timestamp=datetime.utcnow(), + acknowledged_msg_id=msg.msg_id + ) + await ctx.send(sender, ack) + + # Extract the text content from the ChatMessage + for item in msg.content: + if isinstance(item, TextContent): + user_query = item.text + ctx.logger.info(f"Received research request from {sender[-8:]} for topic: '{user_query}'") + + try: + # Prompt Engineering for the Agent + system_instruction = "You are a concise, highly factual research assistant." + prompt = ( + f"{system_instruction}\n\nProvide a structured summary about: {user_query}" + ) + + # Call the Gemini API + response = gemini_client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + ) + + # Package the AI-generated response into a ChatMessage + response_msg = ChatMessage( + timestamp=datetime.utcnow(), + msg_id=uuid4(), + content=[TextContent(type="text", text=response.text)] + ) + + await ctx.send(sender, response_msg) + ctx.logger.info("Successfully generated and returned the research summary.") + + except Exception as e: + ctx.logger.error(f"Gemini API Error: {e}") + + # Package the error into a ChatMessage + error_msg = ChatMessage( + timestamp=datetime.utcnow(), + msg_id=uuid4(), + content=[TextContent(type="text", text=f"Agent Error: Could not process request. {str(e)}")] + ) + await ctx.send(sender, error_msg) + +@chat_proto.on_message(ChatAcknowledgement) +async def handle_acknowledgement(ctx: Context, sender: str, msg: ChatAcknowledgement): + ctx.logger.info(f"Received acknowledgement from {sender} for message: {msg.acknowledged_msg_id}") + +# Include the protocol in your agent +research_agent.include(chat_proto, publish_manifest=True) + +if __name__ == "__main__": + research_agent.run() \ No newline at end of file diff --git a/gemini-research-agent/requirements.txt b/gemini-research-agent/requirements.txt new file mode 100644 index 0000000..54f9952 --- /dev/null +++ b/gemini-research-agent/requirements.txt @@ -0,0 +1,4 @@ +uagents +uagents-core +google-genai +python-dotenv \ No newline at end of file diff --git a/gemini-research-agent/user_agent.py b/gemini-research-agent/user_agent.py new file mode 100644 index 0000000..c9ec509 --- /dev/null +++ b/gemini-research-agent/user_agent.py @@ -0,0 +1,74 @@ +import asyncio +from datetime import datetime +from uuid import uuid4 +from uagents import Agent, Context, Protocol +from uagents_core.contrib.protocols.chat import ( + ChatMessage, + ChatAcknowledgement, + TextContent, + chat_protocol_spec, +) + +# --- THE FIX: Manually jumpstart the asyncio event loop for Python 3.12+ --- +try: + asyncio.get_event_loop() +except RuntimeError: + asyncio.set_event_loop(asyncio.new_event_loop()) + +# --- 1. Initialize the User Agent --- +user_agent = Agent( + name="user_client", + port=8001, + seed="user_client_secret_seed", + endpoint=["http://127.0.0.1:8001/submit"], +) + +# Initialize the chat protocol +chat_proto = Protocol(spec=chat_protocol_spec) + +# YOUR TARGET ADDRESS +TARGET_AGENT_ADDRESS = ( + "agent1qtjys8khgg88v6gvjudrlxdp7njxn9utettjclyj68pfevm7df9e6nsseng" +) + +# --- 2. Define the Triggers and Handlers --- +@user_agent.on_event("startup") +async def send_research_request(ctx: Context): + topic = "The architecture of transformer-based Large Language Models" + ctx.logger.info(f"Sending research request for: {topic}") + + # Package the prompt into a TextContent object inside a ChatMessage + research_message = ChatMessage( + timestamp=datetime.utcnow(), + msg_id=uuid4(), + content=[TextContent(type="text", text=topic)] + ) + + # Send the request to the Gemini Agent + await ctx.send(TARGET_AGENT_ADDRESS, research_message) + +@chat_proto.on_message(ChatMessage) +async def handle_response(ctx: Context, sender: str, msg: ChatMessage): + # Extract the summary text from the incoming ChatMessage + for item in msg.content: + if isinstance(item, TextContent): + ctx.logger.info("\n=== Received Research Summary ===") + ctx.logger.info(item.text) + ctx.logger.info("=================================\n") + + # Send an acknowledgment back to the Gemini agent + ack = ChatAcknowledgement( + timestamp=datetime.utcnow(), + acknowledged_msg_id=msg.msg_id + ) + await ctx.send(sender, ack) + +@chat_proto.on_message(ChatAcknowledgement) +async def handle_acknowledgement(ctx: Context, sender: str, msg: ChatAcknowledgement): + ctx.logger.info(f"Received acknowledgement from {sender} for message: {msg.acknowledged_msg_id}") + +# Include the protocol in your agent +user_agent.include(chat_proto, publish_manifest=True) + +if __name__ == "__main__": + user_agent.run() \ No newline at end of file