Issue 05 | Initial Results: Getting the "Research-Writing" Closed Loop Running

Updated on 4/13/2026

🎯 Learning Objectives for this Issue

Hey, future AI architects, welcome back to our LangGraph Masterclass! Last time we discussed the philosophy of LangGraph, and now it's time to turn those theories into actual code. Don't rush to build complex decision trees just yet; let's start with the most fundamental, yet crucial step: getting the MVP version of our AI Content Agency running!

In this issue, we will:

  • Master the core mechanism of LangGraph's sequential execution flow: Understand how add_node and add_edge weave together a fixed workflow.
  • Successfully integrate the "Researcher" and "Writer" Agents into the LangGraph workflow: They are no longer independent scripts, but integral parts of our agency system.
  • Learn to define and manage Graph State: Ensure information flows smoothly and accurately between different Agents.
  • Successfully run the "Research-Writing" MVP loop of the "AI Content Agency": Witness the birth of your first content creation workflow firsthand!

📖 Understanding the Concepts

Why Start with a Fixed Sequence? — The Triumph of MVP Thinking

In software engineering, we always emphasize the concept of MVP (Minimum Viable Product). What is the MVP for our AI Content Agency? It is the minimum loop capable of completing a "from topic to content" process. The most direct way is to have a researcher investigate first, and then hand over the research results to a writer to draft the content. In between, there are no complex decisions, no retries, no branches—just a straight line.

Why do it this way?

  1. Reduce Complexity: The most common mistake beginners make is trying to cram all the logic in from the start. LangGraph is undoubtedly powerful, but if you can't even handle the simplest sequential flow, how can you tackle complex conditional branches? Getting it running first to validate core functionality is the golden rule.
  2. Rapid Validation: Through a fixed sequence, we can quickly verify whether each Agent functions correctly and whether data transfer between them is smooth. If an issue arises, troubleshooting is much simpler.
  3. Lay the Foundation: Sequential flow is the foundation of all complex graphs. Once you understand the essence of add_node and add_edge, introducing add_conditional_edges in the future will come naturally. It's like building a house; if the foundation isn't solid, the most magnificent superstructure is just a castle in the air.

The Basic Building Blocks of LangGraph: Node, Edge, State

Remember the core of LangGraph we mentioned last time? It is a directed graph composed of Nodes and Edges.

  • Node: In our "AI Content Agency", each Agent (like Researcher, Writer) is a node. A node receives input, executes a task, and then outputs a result. This "task execution" can be calling an LLM, executing a tool, or even just a simple Python function.
  • Edge: Edges define the direction of flow between nodes. Going from Node A to Node B means that after Node A completes its task, its output will serve as Node B's input (or rather, the state will be updated, and then B reads the state).
  • State: This is the soul of LangGraph! All nodes in the Graph share a mutable state. When a node completes a task, it updates this shared state. The next node will read the information it needs from this updated state. This state-passing mechanism is the foundation of multi-Agent collaboration.

In this issue, we will primarily use add_node to register our Agents and add_edge to define the fixed sequential flow between them.

Mermaid Diagram: The Initial "Research-Writing" Workflow

This is the core workflow we are going to build in this issue. It is very concise and intuitive:

graph TD
    A[Start] --> B(Researcher);
    B --> C(Writer);
    C --> D[End];

This diagram clearly shows:

  1. Start: The starting point of the workflow, where we pass in the initial topic.
  2. Researcher: Receives the topic, conducts research, and updates the research results to the shared state.
  3. Writer: Reads the research results from the shared state, drafts the content, and updates the draft to the shared state.
  4. End: The endpoint of the workflow, where we can retrieve the written content from the final state.

Simple, isn't it? But it is exactly this simplicity that forms the cornerstone of complex systems.

💻 Practical Code Walkthrough

Alright, enough theory, let's roll up our sleeves and get to work! We will use Python to implement this "Research-Writing" loop.

