Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      June 1, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      June 1, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      June 1, 2025

      How To Prevent WordPress SQL Injection Attacks

      June 1, 2025

      My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

      June 1, 2025

      A week of hell with my Windows 11 PC really makes me appreciate the simplicity of Google’s Chromebook laptops

      June 1, 2025

      Elden Ring Nightreign Night Aspect: How to beat Heolstor the Nightlord, the final boss

      June 1, 2025

      New Xbox games launching this week, from June 2 through June 8 — Zenless Zone Zero finally comes to Xbox

      June 1, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Student Record Android App using SQLite

      June 1, 2025
      Recent

      Student Record Android App using SQLite

      June 1, 2025

      When Array uses less memory than Uint8Array (in V8)

      June 1, 2025

      Laravel 12 Starter Kits: Definite Guide Which to Choose

      June 1, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

      June 1, 2025
      Recent

      My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

      June 1, 2025

      A week of hell with my Windows 11 PC really makes me appreciate the simplicity of Google’s Chromebook laptops

      June 1, 2025

      Elden Ring Nightreign Night Aspect: How to beat Heolstor the Nightlord, the final boss

      June 1, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»Machine Learning»Creating an AI Agent-Based System with LangGraph: Putting a Human in the Loop

    Creating an AI Agent-Based System with LangGraph: Putting a Human in the Loop

    February 5, 2025

    In our previous tutorial, we built an AI agent capable of answering queries by surfing the web and added persistence to maintain state. However, in many scenarios, you may want to put a human in the loop to monitor and approve the agent’s actions. This can be easily accomplished with LangGraph. Let’s explore how this works.

    Setting Up the Agent

    We’ll continue from where we left off in the last lesson. First, set up the environment variables, make the necessary imports, and configure the checkpointer.

    Copy CodeCopiedUse a different Browser
    pip install langgraph==0.2.53 langgraph-checkpoint==2.0.6 langgraph-sdk==0.1.36 langchain-groq langchain-community langgraph-checkpoint-sqlite==2.0.1
    Copy CodeCopiedUse a different Browser
    import os
    os.environ['TAVILY_API_KEY'] = "<TAVILY_API_KEY>"
    os.environ['GROQ_API_KEY'] = "<GROQ_API_KEY>"
    Copy CodeCopiedUse a different Browser
    from langgraph.graph import StateGraph, END
    from typing import TypedDict, Annotated
    import operator
    from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage, ToolMessage, AIMessage
    from langchain_groq import ChatGroq
    from langchain_community.tools.tavily_search import TavilySearchResults
    
    from langgraph.checkpoint.sqlite import SqliteSaver
    import sqlite3
    sqlite_conn = sqlite3.connect("checkpoints.sqlite",check_same_thread=False)
    memory = SqliteSaver(sqlite_conn)
    
    # Initialize the search tool
    tool = TavilySearchResults(max_results=2)

    Defining the Agent

    Copy CodeCopiedUse a different Browser
    class Agent:
        def __init__(self, model, tools, checkpointer, system=""):
            self.system = system
            graph = StateGraph(AgentState)
            graph.add_node("llm", self.call_openai)
            graph.add_node("action", self.take_action)
            graph.add_conditional_edges("llm", self.exists_action, {True: "action", False: END})
            graph.add_edge("action", "llm")
            graph.set_entry_point("llm")
            self.graph = graph.compile(checkpointer=checkpointer)
            self.tools = {t.name: t for t in tools}
            self.model = model.bind_tools(tools)
    
        def call_openai(self, state: AgentState):
            messages = state['messages']
            if self.system:
                messages = [SystemMessage(content=self.system)] + messages
            message = self.model.invoke(messages)
            return {'messages': [message]}
    
        def exists_action(self, state: AgentState):
            result = state['messages'][-1]
            return len(result.tool_calls) > 0
    
        def take_action(self, state: AgentState):
            tool_calls = state['messages'][-1].tool_calls
            results = []
            for t in tool_calls:
                print(f"Calling: {t}")
                result = self.tools[t['name']].invoke(t['args'])
                results.append(ToolMessage(tool_call_id=t['id'], name=t['name'], content=str(result)))
            print("Back to the model!")
            return {'messages': results}

    Setting Up the Agent State

    We now configure the agent state with a slight modification. Previously, the messages list was annotated with operator.add, appending new messages to the existing array. For human-in-the-loop interactions, sometimes we also want to replace existing messages with the same ID rather than append them.

    Copy CodeCopiedUse a different Browser
    from uuid import uuid4
    
    def reduce_messages(left: list[AnyMessage], right: list[AnyMessage]) -> list[AnyMessage]:
        # Assign IDs to messages that don't have them
        for message in right:
            if not message.id:
                message.id = str(uuid4())
        # Merge the new messages with the existing ones
        merged = left.copy()
        for message in right:
            for i, existing in enumerate(merged):
                if existing.id == message.id:
                    merged[i] = message
                    break
            else:
                merged.append(message)
        return merged
    
    class AgentState(TypedDict):
        messages: Annotated[list[AnyMessage], reduce_messages]

    Adding a Human in the Loop

    We introduce an additional modification when compiling the graph. The interrupt_before=[“action”] parameter adds an interrupt before calling the action node, ensuring manual approval before executing tools.

    Copy CodeCopiedUse a different Browser
    class Agent:
        def __init__(self, model, tools, checkpointer, system=""):
            # Everything else remains the same as before
            self.graph = graph.compile(checkpointer=checkpointer, interrupt_before=["action"])
        # Everything else remains unchanged

    Running the Agent

    Now, we will initialize the system with the same prompt, model, and checkpointer as before. When we call the agent, we pass in the thread configuration with a thread ID.

    Copy CodeCopiedUse a different Browser
    prompt = """You are a smart research assistant. Use the search engine to look up information. 
    You are allowed to make multiple calls (either together or in sequence). 
    Only look up information when you are sure of what you want. 
    If you need to look up some information before asking a follow up question, you are allowed to do that!
    """
    model = ChatGroq(model="Llama-3.3-70b-Specdec")
    abot = Agent(model, [tool], system=prompt, checkpointer=memory)
    messages = [HumanMessage(content="Whats the weather in SF?")]
    thread = {"configurable": {"thread_id": "1"}}
    for event in abot.graph.stream({"messages": messages}, thread):
        for v in event.values():
            print(v)

    Responses are streamed back, and the process stops after the AI message, which indicates a tool call. However, the interrupt_before parameter prevents immediate execution. We can also get the current state of the graph for this thread and see what it contains and it also contains what’s the next node to be called (‘action’ here).

    Hostinger
    Copy CodeCopiedUse a different Browser
    abot.graph.get_state(thread)
    abot.graph.get_state(thread).next

    To proceed, we call the stream again with the same thread configuration, passing None as input. This streams back results, including the tool message and final AI message. Since no interrupt was added between the action node and the LLM node, execution continues seamlessly.

    Copy CodeCopiedUse a different Browser
    for event in abot.graph.stream(None, thread):
        for v in event.values():
            print(v)

    Interactive Human Approval

    We can implement a simple loop prompting the user for approval before continuing execution. A new thread ID is used for fresh execution. If the user chooses not to proceed, the agent stops.

    Copy CodeCopiedUse a different Browser
    messages = [HumanMessage("What's the weather in LA?")]
    thread = {"configurable": {"thread_id": "2"}}
    
    for event in abot.graph.stream({"messages": messages}, thread):
        for v in event.values():
            print(v)
    
    while abot.graph.get_state(thread).next:
        print("n", abot.graph.get_state(thread), "n")
        _input = input("Proceed? (y/n): ")
        if _input.lower() != "y":
            print("Aborting")
            break
        for event in abot.graph.stream(None, thread):
            for v in event.values():
                print(v)

    Great! Now you know how you can involve a human in the loop. Now, try experimenting with different interruptions and see how the agent behaves.

    References: DeepLearning.ai (https://learn.deeplearning.ai/courses/ai-agents-in-langgraph/lesson/6/human-in-the-loop)

    The post Creating an AI Agent-Based System with LangGraph: Putting a Human in the Loop appeared first on MarkTechPost.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleMeta AI Introduces VideoJAM: A Novel AI Framework that Enhances Motion Coherence in AI-Generated Videos
    Next Article Meet Crossfire: An Elastic Defense Framework for Graph Neural Networks under Bit Flip Attacks

    Related Posts

    Machine Learning

    How to Evaluate Jailbreak Methods: A Case Study with the StrongREJECT Benchmark

    June 1, 2025
    Machine Learning

    Enigmata’s Multi-Stage and Mix-Training Reinforcement Learning Recipe Drives Breakthrough Performance in LLM Puzzle Reasoning

    June 1, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    Performance Testing Tool Selection For Desktop Based Application

    Development

    How to Hire an IoT Architect

    Development

    How eBay built a bridge between brand and product with Figma

    Web Development

    The AI Fix #10: An AI cookery dumpster fire, the ARC prize, and a creepy new AI friend

    Development
    Hostinger

    Highlights

    CVE-2025-27523 – Hitachi JP1/IT Desktop Management 2 – Smart Device Manager XXE Injection Vulnerability

    May 15, 2025

    CVE ID : CVE-2025-27523

    Published : May 15, 2025, 7:15 a.m. | 1 hour, 31 minutes ago

    Description : XXE vulnerability in Hitachi JP1/IT Desktop Management 2 – Smart Device Manager on Windows.This issue affects JP1/IT Desktop Management 2 – Smart Device Manager: from 12-00 before 12-00-08, from 11-10 through 11-10-08, from 11-00 through 11-00-05, from 10-50 through 10-50-06.

    Severity: 8.7 | HIGH

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    Discover insights with the Amazon Q Business Microsoft Teams connector

    November 11, 2024
    I tested Razer’s Iskur V2 gaming chair, and it’s just too hard for comfort — but the lumbar support can’t be beat

    I tested Razer’s Iskur V2 gaming chair, and it’s just too hard for comfort — but the lumbar support can’t be beat

    April 9, 2025

    Your Android phone just got a huge audio upgrade for free – including Google and Samsung

    March 17, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.