From abdc740531ac2c30613d76c3cfb4c0d98317f895 Mon Sep 17 00:00:00 2001 From: He Tao Date: Tue, 22 Apr 2025 15:33:53 +0800 Subject: [PATCH] feat: add langgraph.json for langgraph studio debug --- .gitignore | 1 + README.md | 52 ++++++++++++++++++++++++++++++++++++ langgraph.json | 13 +++++++++ src/graph/__init__.py | 3 ++- src/graph/builder.py | 31 ++++++++++++++++----- src/podcast/graph/builder.py | 11 ++++---- src/ppt/graph/builder.py | 3 ++- src/server/app.py | 4 +-- 8 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 langgraph.json diff --git a/.gitignore b/.gitignore index b9bf06e..3d08e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ static/browser_history/*.gif conf.yaml .idea/ +.langgraph_api/ diff --git a/README.md b/README.md index 5f17d81..0066b61 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,58 @@ make lint make format ``` +### Debugging with LangGraph Studio + +DeerFlow uses LangGraph for its workflow architecture. You can use LangGraph Studio to debug and visualize the workflow in real-time. + +#### Running LangGraph Studio Locally + +DeerFlow includes a `langgraph.json` configuration file that defines the graph structure and dependencies for the LangGraph Studio. This file points to the workflow graphs defined in the project and automatically loads environment variables from the `.env` file. + +##### Mac + +```bash +# Install uv package manager if you don't have it +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Install dependencies and start the LangGraph server +uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev +``` + +##### Windows / Linux + +```bash +# Install dependencies +pip install -e . +pip install -U "langgraph-cli[inmem]" + +# Start the LangGraph server +langgraph dev +``` + +After starting the LangGraph server, you'll see several URLs in the terminal: +- API: http://127.0.0.1:2024 +- Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024 +- API Docs: http://127.0.0.1:2024/docs + +Open the Studio UI link in your browser to access the debugging interface. + +#### Using LangGraph Studio + +In the Studio UI, you can: + +1. Visualize the workflow graph and see how components connect +2. Trace execution in real-time to see how data flows through the system +3. Inspect the state at each step of the workflow +4. Debug issues by examining inputs and outputs of each component +5. Provide feedback during the planning phase to refine research plans + +When you submit a research topic in the Studio UI, you'll be able to see the entire workflow execution, including: +- The planning phase where the research plan is created +- The feedback loop where you can modify the plan +- The research and writing phases for each section +- The final report generation + ## Architecture DeerFlow implements a modular multi-agent system architecture designed for automated research and code analysis. The system is built on LangGraph, enabling a flexible state-based workflow where components communicate through a well-defined message passing system. diff --git a/langgraph.json b/langgraph.json new file mode 100644 index 0000000..846db11 --- /dev/null +++ b/langgraph.json @@ -0,0 +1,13 @@ +{ + "dockerfile_lines": [], + "graphs": { + "deep_research": "./src/workflow.py:graph", + "podcast_generation": "./src/podcast/graph/builder.py:workflow", + "ppt_generation": "./src/ppt/graph/builder.py:workflow" + }, + "python_version": "3.12", + "env": "./.env", + "dependencies": [ + "." + ] + } \ No newline at end of file diff --git a/src/graph/__init__.py b/src/graph/__init__.py index ec13d9a..365ab78 100644 --- a/src/graph/__init__.py +++ b/src/graph/__init__.py @@ -1,8 +1,9 @@ # Copyright (c) 2025 Bytedance Ltd. and/or its affiliates # SPDX-License-Identifier: MIT -from .builder import build_graph +from .builder import build_graph_with_memory, build_graph __all__ = [ + "build_graph_with_memory", "build_graph", ] diff --git a/src/graph/builder.py b/src/graph/builder.py index fcb4f39..3aebc89 100644 --- a/src/graph/builder.py +++ b/src/graph/builder.py @@ -3,6 +3,7 @@ from langgraph.graph import StateGraph, START, END from langgraph.checkpoint.memory import MemorySaver + from .types import State from .nodes import ( coordinator_node, @@ -15,13 +16,8 @@ from .nodes import ( ) -def build_graph(): - """Build and return the agent workflow graph.""" - # use persistent memory to save conversation history - # TODO: be compatible with SQLite / PostgreSQL - memory = MemorySaver() - - # build state graph +def _build_base_graph(): + """Build and return the base state graph with all nodes and edges.""" builder = StateGraph(State) builder.add_edge(START, "coordinator") builder.add_node("coordinator", coordinator_node) @@ -32,4 +28,25 @@ def build_graph(): builder.add_node("coder", coder_node) builder.add_node("human_feedback", human_feedback_node) builder.add_edge("reporter", END) + return builder + + +def build_graph_with_memory(): + """Build and return the agent workflow graph with memory.""" + # use persistent memory to save conversation history + # TODO: be compatible with SQLite / PostgreSQL + memory = MemorySaver() + + # build state graph + builder = _build_base_graph() return builder.compile(checkpointer=memory) + + +def build_graph(): + """Build and return the agent workflow graph without memory.""" + # build state graph + builder = _build_base_graph() + return builder.compile() + + +graph = build_graph() diff --git a/src/podcast/graph/builder.py b/src/podcast/graph/builder.py index 55c1498..355dc99 100644 --- a/src/podcast/graph/builder.py +++ b/src/podcast/graph/builder.py @@ -3,10 +3,10 @@ from langgraph.graph import END, START, StateGraph -from .audio_mixer_node import audio_mixer_node -from .script_writer_node import script_writer_node -from .state import PodcastState -from .tts_node import tts_node +from src.podcast.graph.audio_mixer_node import audio_mixer_node +from src.podcast.graph.script_writer_node import script_writer_node +from src.podcast.graph.state import PodcastState +from src.podcast.graph.tts_node import tts_node def build_graph(): @@ -23,13 +23,14 @@ def build_graph(): return builder.compile() +workflow = build_graph() + if __name__ == "__main__": from dotenv import load_dotenv load_dotenv() report_content = open("examples/nanjing_tangbao.md").read() - workflow = build_graph() final_state = workflow.invoke({"input": report_content}) for line in final_state["script"].lines: print("" if line.speaker == "male" else "", line.text) diff --git a/src/ppt/graph/builder.py b/src/ppt/graph/builder.py index 2bf40e5..4000067 100644 --- a/src/ppt/graph/builder.py +++ b/src/ppt/graph/builder.py @@ -20,11 +20,12 @@ def build_graph(): return builder.compile() +workflow = build_graph() + if __name__ == "__main__": from dotenv import load_dotenv load_dotenv() report_content = open("examples/nanjing_tangbao.md").read() - workflow = build_graph() final_state = workflow.invoke({"input": report_content}) diff --git a/src/server/app.py b/src/server/app.py index e7036f4..b53e952 100644 --- a/src/server/app.py +++ b/src/server/app.py @@ -14,7 +14,7 @@ from fastapi.responses import Response, StreamingResponse from langchain_core.messages import AIMessageChunk, ToolMessage from langgraph.types import Command -from src.graph.builder import build_graph +from src.graph.builder import build_graph_with_memory from src.podcast.graph.builder import build_graph as build_podcast_graph from src.ppt.graph.builder import build_graph as build_ppt_graph from src.server.chat_request import ( @@ -43,7 +43,7 @@ app.add_middleware( allow_headers=["*"], # Allows all headers ) -graph = build_graph() +graph = build_graph_with_memory() @app.post("/api/chat/stream")