improve logging

This commit is contained in:
Gaetan Hurel 2025-06-26 16:59:37 +02:00
parent c20be407d5
commit ea1519a208
No known key found for this signature in database
2 changed files with 152 additions and 124 deletions

View File

@ -14,6 +14,9 @@ if __name__ == "__main__":
messages = [] messages = []
print("Welcome to the multi-agent sysadmin assistant!") print("Welcome to the multi-agent sysadmin assistant!")
print("Type your sysadmin question below. Type 'exit' to quit.") print("Type your sysadmin question below. Type 'exit' to quit.")
print("\n💡 Note: When agents execute shell commands, you may see command output")
print(" appear between the structured step logs. This is normal behavior.")
print(" The output belongs to the agent that was most recently active.")
while True: while True:
user_input = input("\n📝 User: ") user_input = input("\n📝 User: ")
if user_input.strip().lower() == 'exit': if user_input.strip().lower() == 'exit':
@ -22,27 +25,10 @@ if __name__ == "__main__":
messages.append({"role": "user", "content": user_input}) messages.append({"role": "user", "content": user_input})
query = {"messages": messages} query = {"messages": messages}
print("\n=== Using invoke() method ===") print("\n=== Processing with detailed step-by-step analysis ===")
result = supervisor.invoke(query)
print("\n📊 FINAL RESULT:")
print("-" * 40)
print(result["messages"][-1].content)
print("-" * 40)
print(f"\n📈 Total messages exchanged: {len(result['messages'])}")
# Add the assistant's reply to the conversation history
messages.append({"role": "assistant", "content": result["messages"][-1].content})
# Ask if the user wants to continue
cont = input("\nWould you like to continue the conversation? (y/n): ")
if cont.strip().lower() not in ('y', 'yes'):
print("Session ended.")
break
print("\n=== Using stream() method for detailed step-by-step analysis ===")
step_count = 0 step_count = 0
max_steps = 20 # Prevent infinite loops max_steps = 20 # Prevent infinite loops
final_result = None
try: try:
chunks_processed = [] chunks_processed = []
@ -51,21 +37,50 @@ if __name__ == "__main__":
chunks_processed.append(chunk) chunks_processed.append(chunk)
print_step_info(step_count, chunk) print_step_info(step_count, chunk)
# Store the final result for conversation history
if isinstance(chunk, dict):
for agent_name, agent_data in chunk.items():
if 'messages' in agent_data and agent_data['messages']:
last_msg = agent_data['messages'][-1]
if hasattr(last_msg, 'content') and last_msg.content:
final_result = last_msg.content
# Safety check to prevent infinite loops # Safety check to prevent infinite loops
if step_count >= max_steps: if step_count >= max_steps:
print(f"\n⚠️ Reached maximum steps ({max_steps}), stopping stream...") print(f"\n⚠️ Reached maximum steps ({max_steps}), stopping stream...")
break break
print(f"\n✅ Streaming completed successfully with {step_count} steps") print(f"\n✅ Analysis completed with {step_count} steps")
print(f"📊 Total chunks processed: {len(chunks_processed)}")
# Check if the last chunk contains a complete final response # Add the assistant's reply to the conversation history
if chunks_processed: if final_result:
last_chunk = chunks_processed[-1] messages.append({"role": "assistant", "content": final_result})
print(f"🔍 Last chunk keys: {list(last_chunk.keys()) if isinstance(last_chunk, dict) else type(last_chunk)}")
print(f"\n📊 FINAL SUMMARY:")
print("-" * 60)
if final_result:
print(final_result)
else:
print("Analysis completed - check the detailed steps above for results")
print("-" * 60)
except Exception as e: except Exception as e:
print(f"\n❌ Streaming error after {step_count} steps: {e}") print(f"\n❌ Streaming error after {step_count} steps: {e}")
print("💡 The invoke() method worked fine, so the supervisor itself is functional.") print("💡 Falling back to basic invoke method...")
import traceback try:
traceback.print_exc() result = supervisor.invoke(query)
final_result = result["messages"][-1].content
messages.append({"role": "assistant", "content": final_result})
print(f"\n📊 FINAL RESULT:")
print("-" * 40)
print(final_result)
print("-" * 40)
except Exception as fallback_error:
print(f"❌ Fallback also failed: {fallback_error}")
continue
# Ask if the user wants to continue
cont = input("\nWould you like to continue the conversation? (y/n): ")
if cont.strip().lower() not in ('y', 'yes'):
print("Session ended.")
break

