Creating a custom deep agent by creating a custom react agent and extending it to a deep agent

Hello fam, i’ve been trying to create a custom deepagent using langgraph, yeah i created a custom react agent and wanted to extend the functionality into a deep agent , after creating the todo list planning tool, i could not add the extended deep agent state that used this todo tool in the custom react agent. Any one who can help please.

hi @AbasEkanem

thank for this question!
It is not easy to refer to this without any code snippets from you, but let me try :slight_smile:

In LangGraph, the graph state is your source of truth—so you can:

  • Define a richer TypedDict state (e.g., messages + todos).
  • Give the ReAct agent a planning tool (e.g., write_todos).
  • Wrap the ReAct runnable in a node that only reads/writes messages.
  • Add a follow-up node that inspects the last ToolMessage and syncs the tool output into your extended state (todos).

I’ve prepared this example:

import json
from typing import Annotated, List

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.messages import AnyMessage, ToolMessage, HumanMessage
from langchain_core.tools import tool
from langchain_core.runnables import RunnableConfig
from typing_extensions import TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.prebuilt import create_react_agent

load_dotenv()

config: RunnableConfig = {"configurable": {"thread_id": "unique_id_1"}}

def get_llm():
    return init_chat_model(
        "claude-3-7-sonnet-latest",
        model_provider="anthropic"
    )


@tool("write_todos")
def write_todos(items: List[str]) -> dict:
    """Replace the current todo list. Return a JSON object: {"todos": [...]}."""
    print(f"Writing todos: {items}")
    return {"todos": items}


class DeepState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]
    todos: List[str]


def build_graph():
    model = get_llm()
    # Instruct the agent to plan using write_todos early, then proceed with normal ReAct
    prompt = (
        "You are a helpful agent. First, produce a concrete task plan by calling the "
        "`write_todos` tool with a concise checklist. Then continue using tools to act."
    )
    react = create_react_agent(model=model, tools=[write_todos], prompt=prompt)

    def react_node(state: DeepState, config: RunnableConfig):
        print(f"React to {state['todos']}")
        # Pass only messages to the ReAct runnable; it returns updated messages
        result = react.invoke({"messages": state["messages"]}, config=config)
        return {"messages": result["messages"]}

    def sync_todos(state: DeepState):
        # Find the most recent write_todos ToolMessage and merge into state
        for msg in reversed(state.get("messages", [])):
            if isinstance(msg, ToolMessage) and msg.name == "write_todos":
                payload = msg.content
                try:
                    data = json.loads(payload) if isinstance(payload, str) else payload
                    items = [str(x) for x in data.get("todos", [])]
                    print(f"Syncing todos: {items}")
                    return {"todos": items}
                except Exception:
                    # Ignore malformed payloads; keep state unchanged
                    print(f"Error parsing payload: {payload}")
                    return {}
        print(f"No `ToolMessage` found in messages: {state['messages']}")
        return {}

    # def sync_todos(state: DeepState):
    #     print(f"Sync to {state['todos']}")
    #     # If the last tool call was write_todos, merge it into the graph state
    #     if not state.get("messages"):
    #         print(f"Nothing to sync")
    #         return {}
    #     last = state["messages"][-2]
    #     if isinstance(last, ToolMessage) and last.name == "write_todos":
    #         payload = last.content
    #         try:
    #             data = json.loads(payload) if isinstance(payload, str) else payload
    #             items = [str(x) for x in data.get("todos", [])]
    #             print(f"Return {items}")
    #             return {"todos": items}
    #         except Exception:
    #             # Ignore malformed payloads; keep state unchanged
    #             print(f"Error parsing payload: {payload}")
    #             return {}
    #     print(f"Return empty: {last}")
    #     return {}

    builder = StateGraph(DeepState)
    builder.add_node("react", react_node)
    builder.add_node("sync_todos", sync_todos)

    builder.add_edge(START, "react")
    builder.add_edge("react", "sync_todos")
    builder.add_edge("sync_todos", END)

    return builder.compile(checkpointer=InMemorySaver())


if __name__ == "__main__":
    app = build_graph()
    out = app.invoke(
        {
            "messages": [
                HumanMessage(
                    "Plan the research steps as todos, then summarize findings on LangGraph."
                )
            ],
            "todos": [],
        },
        config
    )
    print("TODOS:", out.get("todos"))
    print("LAST MESSAGE:", out.get("messages", [])[-1])
1 Like

thank you so much for this , this is helpful…

1 Like

your welcome @AbasEkanem :slight_smile:

yeah also how do you host your ai applications freely apart from langgraph platform?

I don’t know much about free hosts. But you might want to explore Hugging Face Spaces.