Skip to content

Siddhantdamre/Basic-Agentic-App-Demo

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Basic Agentic App Demo: A Beginner's Guide to Full-Stack AI

Welcome! This project is a comprehensive, hands-on guide designed for students and beginners. It walks you through the fundamentals of application development, from building a simple frontend and backend to integrating powerful Large Language Models (LLMs) and creating a sophisticated AI agent with LangGraph.

Think of this as more than just a project; it's a complete, self-contained course that will take you from zero to building your first AI-powered web application.

πŸ“š Table of Contents

  1. What is This Project?
  2. Core Concepts Explained (The Knowledge Base)
  3. Project Structure
  4. Setup and Installation (Let's Get Building!)
  5. How to Run the Application
  6. Exploring the Application (A Guided Tour)
  7. Code Deep Dive
  8. Conclusion and Next Steps

πŸš€ What is This Project?

This project is a simple web-based "Items Manager" application. You can add, view, update, and delete items. But it's much more than that. It's a playground for learning how modern applications are built and how AI can be integrated into them.

It demonstrates:

  • A frontend built with HTML, CSS, and JavaScript that you can interact with in your browser.
  • A backend API built with Python and Flask that manages the data and business logic.
  • API communication that allows the frontend to talk to the backend.
  • AI-powered categorization using a Large Language Model (LLM) via LangChain.
  • A fully-featured AI Agent built with LangGraph that can use "tools" (like a calculator or a random fact generator) to answer your questions.

🧠 Core Concepts Explained (The Knowledge Base)

This section is the heart of the documentation. We'll break down every core concept from scratch.

1. What is a Frontend? (The User Interface)

The frontend is what you, the user, see and interact with in your browser. It's the visual part of the web applicationβ€”the buttons, text, forms, and layout.

  • Core Technologies:
    • HTML (HyperText Markup Language): The skeleton of the page. It defines the structure and content, like headings, paragraphs, and input fields. In our project, index.html is the HTML file.
    • CSS (Cascading Style Sheets): The styling. It controls the colors, fonts, spacing, and layout, making the application look good. In our project, the CSS is inside the <style> tag in index.html.
    • JavaScript: The interactivity. It makes the application dynamic. When you click a button, submit a form, or fetch data, JavaScript is what makes it happen. In our project, the JavaScript is inside the <script> tag in index.html.

In this project: index.html is our frontend. It provides the user interface for managing items and interacting with the AI agent.

2. What is a Backend? (The Brains of the Operation)

The backend is the server-side of the application. It's the part you don't see. It runs on a server and is responsible for:

  • Storing and managing data (like our list of items).
  • Executing business logic (like creating a new item or categorizing a description).
  • Responding to requests from the frontend.
  • Security and authentication.

An API (Application Programming Interface) is a key part of the backend. It's a set of rules and endpoints that the frontend uses to communicate with the backend. For example, the frontend can send a request to the /api/items endpoint to get a list of all items.

In this project: app.py is our backend. It's a Python script using the Flask framework to create a web server and define our API endpoints.

3. How Frontend and Backend Communicate (API Calls)

The frontend and backend are separate, but they need to talk to each other. This communication happens through API calls, which are essentially HTTP requests.

  • The Process:
    1. User Action: The user clicks a button on the frontend (e.g., "Refresh List").
    2. JavaScript Request: The frontend's JavaScript code uses the fetch() function to send an HTTP request to a specific URL on the backend (e.g., http://localhost:5001/api/items).
    3. Backend Processing: The backend server (our Flask app) receives the request, finds the matching route (@app.route('/api/items')), and executes the corresponding Python function (get_items()).
    4. Backend Response: The backend function gathers the data (the list of items) and sends it back to the frontend, usually in a format called JSON (JavaScript Object Notation).
    5. Frontend Update: The frontend's JavaScript receives the JSON data and uses it to update the HTML, displaying the new list of items to the user.

This request-response cycle is the foundation of all modern web applications.

4. What are Large Language Models (LLMs)?

LLMs are a type of artificial intelligence trained on vast amounts of text data. They are incredibly good at understanding and generating human-like text. You've probably heard of models like GPT-4 or Google's Gemini.

They can perform a wide range of tasks, including:

  • Answering questions.
  • Writing essays and code.
  • Translating languages.
  • Summarizing text.
  • Categorizing information (which is what we do in this project!).

5. What is LangChain? (Giving LLMs Superpowers)

While LLMs are powerful, they are just text-in, text-out systems. LangChain is a framework that makes it easier to build applications powered by LLMs. It provides tools and abstractions to:

  • Connect to different LLM providers (like Google, OpenAI, etc.).
  • Manage prompts using templates.
  • Chain multiple LLM calls together to perform more complex tasks.
  • Integrate external tools that the LLM can use.

In this project: We use langchain-google-genai to connect to the Gemini LLM. We create a categorization_chain that combines a prompt template with the LLM to create a simple, reusable AI tool for categorizing item descriptions.

6. What is an AI Agent? (An Autonomous Problem-Solver)

An AI Agent is a more advanced application of an LLM. Instead of just responding to a prompt, an agent can:

  1. Reason: Decide what steps to take to accomplish a goal.
  2. Use Tools: Access external tools (like a calculator, a database, or another API) to get information it doesn't have.
  3. Observe: Take the results from the tools.
  4. Iterate: Repeat the process until it has a final answer.

For example, if you ask an agent, "What is the sum of the first 5 prime numbers?", it might:

  1. Decide: "I need to find the first 5 prime numbers and then add them. I don't know them offhand, so I need a way to calculate or find them."
  2. Decide: "I don't have a 'prime number' tool, but I can use my reasoning. The first few primes are 2, 3, 5, 7, 11."
  3. Decide: "Now I need to add them. I'll use the calculator tool."
  4. Use Tool: Call the calculator tool with the expression "2 + 3 + 5 + 7 + 11".
  5. Observe: The tool returns "28".
  6. Respond: "The sum of the first 5 prime numbers is 28."

7. What is LangGraph? (Building AI Agents)

LangGraph is a library built on top of LangChain that is specifically designed for creating robust, stateful AI agents. It allows you to define the agent's logic as a graph, which is a collection of nodes and edges.

  • Nodes: These are the steps in the agent's workflow. A node can be a function that calls the LLM, or a function that executes a tool.
  • Edges: These are the connections between the nodes. They define the path the agent takes. An edge can be conditional, meaning the path changes based on the current state (e.g., "if the LLM decided to use a tool, go to the 'tools' node, otherwise, end").
  • State: LangGraph agents are stateful. This means they have a memory that is passed between each node. In our project, the state is the history of messages in the conversation.

In this project: We use LangGraph to build our AI agent.

  • AgentState: Defines the structure of our agent's memory.
  • call_model node: Calls the LLM to decide what to do next.
  • ToolNode: A special node that automatically executes any tools the LLM decides to use.
  • should_continue edge: A conditional edge that checks if the LLM's last response included a tool call.

This graph structure makes the agent's logic clear, predictable, and easy to debug.

πŸ“Š The LangGraph Agent Flow (Visual Explanation)

Here's how our agent works as a graph:

                    START
                      ↓
                 [User Query]
                      ↓
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚  AGENT NODE   β”‚ ← (Entry Point)
              β”‚  (call_model) β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                      ↓
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚ CONDITIONAL   β”‚ (should_continue function)
              β”‚   DECISION    β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                      ↓
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
              β”‚               β”‚
        Tool Calls?      No Tool Calls?
              β”‚               β”‚
              ↓               ↓
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    [END]
      β”‚  TOOLS NODE  β”‚    Return final
      β”‚ (ToolNode)   β”‚    response to user
      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             β”‚ (Execute all tools)
             β”‚
             ↓
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚  AGENT NODE  β”‚ ← (Loop back)
      β”‚ (call_model) β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
        (Repeat until
         no more tools
         are needed)

πŸ”„ Understanding the Agent Loop

The agent follows a think β†’ act β†’ observe β†’ think cycle:

  1. THINK (Agent Node - First Call):

    • The LLM receives your question
    • It analyzes what information or capabilities it needs
    • It decides: "Can I answer directly, or do I need to use a tool?"
  2. ACT (Tools Node - If Needed):

    • If the LLM decides it needs a tool, it creates a "tool call"
    • The ToolNode receives this and executes the appropriate function
    • Example: If you ask "What's 5 * 8?", it calls the calculator tool
  3. OBSERVE (Tools Node β†’ Agent Node):

    • The tool returns its result (e.g., "The result of 5 * 8 is 40")
    • This result is added to the conversation history as a ToolMessage
    • The agent loops back to the Agent Node
  4. THINK AGAIN (Agent Node - Second Call):

    • The LLM now sees the tool's result
    • It synthesizes a final answer for the user
    • It creates a response with no more tool calls, which signals the end
  5. END:

    • The final answer is returned to the user through the API

πŸ“ Project Structure

Here's a breakdown of the files in this project:

  • app.py: The Python backend server built with Flask. It contains all the API logic, the LangChain setup, and the LangGraph agent.
  • index.html: The single HTML file that acts as our frontend. It includes all the necessary HTML, CSS, and JavaScript for the user interface.
  • requirements.txt: A list of all the Python packages this project needs to run.
  • .env (You will create this): A file to store your secret API key.

πŸ› οΈ Setup and Installation (Let's Get Building!)

Follow these steps to get the project running on your local machine.

Prerequisites

  • Python: Make sure you have Python 3.8 or newer installed. You can download it from python.org.
  • Google AI API Key: This project uses Google's Gemini model. You'll need an API key.
    • Go to Google AI Studio.
    • Sign in with your Google account.
    • Click "Get API key" and create a new API key in a new project.
    • Important: Keep this key safe and private!

Step-by-Step Guide

  1. Clone the Repository Open your terminal or command prompt and run:

    git clone https://github.com/pranaydeep139/Basic-Agentic-App-Demo.git
    cd Basic-Agentic-App-Demo
  2. Create a Virtual Environment It's a best practice to create a virtual environment for each Python project to keep dependencies separate.

    # For macOS/Linux
    python3 -m venv venv
    source venv/bin/activate
    
    # For Windows
    python -m venv venv
    .\venv\Scripts\activate

    You should see (venv) at the beginning of your terminal prompt.

  3. Install Dependencies Install all the required Python packages from the requirements.txt file.

    pip install -r requirements.txt
  4. Create the .env File Create a new file in the project directory named .env. Open this file and add your Google API key like this:

    GEMINI_API_KEY="YOUR_API_KEY_HERE"
    

    Replace "YOUR_API_KEY_HERE" with the actual key you got from Google AI Studio.

You're all set up!


▢️ How to Run the Application

  1. Start the Backend Server In your terminal (make sure your virtual environment is still active), run the Flask application:

    python app.py

    You should see output indicating that the server is running, usually on http://127.0.0.1:5001.

  2. Open the Frontend This project uses a simple setup where you open the HTML file directly in your browser.

    • Navigate to the project folder in your file explorer.
    • Right-click on index.html and choose "Open with" your favorite web browser (like Chrome, Firefox, or Edge).

You should now see the "Items Manager with AI Agent" application in your browser!


πŸ—ΊοΈ Exploring the Application (A Guided Tour)

The UI is divided into four main sections:

  1. AI Agent with Tools

    • This is the most powerful part of the app. You can ask the agent complex questions.
    • Try these queries:
      • What is 123 * 456? (It will use the calculator tool).
      • Tell me a random fact. (It will use the random_fact_generator tool).
      • What time is it? (It will use the current_datetime tool).
      • Reverse the text 'hello world' (It will use the reverse_text tool).
    • Notice the chat history that appears, showing your message and the agent's response.
  2. Test Tools Directly

    • This section lets you bypass the agent and trigger each tool directly. It's great for understanding what each tool does on its own.
    • Click any of the buttons to see the raw output from that tool.
  3. Manage Items

    • This is the basic CRUD (Create, Read, Update, Delete) part of the application.
    • You can add new items, and they will appear in the list below.
    • Click "Refresh List" to load all items.
    • Use the "Update" and "Delete" buttons to manage existing items.
  4. AI Categorization Tool

    • This section demonstrates a simpler AI integration using a LangChain chain.
    • Enter a description for an item (e.g., "A delicious, freshly baked apple pie").
    • Click "Get AI Category Suggestion". The backend will call the LLM to suggest a category (e.g., "Food").
    • You can then click "Apply to New Item" to copy the suggested category into the "Manage Items" form.

πŸ’» Code Deep Dive

Let's look at the key parts of the code.

app.py (The Backend)

1. Flask Setup & CORS

app = Flask(__name__)
CORS(app) # Allows the frontend (on a different origin) to talk to the backend.

Why CORS? When your HTML file is opened directly in the browser (file://...), it's a different "origin" than your Flask server (http://localhost:5001). Browsers block cross-origin requests by default for security. CORS (Cross-Origin Resource Sharing) tells the browser it's okay for our frontend to talk to our backend.

2. API Endpoints (e.g., Get Items)

@app.route('/api/items', methods=['GET'])
def get_items():
    return jsonify({"items": items})

This defines an API endpoint. When the frontend sends a GET request to /api/items, this function runs and returns the items list as JSON. The @app.route decorator is how Flask knows which function to call for which URL.

3. LangChain Categorization Chain

categorization_prompt = ChatPromptTemplate.from_messages([...])
categorization_chain = categorization_prompt | llm | StrOutputParser()

This is a simple chain. The | (pipe) operator sends the output of one component as the input to the next. So, the user's description goes into the prompt, the prompt goes to the LLM, and the LLM's output is parsed into a simple string.

Flow: User Input β†’ Prompt Template β†’ LLM β†’ String Parser β†’ Result

4. Defining a Tool

@tool
def calculator(expression: str) -> str:
    """Evaluates a mathematical expression. Example: '2 + 2' returns '4'"""
    # ... implementation ...

The @tool decorator from LangChain is what turns a regular Python function into a tool that the AI agent can use.

Important: The function's docstring is critical! The LLM reads this to understand what the tool does and when to use it. A clear, descriptive docstring helps the LLM make better decisions.

5. LangGraph Agent State

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]

This defines the "memory" of our agent. The messages list stores the entire conversation history:

  • HumanMessage: Messages from the user
  • AIMessage: Messages from the LLM (including tool call decisions)
  • ToolMessage: Results from tool executions

The Annotated[list, operator.add] means when nodes return new messages, they are added to the existing list (not replaced).

6. The Agent Node (call_model function)

def call_model(state: AgentState):
    messages = state["messages"]
    
    # Special handling: If the last message is a tool result, prompt for final answer
    if isinstance(messages[-1], ToolMessage):
        messages_with_prompt = messages + [
            HumanMessage(content="Based on the tool results above, please provide a clear and complete answer to my original question.")
        ]
        response = llm_with_tools.invoke(messages_with_prompt)
    else:
        response = llm_with_tools.invoke(messages)
    
    return {"messages": [response]}

What this does:

  1. Takes the current conversation state (all messages)
  2. Checks if we just received a tool result
  3. If yes, adds a prompt asking the LLM to synthesize a final answer
  4. Calls the LLM with the full conversation history
  5. Returns the LLM's response, which gets added to the state

Why the special handling? Some LLMs don't automatically provide a final answer after using tools. This ensures we always get a complete response for the user.

7. The Conditional Edge (should_continue function)

def should_continue(state: AgentState) -> Literal["tools", "end"]:
    messages = state["messages"]
    last_message = messages[-1]
    
    if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
        return "tools"  # Go to tools node
    
    return "end"  # End the graph and return result

What this does:

  • Examines the last message in the state
  • If it's an AIMessage with tool calls β†’ route to "tools" node
  • If it's an AIMessage without tool calls β†’ route to "end" (we're done!)

This is the decision point that determines whether the agent loop continues or ends.

8. Building the LangGraph Workflow

# Define the state
workflow = StateGraph(AgentState)

# Add nodes
workflow.add_node("agent", call_model)
workflow.add_node("tools", ToolNode(tools))

# Set entry point
workflow.set_entry_point("agent")

# Add conditional edges
workflow.add_conditional_edges(
    "agent",
    should_continue,
    {
        "tools": "tools",
        "end": END
    }
)

# Add edge from tools back to agent
workflow.add_edge("tools", "agent")

# Compile the graph
agent_executor = workflow.compile()

Step-by-step construction:

  1. Create a StateGraph with our AgentState structure
  2. Add the "agent" node (calls the LLM)
  3. Add the "tools" node (executes tools)
  4. Set "agent" as the starting point
  5. Add a conditional edge from "agent" that routes based on should_continue's return value
  6. Add a fixed edge from "tools" back to "agent" (always loop back after tool execution)
  7. Compile the graph into an executable agent

9. Agent Execution in the API Endpoint

@app.route('/api/agent/query', methods=['POST'])
def agent_query():
    user_query = data.get("query")
    
    # Create initial state
    initial_state = {
        "messages": [HumanMessage(content=user_query)]
    }
    
    # Run the agent
    result = agent_executor.invoke(initial_state)
    
    # Extract final response from the last AIMessage with content
    messages = result.get("messages", [])
    for msg in reversed(messages):
        if isinstance(msg, AIMessage) and msg.content:
            final_response = msg.content
            break
    
    return jsonify({"query": user_query, "response": final_response})

The complete flow:

  1. Receive user query from frontend
  2. Create initial state with user's message
  3. Invoke the agent (this runs the entire graph until it reaches END)
  4. Extract the final response from the conversation history
  5. Return it to the frontend as JSON

🎯 Real Example: Tracing a Query Through the Agent

Let's trace what happens when a user asks: "What is 25 * 4?"

Step 1: Initial State

{
    "messages": [
        HumanMessage(content="What is 25 * 4?")
    ]
}

Step 2: First Call to Agent Node

  • LLM receives: "What is 25 * 4?"
  • LLM thinks: "I need to calculate this. I have a calculator tool!"
  • LLM returns: AIMessage(tool_calls=[{name: "calculator", args: {expression: "25 * 4"}}])
  • State now:
{
    "messages": [
        HumanMessage(content="What is 25 * 4?"),
        AIMessage(tool_calls=[...], content="")
    ]
}

Step 3: Conditional Edge Decision

  • should_continue checks last message
  • Finds tool calls β†’ returns "tools"
  • Graph routes to Tools Node

Step 4: Tools Node Execution

  • ToolNode sees the calculator tool call
  • Executes: calculator("25 * 4")
  • Tool returns: "The result of 25 * 4 is 100"
  • ToolNode adds: ToolMessage(content="The result of 25 * 4 is 100")
  • State now:
{
    "messages": [
        HumanMessage(content="What is 25 * 4?"),
        AIMessage(tool_calls=[...], content=""),
        ToolMessage(content="The result of 25 * 4 is 100")
    ]
}

Step 5: Back to Agent Node (Loop)

  • Graph automatically routes back to agent node
  • call_model detects last message is ToolMessage
  • Adds prompt: "Based on the tool results above, please provide a clear and complete answer..."
  • LLM sees: Original question + Tool call + Tool result + Prompt for final answer
  • LLM returns: AIMessage(content="The result of 25 * 4 is 100.", tool_calls=[])
  • State now:
{
    "messages": [
        HumanMessage(content="What is 25 * 4?"),
        AIMessage(tool_calls=[...], content=""),
        ToolMessage(content="The result of 25 * 4 is 100"),
        AIMessage(content="The result of 25 * 4 is 100.", tool_calls=[])
    ]
}

Step 6: Conditional Edge Decision (Final)

  • should_continue checks last message
  • No tool calls found β†’ returns "end"
  • Graph terminates

Step 7: Response Extraction

  • Backend finds last AIMessage with content: "The result of 25 * 4 is 100."
  • Returns this to the frontend
  • User sees the answer! πŸŽ‰

index.html (The Frontend)

  • API URLs:

    const API_URL = 'http://localhost:5001/api/items';
    const AGENT_API_URL = 'http://localhost:5001/api/agent/query';

    These constants define the backend endpoints that the JavaScript will call.

  • Fetching Data (e.g., Loading Items):

    async function loadItems() {
        const response = await fetch(API_URL); // Send GET request
        const data = await response.json();   // Parse JSON response
        displayItems(data.items);             // Update the UI
    }

    This is a classic example of an asynchronous fetch call to get data from the backend.

  • Sending Data (e.g., Querying the Agent):

    async function queryAgent() {
        const query = document.getElementById('agentQuery').value;
    
        const response = await fetch(AGENT_API_URL, {
            method: 'POST', // Use POST to send data
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ query: query }) // The data we're sending
        });
    
        const data = await response.json();
        // ... update UI with agent's response ...
    }

    This function shows how to send data to the backend. We use the POST method and include the user's query in the body of the request, formatted as a JSON string.