View File

@ -26,12 +26,12 @@ def explain_supervisor_pattern():
def print_step_info(step_count: int, chunk): def print_step_info(step_count: int, chunk):
"""Print formatted step information during streaming.""" """Print formatted step information during streaming with clear agent actions."""
print(f"\n🔄 STEP {step_count}:") print(f"\n{'='*60}")
print("-" * 30) print(f"STEP {step_count}")
print(f"{'='*60}")
try: try:
# Extract agent information from chunk
if isinstance(chunk, dict): if isinstance(chunk, dict):
# Look for agent names in the chunk keys # Look for agent names in the chunk keys
agent_names = [key for key in chunk.keys() if key in [ agent_names = [key for key in chunk.keys() if key in [
@ -41,102 +41,115 @@ def print_step_info(step_count: int, chunk):
]] ]]
if agent_names: if agent_names:
current_agent = agent_names[0] current_agent = agent_names[0].upper()
print(f"🤖 ACTIVE AGENT: {current_agent}") agent_data = chunk[agent_names[0]]
# Show the messages from this agent if 'messages' in agent_data and agent_data['messages']:
agent_data = chunk[current_agent] last_message = agent_data['messages'][-1]
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__ message_type = type(last_message).__name__
print(f"💬 MESSAGE TYPE: {message_type}")
# Show content preview if available # Handle different message types with clear formatting
if hasattr(last_message, 'content') and last_message.content: if message_type == 'HumanMessage':
content = last_message.content # This is typically the user query or supervisor instruction
content_length = len(content) content = getattr(last_message, 'content', '')
print(f"📏 CONTENT LENGTH: {content_length} characters") if current_agent == 'SUPERVISOR':
print(f"[ SUPERVISOR ] received user query: {content[:100]}{'...' if len(content) > 100 else ''}")
# 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: else:
# Truncate other message types for brevity print(f"[ {current_agent} ] received prompt from supervisor: {content[:100]}{'...' if len(content) > 100 else ''}")
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 elif message_type == 'ToolMessage':
if hasattr(last_message, 'tool_calls') and last_message.tool_calls: # Result of tool execution
tool_calls = last_message.tool_calls tool_name = getattr(last_message, 'name', 'unknown')
print(f"🔧 TOOL CALLS: {len(tool_calls)} tool(s)") content = getattr(last_message, 'content', '')
for i, tool_call in enumerate(tool_calls):
tool_name = getattr(tool_call, 'name', 'unknown') if "Successfully transferred" in content:
print(f" {i+1}. {tool_name}")
# Show transfer details for supervisor delegation
if tool_name.startswith('transfer_to_'): if tool_name.startswith('transfer_to_'):
target_agent = tool_name.replace('transfer_to_', '') target_agent = tool_name.replace('transfer_to_', '').upper()
print(f" 🎯 DELEGATING to: {target_agent}") print(f"[ SUPERVISOR ] successfully transferred control to {target_agent}")
# Show the arguments/context being passed print(f"[ SUPERVISOR ] {target_agent} will now analyze the situation and execute necessary commands")
if hasattr(tool_call, 'args') and tool_call.args: print(f"[ SUPERVISOR ] (Any shell command output below belongs to {target_agent})")
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': elif tool_name == 'transfer_back_to_supervisor':
print(f" EXPLANATION: Agent completed its task and returned control to supervisor") print(f"[ {current_agent} ] completed analysis and transferred control back to supervisor")
print(f" Supervisor will decide the next step based on results") print(f"[ {current_agent} ] (Any shell command output above was from {current_agent})")
if hasattr(last_message, 'tool_call_id'): # Show the result being sent back to supervisor
print(f"🔧 TOOL CALL ID: {last_message.tool_call_id}") # Look for the last AIMessage before this transfer to get the result
# Show conversation context for better understanding
agent_data = chunk[current_agent]
if 'messages' in agent_data and len(agent_data['messages']) > 1: if 'messages' in agent_data and len(agent_data['messages']) > 1:
print(f"\n📚 CONVERSATION CONTEXT ({len(agent_data['messages'])} messages):") # Look for the most recent AIMessage with content
for i, msg in enumerate(agent_data['messages'][-3:], start=max(0, len(agent_data['messages'])-3)): for msg in reversed(agent_data['messages'][:-1]): # Exclude current ToolMessage
msg_type = type(msg).__name__ if type(msg).__name__ == 'AIMessage' and hasattr(msg, 'content') and msg.content:
if hasattr(msg, 'content') and msg.content: result_content = msg.content
preview = msg.content[:100].replace('\n', ' ') if len(result_content) > 300:
if len(msg.content) > 100: preview = result_content[:300] + "..."
preview += "..." print(f"[ {current_agent} ] sending result to supervisor (preview): {preview}")
print(f" {i+1}. {msg_type}: {preview}") print(f"[ {current_agent} ] (full result length: {len(result_content)} characters)")
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: else:
print(f" {i+1}. {msg_type}: (no content)") print(f"[ {current_agent} ] sending result to supervisor: {result_content}")
break
else:
print(f"[ {current_agent} ] sending analysis results to supervisor")
else:
print(f"[ {current_agent} ] sending analysis results to supervisor")
else:
# Other tool execution result
if len(content) > 200:
preview = content[:200] + "..."
print(f"[ {current_agent} ] tool result preview: {preview}")
print(f"[ {current_agent} ] (full result length: {len(content)} characters)")
else:
print(f"[ {current_agent} ] tool result: {content}")
elif message_type == 'AIMessage':
# Agent is responding or making tool calls
content = getattr(last_message, 'content', '')
tool_calls = getattr(last_message, 'tool_calls', [])
if tool_calls:
for tool_call in tool_calls:
tool_name = getattr(tool_call, 'name', 'unknown')
if tool_name.startswith('transfer_to_'):
target_agent = tool_name.replace('transfer_to_', '').upper()
args = getattr(tool_call, 'args', {})
context = str(args)[:150] + "..." if len(str(args)) > 150 else str(args)
print(f"[ SUPERVISOR ] calling {target_agent} with context: {context}")
elif tool_name == 'transfer_back_to_supervisor':
print(f"[ {current_agent} ] completed task, transferring back to supervisor")
print() # Extra spacing for readability
else: else:
print("📋 CHUNK DATA:") print(f"[ {current_agent} ] using tool: {tool_name}")
# Show first few keys for debugging args = getattr(tool_call, 'args', {})
chunk_keys = list(chunk.keys())[:3] if args:
print(f" Keys: {chunk_keys}") args_preview = str(args)[:100] + "..." if len(str(args)) > 100 else str(args)
print(f"[ {current_agent} ] tool arguments: {args_preview}")
elif content:
# Final response from agent
if len(content) > 200:
preview = content[:200] + "..."
print(f"[ {current_agent} ] response preview: {preview}")
print(f"[ {current_agent} ] (full response length: {len(content)} characters)")
else: else:
print(f"📦 CHUNK TYPE: {type(chunk)}") print(f"[ {current_agent} ] response: {content}")
print(f"📄 CONTENT: {str(chunk)[:100]}...")
else:
print(f"[ {current_agent} ] {message_type}: {getattr(last_message, 'content', 'No content')[:100]}")
else:
print(f"[ {current_agent} ] no message data available")
else:
print("[ SYSTEM ] processing chunk with keys:", list(chunk.keys())[:3])
else:
print(f"[ SYSTEM ] received {type(chunk).__name__}: {str(chunk)[:100]}{'...' if len(str(chunk)) > 100 else ''}")
except Exception as e: except Exception as e:
print(f"❌ Error processing chunk: {e}") print(f"[ ERROR ] processing step {step_count}: {e}")
print(f"📦 CHUNK TYPE: {type(chunk)}") print(f"[ DEBUG ] chunk type: {type(chunk)}")
if hasattr(chunk, '__dict__'): if hasattr(chunk, '__dict__'):
print(f"📄 CHUNK ATTRIBUTES: {list(chunk.__dict__.keys())}") print(f"[ DEBUG ] chunk attributes: {list(chunk.__dict__.keys())}")
print("-" * 30) print(f"{'='*60}")
print(f"NOTE: Shell command output may appear below before the next step")