Skip to content

Commit 8c8e751

Browse files
authored
Merge pull request #20 from ks6088ts-labs/feature/issue-19_human-in-the-loop
human in the loop
2 parents e71cc29 + 3bc235e commit 8c8e751

File tree

12 files changed

+143
-25
lines changed

12 files changed

+143
-25
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,4 @@ ci-test-docs: install-deps-docs docs ## run CI test for documentation
114114

115115
.PHONY: langgraph-studio
116116
langgraph-studio: ## run LangGraph Studio
117-
uv run langgraph dev --no-reload
117+
uv run langgraph dev

docs/index.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ uv run python -m template_langgraph.tasks.search_documents_on_elasticsearch
2525
uv run python -m template_langgraph.tasks.run_kabuto_helpdesk_agent "KABUTOの起動時に、画面全体が紫色に点滅し、システムがフリーズします。"
2626
uv run python -m template_langgraph.tasks.run_kabuto_helpdesk_agent "KABUTOのマニュアルから禅モードに関する情報を教えて下さい"
2727

28-
# BasicWorkflowAgent
29-
uv run python -m template_langgraph.tasks.draw_basic_workflow_agent_mermaid_png "data/basic_workflow_agent.png"
30-
uv run python -m template_langgraph.tasks.run_basic_workflow_agent
28+
# ChatWithToolsAgent
29+
uv run python -m template_langgraph.tasks.draw_chat_with_tools_agent_mermaid_png "data/chat_with_tools_agent.png"
30+
uv run python -m template_langgraph.tasks.run_chat_with_tools_agent
3131
# KABUTOの起動時に、画面全体が紫色に点滅し、システムがフリーズします。KABUTO のマニュアルから、関連する情報を取得したり過去のシステムのトラブルシュート事例が蓄積されたデータベースから、関連する情報を取得して質問に答えてください
3232
# 天狗のいたずら という現象について KABUTO のマニュアルから、関連する情報を取得したり過去のシステムのトラブルシュート事例が蓄積されたデータベースから、関連する情報を取得して質問に答えてください
3333

@@ -42,6 +42,7 @@ uv run python -m template_langgraph.tasks.run_issue_formatter_agent
4242
### LangGraph
4343

4444
- [Build a custom workflow](https://langchain-ai.github.io/langgraph/concepts/why-langgraph/)
45+
- [LangGraphの(LLMなし)Human-in-the-loopを試してみた](https://qiita.com/te_yama/items/db38201af60dec76384d)
4546

4647
### Sample Codes
4748

langgraph.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
"."
44
],
55
"graphs": {
6-
"basic_workflow_agent": "template_langgraph.agents.basic_workflow_agent.agent:graph",
6+
"chat_with_tools_agent": "template_langgraph.agents.chat_with_tools_agent.agent:graph",
77
"kabuto_helpdesk_agent": "template_langgraph.agents.kabuto_helpdesk_agent:graph",
8-
"issue_formatter_agent": "template_langgraph.agents.issue_formatter_agent.agent:graph"
8+
"issue_formatter_agent": "template_langgraph.agents.issue_formatter_agent.agent:graph",
9+
"task_decomposer_agent": "template_langgraph.agents.task_decomposer_agent.agent:graph"
910
},
1011
"env": ".env"
1112
}
File renamed without changes.

template_langgraph/agents/basic_workflow_agent/agent.py renamed to template_langgraph/agents/chat_with_tools_agent/agent.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from langchain_core.messages import ToolMessage
44
from langgraph.graph import END, StateGraph
55

6-
from template_langgraph.agents.basic_workflow_agent.models import AgentState
6+
from template_langgraph.agents.chat_with_tools_agent.models import AgentState
77
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
88
from template_langgraph.loggers import get_logger
99
from template_langgraph.tools.elasticsearch_tool import search_elasticsearch
@@ -36,7 +36,7 @@ def __call__(self, inputs: dict):
3636
return {"messages": outputs}
3737

3838

39-
class BasicWorkflowAgent:
39+
class ChatWithToolsAgent:
4040
def __init__(self):
4141
self.llm = AzureOpenAiWrapper().chat_model
4242

@@ -80,7 +80,7 @@ def create_graph(self):
8080

8181
def initialize(self, state: AgentState) -> AgentState:
8282
"""Initialize the agent with the given state."""
83-
logger.info(f"Initializing BasicWorkflowAgent with state: {state}")
83+
logger.info(f"Initializing ChatWithToolsAgent with state: {state}")
8484
# Here you can add any initialization logic if needed
8585
return state
8686