πŸ” Understanding the Backend Logs (Debugging Your Agent)

When you run the Flask app, you'll see detailed logs in your terminal. Understanding these logs is crucial for debugging and learning how the agent works. Let's break down what you'll see:

Application Startup Logs

=== Building LangGraph Workflow ===
Workflow nodes added: agent, tools
Entry point set: agent
Conditional edges added: agent -> tools/end
Edge added: tools -> agent
=== LangGraph Workflow Compiled Successfully ===

=== Flask Application Starting ===
Google API Key configured: True
Number of available tools: 7
Tool names: ['calculator', 'random_fact_generator', ...]

What this means: The application is initializing. The LangGraph is being constructed with its nodes and edges, and Flask is confirming your API key is configured and listing all available tools.

Agent Query Logs (Example: "What is 25 * 4?")

1. Request Reception

=== AGENT_QUERY: New request received ===
AGENT_QUERY: User query: 'What is 25 * 4?'
AGENT_QUERY: Creating initial state...
AGENT_QUERY: Initial state created with 1 message(s)
AGENT_QUERY: Invoking agent executor...

Your query has arrived at the backend and the agent is starting to process it.

2. First Agent Node Call (Decision Making)

=== CALL_MODEL: Starting ===
CALL_MODEL: Number of messages in state: 1
CALL_MODEL: Last message type: HumanMessage
CALL_MODEL: Response type: AIMessage
CALL_MODEL: Response has tool_calls: True
CALL_MODEL: Number of tool calls: 1
CALL_MODEL: Tool call 1: calculator
=== CALL_MODEL: Complete ===