import operator
from typing import Annotated, List, Tuple, TypedDict
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_core.runnables import Runnable
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END

# =============================================================================
# 1. Define the shared state of our AI Content Agency (Agency State)
#    This is the data shared and updated by all nodes during the execution of the Graph.
# =============================================================================
class AgencyState(TypedDict):
    """
    This represents the shared state of our AI Content Agency during content creation.
    """
    topic: str  # The topic for content creation.
    research_data: str  # Data collected by the researcher.
    draft_content: str  # Draft content written by the writer.
    # messages: Annotated[List[BaseMessage], operator.add] # (Optional) If you need to store message history, you can define it like this.

# =============================================================================
# 2. Simulate our Agents (Node Functions)
#    To focus on LangGraph itself, we use simple Python functions here to simulate Agent behavior.
#    In a real project, these would be more complex LangChain Agents or custom Runnables.
# =============================================================================

# Simulate Researcher Agent
def research_agent(state: AgencyState) -> AgencyState:
    """
    Simulates the Researcher Agent.
    It takes the current state, gets the topic from 'topic', simulates research,
    and updates 'research_data'.
    """
    print(f"\n--- Researcher is researching topic: {state['topic']} ---") 
    # Simulate a time-consuming operation and research results.
    simulated_research_result = f"Preliminary research results for '{state['topic']}':\n" \
                                "1. Market trends show high recent interest in this type of content.\n" \
                                "2. Main competitor content focuses on technical principles, lacking application scenarios.\n" \
                                "3. User feedback generally indicates a desire for more practical case studies and troubleshooting guides."
    
    print("--- Researcher finished research, updating state ---") 
    # Update state and return the new state dictionary.
    return {"research_data": simulated_research_result}

# Simulate Writer Agent
def writer_agent(state: AgencyState) -> AgencyState:
    """
    Simulates the Writer Agent.
    It takes the current state, gets research data from 'research_data', simulates writing,
    and updates 'draft_content'.
    """
    print(f"\n--- Writer is writing content based on research results ---") 
    print(f"Research results summary:\n{state['research_data'][:100]}...") 

    # Simulate a time-consuming operation and writing process.
    simulated_draft = f"Title: A Comprehensive Guide: Practical Strategies and Pitfalls for {state['topic']}\n\n" \
                      f"Introduction: In today's rapidly evolving AI era, {state['topic']} has become a crucial technology that cannot be ignored. This guide will combine the latest market trends and user feedback to provide you with a detailed practical strategy.\n\n" \
                      f"Part 1: Market Insights and Competitor Analysis\n{state['research_data']}\n\n" \
                      f"Part 2: Core Technical Principles and Application Scenarios\n(Technical details omitted here, focusing on practical application)\n\n" \
                      f"Part 3: FAQ and Troubleshooting Guide\n(Suggestions provided here based on user feedback)\n\n" \
                      f"Conclusion: Master {state['topic']} and empower future AI applications!"
    
    print("--- Writer finished writing, updating state ---") 
    # Update state and return the new state dictionary.
    return {"draft_content": simulated_draft}

# =============================================================================
# 3. Build the LangGraph Workflow
#    Use StateGraph to define our Agent nodes and the sequential flow between them.
# =============================================================================

# Instantiate StateGraph, specifying our defined shared state type.
workflow = StateGraph(AgencyState)

# Add Nodes
# Each node is associated with an Agent function we defined above.
workflow.add_node("researcher", research_agent) # Register the researcher node.
workflow.add_node("writer", writer_agent)       # Register the writer node.

# Set Entry Point
# Define where the Graph starts execution.
workflow.set_entry_point("researcher") # Start from the researcher.

# Add Edges
# Define the sequential flow between nodes.
workflow.add_edge("researcher", "writer") # After the researcher completes its task, flow to the writer.

# Set Finish Point
# Define where the Graph finishes execution.
workflow.set_finish_point("writer") # After the writer completes its task, the Graph finishes.

