A1nmol commited on
Commit
6489be0
·
verified ·
1 Parent(s): bcee700

Upload folder using huggingface_hub

Browse files
ARCHITECTURE.md ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deep Research System - Architecture Documentation
2
+
3
+ ## Overview
4
+ This is an agentic research system built using OpenAI Agents SDK that performs deep research on user queries with quality evaluation and automatic improvement.
5
+
6
+ ## Agentic Framework: OpenAI Agents SDK
7
+
8
+ The system uses **OpenAI Agents SDK** which provides:
9
+ - **Agent**: An AI agent with specific instructions and capabilities
10
+ - **Runner**: Executes agents and manages their execution
11
+ - **Tools**: Agents can be converted to tools for use by other agents
12
+ - **Structured Outputs**: Using Pydantic models for type-safe outputs
13
+ - **Tracing**: Built-in tracing for debugging and monitoring
14
+
15
+ ## System Architecture
16
+
17
+ ```
18
+ ┌─────────────────────────────────────────────────────────────────┐
19
+ │ Gradio UI Layer │
20
+ │ (deep_research.py) - User Interface │
21
+ └────────────────────────────┬────────────────────────────────────┘
22
+
23
+
24
+ ┌─────────────────────────────────────────────────────────────────┐
25
+ │ Research Manager │
26
+ │ (research_manager.py) │
27
+ │ Orchestrates the entire research workflow │
28
+ └────────────────────────────┬────────────────────────────────────┘
29
+
30
+ ┌─────────────────────┼─────────────────────┐
31
+ │ │ │
32
+ ▼ ▼ ▼
33
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
34
+ │ Clarifier │ │ Planner │ │ Search │
35
+ │ Agent │ │ Agent │ │ Agent │
36
+ └──────────────┘ └──────────────┘ └──────────────┘
37
+ │ │ │
38
+ │ ▼ │
39
+ │ ┌──────────────┐ │
40
+ │ │ Writer │ │
41
+ │ │ Agent │ │
42
+ │ └──────────────┘ │
43
+ │ │ │
44
+ │ ▼ │
45
+ │ ┌──────────────┐ │
46
+ │ │ Evaluator │ │
47
+ │ │ Agent │ │
48
+ │ └──────────────┘ │
49
+ │ │ │
50
+ │ ▼ │
51
+ │ ┌──────────────┐ │
52
+ │ │ Email │ │
53
+ │ │ Agent │ │
54
+ │ └──────────────┘ │
55
+ │ │ │
56
+ └─────────────────────┴─────────────────────┘
57
+
58
+
59
+ ┌─────────────────┐
60
+ │ Final Report │
61
+ │ (Markdown) │
62
+ └─────────────────┘
63
+ ```
64
+
65
+ ## Agent Details
66
+
67
+ ### 1. Clarifier Agent (`clarifier_agent.py`)
68
+ **Purpose**: Generates 3 clarifying questions based on user query
69
+
70
+ **Input**: User's research query
71
+ **Output**: `Clarification` (Pydantic model with 3 questions)
72
+
73
+ **Example**:
74
+ - Query: "Latest AI Agent frameworks"
75
+ - Output: ["What time period are you interested in?", "Are you looking for commercial or open-source?", "What specific use cases?"]
76
+
77
+ ---
78
+
79
+ ### 2. Planner Agent (`planner_agent.py`)
80
+ **Purpose**: Creates a search plan based on query + clarification answers
81
+
82
+ **Input**:
83
+ - Original query
84
+ - 3 question-answer pairs (structured as `ResearchContext`)
85
+
86
+ **Output**: `WebSearchPlan` (Pydantic model with list of search items)
87
+
88
+ **Schema Used**:
89
+ ```python
90
+ ResearchContext:
91
+ - original_query: str
92
+ - clarification_qa: List[QuestionAnswerPair]
93
+ - question: str
94
+ - answer: str
95
+
96
+ WebSearchPlan:
97
+ - searches: List[WebSearchItem]
98
+ - query: str (search term)
99
+ - reason: str (why this search is important)
100
+ ```
101
+
102
+ **Example**:
103
+ - Query: "Latest AI Agent frameworks in 2025"
104
+ - Answers: ["2025", "Both", "Enterprise automation"]
105
+ - Output: 2 search terms with reasons
106
+
107
+ ---
108
+
109
+ ### 3. Search Agent (`search_agent.py`)
110
+ **Purpose**: Performs web searches for each search term
111
+
112
+ **Input**: Search term + reason
113
+ **Output**: Search results (text)
114
+
115
+ **Execution**: Runs in parallel for all searches
116
+
117
+ ---
118
+
119
+ ### 4. Writer Agent (`writer_agent.py`)
120
+ **Purpose**: Writes comprehensive research report
121
+
122
+ **Input**:
123
+ - Original query
124
+ - Search results
125
+ - Clarification Q&A (optional)
126
+ - Evaluation feedback (optional, for regeneration)
127
+
128
+ **Output**: `ReportData` (Pydantic model)
129
+ ```python
130
+ ReportData:
131
+ - short_summary: str
132
+ - markdown_report: str (full report)
133
+ - follow_up_questions: List[str]
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 5. Evaluator Agent (`evaluator_agent.py`)
139
+ **Purpose**: Evaluates report quality and provides scores
140
+
141
+ **Input**:
142
+ - Generated report
143
+ - Original query
144
+ - Clarification Q&A
145
+
146
+ **Output**: `ReportEvaluation` (Pydantic model)
147
+ ```python
148
+ ReportEvaluation:
149
+ - relevance_score: CriterionScore (1-5)
150
+ - clarification_usage_score: CriterionScore (1-5)
151
+ - formatting_score: CriterionScore (1-5)
152
+ - completeness_score: CriterionScore (1-5)
153
+ - overall_quality_score: CriterionScore (1-5)
154
+ - average_score: float (average of all scores)
155
+ - feedback: str (improvement suggestions)
156
+ ```
157
+
158
+ **Evaluation Criteria**:
159
+ 1. **Relevance**: How well report addresses original query
160
+ 2. **Use of Clarifications**: Incorporation of user's clarification answers
161
+ 3. **Formatting & Clarity**: Structure and readability
162
+ 4. **Completeness**: Comprehensive coverage
163
+ 5. **Overall Quality**: General assessment
164
+
165
+ ---
166
+
167
+ ### 6. Email Agent (`email_agent.py`)
168
+ **Purpose**: Sends the final report via email
169
+
170
+ **Input**: Markdown report
171
+ **Output**: Email sent confirmation
172
+
173
+ **Technology**: Uses Resend API (SendGrid free tier was retired)
174
+
175
+ ---
176
+
177
+ ## Complete Workflow
178
+
179
+ ```
180
+ ┌─────────────────────────────────────────────────────────────────┐
181
+ │ WORKFLOW DIAGRAM │
182
+ └─────────────────────────────────────────────────────────────────┘
183
+
184
+ 1. USER INPUT
185
+
186
+
187
+ 2. CLARIFIER AGENT
188
+ │ Generates 3 clarifying questions
189
+
190
+
191
+ 3. USER ANSWERS
192
+ │ User provides answers to questions
193
+
194
+
195
+ 4. PLANNER AGENT
196
+ │ Creates search plan using:
197
+ │ - Original query
198
+ │ - Question-Answer pairs (ResearchContext)
199
+
200
+
201
+ 5. PARALLEL SEARCHES
202
+ │ Search Agent runs for each search term
203
+ │ (Executed concurrently)
204
+
205
+
206
+ 6. WRITER AGENT
207
+ │ Generates comprehensive report using:
208
+ │ - Original query
209
+ │ - Search results
210
+ │ - Clarification Q&A
211
+
212
+
213
+ 7. EVALUATOR AGENT ⭐
214
+ │ Evaluates report quality:
215
+ │ - Scores 5 criteria (1-5 each)
216
+ │ - Calculates average score
217
+ │ - Provides feedback
218
+
219
+
220
+ 8. QUALITY CHECK
221
+
222
+ ├─→ Score >= 3.0? ──YES──→ 9. EMAIL AGENT
223
+ │ Send report
224
+
225
+ └─→ Score < 3.0? ──NO──→ REGENERATION LOOP
226
+ │ (Max 2 attempts)
227
+
228
+ ├─→ Attempt 1: Regenerate with feedback
229
+ │ └─→ Re-evaluate
230
+
231
+ ├─→ Attempt 2: Regenerate with feedback
232
+ │ └─→ Re-evaluate
233
+
234
+ └─→ Final: Send best report (even if < 3.0)
235
+ └─→ 9. EMAIL AGENT
236
+ ```
237
+
238
+ ## Data Flow
239
+
240
+ ```
241
+ User Query (string)
242
+
243
+ ├─→ Clarifier Agent
244
+ │ └─→ Clarification (questions: List[str])
245
+
246
+ └─→ User Answers (List[str])
247
+
248
+ ├─→ ResearchContext (Pydantic)
249
+ │ ├─→ original_query: str
250
+ │ └─→ clarification_qa: List[QuestionAnswerPair]
251
+
252
+ ├─→ Planner Agent
253
+ │ └─→ WebSearchPlan
254
+ │ └─→ searches: List[WebSearchItem]
255
+
256
+ ├─→ Search Agent (parallel)
257
+ │ └─→ Search Results: List[str]
258
+
259
+ ├─→ Writer Agent
260
+ │ └─→ ReportData
261
+ │ ├─→ short_summary: str
262
+ │ ├─→ markdown_report: str
263
+ │ └─→ follow_up_questions: List[str]
264
+
265
+ ├��→ Evaluator Agent
266
+ │ └─→ ReportEvaluation
267
+ │ ├─→ Individual scores (5 criteria)
268
+ │ ├─→ average_score: float
269
+ │ └─→ feedback: str
270
+
271
+ └─→ Email Agent
272
+ └─→ Email sent (via Resend API)
273
+ ```
274
+
275
+ ## Key Features
276
+
277
+ ### 1. Structured Data with Pydantic
278
+ All agents use Pydantic models for type-safe, validated outputs:
279
+ - Ensures correct data structure
280
+ - Provides clear schemas
281
+ - Enables automatic validation
282
+
283
+ ### 2. Agent Orchestration
284
+ The `ResearchManager` coordinates all agents:
285
+ - Sequential execution where needed
286
+ - Parallel execution for searches
287
+ - Conditional logic for regeneration
288
+
289
+ ### 3. Quality Assurance Loop
290
+ - Automatic evaluation after report generation
291
+ - Regeneration if score < 3.0/5.0
292
+ - Up to 2 regeneration attempts
293
+ - Feedback-driven improvements
294
+
295
+ ### 4. Context Preservation
296
+ - Original query maintained throughout
297
+ - Clarification Q&A passed to planner and writer
298
+ - Evaluation feedback used for regeneration
299
+
300
+ ## Technology Stack
301
+
302
+ - **Framework**: OpenAI Agents SDK (`openai-agents`)
303
+ - **UI**: Gradio (Python web interface)
304
+ - **Email**: Resend API
305
+ - **Type Safety**: Pydantic models
306
+ - **Async**: Python asyncio for concurrent operations
307
+ - **Tracing**: OpenAI platform tracing for debugging
308
+
309
+ ## File Structure
310
+
311
+ ```
312
+ deep_research/
313
+ ├── deep_research.py # Gradio UI
314
+ ├── research_manager.py # Main orchestrator
315
+ ├── clarifier_agent.py # Question generation
316
+ ├── planner_agent.py # Search planning
317
+ ├── search_agent.py # Web search execution
318
+ ├── writer_agent.py # Report writing
319
+ ├── evaluator_agent.py # Quality evaluation ⭐
320
+ ├── email_agent.py # Email sending
321
+ └── ARCHITECTURE.md # This file
322
+ ```
323
+
324
+ ## Execution Flow Example
325
+
326
+ 1. **User**: "Latest AI Agent frameworks in 2025"
327
+ 2. **Clarifier**: Generates 3 questions
328
+ 3. **User**: Answers questions
329
+ 4. **Planner**: Creates 2 search terms based on query + answers
330
+ 5. **Searches**: Run in parallel, collect results
331
+ 6. **Writer**: Generates report incorporating clarifications
332
+ 7. **Evaluator**: Scores report (e.g., 2.8/5.0)
333
+ 8. **System**: Detects score < 3.0, regenerates with feedback
334
+ 9. **Evaluator**: Re-scores (e.g., 3.5/5.0)
335
+ 10. **System**: Approves, sends email
336
+ 11. **User**: Receives high-quality report
337
+
338
+ ## Benefits of This Architecture
339
+
340
+ 1. **Modularity**: Each agent has a single responsibility
341
+ 2. **Reusability**: Agents can be used as tools by other agents
342
+ 3. **Quality Control**: Automatic evaluation and improvement
343
+ 4. **User Experience**: Clarifying questions improve relevance
344
+ 5. **Transparency**: Users see scores and regeneration attempts
345
+ 6. **Scalability**: Easy to add new agents or modify workflow
346
+
README.md CHANGED
@@ -1,12 +1,49 @@
1
  ---
2
- title: Deep Research
3
- emoji: 🚀
4
- colorFrom: gray
5
- colorTo: indigo
6
- sdk: gradio
7
- sdk_version: 6.2.0
8
  app_file: app.py
9
- pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: deep-research
 
 
 
 
 
3
  app_file: app.py
4
+ sdk: gradio
5
+ sdk_version: 5.33.1
6
  ---
7
 
8
+ # Deep Research Multi-Agent System
9
+
10
+ An intelligent research system that uses multiple AI agents to perform comprehensive research on any topic. The system includes:
11
+
12
+ - **Clarifier Agent**: Generates 3 clarifying questions to better understand your research needs
13
+ - **Planner Agent**: Creates an optimized search plan based on your query and clarifications
14
+ - **Search Agent**: Performs web searches in parallel
15
+ - **Writer Agent**: Synthesizes information into a comprehensive report
16
+ - **Evaluator Agent**: Evaluates report quality and automatically improves it if needed
17
+ - **Email Agent**: Sends the final report via email
18
+
19
+ ## Features
20
+
21
+ - 🤔 **Clarifying Questions**: Get more targeted research by answering 3 clarifying questions
22
+ - 🔍 **Intelligent Search Planning**: Search terms are optimized based on your clarifications
23
+ - 📊 **Quality Evaluation**: Reports are automatically evaluated on 5 criteria (relevance, clarity, completeness, etc.)
24
+ - 🔄 **Auto-Improvement**: Reports scoring below 3.0/5.0 are automatically regenerated with feedback
25
+ - 📧 **Email Delivery**: Receive your research report via email
26
+
27
+ ## How to Use
28
+
29
+ 1. Enter your research query
30
+ 2. Answer the 3 clarifying questions that appear
31
+ 3. Wait for the system to:
32
+ - Plan searches
33
+ - Perform web searches
34
+ - Write the report
35
+ - Evaluate quality
36
+ - Improve if needed
37
+ 4. View your comprehensive research report
38
+
39
+ ## Environment Variables
40
+
41
+ The following secrets need to be configured in Hugging Face Spaces:
42
+
43
+ - `OPENAI_API_KEY`: Your OpenAI API key (required)
44
+ - `RESEND_API_KEY`: Your Resend API key (for email functionality)
45
+
46
+ ## Architecture
47
+
48
+ This system uses the OpenAI Agents SDK with a multi-agent architecture. See `ARCHITECTURE.md` for detailed documentation.
49
+
app.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from dotenv import load_dotenv
3
+ from research_manager import ResearchManager
4
+ from clarifier_agent import clarifier_agent
5
+ from agents import Runner
6
+
7
+ load_dotenv(override=True)
8
+
9
+
10
+ async def generate_questions(query: str):
11
+ """Automatically generate and display clarifying questions for the query"""
12
+ # Show loading message first
13
+ yield "Analyzing your query and generating clarifying questions...", []
14
+
15
+ # Generate questions
16
+ result = await Runner.run(clarifier_agent, f"User query: {query}")
17
+ questions = result.final_output.questions
18
+
19
+ # Format questions for display
20
+ questions_text = "**Please answer these clarifying questions:**\n\n"
21
+ for i, q in enumerate(questions, 1):
22
+ questions_text += f"{i}. {q}\n\n"
23
+
24
+ # Yield the formatted questions and the questions list for state
25
+ yield questions_text, questions
26
+
27
+
28
+ async def run_research(query: str, answer1: str, answer2: str, answer3: str, questions: list):
29
+ """Run the research process with clarification answers"""
30
+ manager = ResearchManager()
31
+ answers = [answer1, answer2, answer3]
32
+
33
+ async for chunk in manager.run(query, questions, answers):
34
+ yield chunk
35
+
36
+
37
+ with gr.Blocks(theme=gr.themes.Default(primary_hue="sky")) as ui:
38
+ gr.Markdown("# Deep Research")
39
+
40
+ # State to store generated questions
41
+ questions_state = gr.State(value=[])
42
+
43
+ query_textbox = gr.Textbox(
44
+ label="What topic would you like to research?",
45
+ placeholder="e.g., Latest AI Agent frameworks in 2025"
46
+ )
47
+
48
+ submit_btn = gr.Button("Start Research", variant="primary")
49
+
50
+ questions_output = gr.Markdown(
51
+ label="Clarifying Questions",
52
+ value="*Enter your research query above and click 'Start Research' to generate clarifying questions.*",
53
+ visible=True
54
+ )
55
+
56
+ with gr.Row():
57
+ answer1 = gr.Textbox(label="Answer 1", placeholder="Enter your answer to question 1", visible=False)
58
+ answer2 = gr.Textbox(label="Answer 2", placeholder="Enter your answer to question 2", visible=False)
59
+ answer3 = gr.Textbox(label="Answer 3", placeholder="Enter your answer to question 3", visible=False)
60
+
61
+ continue_btn = gr.Button("Continue Research", variant="primary", visible=False)
62
+ report = gr.Markdown(label="Research Report")
63
+
64
+ def show_questions_ui():
65
+ """Show the questions and answer fields"""
66
+ return (
67
+ gr.update(visible=True), # questions_output
68
+ gr.update(visible=True), # answer1
69
+ gr.update(visible=True), # answer2
70
+ gr.update(visible=True), # answer3
71
+ gr.update(visible=True), # continue_btn
72
+ gr.update(visible=False) # submit_btn
73
+ )
74
+
75
+ def hide_questions_ui():
76
+ """Hide the questions and answer fields after research starts"""
77
+ return (
78
+ gr.update(visible=False), # questions_output
79
+ gr.update(visible=False), # answer1
80
+ gr.update(visible=False), # answer2
81
+ gr.update(visible=False), # answer3
82
+ gr.update(visible=False), # continue_btn
83
+ gr.update(visible=True) # submit_btn
84
+ )
85
+
86
+ # When user submits query, automatically generate questions
87
+ submit_btn.click(
88
+ fn=generate_questions,
89
+ inputs=query_textbox,
90
+ outputs=[questions_output, questions_state]
91
+ ).then(
92
+ fn=show_questions_ui,
93
+ outputs=[questions_output, answer1, answer2, answer3, continue_btn, submit_btn]
94
+ )
95
+
96
+ query_textbox.submit(
97
+ fn=generate_questions,
98
+ inputs=query_textbox,
99
+ outputs=[questions_output, questions_state]
100
+ ).then(
101
+ fn=show_questions_ui,
102
+ outputs=[questions_output, answer1, answer2, answer3, continue_btn, submit_btn]
103
+ )
104
+
105
+ # Continue research with answers
106
+ continue_btn.click(
107
+ fn=run_research,
108
+ inputs=[query_textbox, answer1, answer2, answer3, questions_state],
109
+ outputs=report
110
+ ).then(
111
+ fn=hide_questions_ui,
112
+ outputs=[questions_output, answer1, answer2, answer3, continue_btn, submit_btn]
113
+ )
114
+
115
+ if __name__ == "__main__":
116
+ ui.launch()
117
+
clarifier_agent.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from agents import Agent
3
+ from typing import List
4
+
5
+ import os
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv(override=True)
9
+
10
+ openai_api_key = os.getenv('OPENAI_API_KEY')
11
+
12
+ INSTRUCTIONS = """You are a helpful research assistant.
13
+ Given a user's research query, you come up with 3 clarifying questions to better understand what they need.
14
+
15
+ Rules:
16
+ - Keep the questions short (one sentence each question)
17
+ - Do not answer the questions yourself
18
+ - Ask exactly 3 questions
19
+ - Every question must end with a '?'
20
+ - Focus on understanding: scope, depth, timeframe, specific aspects, or context
21
+ - Make questions relevant to the research query"""
22
+
23
+
24
+ class Clarification(BaseModel):
25
+ questions: List[str] = Field(
26
+ description="A list of exactly 3 clarifying questions",
27
+ min_items=3,
28
+ max_items=3
29
+ )
30
+
31
+
32
+ clarifier_agent = Agent(
33
+ name='ClarifyAgent',
34
+ instructions=INSTRUCTIONS,
35
+ model='gpt-4o-mini',
36
+ output_type=Clarification
37
+ )
38
+
39
+ clarify_tool = clarifier_agent.as_tool(
40
+ tool_name='clarify',
41
+ tool_description='Ask 3 clarifying questions based on the user query'
42
+ )
43
+
deep_research.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from dotenv import load_dotenv
3
+ from research_manager import ResearchManager
4
+ from clarifier_agent import clarifier_agent
5
+ from agents import Runner
6
+
7
+ load_dotenv(override=True)
8
+
9
+
10
+ async def generate_questions(query: str):
11
+ """Automatically generate and display clarifying questions for the query"""
12
+ # Show loading message first
13
+ yield "Analyzing your query and generating clarifying questions...", []
14
+
15
+ # Generate questions
16
+ result = await Runner.run(clarifier_agent, f"User query: {query}")
17
+ questions = result.final_output.questions
18
+
19
+ # Format questions for display
20
+ questions_text = "**Please answer these clarifying questions:**\n\n"
21
+ for i, q in enumerate(questions, 1):
22
+ questions_text += f"{i}. {q}\n\n"
23
+
24
+ # Yield the formatted questions and the questions list for state
25
+ yield questions_text, questions
26
+
27
+
28
+ async def run_research(query: str, answer1: str, answer2: str, answer3: str, questions: list):
29
+ """Run the research process with clarification answers"""
30
+ manager = ResearchManager()
31
+ answers = [answer1, answer2, answer3]
32
+
33
+ async for chunk in manager.run(query, questions, answers):
34
+ yield chunk
35
+
36
+
37
+ with gr.Blocks(theme=gr.themes.Default(primary_hue="sky")) as ui:
38
+ gr.Markdown("# Deep Research")
39
+
40
+ # State to store generated questions
41
+ questions_state = gr.State(value=[])
42
+
43
+ query_textbox = gr.Textbox(
44
+ label="What topic would you like to research?",
45
+ placeholder="e.g., Latest AI Agent frameworks in 2025"
46
+ )
47
+
48
+ submit_btn = gr.Button("Start Research", variant="primary")
49
+
50
+ questions_output = gr.Markdown(
51
+ label="Clarifying Questions",
52
+ value="*Enter your research query above and click 'Start Research' to generate clarifying questions.*",
53
+ visible=True
54
+ )
55
+
56
+ with gr.Row():
57
+ answer1 = gr.Textbox(label="Answer 1", placeholder="Enter your answer to question 1", visible=False)
58
+ answer2 = gr.Textbox(label="Answer 2", placeholder="Enter your answer to question 2", visible=False)
59
+ answer3 = gr.Textbox(label="Answer 3", placeholder="Enter your answer to question 3", visible=False)
60
+
61
+ continue_btn = gr.Button("Continue Research", variant="primary", visible=False)
62
+ report = gr.Markdown(label="Research Report")
63
+
64
+ def show_questions_ui():
65
+ """Show the questions and answer fields"""
66
+ return (
67
+ gr.update(visible=True), # questions_output
68
+ gr.update(visible=True), # answer1
69
+ gr.update(visible=True), # answer2
70
+ gr.update(visible=True), # answer3
71
+ gr.update(visible=True), # continue_btn
72
+ gr.update(visible=False) # submit_btn
73
+ )
74
+
75
+ def hide_questions_ui():
76
+ """Hide the questions and answer fields after research starts"""
77
+ return (
78
+ gr.update(visible=False), # questions_output
79
+ gr.update(visible=False), # answer1
80
+ gr.update(visible=False), # answer2
81
+ gr.update(visible=False), # answer3
82
+ gr.update(visible=False), # continue_btn
83
+ gr.update(visible=True) # submit_btn
84
+ )
85
+
86
+ # When user submits query, automatically generate questions
87
+ submit_btn.click(
88
+ fn=generate_questions,
89
+ inputs=query_textbox,
90
+ outputs=[questions_output, questions_state]
91
+ ).then(
92
+ fn=show_questions_ui,
93
+ outputs=[questions_output, answer1, answer2, answer3, continue_btn, submit_btn]
94
+ )
95
+
96
+ query_textbox.submit(
97
+ fn=generate_questions,
98
+ inputs=query_textbox,
99
+ outputs=[questions_output, questions_state]
100
+ ).then(
101
+ fn=show_questions_ui,
102
+ outputs=[questions_output, answer1, answer2, answer3, continue_btn, submit_btn]
103
+ )
104
+
105
+ # Continue research with answers
106
+ continue_btn.click(
107
+ fn=run_research,
108
+ inputs=[query_textbox, answer1, answer2, answer3, questions_state],
109
+ outputs=report
110
+ ).then(
111
+ fn=hide_questions_ui,
112
+ outputs=[questions_output, answer1, answer2, answer3, continue_btn, submit_btn]
113
+ )
114
+
115
+ ui.launch(inbrowser=True)
116
+
email_agent.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Dict
3
+
4
+ import resend
5
+ from agents import Agent, function_tool
6
+
7
+ # Note: SendGrid free plans were retired (May 2025). Using Resend instead.
8
+ # Resend free tier: 3,000 emails/month
9
+ # Get API key from: https://resend.com/api-keys
10
+ # Add to .env: RESEND_API_KEY=re_xxxxx
11
+
12
+
13
+ @function_tool
14
+ def send_email(subject: str, html_body: str) -> Dict[str, str]:
15
+ """Send an email with the given subject and HTML body using Resend"""
16
+ api_key = os.environ.get('RESEND_API_KEY')
17
+ if not api_key:
18
+ return {"status": "error", "message": "RESEND_API_KEY not found in environment variables"}
19
+
20
+ resend.api_key = api_key
21
+
22
+ from_email = "onboarding@resend.dev" # Change to your verified sender
23
+ to_email = "anmolkumarimusician@gmail.com" # Change to your recipient
24
+
25
+ try:
26
+ r = resend.Emails.send({
27
+ "from": from_email,
28
+ "to": to_email,
29
+ "subject": subject,
30
+ "html": html_body
31
+ })
32
+ print(f"Email sent successfully! Email ID: {r.get('id', 'N/A')}")
33
+ return {"status": "success", "id": r.get('id')}
34
+ except Exception as e:
35
+ print(f"Failed to send email: {str(e)}")
36
+ return {"status": "error", "message": f"Failed to send email: {str(e)}"}
37
+
38
+
39
+ INSTRUCTIONS = """You are able to send a nicely formatted HTML email based on a detailed report.
40
+ You will be provided with a detailed report. You should use your tool to send one email, providing the
41
+ report converted into clean, well presented HTML with an appropriate subject line."""
42
+
43
+ email_agent = Agent(
44
+ name="Email agent",
45
+ instructions=INSTRUCTIONS,
46
+ tools=[send_email],
47
+ model="gpt-4o-mini",
48
+ )
evaluator_agent.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from agents import Agent
3
+ from typing import List
4
+
5
+ INSTRUCTIONS = """You are an expert evaluator for research reports. Your task is to evaluate a research report
6
+ based on the following criteria:
7
+
8
+ 1. **Relevance**: How well does the report address the original query?
9
+ 2. **Use of Clarifications**: How well does the report incorporate the clarification answers provided by the user?
10
+ 3. **Formatting and Clarity**: Is the report well-structured, clearly formatted, and easy to read?
11
+ 4. **Completeness**: Does the report provide comprehensive information on the topic?
12
+ 5. **Overall Quality**: Overall assessment of the report's quality and usefulness
13
+
14
+ For each criterion, provide a score from 1 to 5 (where 1 is poor and 5 is excellent) and a brief justification.
15
+ Calculate an overall average score by taking the mean of all 5 criterion scores.
16
+
17
+ In your feedback, be specific about:
18
+ - What areas need improvement
19
+ - How to better incorporate the clarification answers
20
+ - Formatting and structure suggestions
21
+ - Any missing information or gaps
22
+
23
+ Be thorough but fair in your evaluation. Consider that the report should directly address the user's original query
24
+ and incorporate insights from their clarification answers."""
25
+
26
+
27
+ class CriterionScore(BaseModel):
28
+ criterion: str = Field(description="The name of the evaluation criterion")
29
+ score: int = Field(description="Score from 1 to 5", ge=1, le=5)
30
+ justification: str = Field(description="Brief explanation for the score")
31
+
32
+
33
+ class ReportEvaluation(BaseModel):
34
+ relevance_score: CriterionScore = Field(description="Score for relevance to original query")
35
+ clarification_usage_score: CriterionScore = Field(description="Score for use of clarification answers")
36
+ formatting_score: CriterionScore = Field(description="Score for formatting and clarity")
37
+ completeness_score: CriterionScore = Field(description="Score for completeness of information")
38
+ overall_quality_score: CriterionScore = Field(description="Overall quality assessment")
39
+ average_score: float = Field(description="Average of all scores (out of 5)")
40
+ feedback: str = Field(description="Overall feedback and suggestions for improvement")
41
+
42
+
43
+ evaluator_agent = Agent(
44
+ name="EvaluatorAgent",
45
+ instructions=INSTRUCTIONS,
46
+ model="gpt-4o-mini",
47
+ output_type=ReportEvaluation,
48
+ )
49
+
planner_agent.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from agents import Agent
3
+ from typing import List
4
+
5
+ HOW_MANY_SEARCHES = 2
6
+
7
+
8
+ class QuestionAnswerPair(BaseModel):
9
+ question: str = Field(description="The clarifying question that was asked")
10
+ answer: str = Field(description="The user's answer to the clarifying question")
11
+
12
+
13
+ class ResearchContext(BaseModel):
14
+ original_query: str = Field(description="The original research query from the user")
15
+ clarification_qa: List[QuestionAnswerPair] = Field(
16
+ description="List of question-answer pairs from the clarification process",
17
+ min_items=3,
18
+ max_items=3
19
+ )
20
+
21
+
22
+ class WebSearchItem(BaseModel):
23
+ reason: str = Field(description="Your reasoning for why this search is important to the query.")
24
+ query: str = Field(description="The search term to use for the web search.")
25
+
26
+
27
+ class WebSearchPlan(BaseModel):
28
+ searches: List[WebSearchItem] = Field(description="A list of web searches to perform to best answer the query.")
29
+
30
+ INSTRUCTIONS = f"""You are a helpful research assistant. Given a research context that includes:
31
+ 1. The original user query
32
+ 2. Three clarifying questions and their answers
33
+
34
+ Your task is to come up with {HOW_MANY_SEARCHES} web search terms that will best answer the user's query,
35
+ taking into account both the original query AND the clarification answers provided.
36
+
37
+ The clarification answers provide important context about:
38
+ - What specific aspects the user is interested in
39
+ - The scope and depth they want
40
+ - Timeframe or other constraints
41
+ - Specific focus areas
42
+
43
+ Use this information to create more targeted and relevant search terms. Output exactly {HOW_MANY_SEARCHES} search terms."""
44
+
45
+ planner_agent = Agent(
46
+ name="PlannerAgent",
47
+ instructions=INSTRUCTIONS,
48
+ model="gpt-4o-mini",
49
+ output_type=WebSearchPlan,
50
+ )
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=5.22.0
2
+ openai>=1.68.2
3
+ openai-agents>=0.0.15
4
+ python-dotenv>=1.0.1
5
+ resend>=1.0.0
6
+ pydantic>=2.0.0
7
+ typing-extensions>=4.0.0
8
+ httpx>=0.28.1
9
+
research_manager.py ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents import Runner, trace, gen_trace_id
2
+ from search_agent import search_agent
3
+ from planner_agent import planner_agent, WebSearchItem, WebSearchPlan, ResearchContext, QuestionAnswerPair
4
+ from writer_agent import writer_agent, ReportData
5
+ from email_agent import email_agent
6
+ from clarifier_agent import clarifier_agent
7
+ from evaluator_agent import evaluator_agent, ReportEvaluation
8
+ from typing import Optional, List
9
+ import asyncio
10
+
11
+ class ResearchManager:
12
+
13
+ async def run(self, query: str, clarification_questions: Optional[List[str]] = None, clarification_answers: Optional[List[str]] = None):
14
+ """ Run the deep research process, yielding the status updates and the final report"""
15
+ trace_id = gen_trace_id()
16
+ with trace("Research trace", trace_id=trace_id):
17
+ print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}")
18
+ yield f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}"
19
+
20
+ # Step 1: Get clarifying questions and answers
21
+ if clarification_questions is None or clarification_answers is None:
22
+ yield "Generating clarifying questions..."
23
+ clarification_result = await Runner.run(clarifier_agent, f"User query: {query}")
24
+ questions = clarification_result.final_output.questions
25
+ questions_text = "**Please answer these clarifying questions:**\n\n"
26
+ for i, q in enumerate(questions, 1):
27
+ questions_text += f"{i}. {q}\n\n"
28
+ yield questions_text
29
+ return # Stop here to wait for user answers
30
+
31
+ # Step 2: Continue with structured research context
32
+ print("Starting research with clarifications...")
33
+ yield "Using your clarifications to refine the research...\n\n"
34
+
35
+ # Step 3: Plan searches using structured context
36
+ search_plan = await self.plan_searches(query, clarification_questions, clarification_answers)
37
+ yield "Searches planned, starting to search..."
38
+
39
+ # Step 4: Perform searches
40
+ search_results = await self.perform_searches(search_plan)
41
+ yield "Searches complete, writing report..."
42
+
43
+ # Step 5: Write report
44
+ report = await self.write_report(query, search_results, clarification_questions, clarification_answers)
45
+ yield "Report written, evaluating quality..."
46
+
47
+ # Step 6: Evaluate report
48
+ evaluation = await self.evaluate_report(report, query, clarification_questions, clarification_answers)
49
+
50
+ # Format detailed evaluation results
51
+ eval_details = self._format_evaluation(evaluation, attempt=1)
52
+ yield eval_details
53
+
54
+ # Step 7: If score is low, regenerate report with feedback
55
+ max_attempts = 2
56
+ attempt = 1
57
+ regeneration_attempted = False
58
+
59
+ while evaluation.average_score < 3.0 and attempt < max_attempts:
60
+ regeneration_attempted = True
61
+ attempt += 1
62
+ yield f"\n⚠️ **Report score ({evaluation.average_score:.2f}/5.0) is below threshold (3.0)**\n**Regenerating with improvements (Attempt {attempt})...**\n"
63
+
64
+ report = await self.write_report(
65
+ query,
66
+ search_results,
67
+ clarification_questions,
68
+ clarification_answers,
69
+ feedback=evaluation.feedback
70
+ )
71
+ evaluation = await self.evaluate_report(report, query, clarification_questions, clarification_answers)
72
+
73
+ # Format evaluation results for regenerated report
74
+ eval_details = self._format_evaluation(evaluation, attempt=attempt, is_regeneration=True)
75
+ yield eval_details
76
+
77
+ # Final status
78
+ if regeneration_attempted:
79
+ if evaluation.average_score < 3.0:
80
+ yield f"\n⚠️ **Final Status**: Score {evaluation.average_score:.2f}/5.0 (below threshold, max attempts reached)"
81
+ else:
82
+ yield f"\n✅ **Final Status**: Report improved! Final score: {evaluation.average_score:.2f}/5.0"
83
+ else:
84
+ yield f"\n✅ **Final Status**: Report quality approved! Score: {evaluation.average_score:.2f}/5.0"
85
+
86
+ # Step 8: Send email
87
+ yield "Sending email..."
88
+ await self.send_email(report)
89
+ yield "Email sent, research complete"
90
+ yield report.markdown_report
91
+
92
+ async def plan_searches(self, query: str, questions: List[str], answers: List[str]) -> WebSearchPlan:
93
+ """ Plan the searches to perform using the structured research context """
94
+ print("Planning searches...")
95
+
96
+ # Create structured research context with question-answer pairs
97
+ qa_pairs = [
98
+ QuestionAnswerPair(question=q, answer=a)
99
+ for q, a in zip(questions, answers)
100
+ ]
101
+
102
+ research_context = ResearchContext(
103
+ original_query=query,
104
+ clarification_qa=qa_pairs
105
+ )
106
+
107
+ # Format the context for the planner agent
108
+ context_text = f"""Original Query: {research_context.original_query}
109
+
110
+ Clarification Questions and Answers:
111
+ """
112
+ for i, qa in enumerate(research_context.clarification_qa, 1):
113
+ context_text += f"{i}. Question: {qa.question}\n Answer: {qa.answer}\n\n"
114
+
115
+ result = await Runner.run(
116
+ planner_agent,
117
+ context_text,
118
+ )
119
+ print(f"Will perform {len(result.final_output.searches)} searches")
120
+ return result.final_output_as(WebSearchPlan)
121
+
122
+ async def perform_searches(self, search_plan: WebSearchPlan) -> List[str]:
123
+ """ Perform the searches to perform for the query """
124
+ print("Searching...")
125
+ num_completed = 0
126
+ tasks = [asyncio.create_task(self.search(item)) for item in search_plan.searches]
127
+ results = []
128
+ for task in asyncio.as_completed(tasks):
129
+ result = await task
130
+ if result is not None:
131
+ results.append(result)
132
+ num_completed += 1
133
+ print(f"Searching... {num_completed}/{len(tasks)} completed")
134
+ print("Finished searching")
135
+ return results
136
+
137
+ async def search(self, item: WebSearchItem) -> Optional[str]:
138
+ """ Perform a search for the query """
139
+ input = f"Search term: {item.query}\nReason for searching: {item.reason}"
140
+ try:
141
+ result = await Runner.run(
142
+ search_agent,
143
+ input,
144
+ )
145
+ return str(result.final_output)
146
+ except Exception:
147
+ return None
148
+
149
+ async def write_report(
150
+ self,
151
+ query: str,
152
+ search_results: List[str],
153
+ clarification_questions: Optional[List[str]] = None,
154
+ clarification_answers: Optional[List[str]] = None,
155
+ feedback: Optional[str] = None
156
+ ) -> ReportData:
157
+ """ Write the report for the query """
158
+ print("Thinking about report...")
159
+
160
+ # Build input with query, search results, and clarifications
161
+ input_parts = [f"Original query: {query}"]
162
+ input_parts.append(f"Summarized search results: {search_results}")
163
+
164
+ if clarification_questions and clarification_answers:
165
+ input_parts.append("\nClarification Questions and Answers:")
166
+ for i, (q, a) in enumerate(zip(clarification_questions, clarification_answers), 1):
167
+ input_parts.append(f"{i}. Question: {q}\n Answer: {a}")
168
+
169
+ if feedback:
170
+ input_parts.append(f"\n\nIMPORTANT: Previous evaluation feedback for improvement:\n{feedback}\n\nPlease address these issues in your report.")
171
+
172
+ input_text = "\n".join(input_parts)
173
+
174
+ result = await Runner.run(
175
+ writer_agent,
176
+ input_text,
177
+ )
178
+
179
+ print("Finished writing report")
180
+ return result.final_output_as(ReportData)
181
+
182
+ async def evaluate_report(
183
+ self,
184
+ report: ReportData,
185
+ query: str,
186
+ clarification_questions: Optional[List[str]],
187
+ clarification_answers: Optional[List[str]]
188
+ ) -> ReportEvaluation:
189
+ """ Evaluate the quality of the report """
190
+ print("Evaluating report...")
191
+
192
+ # Build evaluation context
193
+ eval_context = f"""Original Query: {query}
194
+
195
+ Clarification Questions and Answers:
196
+ """
197
+ if clarification_questions and clarification_answers:
198
+ for i, (q, a) in enumerate(zip(clarification_questions, clarification_answers), 1):
199
+ eval_context += f"{i}. Question: {q}\n Answer: {a}\n\n"
200
+
201
+ eval_context += f"""
202
+ Report to Evaluate:
203
+ {report.markdown_report}
204
+
205
+ Please evaluate this report based on:
206
+ 1. How well it addresses the original query
207
+ 2. How well it incorporates the clarification answers
208
+ 3. Formatting and clarity
209
+ 4. Completeness
210
+ 5. Overall quality
211
+ """
212
+
213
+ result = await Runner.run(
214
+ evaluator_agent,
215
+ eval_context,
216
+ )
217
+
218
+ evaluation = result.final_output_as(ReportEvaluation)
219
+ print(f"Evaluation complete. Average score: {evaluation.average_score:.2f}/5.0")
220
+ return evaluation
221
+
222
+ async def send_email(self, report: ReportData) -> None:
223
+ print("Writing email...")
224
+ result = await Runner.run(
225
+ email_agent,
226
+ report.markdown_report,
227
+ )
228
+ print("Email sent")
229
+ return report
230
+
231
+ def _format_evaluation(self, evaluation: ReportEvaluation, attempt: int = 1, is_regeneration: bool = False) -> str:
232
+ """Format evaluation results for display"""
233
+ header = "🔄 **Regeneration Attempt " + str(attempt) + "**\n\n" if is_regeneration else "📊 **Evaluation Results**\n\n"
234
+
235
+ details = f"""{header}
236
+ **Overall Score: {evaluation.average_score:.2f}/5.0**
237
+
238
+ **Detailed Scores:**
239
+ - **Relevance**: {evaluation.relevance_score.score}/5 - {evaluation.relevance_score.justification}
240
+ - **Use of Clarifications**: {evaluation.clarification_usage_score.score}/5 - {evaluation.clarification_usage_score.justification}
241
+ - **Formatting & Clarity**: {evaluation.formatting_score.score}/5 - {evaluation.formatting_score.justification}
242
+ - **Completeness**: {evaluation.completeness_score.score}/5 - {evaluation.completeness_score.justification}
243
+ - **Overall Quality**: {evaluation.overall_quality_score.score}/5 - {evaluation.overall_quality_score.justification}
244
+
245
+ **Feedback:**
246
+ {evaluation.feedback}
247
+ """
248
+ return details
search_agent.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents import Agent, WebSearchTool, ModelSettings
2
+
3
+ INSTRUCTIONS = (
4
+ "You are a research assistant. Given a search term, you search the web for that term and "
5
+ "produce a concise summary of the results. The summary must 2-3 paragraphs and less than 300 "
6
+ "words. Capture the main points. Write succintly, no need to have complete sentences or good "
7
+ "grammar. This will be consumed by someone synthesizing a report, so its vital you capture the "
8
+ "essence and ignore any fluff. Do not include any additional commentary other than the summary itself."
9
+ )
10
+
11
+ search_agent = Agent(
12
+ name="Search agent",
13
+ instructions=INSTRUCTIONS,
14
+ tools=[WebSearchTool(search_context_size="low")],
15
+ model="gpt-4o-mini",
16
+ model_settings=ModelSettings(tool_choice="required"),
17
+ )
writer_agent.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from agents import Agent
3
+ from typing import List
4
+
5
+ INSTRUCTIONS = (
6
+ "You are a senior researcher tasked with writing a cohesive report for a research query. "
7
+ "You will be provided with the original query, and some initial research done by a research assistant.\n"
8
+ "You should first come up with an outline for the report that describes the structure and "
9
+ "flow of the report. Then, generate the report and return that as your final output.\n"
10
+ "The final output should be in markdown format, and it should be lengthy and detailed. Aim "
11
+ "for 5-10 pages of content, at least 1000 words.\n"
12
+ "If clarification questions and answers are provided, make sure to incorporate those insights into your report.\n"
13
+ "If feedback from evaluation is provided, address those specific points to improve the report."
14
+ )
15
+
16
+
17
+ class ReportData(BaseModel):
18
+ short_summary: str = Field(description="A short 2-3 sentence summary of the findings.")
19
+
20
+ markdown_report: str = Field(description="The final report")
21
+
22
+ follow_up_questions: List[str] = Field(description="Suggested topics to research further")
23
+
24
+
25
+ writer_agent = Agent(
26
+ name="WriterAgent",
27
+ instructions=INSTRUCTIONS,
28
+ model="gpt-4o-mini",
29
+ output_type=ReportData,
30
+ )