The LLM analyzed your query and decided to use the calculator tool. Notice tool_calls: True.

3. Conditional Routing Decision

=== SHOULD_CONTINUE: Starting ===
SHOULD_CONTINUE: Total messages: 2
SHOULD_CONTINUE: Tool calls detected: 1
SHOULD_CONTINUE: Decision -> 'tools'
=== SHOULD_CONTINUE: Complete ===

The conditional edge detected tool calls and routed to the tools node.

4. Second Agent Node Call (Synthesizing Answer)

=== CALL_MODEL: Starting ===
CALL_MODEL: Number of messages in state: 3
CALL_MODEL: Last message type: ToolMessage
CALL_MODEL: Response type: AIMessage
CALL_MODEL: Response has tool_calls: False
CALL_MODEL: Response content preview: The result of 25 * 4 is 100.
=== CALL_MODEL: Complete ===

The LLM has received the tool result and is now generating a final answer. Notice the state has grown to 3 messages, and tool_calls: False (no more tools needed).

5. Final Routing Decision

=== SHOULD_CONTINUE: Starting ===
SHOULD_CONTINUE: No tool calls detected
SHOULD_CONTINUE: Decision -> 'end'
=== SHOULD_CONTINUE: Complete ===

No tool calls detected, so the agent terminates.

