2025-06-26 14:52:36 +02:00

143 lines
8.1 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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)