Multi-Agent Collaboration#
This page covers how to design systems where multiple specialized agents work together, including common collaboration patterns, orchestration strategies, context injection, and how agents decide when to escalate or hand off to another agent.
Learning Objectives#
Understand multi-agent system architecture
Implement agent collaboration patterns
Design agent teams
Coordinate agent workflows
Master context injection and state management
Understand how agents decide to end or escalate
Multi-Agent Systems#
Why Multiple Agents?#
Specialization#
Each agent specializes in a specific domain, improving accuracy and efficiency.
Parallel processing#
Multiple agents can process independent tasks in parallel, reducing processing time.
Modularity#
System is easier to maintain and extend when each agent is an independent module.
Scalability#
Easy to add new agents without affecting existing agents.
Use Cases#
Software development team#
Product Manager: Define requirements
Architect: Design system
Developer: Write code
Tester: Test and report bugs
Customer service#
FAQ Agent: Answer frequently asked questions
Ticket Agent: Manage support tickets
IT Support Agent: Technical support
Booking Agent: Schedule/book rooms
Content creation pipeline#
Research Agent: Search for information
Writer Agent: Write content
Editor Agent: Edit content
Collaboration Patterns#
1. Sequential (Pipeline)#
Agent 1 β Agent 2 β Agent 3#
Output of previous agent is input of next agent, linear workflow.
Example:
Research Agent β Analyzer Agent β Writer Agent
2. Hierarchical (Supervisor)#
Primary Assistant β Specialized Agents#
Primary assistant coordinates and routes to specialized agents.
FPT Chatbot Example:
Primary Assistant
βββ FAQ Agent (RAG)
βββ Ticket Support Agent
βββ IT Support Agent
βββ Booking Agent
3. Network (Peer-to-Peer)#
Agents communicate directly#
Agents communicate directly with each other, no supervisor needed.
4. Competitive#
Multiple agents solve same problem#
Multiple agents solve the same problem, choose the best solution.
Hierarchical Multi-Agent System#
Architecture#
Primary Assistant (Supervisor)
βββ FAQ Agent
βββ Ticket Support Agent
βββ IT Support Agent
βββ Booking Agent
State Management in Hierarchical System#
Each Agent has its own State#
In hierarchical systems, each agent can have its own state, but all share a common shared state.
State Definition:
from typing import Annotated, Optional, List
from typing_extensions import TypedDict, Literal
from langchain_core.messages import AnyMessage
from langgraph.graph import add_messages
from pydantic import EmailStr
class AgenticState(TypedDict):
"""Shared state for all agents"""
# Messages - automatically merged
messages: Annotated[list[AnyMessage], add_messages]
# Dialog stack - track agent hierarchy
dialog_state: Annotated[
list[Literal["primary_assistant", "ticket_agent", "it_agent", "booking_agent"]],
update_dialog_stack,
]
# Context information
conversation_id: Annotated[str, "Conversation ID"]
user_id: Annotated[str, "User ID"]
email: Annotated[Optional[EmailStr], "Email from context (optional)"]
Dialog Stack Management#
Push State (Switch to child agent):
def update_dialog_stack(left: list[str], right: Optional[str]) -> list[str]:
"""Push or pop dialog stack"""
if right is None:
return left
if right == "pop":
return left[:-1] # Pop: return to previous agent
return left + [right] # Push: add new agent to stack
Dialog Stack Example:
# Start
dialog_state = ["primary_assistant"]
# Switch to Ticket Agent
dialog_state = ["primary_assistant", "ticket_agent"]
# Pop (return to Primary)
dialog_state = ["primary_assistant"]
Pop State (Return to Primary Assistant)#
def pop_dialog_state(state: AgenticState) -> dict:
"""Pop dialog stack and return to primary assistant"""
messages = []
if state["messages"][-1].tool_calls:
messages.append(
ToolMessage(
content="Resuming dialog with the host assistant. "
"Please reflect on the past conversation and assist the user as needed.",
tool_call_id=state["messages"][-1].tool_calls[0]["id"],
)
)
return {
"dialog_state": "pop", # Trigger pop operation
"messages": messages,
}
Context Injection#
Why Context Injection?#
Context injection allows automatic injection of user information (email, user_id) into all tool calls without agents needing to remember and pass it each time.
Context Injection Implementation#
1. Store Context in State:
class AgenticState(TypedDict):
# ... other fields
user_id: Annotated[str, "User ID"]
email: Annotated[Optional[EmailStr], "Email from context (optional)"]
2. Inject Context into Tool Calls:
def inject_user_info(state: AgenticState, result):
"""Automatically inject user_id and email into tool calls"""
if hasattr(result, "tool_calls") and result.tool_calls:
for tool_call in result.tool_calls:
# Inject into tool calls that need user context
if tool_call["name"] in [
"ToTicketAssistant",
"ToITAssistant",
"ToBookingAssistant"
]:
# Automatically add user_id and email from state
tool_call["args"]["user_id"] = state["user_id"]
if state.get("email"):
tool_call["args"]["email"] = state["email"]
return result
3. Use in Primary Assistant:
def assistant_runnable_with_user_info(state):
"""Primary assistant with context injection"""
# Bind tools for primary assistant
result = (
primary_assistant_prompt
| llm.bind_tools([
ToTicketAssistant,
ToITAssistant,
ToBookingAssistant,
RAG_Agent
])
).invoke(state)
# Inject user info into tool calls
return inject_user_info(state, result)
4. Tool Schema with Optional Email:
from pydantic import BaseModel, EmailStr
from typing import Optional
class CreateTicket(BaseModel):
"""Schema for create_ticket tool"""
content: str
description: Optional[str] = None
customer_name: Optional[str] = None
customer_phone: Optional[str] = None
# Email is OPTIONAL - can be injected from context
email: Optional[EmailStr] = None
# user_id will be automatically injected
user_id: Optional[str] = None
5. Tool Implementation using Context:
@tool("create_ticket", args_schema=CreateTicket)
def create_ticket(
content: str,
description: Optional[str] = None,
customer_name: Optional[str] = None,
customer_phone: Optional[str] = None,
email: Optional[EmailStr] = None, # Optional - can be from context
user_id: Optional[str] = None, # Will be automatically injected
) -> dict:
"""Create new ticket"""
# Email may have been injected from context
# If not available, tool still works normally
ticket_id = f"TICKET_{generate_short_id()}"
new_ticket = Ticket(
ticket_id=ticket_id,
content=content,
description=description,
customer_name=customer_name,
customer_phone=customer_phone,
email=email, # Can be None
user_id=user_id,
status="Pending"
)
# Save to database
db.add(new_ticket)
db.commit()
# Only send email if email exists
if email:
send_email_notification(email, ticket_id)
return {
"ticket_id": ticket_id,
"message": f"Ticket created successfully. Email sent to {email}" if email else "Ticket created successfully."
}
State Transitions#
Entry Node (Enter Child Agent)#
def create_entry_node(assistant_name: str, new_dialog_state: str):
"""Create entry node to switch to child agent"""
def entry_node(state: AgenticState) -> dict:
tool_call_id = state["messages"][-1].tool_calls[0]["id"]
return {
"messages": [
ToolMessage(
content=f"The assistant is now the {assistant_name}. "
f"Reflect on the above conversation. "
f"Use the provided tools to assist the user. "
f"If task is complete, call CompleteOrEscalate to return control.",
tool_call_id=tool_call_id,
)
],
"dialog_state": new_dialog_state, # Push to stack
}
return entry_node
Routing Logic#
def route_primary_assistant(state: AgenticState):
"""Route from primary assistant to appropriate agent"""
last_message = state["messages"][-1]
if not hasattr(last_message, "tool_calls") or not last_message.tool_calls:
return END
tool_name = last_message.tool_calls[0]["name"]
# Route based on called tool
routing_map = {
"ToTicketAssistant": "enter_ticket_node",
"ToITAssistant": "enter_it_node",
"ToBookingAssistant": "enter_booking_node",
"rag_agent": "rag_agent_node",
}
return routing_map.get(tool_name, END)
Graph Construction#
def setup_agentic_graph():
"""Create graph with nodes and edges"""
builder = StateGraph(AgenticState)
# Primary Assistant
builder.add_node("primary_assistant", Assistant(assistant_runnable, "Primary Assistant"))
# Ticket Agent nodes
builder.add_node("enter_ticket_node", create_entry_node("Ticket Agent", "ticket_agent"))
builder.add_node("call_ticket_agent", Assistant(ticket_runnable, "Ticket Agent"))
builder.add_node("ticket_tools", create_tool_node_with_fallback(ticket_tools))
# IT Agent nodes
builder.add_node("enter_it_node", create_entry_node("IT Agent", "it_agent"))
builder.add_node("call_it_agent", Assistant(it_runnable, "IT Agent"))
builder.add_node("it_tools", create_tool_node_with_fallback(it_tools))
# Booking Agent nodes
builder.add_node("enter_booking_node", create_entry_node("Booking Agent", "booking_agent"))
builder.add_node("call_booking_agent", Assistant(booking_runnable, "Booking Agent"))
builder.add_node("booking_tools", create_tool_node_with_fallback(booking_tools))
# Pop state node
builder.add_node("leave_skill", pop_dialog_state)
# Edges
builder.add_edge(START, "primary_assistant")
builder.add_edge("enter_ticket_node", "call_ticket_agent")
builder.add_edge("ticket_tools", "call_ticket_agent")
builder.add_edge("enter_it_node", "call_it_agent")
builder.add_edge("it_tools", "call_it_agent")
builder.add_edge("enter_booking_node", "call_booking_agent")
builder.add_edge("booking_tools", "call_booking_agent")
builder.add_edge("leave_skill", "primary_assistant")
# Conditional routing
builder.add_conditional_edges(
"primary_assistant",
route_primary_assistant,
["enter_ticket_node", "enter_it_node", "enter_booking_node", "rag_agent_node", END]
)
builder.add_conditional_edges(
"call_ticket_agent",
route_ticket_agent,
["ticket_tools", "leave_skill", END]
)
return builder.compile(checkpointer=saver)
Tool Call Fallback#
Why Fallback?#
When tool call fails (network error, database error, validation error), system needs fallback mechanism to handle gracefully.
Fallback Implementation#
def create_tool_node_with_fallback(tools: list):
"""Create tool node with fallback handling"""
def log_tool_result(state):
"""Wrapper to log and handle errors"""
try:
# Execute tool
result = ToolNode(tools).invoke(state)
return result
except Exception as e:
# Fallback: return error message
last_message = state.get("messages", [])[-1]
tool_call_id = "unknown"
if last_message and hasattr(last_message, "tool_calls"):
tool_call_id = last_message.tool_calls[0]["id"]
return {
"messages": [
ToolMessage(
content=f"Error executing tool: {str(e)}. Please try again or contact support.",
tool_call_id=tool_call_id
)
]
}
# Use with_fallbacks from LangChain
tool_node = RunnableLambda(log_tool_result)
return tool_node.with_fallbacks(
[RunnableLambda(lambda state: {
"messages": [
ToolMessage(
content="Tool execution failed. Please try again.",
tool_call_id=state["messages"][-1].tool_calls[0]["id"]
if state.get("messages") and state["messages"][-1].tool_calls else "unknown"
)
]
})],
exception_key="error"
)
Complete or Escalate Pattern#
When should Agent End?#
Agent should end (END) when:
Task completed successfully
Fully answered userβs question
No additional information needed from user
When should Agent Escalate?#
Agent should escalate (return to Primary Assistant) when:
Task outside agentβs scope
Need capabilities only available in Primary Assistant
User changes mind or cancels task
Need clarification from user but cannot handle independently
Implementation CompleteOrEscalate#
1. Define Schema:
class CompleteOrEscalate(BaseModel):
"""Tool for agent to decide end or escalate"""
cancel: bool = True # True = escalate, False = continue
reason: str # Reason
class Config:
json_schema_extra = {
"example": {
"cancel": True,
"reason": "Task completed successfully. Returning to primary assistant."
},
"example 2": {
"cancel": True,
"reason": "User changed their mind. Need to return to primary assistant."
},
"example 3": {
"cancel": False,
"reason": "Need more information from user to complete the task."
}
}
2. Agent uses CompleteOrEscalate:
# Ticket Agent has tools
ticket_safe_tools = [track_ticket] # Read-only
ticket_sensitive_tools = [create_ticket, update_ticket, cancel_ticket] # Write operations
ticket_tools = ticket_safe_tools + ticket_sensitive_tools + [CompleteOrEscalate]
def create_ticket_tool(model):
"""Create ticket agent with tools"""
ticket_tools_runnable = (
ticket_assistant_prompt
| model.bind_tools(ticket_tools + [CompleteOrEscalate])
)
return ticket_tools_runnable
3. Routing Logic:
def route_ticket_agent(state: AgenticState):
"""Route for ticket agent"""
route = tools_condition(state)
if route == END:
return END
tool_calls = state["messages"][-1].tool_calls
tool_names = [tc.get("name") for tc in tool_calls]
# If calling CompleteOrEscalate with cancel=True -> return to primary
if "CompleteOrEscalate" in tool_names:
for tc in tool_calls:
if tc["name"] == "CompleteOrEscalate":
args = tc.get("args", {})
if args.get("cancel", True):
return "leave_skill" # Pop state
# Distinguish safe tools and sensitive tools
safe_toolnames = [t.name for t in ticket_safe_tools]
if all(tc["name"] in safe_toolnames for tc in tool_calls):
return "ticket_safe_tools"
return "ticket_sensitive_tools"
4. Agent Prompt usage guide:
TICKET_SYSTEM_PROMPT = """
You are a Ticket Support Agent for FPT Customer Service.
Your responsibilities:
- Create tickets for IT or customer support issues
- Track ticket status
- Update ticket information
- Cancel tickets when requested
When to use CompleteOrEscalate:
1. Task completed successfully -> cancel=True, reason="Task completed"
2. User changed their mind -> cancel=True, reason="User changed mind"
3. Question outside your scope -> cancel=True, reason="Out of scope"
4. Need more info but can't proceed -> cancel=False, reason="Need clarification"
Always try to complete the task before escalating.
"""
Tool Arg Schema in Multi-Tool System#
Why Tool Arg Schema?#
Tool arg schema helps:
Validate input before executing tool
Clearly document parameters
Type safety
Auto-completion in IDE
Pydantic Schema for Tools#
1. Basic Schema:
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, Annotated
class CreateTicket(BaseModel):
"""Schema for create_ticket tool"""
content: str = Field(..., description="Ticket content")
description: Optional[str] = Field(None, description="Detailed description")
customer_name: Optional[str] = Field(None, description="Customer name")
customer_phone: Optional[str] = Field(None, description="Phone number")
email: Optional[EmailStr] = Field(None, description="Email (optional, can be injected from context)")
user_id: Optional[str] = Field(None, description="User ID (will be automatically injected)")
class Config:
json_schema_extra = {
"example": {
"content": "Laptop screen is broken",
"description": "Screen repair needed",
"customer_name": "John Doe",
"customer_phone": "0123456789",
"email": "john@example.com"
}
}
2. Schema with Validation:
from pydantic import field_validator
class BookRoom(BaseModel):
"""Schema for book_room tool"""
reason: str = Field(..., description="Reason for booking")
time: datetime = Field(..., description="Booking time")
customer_name: Optional[str] = None
customer_phone: Optional[str] = None
email: Optional[EmailStr] = None
note: Optional[str] = Field(None, max_length=500)
@field_validator("time")
@classmethod
def validate_time(cls, v):
if v < datetime.now():
raise ValueError("Booking time must be in the future")
return v
@field_validator("customer_phone")
@classmethod
def validate_phone(cls, v):
if v and not v.isdigit():
raise ValueError("Phone number must contain only digits")
return v
3. Schema with Literal Types:
from typing import Literal
class UpdateTicket(BaseModel):
"""Schema for update_ticket tool"""
ticket_id: str = Field(..., description="Ticket ID")
content: Optional[str] = None
description: Optional[str] = None
status: Optional[Literal["Pending", "Resolving", "Finished", "Canceled"]] = None
email: Optional[EmailStr] = None
4. Use Schema in Tool:
from langchain_core.tools import tool
@tool("create_ticket", args_schema=CreateTicket)
def create_ticket(
content: str,
description: Optional[str] = None,
customer_name: Optional[str] = None,
customer_phone: Optional[str] = None,
email: Optional[EmailStr] = None,
user_id: Optional[str] = None,
) -> dict:
"""Create new ticket"""
# Implementation...
pass
5. Schema for Multi-Tool System:
# Each agent has its own tool schemas
ticket_tool_schemas = [CreateTicket, UpdateTicket, TrackTicket, CancelTicket]
it_tool_schemas = [TavilySearch] # IT agent only has 1 tool
booking_tool_schemas = [BookRoom, UpdateBooking, TrackBooking, CancelBooking]
# Primary assistant has routing tools
primary_tool_schemas = [
ToTicketAssistant,
ToITAssistant,
ToBookingAssistant,
RAG_Agent
]
Communication Patterns#
Message Passing#
Structured Messages#
{
"from": "primary_assistant",
"to": "ticket_agent",
"type": "tool_call",
"tool": "ToTicketAssistant",
"args": {
"user_id": "user123",
"email": "user@example.com"
}
}
State Updates#
Each agent updates state through messages:
# Agent returns message
return {
"messages": [
AIMessage(
content="Ticket created successfully",
tool_calls=[...]
)
]
}
Error Handling#
Retry logic#
@retry(max_attempts=3, backoff=exponential_backoff)
def create_ticket_with_retry(...):
return create_ticket(...)
Graceful degradation#
def create_ticket_graceful(...):
try:
# Try full flow
return create_ticket(...)
except DatabaseError:
# Fallback: only save to memory
return {"ticket_id": "temp_123", "status": "pending_save"}
Communication Failure#
When agent cannot communicate, system will:
Log error
Return error message to user
Optionally retry or escalate
Caching#
Cache tool call results to avoid repeated calls:
from functools import lru_cache
@lru_cache(maxsize=100)
def get_ticket_status(ticket_id: str):
return db.query(Ticket).filter(Ticket.ticket_id == ticket_id).first()
Multi-Agent Pattern Comparison#
Detailed Comparison Table#
Criteria |
ReAct Agent |
Hierarchical Multi-Agent |
Supervisor Pattern |
|---|---|---|---|
Architecture |
Single agent with reasoning loop |
Multiple specialized agents with hierarchy |
Central supervisor routing to specialists |
Number of Agents |
1 agent |
3-10+ agents |
1 supervisor + N workers |
Complexity |
Low - Simple |
High - Complex |
Medium |
State Management |
Simple state |
Shared state + Dialog stack |
Centralized state |
Reasoning |
Step-by-step with tools |
Distributed reasoning |
Supervisor decides routing |
Tool Access |
All tools available |
Tools distributed by agent |
Tools grouped by specialist |
Scalability |
Limited (1 agent) |
Good (easy to add agents) |
Good (add specialists) |
Latency |
Low |
Medium-High |
Medium |
Cost |
Low |
High |
Medium-High |
Use Cases |
Simple tasks, Q&A |
Complex workflows, enterprises |
Multi-domain support |
Best For |
- FAQ answering- Simple automation- Single-domain tasks |
- Enterprise systems- Multi-step workflows- Department coordination |
- Customer service- Multi-category support- Triage systems |
PRACTICE:#
FPT Customer Chatbot System - Multi-Agent System#
1. System overview#
FPT customer chatbot system is designed with hierarchical multi-agent architecture, allowing users to interact with multiple specialized agents through a primary assistant. The system supports: FAQ about FPT policies, support ticket management, IT support, and meeting room booking.
REFERENCE: https://langchain-ai.github.io/langgraph/tutorials/customer-support/customer-support/
REQUIREMENT: REFERENCE THIS LINK
2. Context Injection#
2.1. User Information#
System must support automatic context injection from user information
Users can provide email when starting conversation (OPTIONAL)
This information is automatically injected into all related tool calls if provided
Email is stored in conversation context and can be used by all agents
2.2. Context Management#
Context is maintained throughout the entire conversation
Child agents can access and use context information from primary assistant
Context can be updated during conversation
3. FAQ Agent - RAG Tool for FPT Policy#
3.1. Functionality#
Answer questions about FPT policies and regulations
Search information from knowledge base about FPT-related issues
Support multiple languages (Vietnamese, English)
3.2. Requirements#
Agent must be able to understand questions and search for related information
Results must be accurate and have reference sources
Support semantic search to understand user intent
4. Ticket Support Agent#
4.1. Functionality#
Ticket Support Agent handles requests to create, track and update support tickets for IT or customer support issues.
4.2. Tools#
4.2.1. Create Ticket#
Purpose: Create new ticket for IT or customer support issues
Required information:
Ticket content (content)
Detailed description (description)
Optional information:
Email (automatically injected from context if available)
Customer name (customer_name)
Phone number (customer_phone)
Result: Create ticket with βPendingβ status and return ticket_id
4.2.2. Track Ticket#
Purpose: Track status and information of ticket
Input: ticket_id
Output: Complete ticket information including:
ticket_id
content
description
status (Pending, Resolving, Canceled, Finished)
customer_name
customer_phone
email (if available)
creation time
user_id
4.2.3. Update Ticket#
Purpose: Update information of existing ticket
Required input: ticket_id
Optional input:
content
description
customer_name
customer_phone
email (automatically injected from context if available)
Rule: Only update provided fields, keep other fields unchanged
4.3. Ticket Status Management#
Status: Pending β Resolving β Finished
Special status: Canceled (can be set from any status before Finished)
5. IT Support Agent#
5.1. Functionality#
IT Support Agent helps users resolve technical issues with computers and electronic devices through Tavily search tool.
5.2. Tool: Tavily Search#
Purpose: Search for troubleshooting information about computers and electronic devices
Input: Question about technical issues
Output:
Troubleshooting guide information
Suggested solutions
Reference links from reliable sources
Requirements:
Search results must be from reliable technology sources
Support search in Vietnamese and English
Return practical and easy-to-understand information
6. Booking Agent#
6.1. Functionality#
Booking Agent handles meeting room booking for users, similar to Appointment Agent but specifically for room booking.
6.2. Tools#
6.2.1. Book Room#
Purpose: Book meeting room
Required information:
Reason for booking (reason)
Time (time)
Optional information:
Email (automatically injected from context if available)
Booker name (customer_name)
Phone number (customer_phone)
Note (note)
Result: Create booking with βScheduledβ status and return booking_id
6.2.2. Track Booking#
Purpose: Track booking information and status
Input: booking_id
Output: Complete booking information
6.2.3. Update Booking#
Purpose: Update booking information
Required input: booking_id
Optional input: reason, time, customer_name, customer_phone, note, email
Rule: Only update provided fields
6.2.4. Cancel Booking#
Purpose: Cancel booking
Input: booking_id
Rule: Can only cancel unfinished bookings
6.3. Booking Status Management#
Status: Scheduled β Finished
Special status: Canceled
7. Primary Assistant#
7.1. Functionality#
Primary Assistant is the main agent that coordinates and routes requests to appropriate specialized agents.
7.2. Routing Requirements#
Analyze user intent
Route to FAQ Agent when questions about FPT policies
Route to Ticket Support Agent when user wants to create/track/update ticket
Route to IT Support Agent when user has troubleshooting issues
Route to Booking Agent when user wants to book room
Support returning to primary assistant after completing task in child agent
8. Database Schema#
8.2. Ticket Table#
Store support ticket information:
ticket_id(Primary Key): Unique ticket IDcontent: Ticket contentdescription: Detailed descriptioncustomer_name: Customer namecustomer_phone: Phone numberemail: Email (optional, can be null)time: Ticket creation timestatus: Status (Pending, Resolving, Canceled, Finished)
8.3. Booking Table#
Store booking information:
booking_id(Primary Key): Unique booking IDcustomer_name: Booker namecustomer_phone: Phone numberemail: Email (optional, can be null)reason: Reason for bookingtime: Booking timenote: Note (optional)status: Status (Scheduled, Canceled, Finished)
8.4. Conversation Context Table#
Store conversation context:
conversation_id(Primary Key): Unique conversation IDuser_id(Foreign Key): Reference to Customer Infoemail: Email from context (optional)created_at: Conversation creation timeupdated_at: Last update time
9. Business Logic Requirements#
9.1. Ticket Management#
New tickets always have βPendingβ status
Can only update tickets not in βFinishedβ or βCanceledβ status
Email is OPTIONAL, can be injected from context or set to null
System still creates ticket successfully even without email
9.2. Booking Management#
New bookings always have βScheduledβ status
Can only cancel bookings not in βFinishedβ status
Email is OPTIONAL, can be injected from context or set to null
System still creates booking successfully even without email
9.3. Context Management#
Email is stored in conversation context when user provides it
Tools automatically use email from context if not provided directly (if available in context)
Users can override email in tool call if desired
9.4. Agent Communication#
Primary assistant can transfer control to child agent
Child agent can return to primary assistant after completing task
Context is shared between all agents in the same conversation
10. ADVANCED - Advanced Features (OPTIONAL)#
10.1. Email Notification (OPTIONAL)#
Feature: Send email notification to users when there are changes to ticket or booking
Requirements:
Email is completely OPTIONAL in all tools related to ticket and booking
System only sends email if user provides email in context or in tool call
If no email, system still works normally and does not send notification
Email can be automatically injected from context if previously provided
Users can override email in each tool call if they want to use different email
10.2. Human-in-the-Loop Confirmation (OPTIONAL)#
Feature: User confirmation before performing important operations
Requirements:
Human-in-the-loop confirmation is completely OPTIONAL
System can operate with or without confirmation
If enabled, system will request confirmation before:
Creating new ticket
Creating new booking
Canceling ticket or booking
Updating important information
If not enabled, system will perform operations directly
Confirmation can be configured at conversation or tool level
10.3. Notes on Advanced Features#
All features in ADVANCED section are OPTIONAL
System must function fully even without email or human-in-the-loop confirmation
Implementation of these features does not affect core system functionality
Users can use system without providing email or without confirmation
12. Workflow#
12.1. Ticket Creation Flow#
User can provide email (OPTIONAL - first time)
Primary assistant receives ticket creation request
Route to Ticket Support Agent
Ticket Support Agent uses create_ticket tool
Tool automatically injects email from context (if available in context)
Create ticket in database (with or without email)
Return ticket_id to user
12.2. Booking Flow#
User requests room booking
Primary assistant routes to Booking Agent
Booking Agent collects information (reason, time)
Use book_room tool with email from context (if available in context)
Create booking in database (with or without email)
Return booking_id to user
12.3. FAQ Flow#
User asks about FPT policies
Primary assistant routes to FAQ Agent
FAQ Agent uses RAG tool to search
Return information from knowledge base
12.4 IT Support Flow#
User asks about technical issues
Primary assistant routes to IT Support Agent
IT Support Agent uses Tavily tool to search
Return troubleshooting guide