6. Response Extraction

AGENT_QUERY: Agent executor completed
AGENT_QUERY: Total messages in result: 4
AGENT_QUERY: Examining message 4 (type: AIMessage)
AGENT_QUERY: Found final AI response: 'The result of 25 * 4 is 100.'
AGENT_QUERY: Returning response to client

The backend found the final answer and is sending it back to the frontend.

🚨 Common Issues and What to Look For

Problem: "WARNING: No final response found in messages"

What it means: The agent completed but didn't generate a final text response.

Common causes:

  • The LLM returned an empty AIMessage after tool execution
  • The fix we implemented (adding a prompt for final answer) should prevent this

Look for: Check if the last CALL_MODEL shows Response content preview: No content

Problem: Agent loops infinitely

What it means: The agent keeps calling tools and never reaches END.

Common causes:

  • The LLM keeps deciding it needs more tools
  • A tool is returning unclear or incomplete results

Look for: Multiple SHOULD_CONTINUE: Decision -> 'tools' without ever reaching Decision -> 'end'

Problem: Tool execution errors

What it means: A tool failed during execution.

Look for:

ERROR: Tool execution failed: [error message]

Check the tool's implementation and the arguments the LLM provided.


πŸŽ‰ Conclusion and Next Steps

Congratulations! You've just set up and explored a full-stack application with a sophisticated AI agent. You've learned about frontends, backends, APIs, LLMs, LangChain, and LangGraph.

