🤖 Agentic AI
⏱ 12 min read
📅 March 23, 2026
BEGINNER FRIENDLY
Build a Personal AI Research Assistant
with Claude API and Python
in 30 Minutes
Stop copy-pasting from ten browser tabs. Build an AI agent that reads, summarises, and synthesises research for you — fully in Python, no frameworks required.
🐍Pure Python
⚡Claude API (Sonnet 4)
🧠Conversation Memory
🕐30 Minutes to Deploy
Every professional researcher, student, and developer wastes hours every week doing the same thing: opening dozens of tabs, reading the same introductory paragraphs, manually piecing together summaries. It is tedious, slow, and completely solvable with a few dozen lines of Python.
In this tutorial you will build a Personal AI Research Assistant — a Python agent powered by the Claude API that fetches web content, summarises findings, maintains conversation memory, and produces a clean final report.
"The difference between a basic API call and a real AI agent is memory, structure, and purpose."
What You Will Build
✓
Web Content Reader
Fetches and parses any URL for research input
✓
AI Summariser
Claude distils key insights from long-form content
✓
Conversation Memory
Remembers your session so follow-up questions work
✓
Multi-Source Synthesis
Combines findings from multiple sources into one report
✓
Save to File
Export your final research report as a .txt file
✓
CLI Interface
Run from terminal — no web framework needed
BEFORE WE START
Prerequisites & Setup
You need Python 3.9+ and a free Anthropic API key. No GPU, no cloud server, no database.
🐍
Python 3.9+
Check: python --version
🔑
Anthropic API Key
Free at console.anthropic.com
📦
pip packages
anthropic · requests · bs4
⏱️
30 minutes
From zero to working assistant
Install the dependencies:
pip install anthropic requests beautifulsoup4
Set your API key as an environment variable:
# macOS / Linux
export ANTHROPIC_API_KEY='your-key-here'# Windows (Command Prompt)
set ANTHROPIC_API_KEY=your-key-here
💡 Pro tip: Store your key in a .env file and use python-dotenv so it never gets committed to GitHub.
01
Project Structure
~2 minutesCreate a new folder and file:
mkdir ai-research-assistant
cd ai-research-assistant
touch research_assistant.py
Final folder structure:
ai-research-assistant/
├── research_assistant.py # main file
├── .env # API key (never commit)
└── reports/ # saved research reports
02
Build the Web Content Reader
~5 minutesThis module fetches a webpage and strips HTML noise, leaving only readable text for Claude. We use requests to download and BeautifulSoup to parse.
🔍
Why BeautifulSoup?
Raw HTML contains nav menus, ads, and scripts — none useful for research. BeautifulSoup extracts only paragraph text, giving Claude clean input and saving API tokens.
research_assistant.py — Part 1: Web Reader import os
import requests
from bs4 import BeautifulSoup
import anthropic# Initialise the Claude client
client = anthropic.Anthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY")
)def fetch_webpage_content(url: str, max_chars: int = 8000) -> str:
"""Fetch a URL and return clean paragraph text only."""
try:
headers = {'User-Agent': 'Mozilla/5.0 (ResearchBot/1.0)'}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()soup = BeautifulSoup(response.text, 'html.parser')# Remove noise: scripts, styles, nav, ads
for tag in soup(['script', 'style', 'nav', 'footer', 'header', 'aside']):
tag.decompose()# Extract paragraph text
paragraphs = soup.find_all('p')
text = ' '.join(
p.get_text(strip=True)
for p in paragraphs
if p.get_text(strip=True)
)# Trim to token-friendly length
return text[:max_chars] + ("... [truncated]" if len(text) > max_chars else "")except Exception as e:
return f"Error fetching {url}: {str(e)}"
📌 Key concept: We cap at 8,000 characters to keep API costs low. Adjust max_chars based on the detail level you need.
03
Build the AI Summariser
~5 minutesWe pass the cleaned text and the research question to Claude, which returns a structured, focused summary. Prompt engineering is critical here.
🧠 Prompt Engineering Principle
Always give Claude a role, a specific task, and an output format. "Summarise this" is vague. "You are a research analyst. Extract the 3 main findings relevant to [topic] in bullet points" is precise and powerful.
research_assistant.py — Part 2: AI Summariser def summarise_with_claude(
content: str,
research_question: str,
source_url: str
) -> str:
"""Send content to Claude and get a research-focused summary."""system_prompt = """You are a precise research analyst. Extract and
summarise information directly relevant to the user's question.
Structure your response as:FINDINGS — 3-5 bullet points of the most relevant facts
INSIGHT — 2-3 sentences of your analytical interpretation
LIMITATIONS — what this source does NOT cover
SOURCE QUALITY — rate High / Medium / Low with one-line reason
"""user_message = f"""Research question: {research_question}Source URL: {source_url}Content to analyse:
{content}Please summarise the above content relative to my research question."""response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system=system_prompt,
messages=[{"role": "user", "content": user_message}]
)
return response.content[0].text
04
Add Conversation Memory
~5 minutesThis is what separates a basic API call from a true AI agent. By keeping a conversation history list and passing it on every call, Claude remembers everything from your session.
How Memory Works
User:
Question 1
→
Claude:
Answer 1
→
User:
Question 2
→
history[]
sent every time
→
Claude:
Contextual Answer
research_assistant.py — Part 3: Memory # Conversation history — persists for the whole session
conversation_history = []def chat_with_memory(user_message: str) -> str:
"""Send a message to Claude with full conversation history."""# Append the new user turn
conversation_history.append({
"role": "user",
"content": user_message
})response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system="""You are a personal AI research assistant.
You maintain full context of this research session and
help synthesise findings from multiple sources.""",
messages=conversation_history # full history every time
)assistant_reply = response.content[0].text# Append Claude's reply to maintain the thread
conversation_history.append({
"role": "assistant",
"content": assistant_reply
})return assistant_reply
05
Multi-Source Synthesis + Save Report
~8 minutesLoop over multiple URLs, collect summaries, then ask Claude to synthesise them into a single coherent report — saved automatically to a file.
research_assistant.py — Part 4: Synthesis + Save def synthesise_sources(summaries: list, research_question: str) -> str:
"""Combine multiple source summaries into one final report."""
combined = "\n\n---\n\n".join(
f"SOURCE {i+1}:\n{s}"
for i, s in enumerate(summaries)
)
synthesis_prompt = f"""Research question: {research_question}I have gathered summaries from {len(summaries)} sources.
Synthesise them into a final research report with:## EXECUTIVE SUMMARY (3-4 sentences)
## MAIN FINDINGS (consolidated bullet points)
## AREAS OF AGREEMENT (what all/most sources agree on)
## CONFLICTING VIEWS (where sources disagree)
## RESEARCH GAPS (what no source adequately covered)
## NEXT STEPS (2-3 actionable follow-up questions)Source summaries:
{combined}"""return chat_with_memory(synthesis_prompt)def save_report(report: str, filename: str = "research_report.txt"):
"""Save the final report to a file."""
os.makedirs("reports", exist_ok=True)
path = f"reports/{filename}"
with open(path, "w", encoding="utf-8") as f:
f.write(report)
print(f"\nReport saved to: {path}")
Full Code — Everything Together
Copy the complete research_assistant.py below, add your API key, and run it.
research_assistant.py — COMPLETE FILE
FULL CODE #!/usr/bin/env python3
"""
Personal AI Research Assistant
Built with Claude API (claude-sonnet-4-20250514)
AiBytec.com — Agentic AI Tutorial
"""import os
import requests
from bs4 import BeautifulSoup
import anthropic
from datetime import datetime# Client & memory
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
conversation_history = []def fetch_webpage_content(url: str, max_chars: int = 8000) -> str:
try:
r = requests.get(url, headers={'User-Agent': 'ResearchBot/1.0'}, timeout=10)
soup = BeautifulSoup(r.text, 'html.parser')
for tag in soup(['script', 'style', 'nav', 'footer', 'header']):
tag.decompose()
text = ' '.join(p.get_text(strip=True) for p in soup.find_all('p'))
return text[:max_chars]
except Exception as e:
return f"Error: {str(e)}"def chat_with_memory(user_message: str) -> str:
conversation_history.append({"role": "user", "content": user_message})
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1500,
system="You are a personal AI research assistant. You maintain full context of this research session and help synthesise findings from multiple sources into clear, actionable reports.",
messages=conversation_history
)
reply = response.content[0].text
conversation_history.append({"role": "assistant", "content": reply})
return replydef summarise_source(url: str, research_question: str) -> str:
print(f" Fetching: {url}...")
content = fetch_webpage_content(url)
prompt = f"""Analyse this web content for my research question: "{research_question}"Source: {url}
Content: {content}Provide:
FINDINGS — 3-5 bullets relevant to my question
INSIGHT — 2-3 sentences of analysis
LIMITATIONS — what this source misses"""
return chat_with_memory(prompt)def synthesise_and_save(summaries: list, research_question: str):
combined = "\n\n---\n\n".join(
f"SOURCE {i+1}:\n{s}" for i, s in enumerate(summaries)
)
final = chat_with_memory(
f"""Synthesise these {len(summaries)} source summaries into a final report for:
"{research_question}"Include: Executive Summary, Main Findings, Areas of Agreement,
Conflicting Views, Research Gaps, Recommended Next Steps{combined}"""
)
os.makedirs("reports", exist_ok=True)
filename = f"reports/report_{datetime.now().strftime('%Y%m%d_%H%M')}.txt"
with open(filename, "w", encoding="utf-8") as f:
f.write(f"RESEARCH QUESTION: {research_question}\n\n")
f.write(final)
print(f"\nReport saved to: {filename}")
return final# MAIN
if __name__ == "__main__":
print("Personal AI Research Assistant — AiBytec.com")
print("=" * 50)research_question = input("\nEnter your research question: ")
print("\nEnter source URLs (one per line, blank line when done):")urls = []
while True:
url = input(f" URL {len(urls)+1}: ").strip()
if not url:
break
urls.append(url)print(f"\nProcessing {len(urls)} sources...\n")
summaries = [summarise_source(url, research_question) for url in urls]print("\nGenerating final synthesis report...\n")
report = synthesise_and_save(summaries, research_question)
print("\n" + "=" * 50)
print(report)# Follow-up questions with full memory
print("\nAsk follow-up questions (type 'exit' to quit):")
while True:
q = input("\nYou: ")
if q.lower() == 'exit':
break
print(f"\nAssistant: {chat_with_memory(q)}")
$ run it
python research_assistant.pyPersonal AI Research Assistant — AiBytec.com
==================================================Enter your research question: What are the best AI agent use cases in 2026?Enter source URLs (one per line, blank line when done):
URL 1: https://example.com/article-1
URL 2: https://example.com/article-2
URL 3:Processing 2 sources...
Fetching: https://example.com/article-1...
Fetching: https://example.com/article-2...Generating final synthesis report...
Report saved to: reports/report_20260323_1430.txt
Next Steps & Extensions
You now have a working AI research assistant. Here is how to level it up:
🖥️
Streamlit UI
Wrap the CLI in a Streamlit dashboard with URL input fields and a formatted output panel.
🔎
Auto Web Search
Integrate Tavily or SerpAPI to auto-find sources — no manual URLs needed.
🗄️
Vector Memory (RAG)
Store past reports in ChromaDB or Pinecone for long-term semantic recall.
📄
PDF Input Support
Use Claude's native document API to feed research papers directly — no scraping needed.
"The difference between a basic API call and a true AI agent is memory, structure, and purpose. You now have all three."
You Built a Real AI Agent Today
In 30 minutes you went from zero to a working Personal AI Research Assistant — reading web content, summarising with purpose, remembering context, and delivering reports you can save and act on.
This is exactly the agentic AI skill that separates developers who understand AI from developers who just use AI. Keep building.
🎓
Want to Build More Agents Like This?
Join Certificate 2: Agentic AI Developer at AiBytec — Pakistan's most practical AI course. Real projects. Claude API. OpenAI Agents SDK. FastAPI. LangChain.
Enroll at AiBytec.com →💡 Found this useful? Share it on LinkedIn and help a fellow developer level up.
#AgenticAI
#ClaudeAPI
#PythonAI
#AIResearchAssistant
#ConversationMemory
#BuildWithAI
#AiBytec