@@ -119,7 +119,7 @@ def route_tools(
119119

120120
def finalize(self, state: AgentState) -> AgentState:
121121
"""Finalize the agent's work and prepare the output."""
122-
logger.info(f"Finalizing BasicWorkflowAgent with state: {state}")
122+
logger.info(f"Finalizing ChatWithToolsAgent with state: {state}")
123123
# Here you can add any finalization logic if needed
124124
return state
125125

@@ -128,4 +128,4 @@ def draw_mermaid_png(self) -> bytes:
128128
return self.create_graph().get_graph().draw_mermaid_png()
129129

130130

131-
graph = BasicWorkflowAgent().create_graph()
131+
graph = ChatWithToolsAgent().create_graph()
File renamed without changes.

template_langgraph/agents/task_decomposer_agent/__init__.py

Whitespace-only changes.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from langgraph.graph import END, StateGraph
2+
from langgraph.types import interrupt
3+
4+
from template_langgraph.agents.task_decomposer_agent.models import AgentState, TaskList
5+
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
6+
from template_langgraph.loggers import get_logger
7+
8+
logger = get_logger(__name__)
9+
10+
11+
class TaskDecomposerAgent:
12+
def __init__(self):
13+
self.llm = AzureOpenAiWrapper().chat_model
14+
15+
def create_graph(self):
16+
"""Create the main graph for the agent."""
17+
# Create the workflow state graph
18+
workflow = StateGraph(AgentState)
19+
20+
# Create nodes
21+
workflow.add_node("chat", self.chat)
22+
workflow.add_node("human_feedback", self.human_feedback)
23+
24+
# Create edges
25+
workflow.set_entry_point("chat")
26+
workflow.add_edge("chat", "human_feedback")
27+
workflow.add_conditional_edges(
28+
source="human_feedback",
29+
path=self.route_human_feedback,
30+
path_map={
31+
"loopback": "chat",
32+
"end": END,
33+
},
34+
)
35+
return workflow.compile()
36+
37+
def chat(self, state: AgentState) -> AgentState:
38+
"""Chat with tools using the state."""
39+
logger.info(f"Chatting with tools using state: {state}")
40+
41+
task_list = self.llm.with_structured_output(TaskList).invoke(
42+
input=state["messages"],
43+
)
44+
state["task_list"] = task_list
45+
logger.info(f"Decomposed tasks: {task_list}")
46+
return state
47+
48+
def human_feedback(self, state: AgentState) -> AgentState:
49+
"""Handle human feedback."""
50+
logger.info(f"Handling human feedback with state: {state}")
51+
feedback = interrupt("Type your feedback. If you want to end the conversation, type 'end'.")
52+
state["messages"].append(
53+
{
54+
"content": feedback,
55+
"role": "user",
56+
}
57+
)
58+
return state
59+
60+
def route_human_feedback(
61+
self,
62+
state: AgentState,
63+
):
64+
"""
65+
Use in the conditional_edge to route to the HumanFeedbackNode if the last message
66+
has human feedback. Otherwise, route to the end.
67+
"""
68+
human_feedback = state["messages"][-1].content.strip().lower()
69+
if human_feedback == "end":
70+
logger.info("Ending the conversation as per user request.")
71+
return "end"
72+
logger.info("Looping back to chat for further processing.")
73+
return "loopback"
74+
75+
def draw_mermaid_png(self) -> bytes:
76+
"""Draw the graph in Mermaid format."""
77+
return self.create_graph().get_graph().draw_mermaid_png()
78+
79+
80+
graph = TaskDecomposerAgent().create_graph()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from collections.abc import Sequence
2+
from typing import (
3+
Annotated,
4+
TypedDict,
5+
)
6+
7+
from langchain_core.messages import (
8+
BaseMessage,
9+
)
10+
from langgraph.graph.message import add_messages
11+
from pydantic import BaseModel, Field
12+
13+
14+
class Task(BaseModel):
15+
title: str = Field(..., description="Title of the task")
16+
description: str = Field(..., description="Description of the task")
17+
priority: int = Field(..., description="Priority of the task (1-5)")
18+
due_date: str | None = Field(None, description="Due date of the task (YYYY-MM-DD format)")
19+
assigned_to: str | None = Field(None, description="Name of the agent assigned to the task")
20+
21+
22+
class TaskList(BaseModel):
23+
tasks: Sequence[Task] = Field(..., description="List of tasks to be decomposed")
24+
25+
26+
class AgentInput(BaseModel):
27+
request: str = Field(..., description="Request from the user")
28+
29+
30+
class AgentOutput(BaseModel):
31+
response: str = Field(..., description="Response from the agent")
32+
33+
34+
class AgentState(TypedDict):
35+
messages: Annotated[Sequence[BaseMessage], add_messages]
36+
task_list: TaskList

template_langgraph/tasks/draw_basic_workflow_agent_mermaid_png.py

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)