What You've Accomplished

βœ… Built and run a complete web application with frontend and backend
βœ… Understood HTTP requests and API communication
βœ… Integrated a Large Language Model for AI-powered features
βœ… Created a stateful AI agent using LangGraph
βœ… Implemented multiple tools that the agent can use autonomously
βœ… Learned to debug and trace agent execution through logs

πŸš€ Ideas for Expansion

This project is a starting point. Here are some ideas for how you can expand on it:

Beginner Level

  • Add More Tools: Create new Python functions with the @tool decorator. Ideas:
    • A tool that can search the web
    • A tool that converts units (temperature, distance, etc.)
    • A tool that generates random names or passwords
  • Improve the UI: Add better styling, animations, or a dark mode
  • Add More Item Categories: Expand the categorization options

Intermediate Level

  • Persistent Storage: The current item list is in-memory and resets when the server restarts. Try modifying the backend to store items in a database like SQLite or PostgreSQL.
  • Give the Agent Access to Items: Create tools that allow the agent to interact with the items list:
    @tool
    def list_items() -> str:
        """Returns a list of all items in the database."""
        # Implementation
    
    @tool
    def find_item_by_id(item_id: int) -> str:
        """Finds and returns details of an item by its ID."""
        # Implementation
    Then you could ask the agent: "How many items are in the list?" or "What is the description of item 2?"
  • User Authentication: Add login/signup functionality so different users can have their own item lists
  • Streaming Responses: Implement streaming so you can see the agent's response appear word-by-word in real-time

