Project: Autonomous Research Assistant
What You'll Build Today
Welcome to Day 60! You have reached a massive milestone. Today marks the end of Phase 6: Frameworks & Agents. To celebrate, we are going to build something that feels like science fiction: an Autonomous Research Assistant.
Up until now, we've mostly had "conversations" with AI. You ask a question, and it answers based on its training data. Today, we change the dynamic. You will give the AI a goal, and it will go out into the world (the internet), gather information, synthesize it, and write a report for you.
Here is what you will master today:
* End-to-End Agent Design: You will move beyond simple scripts to creating a system that can "think" about which steps to take next without you hard-coding them.
* Tool Integration: You will learn why—and how—to give an LLM "hands" (tools) to interact with the outside world, specifically a web search engine.
* Context Management: You will see how an agent holds onto gathered information to synthesize a final answer, rather than forgetting it immediately.
* Output Formatting: You will learn how to force an agent to structure unstructured web data into a clean, readable Markdown report.
This is the culmination of everything you have learned about Python, APIs, and logic. Let's build an employee that never sleeps.
The Problem
Let's say you want to know the very latest developments in "Solid State Batteries" for a report you are writing.
If you use a standard LLM script (like we did in Phase 4), you run into a major wall: the Knowledge Cutoff. The AI only knows what it was trained on. It cannot browse the web by default.
Here is code that represents the "Old Way." It is frustrating because it is confident, but often wrong or outdated.
import os
from openai import OpenAI
# Assume API key is set in environment variables
client = OpenAI()
def simple_researcher(topic):
print(f"Researching: {topic}...")
# We ask the model directly
response = client.chat.completions.create(
model="gpt-4o", # Or gpt-3.5-turbo
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"Write a report on the latest news about {topic} from yesterday."}
]
)
return response.choices[0].message.content
# THE PAIN:
# If you ask about something that happened today, the AI will either:
# 1. Hallucinate (make up facts).
# 2. Apologize and say "I don't have real-time access to the internet."
print(simple_researcher("The stock price of NVIDIA right this second"))
The Pain Points:
There has to be a way to give the AI a web browser and let it do the clicking and reading for us.
Let's Build It
We will use LangChain to build this. While you can write raw Python to call APIs, LangChain provides a standard interface for "Agents"—systems that use an LLM as a reasoning engine to decide which tools to use.
We will use DuckDuckGo as our search engine because it is free and requires no API key setup for this tutorial.
Step 1: Install Dependencies
We need the LangChain ecosystem and the search tool.
Note: You may need to run this in your terminal first.pip install langchain langchain-openai langchain-community duckduckgo-search
Step 2: Initialize the LLM and the Tools
First, let's set up our "Brain" (OpenAI) and our "Hands" (DuckDuckGo Search). We need to verify the search tool works before giving it to the agent.
import os
from langchain_openai import ChatOpenAI
from langchain_community.tools import DuckDuckGoSearchRun
# 1. Setup the LLM (The Brain)
# We use temperature=0 because we want factual research, not creative fiction.
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 2. Setup the Tool (The Hands)
search_tool = DuckDuckGoSearchRun()
# TEST: Let's verify the tool works on its own
print("--- Testing Search Tool ---")
test_result = search_tool.invoke("Current CEO of OpenAI")
print(test_result)
Why this matters: Never build a complex agent without testing the individual parts. If the search tool fails here, the whole agent will fail later.
Step 3: Define the Prompt Template
An agent needs a specific type of prompt. It needs to know:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# Define the prompt
prompt = ChatPromptTemplate.from_messages([
("system", "You are a world-class technical researcher. "
"Your goal is to research a topic thoroughly using the search tool provided. "
"Summarize your findings into a professional markdown report. "
"Always cite your sources."),
("user", "{input}"),
# This placeholder is CRITICAL. It is where the agent's internal monologue
# and tool outputs are injected back into the chat history.
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
Why this matters: The agent_scratchpad is the magic. It creates a loop. The AI outputs a "function call," the system runs the function, puts the result in the scratchpad, and sends it back to the AI.
Step 4: Construct the Agent
Now we bind the LLM, the tools, and the prompt together. We use a specific LangChain constructor for OpenAI tools, as OpenAI models are fine-tuned to detect when to call functions.
from langchain.agents import create_tool_calling_agent, AgentExecutor
# List of tools (we only have one right now, but we could add Wikipedia, Calculator, etc.)
tools = [search_tool]
# Create the agent structure
agent = create_tool_calling_agent(llm, tools, prompt)
# Create the Executor
# The Agent is the "brain", the Executor is the "runtime" that actually loops
# and executes the tools.
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True # This lets us see the "thinking" process in the console
)
Why this matters: The AgentExecutor handles the while loop for us. It keeps asking the LLM "Are you done?" If the LLM says "No, I need to search for X," the Executor runs the search and feeds the result back. If the LLM says "Yes, here is the answer," the loop ends.
Step 5: Run the Autonomous Researcher
Now, let's run it. We will ask a question that requires current knowledge.
print("--- Starting Research Agent ---")
topic = "What are the latest features released in Python 3.12 and 3.13?"
result = agent_executor.invoke({"input": topic})
print("\n\n=== FINAL REPORT ===\n")
print(result['output'])
What to expect in the output:
Because we set verbose=True, you will see green text in your console showing the agent's thought process:
Step 6: Complete Runnable Code
Here is the entire script combined into one file.
import os
from langchain_openai import ChatOpenAI
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import create_tool_calling_agent, AgentExecutor
# --- CONFIGURATION ---
# Ensure your OPENAI_API_KEY is in your environment variables
def main():
# 1. Tools
search = DuckDuckGoSearchRun()
tools = [search]
# 2. LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 3. Prompt
prompt = ChatPromptTemplate.from_messages([
("system", "You are a Senior Research Assistant. "
"When given a topic, search for the most recent and relevant information. "
"Compile a report in Markdown format. "
"Include a 'Key Takeaways' section and a 'Sources' section with URLs."),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
# 4. Agent Construction
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 5. Execution
topic = input("Enter a topic to research: ")
print(f"\nResearching '{topic}'... please wait.\n")
try:
response = agent_executor.invoke({"input": topic})
print("\n" + "="*40)
print("RESEARCH REPORT")
print("="*40 + "\n")
print(response["output"])
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
main()
Now You Try
You have a working agent. Now, let's make it smarter and more robust. Try these three extensions:
LangChain has a built-in Wikipedia tool.
* Install: pip install wikipedia
* Import: from langchain_community.tools import WikipediaQueryRun and from langchain_community.utilities import WikipediaAPIWrapper
* Setup: wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
tools list (tools = [search, wikipedia]). Watch how the agent decides which* tool to use based on your question!
Modify the System Message in the prompt. Change it from "Senior Research Assistant" to "Explain Like I'm 5."
* Task: Run the same query about complex tech (like "Quantum Entanglement") and see how the output style changes completely while the research data remains accurate.
Right now, the report just prints to the console.
* Task: Add standard Python file I/O at the end of the script to save response["output"] to a file named research_report.md.
Challenge Project: The "Publish-Ready" Reporter
Your main project prints to the console, but that is hard to read for long reports. Your challenge is to build a full CLI (Command Line Interface) wrapper around your agent.
Requirements:ai_news_report.md).> Enter topic: Apple Vision Pro reviews
Researching...
[Agent thinking...]
Report saved to apple_vision_pro_reviews.md
> Enter topic: exit
Goodbye!
Hint:
* Use Python's string method .replace(" ", "_") for the filename.
* Use a while True: loop for the main program interaction.
What You Learned
Today you graduated from "User" to "Architect."
* Agents vs. Chains: You learned that a Chain follows a fixed path (A -> B -> C), but an Agent uses the LLM to decide the path (A -> ? -> ?).
* Tool Calling: You saw that LLMs aren't just text generators; they can be "routers" that pick the right function to call to solve a problem.
* The Loop: You observed the Thought -> Action -> Observation loop that allows AI to correct itself and gather more data.
In the real world, data isn't static. It lives in databases, APIs, and websites. The ability to build Agents that can reach out and grab that data is what separates a toy project from enterprise-grade software.
Phase 6 Complete! You have conquered frameworks and agents. Tomorrow: We enter Phase 7. We will stop using generic models and start looking at Fine-Tuning—training the model on your data to think exactly how you want it to. See you there!