StateGraph: Flujo completo del Agente de Soporte
Nodos tipados con TypedDict — ejecución durable con checkpointing por sesión
START
user_message
Entrada del usuario
→
🔍 retrieve
Busca en Pinecone
top-4 chunks KB
→
⚖️ classify
Evalúa confianza
>0.7 → responder
alta confianza
→
baja confianza
→
✅ respond
Genera respuesta
+ send_email
🚨 escalate
Ticket humano
+ notificación
→
END
Respuesta final
al cliente
# Inicialización (una sola vez al arrancar)
embeddings = VoyageAIEmbeddings(
model="voyage-3-large"
)
vectorstore = PineconeVectorStore(
index_name="nutriflow-kb",
embedding=embeddings
)
retriever = vectorstore.as_retriever(
search_kwargs={"k": 4}
)
# Nodo de recuperación
async def retrieve(state: SupportState):
last_msg = state["messages"][-1]
query = last_msg.content
# Búsqueda semántica en la KB
docs = await retriever.ainvoke(query)
return {"context": docs}
async def classify(state: SupportState):
ctx_text = "\n".join(
d.page_content for d in state["context"]
)
# Pide al LLM un score 0-1
prompt = ChatPromptTemplate.from_template("""
Dado el contexto:\n{context}\n
¿Puedes responder con alta confianza (>0.7)?
Responde solo con un número entre 0 y 1.""")
response = await llm.ainvoke(
prompt.format(context=ctx_text)
)
return {"confidence": float(response.content.strip())}
def route_confidence(state):
if state["confidence"] >= 0.7:
return "respond"
return "escalate"
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_anthropic import ChatAnthropic
# LLM principal
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0)
# Construir el grafo
builder = StateGraph(SupportState)
# Añadir nodos
builder.add_node("retrieve", retrieve)
builder.add_node("classify", classify)
builder.add_node("respond", respond_node)
builder.add_node("escalate", escalate_node)
# Aristas
builder.add_edge(START, "retrieve")
builder.add_edge("retrieve", "classify")
builder.add_conditional_edges(
"classify",
route_confidence,
{"respond": "respond", "escalate": "escalate"}
)
builder.add_edge("respond", END)
builder.add_edge("escalate", END)
# Checkpointer (dev=MemorySaver, prod=PostgresSaver)
checkpointer = MemorySaver()
# Compilar
support_agent = builder.compile(checkpointer=checkpointer)
# Invocar con thread_id para memoria por sesión
config = {"configurable": {"thread_id": "user-sara-manager-42"}}
result = await support_agent.ainvoke(
{"messages": [("user", "¿Cómo importo empleados en CSV?")],
"user_email": "sara@empresa.com"},
config=config
)