Added writer agent to generate answers
Browse files- agents/llama_index_agent.py +105 -3
- app.py +75 -12
agents/llama_index_agent.py
CHANGED
@@ -1,4 +1,7 @@
|
|
1 |
-
from llama_index.core.agent.workflow import
|
|
|
|
|
|
|
2 |
from llama_index.core.llms import LLM
|
3 |
import os
|
4 |
from typing import Optional, List, Any
|
@@ -61,6 +64,10 @@ class GaiaAgent(ReActAgent):
|
|
61 |
# Use default system prompt if not provided
|
62 |
if system_prompt is None:
|
63 |
system_prompt = self._get_default_system_prompt()
|
|
|
|
|
|
|
|
|
64 |
|
65 |
# Initialize the parent ReActAgent
|
66 |
super().__init__(
|
@@ -69,6 +76,7 @@ class GaiaAgent(ReActAgent):
|
|
69 |
llm=llm,
|
70 |
system_prompt=system_prompt,
|
71 |
tools=tools,
|
|
|
72 |
**kwargs
|
73 |
)
|
74 |
|
@@ -84,9 +92,9 @@ class GaiaAgent(ReActAgent):
|
|
84 |
|
85 |
else:
|
86 |
raise ValueError(f"Unsupported model provider: {model_provider}. "
|
87 |
-
f"Supported providers are: openai, anthropic
|
88 |
|
89 |
-
def
|
90 |
"""Return the default system prompt for GAIA benchmark tasks."""
|
91 |
return """
|
92 |
You are the lead coordinator for a team of specialized AI agents tackling the GAIA benchmark. Your job is to analyze each question with extreme precision, determine the exact format required for the answer, break the task into logical steps, and either solve it yourself or delegate to the appropriate specialized agents.
|
@@ -136,3 +144,97 @@ class GaiaAgent(ReActAgent):
|
|
136 |
|
137 |
IMPORTANT: Your value is in providing PRECISELY what was asked for - not in showing your work or explaining how you got there.
|
138 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from llama_index.core.agent.workflow import (
|
2 |
+
ReActAgent,
|
3 |
+
FunctionAgent
|
4 |
+
)
|
5 |
from llama_index.core.llms import LLM
|
6 |
import os
|
7 |
from typing import Optional, List, Any
|
|
|
64 |
# Use default system prompt if not provided
|
65 |
if system_prompt is None:
|
66 |
system_prompt = self._get_default_system_prompt()
|
67 |
+
|
68 |
+
can_handoff_to = [
|
69 |
+
"writer_agent"
|
70 |
+
]
|
71 |
|
72 |
# Initialize the parent ReActAgent
|
73 |
super().__init__(
|
|
|
76 |
llm=llm,
|
77 |
system_prompt=system_prompt,
|
78 |
tools=tools,
|
79 |
+
can_handoff_to=can_handoff_to,
|
80 |
**kwargs
|
81 |
)
|
82 |
|
|
|
92 |
|
93 |
else:
|
94 |
raise ValueError(f"Unsupported model provider: {model_provider}. "
|
95 |
+
f"Supported providers are: openai, anthropic")
|
96 |
|
97 |
+
def _get_default_system_prompt_legacy(self) -> str:
|
98 |
"""Return the default system prompt for GAIA benchmark tasks."""
|
99 |
return """
|
100 |
You are the lead coordinator for a team of specialized AI agents tackling the GAIA benchmark. Your job is to analyze each question with extreme precision, determine the exact format required for the answer, break the task into logical steps, and either solve it yourself or delegate to the appropriate specialized agents.
|
|
|
144 |
|
145 |
IMPORTANT: Your value is in providing PRECISELY what was asked for - not in showing your work or explaining how you got there.
|
146 |
"""
|
147 |
+
|
148 |
+
def _get_default_system_prompt(self) -> str:
|
149 |
+
"""Return the default system prompt for GAIA benchmark tasks."""
|
150 |
+
return """
|
151 |
+
You are the lead coordinator for a team of specialized AI agents tackling the GAIA benchmark. Your job is to analyze questions and generate detailed analysis, which you'll pass to a specialized formatting agent for final answer preparation.
|
152 |
+
|
153 |
+
## QUESTION ANALYSIS PROCESS
|
154 |
+
1. First, carefully read and parse the entire question
|
155 |
+
2. Identify the EXACT output format required (single word, name, number, comma-separated list, etc.)
|
156 |
+
3. Note any special formatting requirements (alphabetical order, specific notation, etc.)
|
157 |
+
4. Identify what type of task this is (research, audio analysis, video analysis, code execution, data analysis, etc.)
|
158 |
+
5. Break the question into sequential steps
|
159 |
+
|
160 |
+
## SOLVING METHODOLOGY
|
161 |
+
1. For each question, thoroughly work through the reasoning step-by-step
|
162 |
+
2. Use available tools (reverse_text_tool, search tools) when needed
|
163 |
+
3. Document your full analysis, including all key facts, calculations, and relevant information
|
164 |
+
4. Clearly identify what you believe the correct answer is
|
165 |
+
5. Be extremely explicit about the required formatting for the final answer
|
166 |
+
|
167 |
+
## DELEGATION TO WRITER AGENT
|
168 |
+
After completing your analysis, ALWAYS delegate the final answer preparation to the writer_agent with:
|
169 |
+
- query: The original question
|
170 |
+
- research_notes: Your complete analysis, all relevant facts, and what you believe is the correct answer
|
171 |
+
- answer_format: EXPLICIT instructions on exactly how the answer should be formatted (single word, comma-separated list, etc.)
|
172 |
+
|
173 |
+
Example handoff to writer_agent:
|
174 |
+
```
|
175 |
+
I'll delegate to writer_agent to format the final answer.
|
176 |
+
|
177 |
+
query: What is the first name of the scientist who discovered penicillin?
|
178 |
+
research_notes: After researching, I found that Sir Alexander Fleming discovered penicillin in 1928. The full answer is "Alexander Fleming" but the question only asks for the first name, which is "Alexander".
|
179 |
+
answer_format: Return ONLY the first name, with no additional text, punctuation, or explanation.
|
180 |
+
```
|
181 |
+
|
182 |
+
IMPORTANT: NEVER provide the final answer directly to the user. ALWAYS hand off to the writer_agent for proper formatting.
|
183 |
+
"""
|
184 |
+
|
185 |
+
|
186 |
+
def create_writer_agent(model_config: Dict[str, Any]) -> ReActAgent:
|
187 |
+
"""
|
188 |
+
Create a writer agent that formats final answers based on research notes.
|
189 |
+
|
190 |
+
Args:
|
191 |
+
model_config: Dictionary containing model_provider, model_name, and api_key
|
192 |
+
|
193 |
+
Returns:
|
194 |
+
A configured ReActAgent for formatting final answers
|
195 |
+
"""
|
196 |
+
# Initialize LLM based on the provided configuration
|
197 |
+
model_provider = model_config.get("model_provider", "openai")
|
198 |
+
model_name = model_config.get("model_name", "gpt-4o")
|
199 |
+
api_key = model_config.get("api_key")
|
200 |
+
|
201 |
+
if model_provider.lower() == "openai":
|
202 |
+
llm = OpenAI(model=model_name, api_key=api_key or os.getenv("OPENAI_API_KEY"))
|
203 |
+
elif model_provider.lower() == "anthropic":
|
204 |
+
llm = Anthropic(model=model_name, api_key=api_key or os.getenv("ANTHROPIC_API_KEY"))
|
205 |
+
else:
|
206 |
+
raise ValueError(f"Unsupported model provider for writer agent: {model_provider}")
|
207 |
+
|
208 |
+
# Create and return the writer agent
|
209 |
+
return ReActAgent(
|
210 |
+
name="writer_agent",
|
211 |
+
description="Formats the final answer exactly as specified for GAIA benchmark questions",
|
212 |
+
system_prompt="""
|
213 |
+
You are a specialized formatting agent for the GAIA benchmark. Your ONLY job is to take the research from the main agent and format the answer EXACTLY as required by the benchmark question.
|
214 |
+
|
215 |
+
## YOUR ROLE
|
216 |
+
You will receive:
|
217 |
+
- query: The original question
|
218 |
+
- research_notes: The main agent's complete analysis and reasoning
|
219 |
+
- answer_format: Specific formatting instructions for the final answer
|
220 |
+
|
221 |
+
## CRITICAL RULES
|
222 |
+
1. Your response MUST CONTAIN ONLY THE ANSWER - no explanations, no "the answer is" prefix
|
223 |
+
2. Follow the answer_format instructions precisely
|
224 |
+
3. Remove ALL unnecessary characters, spaces, punctuation, or wording
|
225 |
+
4. If asked for a name, provide ONLY the name
|
226 |
+
5. If asked for a number, provide ONLY the number
|
227 |
+
6. If asked for a list, format it EXACTLY as specified (comma-separated, alphabetical, etc.)
|
228 |
+
7. NEVER include your own thoughts or analysis
|
229 |
+
8. NEVER add preamble or conclusion text
|
230 |
+
|
231 |
+
## EXAMPLES OF CORRECT RESPONSES:
|
232 |
+
When asked for "first name only": Alexander
|
233 |
+
When asked for "comma-separated list in alphabetical order": apple, banana, cherry
|
234 |
+
When asked for "single number": 42
|
235 |
+
When asked for "opposite of word 'right'": left
|
236 |
+
|
237 |
+
REMEMBER: Your ENTIRE response should be just the answer - nothing more, nothing less.
|
238 |
+
""",
|
239 |
+
llm=llm
|
240 |
+
)
|
app.py
CHANGED
@@ -3,8 +3,10 @@ import gradio as gr
|
|
3 |
import requests
|
4 |
import inspect
|
5 |
import pandas as pd
|
6 |
-
from agents.llama_index_agent import GaiaAgent
|
7 |
import asyncio
|
|
|
|
|
|
|
8 |
# (Keep Constants as is)
|
9 |
# --- Constants ---
|
10 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
@@ -23,34 +25,95 @@ OPENAI = {
|
|
23 |
class BasicAgent:
|
24 |
def __init__(
|
25 |
self,
|
26 |
-
model_provider="
|
27 |
-
model_name="
|
28 |
-
api_key=None
|
|
|
|
|
|
|
29 |
):
|
30 |
"""
|
31 |
-
Initialize the BasicAgent with
|
32 |
|
33 |
Args:
|
34 |
-
model_provider: LLM provider
|
35 |
-
model_name:
|
36 |
-
api_key:
|
|
|
|
|
|
|
37 |
"""
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
def __call__(self, question: str) -> str:
|
42 |
"""Process a GAIA benchmark question and return the formatted answer."""
|
43 |
print(f"Agent received question (first 50 chars): {question[:50]}...")
|
44 |
|
45 |
async def agentic_main():
|
46 |
-
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
response = asyncio.run(agentic_main())
|
|
|
|
|
50 |
final_answer = response.response.blocks[-1].text
|
51 |
print(f"Agent returning answer: {final_answer}")
|
52 |
return final_answer
|
53 |
|
|
|
54 |
def run_and_submit_all( profile: gr.OAuthProfile | None):
|
55 |
"""
|
56 |
Fetches all questions, runs the BasicAgent on them, submits all answers,
|
|
|
3 |
import requests
|
4 |
import inspect
|
5 |
import pandas as pd
|
|
|
6 |
import asyncio
|
7 |
+
from llama_index.core.agent.workflow import AgentWorkflow
|
8 |
+
from agents.llama_index_agent import GaiaAgent, create_writer_agent
|
9 |
+
|
10 |
# (Keep Constants as is)
|
11 |
# --- Constants ---
|
12 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
|
|
25 |
class BasicAgent:
|
26 |
def __init__(
|
27 |
self,
|
28 |
+
model_provider="anthropic",
|
29 |
+
model_name="claude-3-7-sonnet-latest",
|
30 |
+
api_key=None,
|
31 |
+
use_separate_writer_model=True,
|
32 |
+
writer_model_provider="openai",
|
33 |
+
writer_model_name="gpt-4o"
|
34 |
):
|
35 |
"""
|
36 |
+
Initialize the BasicAgent with a multi-agent workflow.
|
37 |
|
38 |
Args:
|
39 |
+
model_provider: LLM provider for main agent
|
40 |
+
model_name: Model name for main agent
|
41 |
+
api_key: API key for main agent
|
42 |
+
use_separate_writer_model: Whether to use a different model for the writer agent
|
43 |
+
writer_model_provider: LLM provider for writer agent (if separate)
|
44 |
+
writer_model_name: Model name for writer agent (if separate)
|
45 |
"""
|
46 |
+
# Configure the main reasoning agent
|
47 |
+
main_model_config = {
|
48 |
+
"model_provider": model_provider,
|
49 |
+
"model_name": model_name,
|
50 |
+
"api_key": api_key
|
51 |
+
}
|
52 |
+
|
53 |
+
# Configure the writer agent (either same as main or different)
|
54 |
+
if use_separate_writer_model:
|
55 |
+
writer_model_config = {
|
56 |
+
"model_provider": writer_model_provider,
|
57 |
+
"model_name": writer_model_name,
|
58 |
+
"api_key": api_key # Use same API key for simplicity
|
59 |
+
}
|
60 |
+
else:
|
61 |
+
writer_model_config = main_model_config
|
62 |
+
|
63 |
+
# Create the main agent
|
64 |
+
self.main_agent = GaiaAgent(**main_model_config)
|
65 |
+
|
66 |
+
# Create the writer agent
|
67 |
+
self.writer_agent = create_writer_agent(writer_model_config)
|
68 |
+
|
69 |
+
# Set up the agent workflow with shared context
|
70 |
+
self.agent_workflow = AgentWorkflow(
|
71 |
+
agents=[self.main_agent, self.writer_agent],
|
72 |
+
root_agent=self.main_agent.name,
|
73 |
+
initial_state={
|
74 |
+
"original_question": "",
|
75 |
+
"analysis_notes": "",
|
76 |
+
"format_requirements": "",
|
77 |
+
"next_agent": "",
|
78 |
+
"final_answer": ""
|
79 |
+
}
|
80 |
+
)
|
81 |
+
|
82 |
+
print(f"BasicAgent initialized with main agent: {model_provider} {model_name}")
|
83 |
+
if use_separate_writer_model:
|
84 |
+
print(f"Writer agent using: {writer_model_provider} {writer_model_name}")
|
85 |
+
else:
|
86 |
+
print(f"Writer agent using same model as main agent")
|
87 |
|
88 |
def __call__(self, question: str) -> str:
|
89 |
"""Process a GAIA benchmark question and return the formatted answer."""
|
90 |
print(f"Agent received question (first 50 chars): {question[:50]}...")
|
91 |
|
92 |
async def agentic_main():
|
93 |
+
# Initialize context with the question
|
94 |
+
initial_state = {
|
95 |
+
"original_question": question,
|
96 |
+
"analysis_notes": "",
|
97 |
+
"format_requirements": "",
|
98 |
+
"next_agent": "",
|
99 |
+
"final_answer": ""
|
100 |
+
}
|
101 |
+
|
102 |
+
# Use the workflow to process the question
|
103 |
+
workflow_response = await self.agent_workflow.arun(
|
104 |
+
question,
|
105 |
+
initial_state=initial_state
|
106 |
+
)
|
107 |
+
return workflow_response
|
108 |
|
109 |
response = asyncio.run(agentic_main())
|
110 |
+
|
111 |
+
# Extract the final answer from the writer agent's response
|
112 |
final_answer = response.response.blocks[-1].text
|
113 |
print(f"Agent returning answer: {final_answer}")
|
114 |
return final_answer
|
115 |
|
116 |
+
|
117 |
def run_and_submit_all( profile: gr.OAuthProfile | None):
|
118 |
"""
|
119 |
Fetches all questions, runs the BasicAgent on them, submits all answers,
|