Graded Assignment
In this lab, you will build an agentic system that generates a short research report through planning, external tool usage, and feedback integration. Your workflow will involve:
Agents
- Planning Agent / Writer: Creates an outline and coordinates tasks.
- Research Agent: Gathers external information using tools like Arxiv, Tavily, and Wikipedia.
- Editor Agent: Reflects on the report and provides suggestions for improvement.
<h4 style="color:green; font-weight:bold;">TIPS FOR SUCCESSFUL GRADING OF YOUR ASSIGNMENT:</h4>
-
All cells are frozen except for the ones where you need to write your solution code or when explicitly mentioned you can interact with it.
-
In each exercise cell, look for comments
### START CODE HERE ###and### END CODE HERE ###. These show you where to write the solution code. Do not add or change any code that is outside these comments. -
You can add new cells to experiment but these will be omitted by the grader, so don't rely on newly created cells to host your solution code, use the provided places for this.
-
Avoid using global variables unless you absolutely have to. The grader tests your code in an isolated environment without running all cells from the top. As a result, global variables may be unavailable when scoring your submission. Global variables that are meant to be used will be defined in UPPERCASE.
-
To submit your notebook for grading, first save it by clicking the ๐พ icon on the top left of the page and then click on the Submit assignment button on the top right of the page.
Research Tools
By importing research_tools, you gain access to several search utilities:
-
research_tools.arxiv_search_tool(query)โ search academic papers from arXivExample:
research_tools.arxiv_search_tool("neural networks for climate modeling") -
research_tools.tavily_search_tool(query)โ perform web searches with the Tavily APIExample:
research_tools.tavily_search_tool("latest trends in sunglasses fashion") -
research_tools.wikipedia_search_tool(query)โ retrieve summaries from WikipediaExample:
research_tools.wikipedia_search_tool("Ensemble Kalman Filter")
Run the cell below to make them available.
# =========================
# Imports
# =========================
# --- Standard library
from datetime import datetime
import re
import json
import ast
# --- Third-party ---
from IPython.display import Markdown, display
from aisuite import Client
# --- Local / project ---
import research_toolsimport unittestsInitialize client
Create a shared client instance for upcoming calls.
CLIENT = Client()Exercise 1: planner_agent
Objective
Correctly set up a call to a language model (LLM) to generate a research plan.
Instructions
- Focus Areas:
- Ensure
CLIENT.chat.completions.createis correctly configured. - Pass the
modelandmessagesparameters correctly:- Model: Use
"openai:o4-mini"by default. - Messages: Set with
{"role": "user", "content": user_prompt}. - Temperature: Fixed at 1 for creative outputs.
- Model: Use
- Ensure
Notes
- The prompt is pre-defined and guides the LLM on task requirements.
- Only return a formatted list of steps โ no extra text.
Focus on the LLM call setup to complete the task.
# GRADED FUNCTION: planner_agent
def planner_agent(topic: str, model: str = "openai:o4-mini") -> list[str]:
"""
Generates a plan as a Python list of steps (strings) for a research workflow.
Args:
topic (str): Research topic to investigate.
model (str): Language model to use.
Returns:
List[str]: A list of executable step strings.
"""
# Build the user prompt
user_prompt = f"""
You are a planning agent responsible for organizing a research workflow with multiple intelligent agents.
๐ง Available agents:
- A research agent who can search the web, Wikipedia, and arXiv.
- A writer agent who can draft research summaries.
- An editor agent who can reflect and revise the drafts.
๐ฏ Your job is to write a clear, step-by-step research plan **as a valid Python list**, where each step is a string.
Each step should be atomic, executable, and must rely only on the capabilities of the above agents.
๐ซ DO NOT include irrelevant tasks like "create CSV", "set up a repo", "install packages", etc.
โ
DO include real research-related tasks (e.g., search, summarize, draft, revise).
โ
DO assume tool use is available.
โ
DO NOT include explanation text โ return ONLY the Python list.
โ
The final step should be to generate a Markdown document containing the complete research report.
Topic: "{topic}"
"""
# Add the user prompt to the messages list
messages = [{"role": "user", "content": user_prompt}]
### START CODE HERE ###
# Call the LLM
response = CLIENT.chat.completions.create(
# Pass in the model
model=model,
# Define the messages. Remember this is meant to be a user prompt!
messages=messages,
# Keep responses creative
temperature=1,
)
### END CODE HERE ###
# Extract message from response
steps_str = response.choices[0].message.content.strip()
# Parse steps
steps = ast.literal_eval(steps_str)
return steps# Test your code!
unittests.test_planner_agent(planner_agent) All tests passed!Exercise 2: research_agent
Objective
Set up a call to a language model (LLM) to perform a research task using various tools.
Instructions
Focus Areas:
-
Creating a Custom Prompt:
- Define the Role: Clearly specify the role, such as "research assistant."
- List Available Tools (as strings inside the prompt, not the actual functions):
- Use
arxiv_toolto find academic papers. - Use
tavily_toolfor general web searches. - Use
wikipedia_toolfor accessing encyclopedic knowledge.
- Use
- Specify the Task: Include a placeholder in your prompt for defining the specific task that needs to be accomplished.
- Include Date Information: Add a placeholder for the current date or time to provide context.
-
Creating Messages Dict:
- Ensure the
messagesare correctly set with{"role": "user", "content": prompt}.
- Ensure the
-
Creating Tools List:
- Create a list of tools for use, such as
research_tools.arxiv_search_tool,research_tools.tavily_search_tool, andresearch_tools.wikipedia_search_tool.
- Create a list of tools for use, such as
-
Correctly Setting the Call to the LLM:
- Pass the
model,messages, andtoolsparameters accurately. - Set
tool_choiceto"auto"for automatic tool selection. - Limit interactions with
max_turns=6.
- Pass the
Notes
- The function provides pre-coded blocks where you need to replace placeholder values.
- The approach allows the LLM to use tools dynamically based on the task.
Focus on accurately setting the messages, tools, and LLM call parameters to complete the task.
# GRADED FUNCTION: research_agent
def research_agent(task: str, model: str = "openai:gpt-4o", return_messages: bool = False):
"""
Executes a research task using tools via aisuite (no manual loop).
Returns either the assistant text, or (text, messages) if return_messages=True.
"""
print("==================================")
print("๐ Research Agent")
print("==================================")
current_time = datetime.now().strftime('%Y-%m-%d')
### START CODE HERE ###
# Create a customizable prompt by defining the role (e.g., "research assistant"),
# listing tools (arxiv_tool, tavily_tool, wikipedia_tool) for various searches,
# specifying the task with a placeholder, and including a current_time placeholder.
prompt = f"""
You are a research assistant.
You have access to the following tools:
- arxiv_search_tool: to find and summarize academic papers.
- tavily_search_tool: to search the general web for relevant information.
- wikipedia_search_tool: to access encyclopedic knowledge.
Your task is to: {task}
Current date and time: {current_time}
Follow these steps:
1. Use the most relevant tool(s) to gather information.
2. Analyze the information carefully.
3. Provide a clear, concise, and well-structured response to accomplish the task.
"""
# Create the messages dict to pass to the LLM. Remember this is a user prompt!
messages = [{"role": "user", "content": prompt}]
# Save all of your available tools in the tools list. These can be found in the research_tools module.
# You can identify each tool in your list like this:
# research_tools.<name_of_tool>, where <name_of_tool> is replaced with the function name of the tool.
tools = [
research_tools.arxiv_search_tool,
research_tools.tavily_search_tool,
research_tools.wikipedia_search_tool
]
# Call the model with tools enabled
response = CLIENT.chat.completions.create(
# Set the model
model=model,
# Pass in the messages. You already defined this!
messages=messages,
# Pass in the tools list. You already defined this!
tools=tools,
# Set the LLM to automatically choose the tools
tool_choice="auto",
# Set the max turns to 6
max_turns=6
)
### END CODE HERE ###
content = response.choices[0].message.content
print("โ
Output:\n", content)
return (content, messages) if return_messages else content # Test your code!
unittests.test_research_agent(research_agent)==================================
๐ Research Agent
==================================
โ
Output:
To accomplish your task of finding and summarizing three key references, I'll employ various tools to search for pertinent information in academic papers, general web content, and encyclopedic summaries. I'll start this process by running searches across different platforms, then evaluate and summarize the results. Please specify the subject or topic you are interested in, so I can narrow down the search accordingly.
==================================
๐ Research Agent
==================================
โ
Output:
The paper "On non-bipartite graphs with integral signless Laplacian eigenvalues at most 6" by Semin Oh et al., published in 2025, classifies connected non-bipartite graphs with certain eigenvalue properties, contributing a precise understanding of graph structures in advanced mathematics. Another influential work, "Star Stability and Star Regularity for Mori Domains" by Stefania Gabelli and Giampaolo Picozza, explores extensions of stability and regularity within algebraic structures known as Mori domains, enhancing fundamental concepts in the study of commutative algebra. These papers respectively advance the fields of graph theory and commutative algebra through their rigorous exploration and classification of complex mathematical constructs.
All tests passed!Exercise 3: writer_agent
Objective
Set up a call to a language model (LLM) for executing writing tasks like drafting, expanding, or summarizing text.
Instructions
- Focus Areas:
- System Prompt:
- Define
system_promptto assign the LLM the role of a writing agent focused on generating academic or technical content.
- Define
- System and User Messages:
- Create
system_msgusing{"role": "system", "content": system_prompt}. - Create
user_msgusing{"role": "user", "content": task}.
- Create
- Messages List:
- Combine
system_msganduser_msginto amessageslist.
- Combine
- System Prompt:
Notes
- The function is designed to produce well-structured text by setting the correct prompts.
- Temperature is set to 1.0 to allow for creative variance in the writing outputs.
Ensure the system prompt and messages are defined properly to achieve a structured output from the LLM.
# GRADED FUNCTION: writer_agent
def writer_agent(task: str, model: str = "openai:gpt-4o") -> str: # @REPLACE def writer_agent(task: str, model: str = None) -> str:
"""
Executes writing tasks, such as drafting, expanding, or summarizing text.
"""
print("==================================")
print("โ๏ธ Writer Agent")
print("==================================")
### START CODE HERE ###
# Create the system prompt.
# This should assign the LLM the role of a writing agent specialized in generating well-structured academic or technical content
system_prompt = "You are a specialized writing agent. You should generate a well structured academic/technical content based on the data/task provided to you."
# Define the system msg by using the system_prompt and assigning the role of system
system_msg = {"role": "system", "content": system_prompt}
# Define the user msg. In this case the user prompt should be the task passed to the function
user_msg = {"role": "user", "content": task}
# Add both system and user messages to the messages list
messages = [system_msg, user_msg]
### END CODE HERE ###
response = CLIENT.chat.completions.create(
model=model,
messages=messages,
temperature=1.0
)
return response.choices[0].message.content# Test your code!
unittests.test_writer_agent(writer_agent)==================================
โ๏ธ Writer Agent
==================================
All tests passed!Exercise 4: editor_agent
Objective
Configure a call to a language model (LLM) to perform editorial tasks such as reflecting, critiquing, or revising drafts.
Instructions
- Focus Areas:
- System Prompt:
- Define
system_promptto assign the LLM the role of an editor agent whose task is to reflect on, critique, or improve drafts.
- Define
- System and User Messages:
- Create
system_msgusing{"role": "system", "content": system_prompt}. - Create
user_msgusing{"role": "user", "content": task}.
- Create
- Messages List:
- Combine
system_msganduser_msginto amessageslist.
- Combine
- System Prompt:
Notes
- The editor agent is tailored for enhancing the quality of text by setting an appropriate role and task in the prompts.
- Temperature is set to 0.7, balancing creativity and coherence in editorial outputs.
Ensure the system prompt and messages are accurately set up to perform effective editorial tasks with the LLM.
# GRADED FUNCTION: editor_agent
def editor_agent(task: str, model: str = "openai:gpt-4o") -> str:
"""
Executes editorial tasks such as reflection, critique, or revision.
"""
print("==================================")
print("๐ง Editor Agent")
print("==================================")
### START CODE HERE ###
# Create the system prompt.
# This should assign the LLM the role of an editor agent specialized in reflecting on, critiquing, or improving existing drafts.
system_prompt = "You are a specialized editor agent. You should reflect on, cretique on or improve existing drafts based on the data/task provided to you."
# Define the system msg by using the system_prompt and assigning the role of system
system_msg = {"role": "system", "content": system_prompt}
# Define the user msg. In this case the user prompt should be the task passed to the function
user_msg = {"role": "user", "content": task}
# Add both system and user messages to the messages list
messages = [system_msg, user_msg]
### END CODE HERE ###
response = CLIENT.chat.completions.create(
model=model,
messages=messages,
temperature=0.7
)
return response.choices[0].message.content# Test your code!
unittests.test_editor_agent(editor_agent)==================================
๐ง Editor Agent
==================================
All tests passed!๐ฏ The Executor Agent
The executor_agent manages the workflow by executing each step of a given plan. It:
- Decides which agent (
research_agent,writer_agent, oreditor_agent) should handle the step. - Builds context from the outputs of previous steps.
- Sends the enriched task to the selected agent.
- Collects and stores the results in a shared history.
๐ Do not implement or modify this function. It is already provided as the orchestration component of the multi-agent pipeline.
Notice that planner_agent might return a long list of steps. Because of this, the maximum number of steps is set to a maximum of 4 to keep running time reasonable.
agent_registry = {
"research_agent": research_agent,
"editor_agent": editor_agent,
"writer_agent": writer_agent,
}
def clean_json_block(raw: str) -> str:
"""
Clean the contents of a JSON block that may come wrapped with Markdown backticks.
"""
raw = raw.strip()
if raw.startswith("```"):
raw = re.sub(r"^```(?:json)?\n?", "", raw)
raw = re.sub(r"\n?```$", "", raw)
return raw.strip()def executor_agent(topic, model: str = "openai:gpt-4o", limit_steps: bool = True):
plan_steps = planner_agent(topic)
max_steps = 4
if limit_steps:
plan_steps = plan_steps[:min(len(plan_steps), max_steps)]
history = []
print("==================================")
print("๐ฏ Editor Agent")
print("==================================")
for i, step in enumerate(plan_steps):
agent_decision_prompt = f"""
You are an execution manager for a multi-agent research team.
Given the following instruction, identify which agent should perform it and extract the clean task.
Return only a valid JSON object with two keys:
- "agent": one of ["research_agent", "editor_agent", "writer_agent"]
- "task": a string with the instruction that the agent should follow
Only respond with a valid JSON object. Do not include explanations or markdown formatting.
Instruction: "{step}"
"""
response = CLIENT.chat.completions.create(
model=model,
messages=[{"role": "user", "content": agent_decision_prompt}],
temperature=0,
)
raw_content = response.choices[0].message.content
cleaned_json = clean_json_block(raw_content)
agent_info = json.loads(cleaned_json)
agent_name = agent_info["agent"]
task = agent_info["task"]
context = "\n".join([
f"Step {j+1} executed by {a}:\n{r}"
for j, (s, a, r) in enumerate(history)
])
enriched_task = f"""
You are {agent_name}.
Here is the context of what has been done so far:
{context}
Your next task is:
{task}
"""
print(f"\n๐ ๏ธ Executing with agent: `{agent_name}` on task: {task}")
if agent_name in agent_registry:
output = agent_registry[agent_name](enriched_task)
history.append((step, agent_name, output))
else:
output = f"โ ๏ธ Unknown agent: {agent_name}"
history.append((step, agent_name, output))
print(f"โ
Output:\n{output}")
return history# If you want to see the full workflow without limiting the number of steps. Set limit_steps to False
# Keep in mind this could take more than 10 minutes to complete
executor_history = executor_agent("The ensemble Kalman filter for time series forecasting", limit_steps=True)
md = executor_history[-1][-1].strip("`")
display(Markdown(md))Check grading feedback
If you have collapsed the right panel to have more screen space for your code, as shown below:
[Image not included: course material, property of DeepLearning.AI.]
You can click on the left-facing arrow button (highlighted in red) to view feedback for your submission after submitting it for grading. Once expanded, it should display like this:
[Image not included: course material, property of DeepLearning.AI.]
Copyright ยฉ 2024-2026 Muhammed Muzzammil Shah