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: message_type = type(last_message).__name__
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 # 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:
# 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: else:
print(f" {i+1}. {msg_type}: (no content)") print(f"[ {current_agent} ] received prompt from supervisor: {content[:100]}{'...' if len(content) > 100 else ''}")
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_', '').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"[ {current_agent} ] completed analysis and transferred control back to supervisor")
print(f"[ {current_agent} ] (Any shell command output above was from {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:
# 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"[ {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")
else:
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"[ {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")
print() # Extra spacing for readability
else: else:
print("📋 CHUNK DATA:") print("[ SYSTEM ] processing chunk with keys:", list(chunk.keys())[:3])
# Show first few keys for debugging
chunk_keys = list(chunk.keys())[:3]
print(f" Keys: {chunk_keys}")
else: else:
print(f"📦 CHUNK TYPE: {type(chunk)}") print(f"[ SYSTEM ] received {type(chunk).__name__}: {str(chunk)[:100]}{'...' if len(str(chunk)) > 100 else ''}")
print(f"📄 CONTENT: {str(chunk)[:100]}...")
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")