# Compile the Graph
# Compile the defined workflow into an executable Runnable.
app = workflow.compile()

# =============================================================================
# 4. Run our AI Content Agency MVP
#    Pass in the initial state and observe the Graph's execution process and final result.
# =============================================================================
if __name__ == "__main__":
    print("--- AI Content Agency MVP started ---") 

    # Define initial state: the topic we want to create content about.
    initial_state = {"topic": "Application of LangGraph Multi-Agent Collaboration in Content Creation", "research_data": "", "draft_content": ""}

    # Run the Graph
    # Calling app.stream() allows you to see state updates at each node step by step.
    final_state = None
    for s in app.stream(initial_state):
        # s is a dictionary where the key is the name of the currently executing node,
        # and the value is the state update after that node's execution.
        print(s)
        print("----") # Separator for readability.
        final_state = s # Keep track of the last state for final output.

    print("\n--- AI Content Agency MVP finished running ---") 

    # Print the final generated content.
    if final_state and "writer" in final_state: # Ensure final state exists and contains the result from the writer node.
        print("\n🎉 Final Generated Content Draft:")
        print(final_state["writer"]["draft_content"])
    elif final_state and END in final_state: # If the final state is directly END, get it from the END node.
        print("\n🎉 Final Generated Content Draft:")
        print(final_state[END]["draft_content"])
    else:
        print("\n⚠️ Failed to retrieve final content draft.") 

