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 = []
print("Welcome to the multi-agent sysadmin assistant!")
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:
user_input = input("\n📝 User: ")
if user_input.strip().lower() == 'exit':
@ -22,27 +25,10 @@ if __name__ == "__main__":
messages.append({"role": "user", "content": user_input})
query = {"messages": messages}
print("\n=== Using invoke() method ===")
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 ===")
print("\n=== Processing with detailed step-by-step analysis ===")
step_count = 0
max_steps = 20 # Prevent infinite loops
final_result = None
try:
chunks_processed = []
@ -51,21 +37,50 @@ if __name__ == "__main__":
chunks_processed.append(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
if step_count >= max_steps:
print(f"\n⚠️ Reached maximum steps ({max_steps}), stopping stream...")
break
print(f"\n✅ Streaming completed successfully with {step_count} steps")
print(f"📊 Total chunks processed: {len(chunks_processed)}")
print(f"\n✅ Analysis completed with {step_count} steps")
# Check if the last chunk contains a complete final response
if chunks_processed:
last_chunk = chunks_processed[-1]
print(f"🔍 Last chunk keys: {list(last_chunk.keys()) if isinstance(last_chunk, dict) else type(last_chunk)}")
# Add the assistant's reply to the conversation history
if final_result:
messages.append({"role": "assistant", "content": final_result})
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:
print(f"\n❌ Streaming error after {step_count} steps: {e}")
print("💡 The invoke() method worked fine, so the supervisor itself is functional.")
import traceback
traceback.print_exc()
print("💡 Falling back to basic invoke method...")
try:
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):
"""Print formatted step information during streaming."""
print(f"\n🔄 STEP {step_count}:")
print("-" * 30)
"""Print formatted step information during streaming with clear agent actions."""
print(f"\n{'='*60}")
print(f"STEP {step_count}")
print(f"{'='*60}")
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 [
@ -41,102 +41,115 @@ def print_step_info(step_count: int, chunk):
]]
if agent_names:
current_agent = agent_names[0]
print(f"🤖 ACTIVE AGENT: {current_agent}")
current_agent = agent_names[0].upper()
agent_data = chunk[agent_names[0]]
# 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
if 'messages' in agent_data and agent_data['messages']:
last_message = agent_data['messages'][-1]
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
# Handle different message types with clear formatting
if message_type == 'HumanMessage':
# This is typically the user query or supervisor instruction
content = getattr(last_message, 'content', '')
if current_agent == 'SUPERVISOR':
print(f"[ SUPERVISOR ] received user query: {content[:100]}{'...' if len(content) > 100 else ''}")
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
print(f"[ {current_agent} ] received prompt from supervisor: {content[:100]}{'...' if len(content) > 100 else ''}")
# 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
elif message_type == 'ToolMessage':
# Result of tool execution
tool_name = getattr(last_message, 'name', 'unknown')
content = getattr(last_message, 'content', '')
if "Successfully transferred" in content:
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")
target_agent = tool_name.replace('transfer_to_', '').upper()
print(f"[ SUPERVISOR ] successfully transferred control to {target_agent}")
print(f"[ SUPERVISOR ] {target_agent} will now analyze the situation and execute necessary commands")
print(f"[ SUPERVISOR ] (Any shell command output below belongs to {target_agent})")
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")
print(f"[ {current_agent} ] completed analysis and transferred control back to supervisor")
print(f"[ {current_agent} ] (Any shell command output above was from {current_agent})")
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]
# Show the result being sent back to supervisor
# Look for the last AIMessage before this transfer to get the result
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}")
# Look for the most recent AIMessage with content
for msg in reversed(agent_data['messages'][:-1]): # Exclude current ToolMessage
if type(msg).__name__ == 'AIMessage' and hasattr(msg, 'content') and msg.content:
result_content = msg.content
if len(result_content) > 300:
preview = result_content[:300] + "..."
print(f"[ {current_agent} ] sending result to supervisor (preview): {preview}")
print(f"[ {current_agent} ] (full result length: {len(result_content)} characters)")
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:
print("📋 CHUNK DATA:")
# Show first few keys for debugging
chunk_keys = list(chunk.keys())[:3]
print(f" Keys: {chunk_keys}")
print(f"[ {current_agent} ] using tool: {tool_name}")
args = getattr(tool_call, 'args', {})
if args:
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:
print(f"📦 CHUNK TYPE: {type(chunk)}")
print(f"📄 CONTENT: {str(chunk)[:100]}...")
print(f"[ {current_agent} ] response: {content}")
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:
print(f"❌ Error processing chunk: {e}")
print(f"📦 CHUNK TYPE: {type(chunk)}")
print(f"[ ERROR ] processing step {step_count}: {e}")
print(f"[ DEBUG ] chunk type: {type(chunk)}")
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")