Advanced Level

  • Multi-Step Reasoning: Create complex tasks that require the agent to use multiple tools in sequence
  • Memory Across Sessions: Implement persistent conversation history so the agent remembers previous interactions
  • Custom LangGraph Nodes: Add specialized nodes for specific workflows (e.g., a "validation" node that checks tool outputs)
  • Deploy Your App: Learn to deploy your application to a cloud platform like Heroku, AWS, or Google Cloud
  • Use Different LLMs: Experiment with OpenAI's GPT-4, Anthropic's Claude, or open-source models like Llama

πŸ“š Further Learning Resources


❓ Frequently Asked Questions (FAQ)

Q: Why do I get a "CORS error" in my browser console?

A: Make sure you have flask_cors installed and CORS(app) in your app.py. The backend needs to explicitly allow the frontend to make requests from a different origin.

Q: The agent isn't using my tool. Why?

A: Check your tool's docstring. The LLM uses this to understand what the tool does. Make it clear and descriptive. Also, ensure your question actually requires that tool.

Q: Can I use a different LLM besides Gemini?

A: Yes! LangChain supports many LLM providers. For example:

# OpenAI
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4", api_key=your_openai_key)

# Anthropic
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-3-sonnet-20240229", api_key=your_anthropic_key)

Q: How do I add a tool that requires API calls (like weather data)?

