CarFinanceAgent / chatbot /graph /workflow.py
emreceniklioglu
Initial commit: LangGraph multi-agent car finance chatbot
49102bb
Raw
History Blame Contribute Delete
7.24 kB
"""
graph/workflow.py — LangGraph StateGraph Tanımı
AGENTIC PATTERN: State Machine (LangGraph)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
LangGraph'ta bir "graph" şunlardan oluşur:
- StateGraph: state type + node'lar + edge'ler
- Node: state alan, state parçası döndüren fonksiyon
- Edge: iki node arasındaki bağlantı
- Conditional Edge: state'e göre dinamik yönlendirme
compile() çağrısı graph'ı çalıştırılabilir hale getirir.
MemorySaver ile her thread (oturum) kendi checkpoint'ını korur.
Görsel üretmek için:
from graph.workflow import create_graph
g = create_graph()
print(g.get_graph().draw_mermaid())
"""
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from graph.state import ConversationState
from graph.nodes import (
input_guard_node,
supervisor_node,
intake_new_node,
intake_used_node,
faq_agent_node,
validation_new_node,
validation_used_node,
reflection_node,
recap_node,
submission_node,
crosssell_node,
wait_node,
)
from graph.edges import (
route_after_supervisor,
route_after_intake_new,
route_after_intake_used,
route_after_validation,
route_after_reflection,
route_after_recap,
route_after_submission,
route_after_crosssell,
route_after_faq,
)
# Singleton compiled graph
_compiled_graph = None
def create_graph():
"""
LangGraph StateGraph'ı oluşturur ve compile eder.
Graf Yapısı:
input_guard → supervisor → [intake_new | intake_used | faq_agent]
↓ ↓
validation_new validation_used
↓ ↓
reflection
recap
submission
crosssell
END
"""
graph = StateGraph(ConversationState)
# ── Node'ları Ekle ─────────────────────────────────────────────────────────
graph.add_node("input_guard", input_guard_node)
graph.add_node("supervisor", supervisor_node)
graph.add_node("intake_new", intake_new_node)
graph.add_node("intake_used", intake_used_node)
graph.add_node("faq_agent", faq_agent_node)
graph.add_node("validation_new", validation_new_node)
graph.add_node("validation_used", validation_used_node)
graph.add_node("reflection", reflection_node)
graph.add_node("recap", recap_node)
graph.add_node("submission", submission_node)
graph.add_node("crosssell", crosssell_node)
graph.add_node("wait_for_input", wait_node)
# ── Başlangıç Node'u ───────────────────────────────────────────────────────
# Her kullanıcı mesajı input_guard'dan girer
graph.set_entry_point("input_guard")
# ── Sabit Edge'ler (her zaman aynı yönlendirme) ───────────────────────────
graph.add_edge("input_guard", "supervisor")
# ── Koşullu Edge'ler (state'e göre dinamik yönlendirme) ──────────────────
# Supervisor kararı
# route_after_supervisor(state) fonksiyonu state'e bakar ve bir string döner.
# LangGraph bu string'i aşağıdaki dictionary'den lookup'tan geçirip ilgili node'a atlar.
graph.add_conditional_edges(
"supervisor",
route_after_supervisor,
{
"intake_new": "intake_new",
"intake_used": "intake_used",
"faq_agent": "faq_agent",
"reflection": "reflection",
"crosssell": "crosssell",
"wait_for_input": "wait_for_input",
"END": END,
},
)
# Yeni araç intake
graph.add_conditional_edges(
"intake_new",
route_after_intake_new,
{
"validation_new": "validation_new",
"wait_for_input": "wait_for_input",
"END": END,
},
)
# 2. El intake
graph.add_conditional_edges(
"intake_used",
route_after_intake_used,
{
"validation_used": "validation_used",
"wait_for_input": "wait_for_input",
"END": END,
},
)
# Validation
graph.add_conditional_edges(
"validation_new",
route_after_validation,
{"reflection": "reflection", "intake_new": "intake_new", "END": END},
)
graph.add_conditional_edges(
"validation_used",
route_after_validation,
{"reflection": "reflection", "intake_used": "intake_used", "END": END},
)
# Reflection
graph.add_conditional_edges(
"reflection",
route_after_reflection,
{"recap": "recap", "intake_new": "intake_new", "intake_used": "intake_used", "END": END},
)
# Recap → Submission
graph.add_conditional_edges(
"recap",
route_after_recap,
{
"submission": "submission",
"intake_new": "intake_new",
"intake_used": "intake_used",
"wait_for_input": "wait_for_input",
"END": END,
},
)
# Submission → CrossSell
graph.add_conditional_edges(
"submission",
route_after_submission,
{"crosssell": "crosssell", "END": END},
)
# CrossSell → END
graph.add_conditional_edges(
"crosssell",
route_after_crosssell,
{"wait_for_input": "wait_for_input", "END": END},
)
# FAQ cevabından kaldığı yere dön
graph.add_conditional_edges(
"faq_agent",
route_after_faq,
{
"supervisor": "supervisor",
"intake_new": "intake_new",
"intake_used": "intake_used",
"recap": "recap",
"wait_for_input": "wait_for_input",
},
)
# Bekleme node'u her zaman durur (kullanıcı girdisi bekliyor)
graph.add_edge("wait_for_input", END)
return graph
def get_compiled_graph():
"""
MemorySaver ile compile edilmiş graph'ı döndürür (singleton).
MemorySaver: Her thread_id için ayrı checkpoint tutar.
thread_id = session_id → her kullanıcı oturumu bağımsız.
"""
global _compiled_graph
if _compiled_graph is None:
checkpointer = MemorySaver()
graph = create_graph()
_compiled_graph = graph.compile(checkpointer=checkpointer)
return _compiled_graph
def get_workflow_mermaid() -> str:
"""Graf yapısını Mermaid diyagramı olarak döndürür (görsel için)."""
graph = create_graph()
compiled = graph.compile()
return compiled.get_graph().draw_mermaid()