Code Walkthrough: Step by Step

  1. Defining AgencyState (TypedDict)

    • We defined AgencyState using TypedDict. This is the single source of truth shared across the entire Graph.
    • topic: When the work starts, we give the agency a topic.
    • research_data: The researcher will write the research results here.
    • draft_content: The writer will write the initial draft here.
    • Key Takeaway: This single, mutable state is the core of LangGraph. Every node reads the information it needs from here and writes its output back to it.
  2. Simulating Agents (research_agent, writer_agent)

    • To keep the focus on LangGraph's structure, I used simple Python functions here to simulate the Agents.
    • Each function receives an AgencyState object (or a subset of it), performs some simulated "work", and then returns a dictionary. This dictionary is used to update the current AgencyState.
    • Note: The returned dictionary will be merged with the current state (by default, it's a shallow merge: dictionary values are overwritten, while list values defined with Annotated[List[..., operator.add]] are appended). Here, we directly overwrite research_data and draft_content.
  3. Building the LangGraph Workflow (workflow = StateGraph(AgencyState))

    • StateGraph(AgencyState): Initializes a state graph and explicitly tells it that the shared state type we are using is AgencyState.
    • workflow.add_node("researcher", research_agent): Registers our research_agent function as a node named "researcher". When the Graph flows to this node, the research_agent function will be executed.
    • workflow.set_entry_point("researcher"): Sets the entry point of the Graph. This means that when the Graph starts, it will begin execution from the "researcher" node.
    • workflow.add_edge("researcher", "writer"): Adds an edge from "researcher" to "writer". This indicates that after the "researcher" node finishes executing, the control flow will unconditionally transfer to the "writer" node.
    • workflow.set_finish_point("writer"): Sets the finish point of the Graph. When the "writer" node finishes executing, the Graph will stop and return the final state.
    • app = workflow.compile(): Compiles our defined workflow into an executable LangChain Runnable object. This app object is the "brain" of our entire AI Content Agency.
  4. Running our MVP (app.stream(initial_state))

    • We create an initial_state dictionary containing only the topic. research_data and draft_content are initialized as empty strings.
    • app.stream(initial_state): Starts the Graph. It returns an iterator, and each iteration outputs a dictionary representing the state change after the current node's execution. This is extremely helpful for debugging and understanding the flow.
    • Finally, we extract draft_content from the final_state. This is the first piece of work completed by our agency!

Pitfalls and Troubleshooting Guide

As an architect, I must not only teach you "how to do it" but also tell you "which pitfalls to avoid."

  1. State Design Pitfall: Information Loss or Contamination

    • Pitfall: Improper design of AgencyState. For example, forgetting to add an Agent's output field to the state, causing the next Agent to be unable to access the required information. Or, different Agents accidentally overwriting each other's critical data.
    • How to Avoid:
      • Plan the state in advance: Before writing code, draw a diagram listing what inputs each Agent needs and what outputs it will produce. These inputs and outputs become the fields of your AgencyState.
      • Apply the Single Responsibility Principle (SRP) to state fields: Try to assign the update responsibility of each state field to a specific Agent. For instance, research_data should primarily be updated by the researcher, and draft_content primarily by the writer.
      • Use Annotated and operator.add to handle lists: If your state needs to accumulate a list (like message history), using Annotated[List[BaseMessage], operator.add] ensures new messages are appended rather than overwritten. We didn't use it in this issue, but we will in the future.
  2. Unclear Node Responsibilities: The "Hodgepodge" Agent

    • Pitfall: Cramming too much logic into a single Agent node, such as having one Agent do both research and writing. This makes the node bloated, difficult to maintain, debug, and reuse.
    • How to Avoid:
      • Keep node granularity appropriate: Each node should only be responsible for a clear, logically independent task. In this issue, research_agent only handles research, and writer_agent only handles writing, which is a great example.
      • Facilitate debugging: Nodes with a single responsibility are easier to test and troubleshoot. If a node fails, you immediately know whether the issue lies in the research logic or the writing logic.
  3. Debugging Difficulties: Black Box Execution

    • Pitfall: Directly calling app.invoke() without printing intermediate states. When the Graph's execution result doesn't meet expectations, you won't know which step went wrong.
    • How to Avoid:
      • Utilize app.stream(): As demonstrated in our code, the stream() method is your best debugging companion. It allows you to see the state updates after each node executes, helping you track data flow and logic.
      • Print logs inside Agent functions: I added print statements inside research_agent and writer_agent. This is extremely useful during the development phase, allowing you to intuitively see what each Agent is doing and what input it has received.
  4. When to Introduce Conditional Logic? Premature Optimization

    • Pitfall: Before the MVP is even running, starting to ponder how to add complex logic like conditional judgments, loop retries, and human-in-the-loop approvals.
    • How to Avoid:
      • Get it running first, then optimize: Emphasizing MVP thinking once again. Ensure the simplest "Research-Writing" loop runs stably and outputs the expected content. This is your solid foundation for moving towards complex workflows.
      • Iterate gradually: Wait until you have mastered sequential flows before considering introducing add_conditional_edges to implement complex decision logic. That will be the challenge for the next stage, but with the foundation built in this issue, you will be much more confident.

📝 Summary of this Issue

Congratulations! We have just manually built and run the first MVP version of the "AI Content Agency"—a "Research-Writing" loop capable of going from a topic to a content draft.

In this issue, we:

  • Clarified the importance of MVP thinking in complex system development, and why starting with a fixed sequential flow is a wise move.
  • Gained a deep understanding of LangGraph's Node, Edge, and State concepts, which are the cornerstones for building all complex workflows.
  • Through practical code, successfully orchestrated the simulated Researcher and Writer Agents into LangGraph, and built a sequential flow using add_node, set_entry_point, add_edge, and set_finish_point.
  • Mastered the key to state management, ensuring seamless information transfer between Agents.
  • Learned common pitfalls and troubleshooting strategies during development, all of which are valuable experiences for a senior architect.

Now, you have a running "skeleton". Next time, we will start thinking: what if the researcher finds the topic too vague, or the writer feels the research data is insufficient? How do we enable them to provide feedback and retry? That's right, we will begin exploring LangGraph's conditional logic and looping mechanisms to make our agency smarter and more robust!

Are you ready? The excitement continues in the next issue!