Agentic Solutions
Knowledge Check Answers
-
Regular LLM responds directly to the user. Agentic LLM can call tools (functions) to get additional information before responding, enabling math, search, database access, etc.
-
tool_choice="auto"lets the AI decide whether to call a tool or respond directly. The alternativetool_choice="required"forces a tool call every time. -
Prevents infinite loops if the AI keeps calling tools without producing a final answer. For example, if a tool keeps returning errors, the AI might retry forever.
-
tool_call_idmatches the tool call result to the original tool call request. OpenAI uses it to associate which tool call result belongs to which request when multiple tool calls happen in one response. -
Chain-of-thought forces the AI to break down problems into intermediate steps, reducing errors from jumping to conclusions. It's especially effective for math, logic, and multi-step reasoning tasks.
Challenge 1 Solution
Replace the mock get_weather with a real weather API:
def get_weather(city: str) -> str:
"""Get current weather for a city using wttr.in."""
try:
response = requests.get(
f"https://wttr.in/{city}?format=%C+%t+%w",
timeout=10,
)
if response.status_code == 200:
return f"Weather in {city}: {response.text}"
else:
return f"Weather error: {response.status_code}"
except Exception as e:
return f"Error getting weather: {str(e)}"
Or with OpenWeatherMap (needs free API key from https://openweathermap.org/api):
def get_weather_openweather(city: str) -> str:
"""Get current weather using OpenWeatherMap API."""
api_key = os.getenv("OPENWEATHER_API_KEY", "")
url = "https://api.openweathermap.org/data/2.5/weather"
params = {"q": city, "appid": api_key, "units": "metric"}
try:
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
temp = data["main"]["temp"]
desc = data["weather"][0]["description"]
return f"Weather in {city}: {temp}°C, {desc}"
else:
return f"Weather not found for {city}"
except Exception as e:
return f"Error: {str(e)}"
Challenge 2 Solution
import sqlite3
def query_database(sql: str) -> str:
"""Execute a SELECT query on the chat database."""
sql_upper = sql.strip().upper()
if not sql_upper.startswith("SELECT"):
return "Error: Only SELECT queries are allowed."
try:
db_path = os.path.join(
os.path.dirname(__file__),
"../backend/instance/chat.db",
)
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute(sql)
rows = cursor.fetchall()
columns = [description[0] for description in cursor.description]
conn.close()
if not rows:
return "No results found."
result = " | ".join(columns) + "\n"
result += "-" * len(result) + "\n"
for row in rows:
result += " | ".join(str(col) for col in row) + "\n"
return result.strip()
except Exception as e:
return f"Database error: {str(e)}"
Add the tool schema:
{
"type": "function",
"function": {
"name": "query_database",
"description": "Execute a SELECT query on the chat database to retrieve data",
"parameters": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "A SQL SELECT query",
}
},
"required": ["sql"],
},
},
}
Test: Ask "How many messages are in the database?" → AI generates SELECT COUNT(*) FROM message and returns the count.
Challenge 3 Solution
import json
MEMORY_FILE = "agent_memory.json"
def load_memory():
try:
with open(MEMORY_FILE, "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {}
def save_memory(memory):
with open(MEMORY_FILE, "w") as f:
json.dump(memory, f, indent=2)
def remember(key: str, value: str) -> str:
"""Store a fact in memory."""
memory = load_memory()
memory[key.lower()] = value
save_memory(memory)
return f"Remembered: {key} = {value}"
def recall(key: str) -> str:
"""Retrieve a fact from memory."""
memory = load_memory()
value = memory.get(key.lower())
if value:
return f"{key}: {value}"
return f"No memory found for '{key}'"
def forget(key: str) -> str:
"""Remove a fact from memory."""
memory = load_memory()
if key.lower() in memory:
del memory[key.lower()]
save_memory(memory)
return f"Forgot: {key}"
return f"No memory found for '{key}'"
def list_memories() -> str:
"""List all stored facts."""
memory = load_memory()
if not memory:
return "No memories stored."
return "\n".join(f"{k}: {v}" for k, v in memory.items())
Add tool schemas for each function and add to the run_tool dispatcher. Test:
User: "Remember my favorite color is blue"
User: "What is my favorite color?"
User: "Forget my favorite color"
Open-Ended Project: Custom Research Assistant
def save_report(filename: str, content: str) -> str:
"""Save content to a markdown file."""
try:
with open(filename, "w") as f:
f.write(content)
return f"Report saved to {filename}"
except Exception as e:
return f"Error saving report: {str(e)}"
def research_topic(topic: str) -> str:
"""Full research pipeline."""
sections = [
("Introduction", f"What is {topic}?"),
("Key Concepts", f"Key concepts in {topic}"),
("History", f"History of {topic}"),
("Applications", f"Applications of {topic}"),
]
report = f"# Research Report: {topic}\n\n"
for section_title, search_query in sections:
print(f" Researching: {section_title}...")
summary = search_wikipedia(search_query)
report += f"## {section_title}\n\n{summary}\n\n---\n\n"
report += "\n*Report generated by AI Research Agent*\n"
filename = f"{topic.lower().replace(' ', '-')}-report.md"
save_report(filename, report)
return f"Report written to {filename}"
Add the tool and schema. Then in interactive mode:
User: "Research renewable energy"