143 lines
8.1 KiB
Python
143 lines
8.1 KiB
Python
"""Utility functions for the multi-agent system."""
|
||
|
||
|
||
def explain_supervisor_pattern():
|
||
"""Explain how the LangGraph supervisor pattern works."""
|
||
print("🏗️ MULTI-AGENT SUPERVISOR PATTERN EXPLANATION:")
|
||
print("=" * 60)
|
||
print("1. 🎯 SUPERVISOR: Receives user query and decides which agent to delegate to")
|
||
print("2. 🔄 TRANSFER: Uses transfer tools (e.g., transfer_to_system_info_worker)")
|
||
print("3. 🤖 AGENT: Specialized agent executes its task with its own prompt/tools")
|
||
print("4. 🔙 RETURN: Agent uses transfer_back_to_supervisor when done")
|
||
print("5. 🧠 DECISION: Supervisor analyzes results and decides next agent or final response")
|
||
print()
|
||
print("📋 WHAT 'Successfully transferred' MEANS:")
|
||
print(" - It's the response from a transfer tool call")
|
||
print(" - Indicates control handoff between supervisor and agent")
|
||
print(" - Each agent gets the full conversation context")
|
||
print(" - Agent's prompt guides how it processes that context")
|
||
print()
|
||
print("🔍 SUPERVISOR PROMPT (from config.py):")
|
||
print(" - Defines available agents and their specialties")
|
||
print(" - Guides delegation strategy (start with system_info & service_inventory)")
|
||
print(" - Agent prompts are in agents/*.py files")
|
||
print("=" * 60)
|
||
print()
|
||
|
||
|
||
def print_step_info(step_count: int, chunk):
|
||
"""Print formatted step information during streaming."""
|
||
print(f"\n🔄 STEP {step_count}:")
|
||
print("-" * 30)
|
||
|
||
try:
|
||
# Extract agent information from chunk
|
||
if isinstance(chunk, dict):
|
||
# Look for agent names in the chunk keys
|
||
agent_names = [key for key in chunk.keys() if key in [
|
||
'system_info_worker', 'service_inventory_worker', 'mariadb_analyzer',
|
||
'nginx_analyzer', 'phpfpm_analyzer', 'network_diag', 'cert_checker',
|
||
'risk_scorer', 'remediation_worker', 'harmonizer_worker', 'supervisor'
|
||
]]
|
||
|
||
if agent_names:
|
||
current_agent = agent_names[0]
|
||
print(f"🤖 ACTIVE AGENT: {current_agent}")
|
||
|
||
# Show the messages from this agent
|
||
agent_data = chunk[current_agent]
|
||
if 'messages' in agent_data:
|
||
messages = agent_data['messages']
|
||
if messages:
|
||
last_message = messages[-1]
|
||
# Get message type from the class name
|
||
message_type = type(last_message).__name__
|
||
print(f"💬 MESSAGE TYPE: {message_type}")
|
||
|
||
# Show content preview if available
|
||
if hasattr(last_message, 'content') and last_message.content:
|
||
content = last_message.content
|
||
content_length = len(content)
|
||
print(f"📏 CONTENT LENGTH: {content_length} characters")
|
||
|
||
# Show full content for final AI responses, abbreviated for others
|
||
if message_type == 'AIMessage':
|
||
print(f"📄 FULL CONTENT:")
|
||
print(content)
|
||
print() # Extra line for readability
|
||
else:
|
||
# Truncate other message types for brevity
|
||
preview = content[:200] + "..." if len(content) > 200 else content
|
||
print(f"📄 CONTENT PREVIEW:")
|
||
print(preview)
|
||
print() # Extra line for readability
|
||
|
||
# Show tool calls if any
|
||
if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
|
||
tool_calls = last_message.tool_calls
|
||
print(f"🔧 TOOL CALLS: {len(tool_calls)} tool(s)")
|
||
for i, tool_call in enumerate(tool_calls):
|
||
tool_name = getattr(tool_call, 'name', 'unknown')
|
||
print(f" {i+1}. {tool_name}")
|
||
# Show transfer details for supervisor delegation
|
||
if tool_name.startswith('transfer_to_'):
|
||
target_agent = tool_name.replace('transfer_to_', '')
|
||
print(f" 🎯 DELEGATING to: {target_agent}")
|
||
# Show the arguments/context being passed
|
||
if hasattr(tool_call, 'args') and tool_call.args:
|
||
print(f" 📋 Context/Args: {tool_call.args}")
|
||
|
||
# Show additional info for ToolMessage
|
||
if message_type == 'ToolMessage':
|
||
if hasattr(last_message, 'name'):
|
||
tool_name = last_message.name
|
||
print(f"🔧 TOOL NAME: {tool_name}")
|
||
|
||
# Explain what "Successfully transferred" means
|
||
if "transfer" in tool_name and "Successfully transferred" in content:
|
||
if tool_name.startswith('transfer_to_'):
|
||
target_agent = tool_name.replace('transfer_to_', '')
|
||
print(f" ℹ️ EXPLANATION: Supervisor delegated control to {target_agent}")
|
||
print(f" ℹ️ The {target_agent} will now execute its specialized tasks")
|
||
elif tool_name == 'transfer_back_to_supervisor':
|
||
print(f" ℹ️ EXPLANATION: Agent completed its task and returned control to supervisor")
|
||
print(f" ℹ️ Supervisor will decide the next step based on results")
|
||
|
||
if hasattr(last_message, 'tool_call_id'):
|
||
print(f"🔧 TOOL CALL ID: {last_message.tool_call_id}")
|
||
|
||
# Show conversation context for better understanding
|
||
agent_data = chunk[current_agent]
|
||
if 'messages' in agent_data and len(agent_data['messages']) > 1:
|
||
print(f"\n📚 CONVERSATION CONTEXT ({len(agent_data['messages'])} messages):")
|
||
for i, msg in enumerate(agent_data['messages'][-3:], start=max(0, len(agent_data['messages'])-3)):
|
||
msg_type = type(msg).__name__
|
||
if hasattr(msg, 'content') and msg.content:
|
||
preview = msg.content[:100].replace('\n', ' ')
|
||
if len(msg.content) > 100:
|
||
preview += "..."
|
||
print(f" {i+1}. {msg_type}: {preview}")
|
||
elif hasattr(msg, 'tool_calls') and msg.tool_calls:
|
||
tool_names = [getattr(tc, 'name', 'unknown') for tc in msg.tool_calls]
|
||
print(f" {i+1}. {msg_type}: Tool calls: {tool_names}")
|
||
else:
|
||
print(f" {i+1}. {msg_type}: (no content)")
|
||
|
||
print() # Extra spacing for readability
|
||
else:
|
||
print("📋 CHUNK DATA:")
|
||
# Show first few keys for debugging
|
||
chunk_keys = list(chunk.keys())[:3]
|
||
print(f" Keys: {chunk_keys}")
|
||
else:
|
||
print(f"📦 CHUNK TYPE: {type(chunk)}")
|
||
print(f"📄 CONTENT: {str(chunk)[:100]}...")
|
||
|
||
except Exception as e:
|
||
print(f"❌ Error processing chunk: {e}")
|
||
print(f"📦 CHUNK TYPE: {type(chunk)}")
|
||
if hasattr(chunk, '__dict__'):
|
||
print(f"📄 CHUNK ATTRIBUTES: {list(chunk.__dict__.keys())}")
|
||
|
||
print("-" * 30)
|