A practical exercise in constructing a foundational LLM agent with a specific role and function guides you through defining an agent's persona, specialized function, and integrating basic tool usage. The goal is to build a "Web Content Summarizer" agent capable of fetching information from a given URL, summarizing it, and extracting its main topics.Defining Our Agent: The Web Content SummarizerBefore writing any code, let's clearly define our agent:Persona: "I am a diligent Research Assistant. My purpose is to process web articles, providing you with a concise summary and a list of the primary topics discussed. I aim for clarity and accuracy."Function:Accept a URL as input.Fetch the textual content from the web page at that URL.Utilize an LLM to generate a summary of the content.Utilize an LLM to identify 3-5 main topics from the content.Return the summary and the list of topics.Tools:A web fetching tool (using libraries like requests and BeautifulSoup4).An LLM for natural language processing tasks (summarization, topic extraction).This agent, while simple, demonstrates the core idea of giving an LLM-powered entity a specific job description and the means to accomplish it.PrerequisitesEnsure you have the following Python libraries installed:pip install openai requests beautifulsoup4You will also need an OpenAI API key. It's best practice to set this as an environment variable:export OPENAI_API_KEY='your_api_key_here'Remember that this hands-on exercise assumes your development environment is configured as outlined in Chapter 1.Agent ImplementationLet's build our WebSummarizerAgent step by step.1. Importing Libraries and Initializing the LLM ClientFirst, we'll import the necessary modules and set up our OpenAI client.import os import requests from bs4 import BeautifulSoup from openai import OpenAI # Initialize the OpenAI client # It will automatically pick up the OPENAI_API_KEY environment variable try: client = OpenAI() except Exception as e: print(f"Error initializing OpenAI client: {e}") print("Please ensure your OPENAI_API_KEY environment variable is set correctly.") exit() # Configuration for the LLM LLM_MODEL = "gpt-3.5-turbo"Using gpt-3.5-turbo provides a good balance of capability and cost for this task. You could substitute other models as needed.2. Crafting the Web Fetching ToolOur agent needs to retrieve content from a URL. We'll create a function for this.def fetch_web_content(url: str) -> str: """ Fetches and extracts text content from a given URL. Returns the text content or an error message. """ try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() # Raises an HTTPError for bad responses (4XX or 5XX) soup = BeautifulSoup(response.content, 'html.parser') # Extract text from common content tags, attempt to get main content paragraphs = soup.find_all(['p', 'article', 'main']) if not paragraphs: # Fallback for simpler pages paragraphs = soup.find_all('body') text_content = "" for p_tag in paragraphs: text_content += p_tag.get_text(separator=' ', strip=True) + "\n" # Basic cleaning: reduce multiple newlines and spaces text_content = '\n'.join([line.strip() for line in text_content.splitlines() if line.strip()]) text_content = ' '.join(text_content.split()) # Limit content length to avoid excessive token usage # This is a simple truncation; more sophisticated chunking might be needed for very long articles max_length = 15000 # Approximately 4k tokens, depends on text return text_content[:max_length] except requests.exceptions.RequestException as e: return f"Error fetching URL {url}: {e}" except Exception as e: return f"Error processing content from {url}: {e}" This fetch_web_content function attempts to grab the main textual parts of a webpage. Web scraping can be much more complex, often requiring site-specific parsers, but this provides a general approach. The content is truncated to prevent excessively long inputs to the LLM.3. Designing the Agent's Core LogicNow, we'll define the agent's main processing function. This function will orchestrate fetching content and then using the LLM for summarization and topic extraction.def process_url_with_agent(url: str) -> dict: """ Orchestrates fetching web content, summarizing, and extracting topics. """ print(f"Processing URL: {url}...") content = fetch_web_content(url) if content.startswith("Error"): return {"error": content} if not content.strip(): return {"error": "No text content found at the URL."} # Persona and instruction for the LLM system_prompt = ( "You are a diligent Research Assistant. " "Your purpose is to process the provided web article text, " "generate a concise summary, and identify 3-5 main topics. " "Format your response as a JSON object with two keys: 'summary' and 'main_topics' (a list of strings)." ) user_prompt = f"Please process the following web article content:\n\n{content}" try: response = client.chat.completions.create( model=LLM_MODEL, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], temperature=0.3, # Lower temperature for more factual and less creative output response_format={"type": "json_object"} # Request JSON output ) # The response content should be a JSON string. # We'll parse it directly. assistant_reply = response.choices[0].message.content # Basic validation if needed before parsing, or rely on OpenAI's JSON mode import json result = json.loads(assistant_reply) return result except Exception as e: return {"error": f"LLM processing error: {e}"} In process_url_with_agent, we first fetch the content. Then, we construct a system prompt that clearly defines the agent's persona and task, crucially instructing it to return a JSON object. This structured output is easier to parse and use in a larger system. The user prompt provides the actual content. We use OpenAI's JSON mode for reliable structured output.A simple diagram illustrates the flow of our agent:digraph WebSummarizerAgent { rankdir=TB; node [shape=box, style="rounded,filled", fillcolor="#a5d8ff", fontname="Arial"]; edge [fontname="Arial"]; URL [label="Input URL", fillcolor="#ffec99"]; Fetcher [label="Web Content Fetcher\n(requests, BeautifulSoup)"]; WebContent [label="Raw Web Content", shape=note, fillcolor="#b2f2bb"]; LLM [label="LLM Core\n(OpenAI API)", fillcolor="#bac8ff"]; Output [label="Structured Output\n(Summary, Main Topics)", shape=note, fillcolor="#d8f5a2"]; URL -> Fetcher; Fetcher -> WebContent [label=" Fetches "]; WebContent -> LLM [label=" Processes "]; LLM -> Output [label=" Generates "]; }The agent takes a URL, fetches its content, processes it with an LLM, and produces a summary and list of topics.4. Executing the Agent and Observing OutputLet's test our agent with a sample URL.if __name__ == "__main__": # Example usage: # Replace with a URL of your choice, preferably an article. # Be mindful of website terms of service regarding scraping. test_url = "https://www.nature.com/articles/d41586-023-03913-3" # Example: A Nature news article print(f"Constructing agent to process: {test_url}") agent_output = process_url_with_agent(test_url) if "error" in agent_output: print(f"\nAgent Error: {agent_output['error']}") else: print("\nAgent Output:") print(f" Summary: {agent_output.get('summary', 'Not available')}") topics = agent_output.get('main_topics', []) if topics: print(" Main Topics:") for i, topic in enumerate(topics): print(f" {i+1}. {topic}") else: print(" Main Topics: Not available") When you run this script, replace test_url with a current news article or blog post. The agent will fetch the content, send it to the LLM with the specified persona and instructions, and print the resulting summary and topics.Example Output (will vary based on the URL and LLM's response):Processing URL: https://www.nature.com/articles/d41586-023-03913-3... Agent Output: Summary: The article discusses the challenges and advancements in detecting gravitational waves from merging supermassive black holes. While individual detections are difficult, projects like NANOGrav are using pulsar timing arrays to search for a background hum of these waves. Recent findings suggest a signal consistent with this background, though more data is needed for confirmation. Detecting these waves would provide insights into galaxy evolution and the formation of massive structures in the universe. Main Topics: 1. Gravitational Wave Detection 2. Supermassive Black Hole Mergers 3. Pulsar Timing Arrays (e.g., NANOGrav) 4. Galaxy Evolution 5. Cosmological Background RadiationDiscussion and Further DevelopmentThis hands-on exercise demonstrated the construction of a simple LLM agent with a defined role, function, and a basic tool (web fetching). We saw how system prompts can establish the agent's persona and guide its output format.Connections to Chapter Concepts:Agent Persona and Functional Specialization: Our agent has a clear "Research Assistant" persona and a specialized function (summarizing web content).Integrating External Tools: The fetch_web_content function acts as a simple, integrated tool. More sophisticated agents might use formal tool registration and calling mechanisms, as discussed in "Integrating External Tools and Functions for Agents."Knowledge Access: The agent accesses external knowledge by fetching web content.Potential Enhancements:Error Handling: The current error handling is basic. A production agent would need more error management, retries for network issues, and better handling of malformed web content.Content Extraction: Web content extraction could be improved using more advanced parsing libraries or techniques to better isolate the main article text from boilerplate (ads, navigation).Memory: For follow-up interactions or more complex tasks, this agent could be endowed with memory (short-term or long-term) to recall previous interactions or information, as covered in "Memory Mechanisms for LLM Agents."Advanced Tool Use: If the agent needed to perform multiple types of web interactions or use other APIs, a more formal tool-use framework (like those in LangChain or LlamaIndex, or custom-built function calling) would be beneficial.Scalability and Organization: This agent is standalone. In a multi-agent system, it could act as a worker, perhaps taking URLs from a dispatcher agent and returning results to an aggregator or analyst agent. This relates to the "Comparative Analysis of Agent Organization Models" and "Designing Agent Systems for Increased Capacity" sections.Dynamic Role Adaptation: While not implemented here, one could imagine this agent dynamically adjusting its summarization style or depth based on input parameters, hinting at "Strategies for Dynamic Role Assignment."This foundational agent serves as a building block. As you progress, you'll learn to combine multiple such specialized agents, orchestrate their interactions, and manage complex workflows to tackle more demanding problems. This exercise should give you a concrete understanding of how individual agent architecture contributes to the capabilities of a larger multi-agent system.