From 8879d85d3f54805eccc7705da67a3e38b172c753 Mon Sep 17 00:00:00 2001 From: Flo Kempenich Date: Wed, 12 Nov 2025 18:06:33 +0900 Subject: [PATCH] Implement emotional summary agent in graph --- langgraph.json | 3 +- src/__init__.py | 1 + src/agents/__init__.py | 1 + src/agents/walkandlearn_summary/__init__.py | 5 ++ src/agents/walkandlearn_summary/graph.py | 85 +++++++++++++++++++++ src/agents/walkandlearn_summary/io.py | 34 +++++++++ src/agents/walkandlearn_summary/prompts.py | 9 +++ 7 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/__init__.py create mode 100644 src/agents/__init__.py create mode 100644 src/agents/walkandlearn_summary/__init__.py create mode 100644 src/agents/walkandlearn_summary/graph.py create mode 100644 src/agents/walkandlearn_summary/io.py create mode 100644 src/agents/walkandlearn_summary/prompts.py diff --git a/langgraph.json b/langgraph.json index 9b241b6..ced3e10 100644 --- a/langgraph.json +++ b/langgraph.json @@ -4,7 +4,8 @@ ], "graphs": { "POC: Simple Agent": "./src/poc/simple_agent.py:graph", - "POC: Deep Agent": "./src/poc/deep_agent.py:graph" + "POC: Deep Agent": "./src/poc/deep_agent.py:graph", + "Walk and Learn Summary": "./src/agents/walkandlearn_summary/graph.py:graph" }, "env": ".env" } \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..39494fe --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +"""Source package.""" diff --git a/src/agents/__init__.py b/src/agents/__init__.py new file mode 100644 index 0000000..b6d0b90 --- /dev/null +++ b/src/agents/__init__.py @@ -0,0 +1 @@ +"""Agents package.""" diff --git a/src/agents/walkandlearn_summary/__init__.py b/src/agents/walkandlearn_summary/__init__.py new file mode 100644 index 0000000..105ff64 --- /dev/null +++ b/src/agents/walkandlearn_summary/__init__.py @@ -0,0 +1,5 @@ +"""Walk and Learn summary agent.""" + +from .graph import graph + +__all__ = ["graph"] diff --git a/src/agents/walkandlearn_summary/graph.py b/src/agents/walkandlearn_summary/graph.py new file mode 100644 index 0000000..d145045 --- /dev/null +++ b/src/agents/walkandlearn_summary/graph.py @@ -0,0 +1,85 @@ +"""LangGraph agent for summarizing conversations with emotional and technical summaries.""" + +from typing_extensions import TypedDict + +from langchain.agents import create_agent +from langchain.chat_models import init_chat_model +from langchain_core.messages import HumanMessage +from langgraph.graph import StateGraph, START, END, MessagesState + +from agents.walkandlearn_summary.io import read_file, write_file, format_summary +from agents.walkandlearn_summary.prompts import EMOTIONAL_SUMMARY_PROMPT, TECHNICAL_SUMMARY_PROMPT + + +MODEL_NAME = "gpt-4o-mini" +MODEL_TEMPERATURE = 0 + +INPUT_FILE_PATH = "TODO_INPUT_FILE_PATH.txt" # TODO: Replace with actual input file path +OUTPUT_FILE_PATH = "TODO_OUTPUT_FILE_PATH.md" # TODO: Replace with actual output file path + + +class SummaryState(MessagesState): + conversation: str + emotional_summary: str + technical_summary: str + + +model = init_chat_model(MODEL_NAME, temperature=MODEL_TEMPERATURE) + +emotional_agent = create_agent( + model=model, + tools=[], + system_prompt=EMOTIONAL_SUMMARY_PROMPT +) + +technical_agent = create_agent( + model=model, + tools=[], + system_prompt=TECHNICAL_SUMMARY_PROMPT +) + + +def load_conversation_node(state: SummaryState) -> dict: + return {"conversation": read_file(INPUT_FILE_PATH)} + + +def emotional_summary_node(state: SummaryState) -> dict: + result = emotional_agent.invoke({ + "messages": [HumanMessage(content=f"Please provide an emotional summary of the following conversation:\n\n{state['conversation']}")] + }) + return {"emotional_summary": result["messages"][-1].content} + + +def technical_summary_node(state: SummaryState) -> dict: + # TODO: Uncomment below to enable technical agent (currently placeholder to save API costs) + return {"technical_summary": "[Technical summary placeholder - agent commented out for cost savings]"} + + # result = technical_agent.invoke({ + # "messages": [HumanMessage(content=f"Please provide a technical summary of the following conversation:\n\n{state['conversation']}")] + # }) + # return {"technical_summary": result["messages"][-1].content} + + +def write_output_node(state: SummaryState) -> dict: + content = format_summary(state['emotional_summary'], state['technical_summary']) + write_file(OUTPUT_FILE_PATH, content) + return {} + + +graph = ( + StateGraph(SummaryState) + + .add_node("load_conversation", load_conversation_node) + .add_node("emotional_summary", emotional_summary_node) + .add_node("technical_summary", technical_summary_node) + .add_node("write_output", write_output_node) + + .add_edge(START, "load_conversation") + .add_edge("load_conversation", "emotional_summary") + .add_edge("load_conversation", "technical_summary") + .add_edge("emotional_summary", "write_output") + .add_edge("technical_summary", "write_output") + .add_edge("write_output", END) + + .compile() +) diff --git a/src/agents/walkandlearn_summary/io.py b/src/agents/walkandlearn_summary/io.py new file mode 100644 index 0000000..996f9b8 --- /dev/null +++ b/src/agents/walkandlearn_summary/io.py @@ -0,0 +1,34 @@ +"""File I/O utilities for the summarization workflow.""" + +from pathlib import Path + + +def read_file(file_path: str) -> str: + try: + with open(Path(file_path), "r", encoding="utf-8") as f: + return f.read() + except FileNotFoundError: + return f"File not found: {file_path}. Please set the correct file path in the configuration constants." + + +def write_file(file_path: str, content: str) -> None: + with open(Path(file_path), "w", encoding="utf-8") as f: + f.write(content) + + +def format_summary(emotional_summary: str, technical_summary: str) -> str: + return f"""# Emotional Summary + +{emotional_summary} + + + +================================================================ +================================================================ + + + +# Technical Summary + +{technical_summary} +""" diff --git a/src/agents/walkandlearn_summary/prompts.py b/src/agents/walkandlearn_summary/prompts.py new file mode 100644 index 0000000..7f29497 --- /dev/null +++ b/src/agents/walkandlearn_summary/prompts.py @@ -0,0 +1,9 @@ +"""System prompts for summarization agents.""" + +EMOTIONAL_SUMMARY_PROMPT = """You are the emotional summary agent. + +TODO: Replace this placeholder with the actual system prompt for emotional summarization.""" + +TECHNICAL_SUMMARY_PROMPT = """You are the technical summary agent. + +TODO: Replace this placeholder with the actual system prompt for technical summarization."""