A: Just use Python's requests library inside your tool function:

import requests

@tool
def get_weather(city: str) -> str:
    """Gets the current weather for a given city."""
    api_key = "your_weather_api_key"
    response = requests.get(f"https://api.weatherapi.com/v1/current.json?key={api_key}&q={city}")
    data = response.json()
    return f"Weather in {city}: {data['current']['condition']['text']}, {data['current']['temp_c']}Β°C"

Q: What if my agent gets stuck in a loop?

A: You can add a maximum iteration limit in LangGraph or add logic to detect loops. Also, review your tool docstrings to ensure they're clear about what each tool does.

Q: How much does it cost to use the Gemini API?

A: Google offers a free tier for Gemini with generous limits. Check ai.google.dev/pricing for current pricing.

Q: Can I use this project structure for a production app?

A: This is a learning project with in-memory storage and simple security. For production, you'd need:

  • A real database (PostgreSQL, MongoDB, etc.)
  • Proper authentication and authorization
  • Input validation and sanitization
  • Error handling and logging
  • Environment-specific configurations
  • HTTPS/SSL certificates
  • Rate limiting and security headers

Q: My agent response is slow. How can I speed it up?

A:

  • Use a faster/smaller model (like gemini-1.5-flash instead of larger models)
  • Reduce the temperature parameter for more deterministic, faster responses
  • Implement caching for repeated queries
  • Consider streaming responses for better perceived performance

🀝 Contributing and Support

This is an educational project! Feel free to:

  • Fork it and experiment
  • Share your improvements
  • Ask questions by opening issues on GitHub
  • Use it as a base for your own projects

Remember: The best way to learn is by building and breaking things. Don't be afraid to modify the code and see what happens!

Happy coding! 🎊

About

Forked agentic app demo for learning CRUD workflows and AI-assisted full-stack application patterns.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • HTML 61.2%
  • Python 38.8%