As AI applications evolve, teams often need to migrate between frameworks to leverage better capabilities, performance, or ecosystem support. This article demonstrates how to migrate a production LangGraph implementation to LlamaIndex or a Multi-Agent Framework (MAF), using a real-world customer support automation system as our use case.
Real-World Use Case: Intelligent Customer Support System
Scenario: A SaaS company needs an AI-powered customer support system that can:
Answer customer queries using product documentation
Escalate complex issues to human agents
Track conversation state and context
Integrate with multiple data sources (docs, tickets, user data)
Current LangGraph Implementation
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage, AIMessage
from typing import TypedDict, Annotated
import operator
class SupportState(TypedDict):
messages: Annotated[list, operator.add]
context: dict
escalated: bool
ticket_id: str
# Define nodes
def retrieve_context(state: SupportState):
"""Retrieve relevant documentation"""
query = state['messages'][-1].content
# Vector search implementation
docs = vector_store.similarity_search(query, k=3)
return {"context": {"docs": docs}}
def generate_response(state: SupportState):
"""Generate AI response"""
context = state['context']['docs']
query = state['messages'][-1].content
response = llm.invoke(
f"Context: {context}\n\nQuery: {query}\n\nProvide helpful response:"
)
return {"messages": [AIMessage(content=response)]}
def check_escalation(state: SupportState):
"""Determine if escalation is needed"""
last_response = state['messages'][-1].content
if "unable to help" in last_response.lower():
return {"escalated": True}
return {"escalated": False}
# Build graph
workflow = StateGraph(SupportState)
workflow.add_node("retrieve", retrieve_context)
workflow.add_node("generate", generate_response)
workflow.add_node("escalate", check_escalation)
workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "generate")
workflow.add_edge("generate", "escalate")
workflow.add_conditional_edges(
"escalate",
lambda x: "end" if x['escalated'] else END
)
app = workflow.compile()Migration Strategy
Why Migrate?
Better Data Integration: LlamaIndex excels at document indexing and retrieval
Simplified Architecture: MAF provides cleaner multi-agent patterns
Performance: Native optimizations for specific use cases
Ecosystem: Better integration with specific tools and services
Migration Approach
Phase 1: Analyze current state management and workflows
Phase 2: Map LangGraph components to LlamaIndex/MAF equivalents
Phase 3: Implement new architecture with gradual rollout
Phase 4: Testing, validation, and optimization
New Implementation with LlamaIndex
Architecture Overview

LlamaIndex Implementation
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.core.workflow import (
StartEvent, StopEvent, Workflow, step, Event
)
from llama_index.core.agent import ReActAgent
from llama_index.llms.openai import OpenAI
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from typing import Dict, Any
import json
# Define custom events
class RetrieveEvent(Event):
query: str
conversation_id: str
class GenerateEvent(Event):
query: str
context: list
conversation_id: str
class EscalateEvent(Event):
query: str
response: str
conversation_id: str
class SupportWorkflow(Workflow):
def __init__(self, vector_store, ticket_system, **kwargs):
super().__init__(**kwargs)
self.vector_store = vector_store
self.ticket_system = ticket_system
self.llm = OpenAI(model="gpt-4")
self.index = VectorStoreIndex.from_vector_store(vector_store)
@step
async def start(self, ctx, ev: StartEvent) -> RetrieveEvent:
"""Entry point - receive user query"""
query = ev.query
conversation_id = ev.conversation_id
# Store conversation state
await ctx.set(f"conv_{conversation_id}", {
"query": query,
"messages": []
})
return RetrieveEvent(query=query, conversation_id=conversation_id)
@step
async def retrieve_context(self, ctx, ev: RetrieveEvent) -> GenerateEvent:
"""Retrieve relevant documentation"""
query_engine = self.index.as_query_engine(similarity_top_k=5)
response = query_engine.query(ev.query)
# Extract source nodes as context
context = [
{
"text": node.text,
"score": node.score,
"metadata": node.metadata
}
for node in response.source_nodes
]
return GenerateEvent(
query=ev.query,
context=context,
conversation_id=ev.conversation_id
)
@step
async def generate_response(self, ctx, ev: GenerateEvent) -> EscalateEvent:
"""Generate AI response with context"""
context_str = "\n\n".join([
f"Doc {i+1}: {doc['text']}"
for i, doc in enumerate(ev.context)
])
prompt = f"""Based on the following documentation, answer the customer query.
If you cannot find relevant information, state that clearly.
Documentation:
{context_str}
Customer Query: {ev.query}
Response:"""
response = await self.llm.acomplete(prompt)
return EscalateEvent(
query=ev.query,
response=str(response),
conversation_id=ev.conversation_id
)
@step
async def check_escalation(self, ctx, ev: EscalateEvent) -> StopEvent:
"""Check if escalation is needed"""
escalation_prompt = f"""Analyze this response and determine if it needs human escalation.
Respond with JSON: {{"escalate": true/false, "reason": "explanation"}}
Response: {ev.response}"""
decision = await self.llm.acomplete(escalation_prompt)
decision_json = json.loads(str(decision))
# Update conversation state
conv_state = await ctx.get(f"conv_{ev.conversation_id}")
conv_state["messages"].append({"role": "assistant", "content": ev.response})
conv_state["escalated"] = decision_json.get("escalate", False)
if decision_json.get("escalate"):
# Create support ticket
ticket_id = await self.ticket_system.create_ticket(
query=ev.query,
response=ev.response,
conversation_id=ev.conversation_id
)
return StopEvent(result={
"response": f"Your query has been escalated. Ticket ID: {ticket_id}",
"escalated": True,
"ticket_id": ticket_id
})
return StopEvent(result={
"response": ev.response,
"escalated": False,
"context_used": len(ev.context)
})
# Usage
async def main():
# Initialize components
vector_store = ... # Your vector store setup
ticket_system = ... # Your ticket system setup
workflow = SupportWorkflow(
vector_store=vector_store,
ticket_system=ticket_system,
timeout=60,
verbose=True
)
# Run workflow
result = await workflow.run(
query="How do I reset my password?",
conversation_id="conv_123"
)
print(result)Alternative: Multi-Agent Framework (MAF) Implementation
Architecture with Multiple Specialized Agents


Join the conversation! Your thoughts help the community grow.