ssh tool WIP: sudo not working

This commit is contained in:
Gaetan Hurel 2025-06-29 15:15:27 +02:00
parent 38b77a657f
commit a81dd5484d
No known key found for this signature in database
14 changed files with 299 additions and 886 deletions

View File

@ -1,172 +0,0 @@
# Implementation Complete: SSH + Shell Tools Integration
## ✅ What We've Implemented
Successfully integrated SSH and Shell tools into both the **simple-react-agent** and **multi-agent-supervisor** approaches, providing comprehensive local and remote system administration capabilities.
## 📁 Files Created/Modified
### Multi-Agent Supervisor
```
multi-agent-supervisor/
├── custom_tools/
│ ├── __init__.py # ✅ Updated: Exports ShellTool, SSHTool, print_poem
│ ├── ssh_tool.py # ✅ New: SSH tool implementation
│ ├── shell_tool_wrapper.py # ✅ New: Shell tool wrapper
│ └── poem_tool.py # ✅ Existing: Motivational poems
└── demo_tools.py # ✅ New: Working demo script
```
### Simple React Agent
```
simple-react-agent/
├── custom_tools/
│ ├── __init__.py # ✅ Updated: Exports ShellTool, SSHTool, print_poem
│ ├── ssh_tool.py # ✅ New: SSH tool implementation
│ ├── shell_tool_wrapper.py # ✅ New: Shell tool wrapper
│ └── poem_tool.py # ✅ Existing: Motivational poems
└── demo_tools.py # ✅ New: Working demo script
```
### Root Level Documentation
```
project-root/
├── SSH_TOOL_INTEGRATION.md # ✅ New: Comprehensive usage guide
└── tools_integration_demo.py # ✅ New: Integration examples
```
## 🛠️ Tools Available
### 1. ShellTool (Local Commands)
- **Purpose**: Execute commands on the local machine
- **Usage**: `shell_tool.invoke({"commands": ["df -h", "ps aux"]})`
- **Features**: Persistent bash process, command validation, safety warnings
### 2. SSHTool (Remote Commands)
- **Purpose**: Execute commands on remote servers via SSH
- **Usage**: `ssh_tool = SSHTool(host="server.com", username="admin", key_filename="~/.ssh/key")`
- **Features**: Persistent SSH connections, key/password auth, human confirmation
### 3. print_poem (Morale Booster)
- **Purpose**: Generate motivational poems for debugging sessions
- **Usage**: `print_poem.invoke({"poem_type": "debugging"})`
- **Features**: Multiple poem types, debugging-themed content
## 🔧 Dependencies Installed
- ✅ **paramiko==3.5.1**: SSH functionality
- ✅ **bcrypt==4.3.0**: SSH encryption support
- ✅ **cryptography==45.0.4**: Security libraries
- ✅ **cffi==1.17.1**: Foreign function interface
- ✅ **pycparser==2.22**: C parser for Python
- ✅ **pynacl==1.5.0**: Networking and cryptography
## 🚀 Demo Results
### Multi-Agent Supervisor Demo
```bash
cd multi-agent-supervisor && python demo_tools.py
```
- ✅ Shell tool: Successfully executed local system diagnostics
- ✅ SSH tool: Configuration examples and integration patterns shown
- ✅ Network analysis: Local network configuration and connectivity tests
- ✅ Poem tool: Generated debugging motivation poem
### Simple React Agent Demo
```bash
cd simple-react-agent && python demo_tools.py
```
- ✅ Shell tool: Local development environment analysis
- ✅ Agent integration: Complete code examples for LangGraph integration
- ✅ Troubleshooting scenario: Real-world usage patterns
- ✅ Poem tool: Tech-themed motivational content
## 🔐 Security Features
1. **Human Confirmation**: `ask_human_input=True` parameter
2. **Connection Timeouts**: Configurable SSH timeouts
3. **Warning Messages**: Automatic security warnings
4. **Secure Authentication**: SSH key-based authentication support
5. **Connection Management**: Proper SSH connection cleanup
## 📊 Tool Integration Patterns
### Local + Remote Analysis
```python
# Check local system
local_result = shell_tool.invoke({"commands": ["df -h", "free -m"]})
# Check remote system
remote_result = ssh_tool.run(commands=["df -h", "free -m"])
```
### Multi-Server Management
```python
# Multiple SSH tools for different servers
web_server = SSHTool(host="web1.com", username="admin", key_filename="~/.ssh/web_key")
db_server = SSHTool(host="db1.com", username="admin", key_filename="~/.ssh/db_key")
tools = [ShellTool(), web_server, db_server, print_poem]
```
### Agent Integration
```python
from custom_tools import ShellTool, SSHTool, print_poem
# Simple React Agent
agent = create_react_agent(llm, [shell_tool, ssh_tool, print_poem])
# Multi-Agent Supervisor
enhanced_tools = [shell_tool, ssh_tool, print_poem]
os_detector = create_os_detector_worker(llm=llm, tools=enhanced_tools)
```
## 🎯 Use Cases Enabled
### System Monitoring
- Compare metrics across local and remote systems
- Monitor distributed infrastructure
- Collect performance data from multiple servers
### Troubleshooting
- Debug network connectivity issues
- Investigate application problems across tiers
- Analyze logs from distributed systems
### Infrastructure Management
- Deploy configuration changes
- Perform maintenance tasks
- Manage server inventories
## 📝 Next Steps
1. **Configure SSH Access**: Set up key-based authentication to target servers
2. **Test Connectivity**: Verify SSH access manually before using tools
3. **Integration**: Add tools to existing agent workflows
4. **Monitoring**: Log SSH tool usage for security compliance
5. **Extension**: Add more specialized tools as needed
## 🔍 Testing Commands
```bash
# Test tool imports
cd multi-agent-supervisor && python -c "from custom_tools import ShellTool, SSHTool, print_poem; print('✅ All tools imported')"
cd simple-react-agent && python -c "from custom_tools import ShellTool, SSHTool, print_poem; print('✅ All tools imported')"
# Run full demos
cd multi-agent-supervisor && python demo_tools.py
cd simple-react-agent && python demo_tools.py
```
## 📚 Documentation
- **SSH_TOOL_INTEGRATION.md**: Comprehensive usage guide with examples
- **tools_integration_demo.py**: Code examples and integration patterns
- **demo_tools.py**: Working demonstrations in both directories
---
**🎉 Implementation Status: COMPLETE**
Both simple-react-agent and multi-agent-supervisor approaches now have full SSH and Shell tool integration with working demos, comprehensive documentation, and security features.

View File

@ -5,20 +5,20 @@ This repository demonstrates two different approaches to building AI-powered sys
## <20> Two Approaches Available
### 1. Simple ReAct Agent (`simple-react-agent/`)
A straightforward [single-agent approach](https://langchain-ai.github.io/langgraph/agents/agents/#1-install-dependencies) using the ReAct (Reasoning and Acting) pattern.
A straightforward [single-agent approach](https://langchain-ai.github.io/langgraph/agents/agents/#1-install-dependencies) using the ReAct (Reasoning and Acting) pattern for both local and remote system administration.
**Best for:**
- Learning LangGraph fundamentals
- Simple log analysis tasks
- System administration tasks (local and remote)
- Resource-constrained environments
- Quick prototyping
### 2. Multi-Agent Supervisor (`multi-agent-supervisor/`)
A sophisticated system with [multiple agents coordinated by a supervisor](https://langchain-ai.github.io/langgraph/agents/multi-agent/#supervisor).
A sophisticated system with [multiple agents coordinated by a supervisor](https://langchain-ai.github.io/langgraph/agents/multi-agent/#supervisor) for comprehensive local and remote system management.
**Best for:**
- Complex system administration tasks
- Comprehensive system analysis
- Comprehensive multi-server system analysis
- Production environments
- When you need domain expertise
@ -115,34 +115,37 @@ python main-multi-agent.py
"Write me a motivational poem"
"Generate a tech poem about programming"
"Check disk usage on the system"
"Connect to my remote server and check CPU usage"
"List files in the current directory"
"SSH to my server and check if nginx is running"
"Show system information"
```
### Multi-Agent Supervisor Examples
```
"Perform a comprehensive system health check"
"Analyze system performance and provide recommendations"
"Analyze system performance on my remote servers"
"Check for security vulnerabilities and suggest hardening"
"Monitor system resources and alert on issues"
"Compare performance between local and remote systems"
```
## 🔍 Decision Guide
**Choose Simple ReAct Agent if:**
- You're new to LangGraph
- You need basic system administration tasks
- You need basic system administration tasks (local or remote)
- You have limited computational resources
- You prefer simplicity and transparency
- You're building a proof of concept
**Choose Multi-Agent Supervisor if:**
- You need comprehensive system analysis
- You're working with multiple services
- You need comprehensive system analysis across multiple servers
- You're working with multiple services and environments
- You want parallel processing
- You need risk assessment capabilities
- You're building a production system
- You want to leverage specialized expertise
- You want to leverage specialized expertise for complex remote operations
## 📚 Learning Path
@ -188,7 +191,7 @@ langgraph-pard0x/
## ⚠️ Safety Note
This agent has shell access for diagnostic purposes. Use with caution and only in safe environments. The agent is designed to help with debugging, not to make system modifications.
This agent has both local shell access and remote SSH access for system administration purposes. Use with caution and only in safe environments. The agents are designed to help with system diagnostics and administration, not to make unauthorized system modifications.
## Troubleshooting

View File

@ -1,103 +0,0 @@
# ✅ IMPLEMENTED: SSH Tool Access Distribution
## Changes Successfully Applied
### 🗑️ **Removed Unnecessary Wrappers**
- ❌ Deleted `shell_tool_wrapper.py` from both projects
- ✅ Direct import from `langchain_community.tools.shell.tool.ShellTool`
### 👥 **Agents WITH SSH + Shell Access**
All worker agents now have both local and remote capabilities:
#### Multi-Agent Supervisor Agents:
1. **OS Detector** (`agents/os_detector.py`)
- ✅ `ShellTool()` - Local system detection
- ✅ `configured_ssh_tool` - Remote system detection
- ✅ `print_poem` - Morale boost
2. **Performance Analyzer** (`agents/performance_analyzer.py`)
- ✅ `ShellTool()` - Local performance monitoring
- ✅ `configured_ssh_tool` - Remote performance monitoring
- ✅ `print_poem` - Morale boost
3. **Logs Analyzer** (`agents/logs_analyzer.py`)
- ✅ `ShellTool()` - Local log analysis
- ✅ `configured_ssh_tool` - Remote log analysis
- ✅ `print_poem` - Morale boost
#### Simple React Agent:
4. **Main Agent** (`main.py`)
- ✅ `ShellTool()` - Local system administration
- ✅ `configured_ssh_tool` - Remote system administration
- ✅ `print_poem` - Morale boost
### 🚫 **Supervisor WITHOUT System Access**
The supervisor (`main-multi-agent.py`) is now a pure coordinator:
- ❌ No `ShellTool` access
- ❌ No SSH tool access
- ✅ Only `print_poem` for morale
- ✅ Updated prompt to clarify delegation role
## 🔧 **Tool Configuration**
**One-time setup** in `custom_tools/__init__.py`:
```python
configured_ssh_tool = SSHTool(
host="your-server.example.com", # Replace with your server
username="admin", # Replace with your username
key_filename="~/.ssh/id_rsa", # Replace with your key path
ask_human_input=True # Safety confirmation
)
```
**Usage everywhere**:
```python
from custom_tools import ShellTool, configured_ssh_tool, print_poem
# Worker agents get full access
tools = [ShellTool(), configured_ssh_tool, print_poem]
# Supervisor only gets coordination tools
supervisor_tools = [print_poem]
```
## 🎯 **Result**
### ✅ **Workers Can:**
- Execute local commands via `ShellTool()`
- Execute remote commands via `configured_ssh_tool`
- Generate motivational poems
- Perform comprehensive system analysis across multiple servers
### ❌ **Supervisor Cannot:**
- Execute any system commands
- Access shell or SSH directly
- Must delegate all technical work to agents
### 🔄 **Workflow:**
1. User asks supervisor to solve a problem
2. Supervisor analyzes and delegates to appropriate agent(s)
3. Agents use shell/SSH tools to gather data and fix issues
4. Supervisor synthesizes results and provides final answer
## 🚀 **Benefits**
- **Separation of Concerns**: Supervisor coordinates, agents execute
- **Security**: Supervisor has no direct system access
- **Efficiency**: Agents have both local and remote capabilities
- **Simplicity**: One SSH configuration, used everywhere
- **Scalability**: Easy to add more agents with same tool access
## 📋 **Testing Results**
✅ All agents import successfully with SSH access
✅ Supervisor imports successfully with limited tools
✅ No wrapper files remain
✅ SSH tool connections are lazy (only connect when used)
✅ Both project approaches work identically
The implementation is complete and tested! 🎉
That's the entire setup. No complexity, no wrappers, just works.

View File

@ -1,72 +0,0 @@
#!/usr/bin/env python3
"""
Simple example showing the correct way to use SSH and Shell tools.
No wrappers, no complications - just import and use.
"""
# Multi-Agent Supervisor Example
def multi_agent_example():
"""How to use tools in multi-agent supervisor."""
from multi_agent_supervisor.custom_tools import ShellTool, configured_ssh_tool, print_poem
from multi_agent_supervisor.agents import create_os_detector_worker
from langchain.chat_models import init_chat_model
llm = init_chat_model("openai:gpt-4o-mini")
# Simple - just use the pre-configured tools
tools = [ShellTool(), configured_ssh_tool, print_poem]
# Add to any agent
os_detector = create_os_detector_worker(llm=llm, tools=tools)
print("✅ Multi-agent setup complete with SSH and Shell tools")
# Simple React Agent Example
def simple_agent_example():
"""How to use tools in simple react agent."""
from simple_react_agent.custom_tools import ShellTool, configured_ssh_tool, print_poem
from langgraph.prebuilt import create_react_agent
from langchain.chat_models import init_chat_model
llm = init_chat_model("openai:gpt-4o-mini")
# Simple - just use the pre-configured tools
tools = [ShellTool(), configured_ssh_tool, print_poem]
# Create agent
agent = create_react_agent(llm, tools)
print("✅ Simple react agent setup complete with SSH and Shell tools")
def main():
print("🚀 SIMPLE SSH + Shell Tools Usage")
print("="*50)
print("\n1⃣ Configure once in custom_tools/__init__.py:")
print("""
configured_ssh_tool = SSHTool(
host="YOUR_SERVER_IP",
username="YOUR_USERNAME",
key_filename="~/.ssh/YOUR_KEY"
)
""")
print("2⃣ Use everywhere:")
print("""
from custom_tools import ShellTool, configured_ssh_tool, print_poem
# That's it! No wrapper classes, no repeated configuration.
tools = [ShellTool(), configured_ssh_tool, print_poem]
""")
print("3⃣ Your agents get both local and remote access automatically!")
# Show the examples (commented out to avoid import errors in this demo)
# multi_agent_example()
# simple_agent_example()
if __name__ == "__main__":
main()

View File

@ -1,187 +0,0 @@
# SSH Tool Integration
This document explains how to use the new SSH tool alongside the existing Shell tool in both the simple-react-agent and multi-agent-supervisor approaches.
## Overview
We now have two complementary tools for system administration:
- **ShellTool**: Execute commands on the local machine
- **SSHTool**: Execute commands on remote servers via SSH
Both tools follow the same pattern as the original LangChain ShellTool, maintaining persistent connections for efficiency.
## Installation
First, install the required dependency:
```bash
uv add paramiko
```
## Usage Examples
### Simple React Agent
```python
from simple_react_agent.custom_tools import ShellTool, SSHTool, print_poem
from langchain.chat_models import init_chat_model
from langgraph.prebuilt import create_react_agent
# Create tools
shell_tool = ShellTool(ask_human_input=True)
ssh_tool = SSHTool(
host="your-server.com",
username="admin",
key_filename="~/.ssh/id_rsa",
ask_human_input=True
)
# Create agent
llm = init_chat_model("openai:gpt-4o-mini")
agent = create_react_agent(llm, [shell_tool, ssh_tool, print_poem])
```
### Multi-Agent Supervisor
```python
from multi_agent_supervisor.custom_tools import ShellTool, SSHTool, print_poem
from multi_agent_supervisor.agents import create_os_detector_worker
# Enhanced tools for all agents
tools = [
ShellTool(),
SSHTool(host="server1.com", username="admin", key_filename="~/.ssh/key"),
print_poem
]
# Create agents with both local and remote capabilities
os_detector = create_os_detector_worker(llm=llm, tools=tools)
```
## SSH Tool Configuration
### Authentication Methods
#### SSH Key Authentication (Recommended)
```python
ssh_tool = SSHTool(
host="server.example.com",
username="admin",
key_filename="~/.ssh/id_rsa", # Path to private key
port=22, # Default SSH port
timeout=30.0, # Connection timeout
ask_human_input=True # Ask for confirmation before executing
)
```
#### Password Authentication
```python
ssh_tool = SSHTool(
host="192.168.1.100",
username="user",
password="secure_password",
port=22
)
```
### Security Features
1. **Human Confirmation**: Set `ask_human_input=True` to require user confirmation
2. **Connection Timeout**: Configurable timeout for connection attempts
3. **Warning Messages**: Automatic warnings about security risks
4. **Persistent Connections**: Single SSH connection reused for multiple commands
## Tool Integration Patterns
### Local + Remote System Analysis
```python
# Check local system
local_result = shell_tool.run(commands=["df -h", "free -m"])
# Check remote system
remote_result = ssh_tool.run(commands=["df -h", "free -m"])
# Compare results for comprehensive analysis
```
### Multi-Server Management
```python
# Create multiple SSH tools for different servers
web_server = SSHTool(host="web1.com", username="admin", key_filename="~/.ssh/web_key")
db_server = SSHTool(host="db1.com", username="admin", key_filename="~/.ssh/db_key")
# Agents can now manage multiple remote systems
tools = [ShellTool(), web_server, db_server, print_poem]
```
## Safety Considerations
1. **Start with Read-Only Commands**: Always begin diagnostics with non-destructive commands
2. **Use Human Confirmation**: Enable `ask_human_input=True` for production systems
3. **Secure Key Management**: Store SSH keys securely, use proper file permissions
4. **Network Security**: Ensure SSH connections are over secure networks
5. **Audit Logging**: Monitor SSH command execution for security compliance
## Example Use Cases
### System Monitoring
- Compare disk usage across local and remote systems
- Monitor performance metrics on multiple servers
- Analyze logs from distributed systems
### Troubleshooting
- Investigate network connectivity between local and remote systems
- Debug application issues across server tiers
- Perform maintenance tasks on remote infrastructure
### Infrastructure Management
- Deploy configuration changes to multiple servers
- Collect system information for inventory management
- Perform bulk operations across server farms
## File Structure
```
project/
├── simple-react-agent/
│ └── custom_tools/
│ ├── __init__.py # Exports ShellTool, SSHTool, print_poem
│ ├── ssh_tool.py # SSH tool implementation
│ ├── shell_tool_wrapper.py # Shell tool wrapper
│ └── poem_tool.py # Existing poem tool
└── multi-agent-supervisor/
└── custom_tools/
├── __init__.py # Exports ShellTool, SSHTool, print_poem
├── ssh_tool.py # SSH tool implementation
├── shell_tool_wrapper.py # Shell tool wrapper
└── poem_tool.py # Existing poem tool
```
## Next Steps
1. Configure SSH access to your target servers
2. Test SSH connectivity manually before using the tool
3. Start with read-only commands to validate functionality
4. Integrate SSH tools into your existing agent workflows
5. Monitor and log SSH tool usage for security compliance
## Troubleshooting
### Common Issues
1. **Connection Refused**: Check if SSH service is running on target server
2. **Authentication Failed**: Verify username, password, or SSH key
3. **Timeout Errors**: Increase timeout value or check network connectivity
4. **Permission Denied**: Ensure proper file permissions on SSH keys (600)
### Debug Commands
```python
# Test SSH connectivity
ssh_tool.run(commands="echo 'SSH connection successful'")
# Check SSH configuration
ssh_tool.run(commands="ssh -V") # SSH version
ssh_tool.run(commands="whoami") # Current user
```

View File

@ -48,6 +48,12 @@ User Input → Supervisor → Specialized Agents → Aggregated Response
## Features
### Core Capabilities
- **Local System Access**: Execute shell commands on the local machine
- **Remote Server Access**: Execute commands on remote servers via SSH
- **Persistent SSH Connections**: Efficient remote operations with connection reuse
- **Cross-Platform Support**: Works on Linux, macOS, BSD, and Windows systems
### Advanced Capabilities
- **Intelligent Delegation**: Supervisor routes tasks to appropriate specialists
- **Parallel Execution**: Multiple agents can work simultaneously
@ -95,13 +101,39 @@ for chunk in supervisor.stream(query):
For the query: *"Nginx returns 502 Bad Gateway on my server. What can I do?"*
1. **Supervisor** analyzes the request
2. **system_info_worker** checks system resources
2. **system_info_worker** checks system resources (local or remote)
3. **service_inventory_worker** lists running services
4. **nginx_analyzer** validates Nginx configuration and checks logs
5. **phpfpm_analyzer** checks PHP-FPM status (common 502 cause)
6. **risk_scorer** assesses the severity
7. **remediation_worker** proposes specific fixes
## Example Queries
The multi-agent system can handle both local and remote system administration:
### Local System Administration
```
"Check local system performance and identify bottlenecks"
"Analyze recent system errors in local logs"
"What services are running on this machine?"
```
### Remote Server Management
```
"Connect to my remote server and check disk usage"
"Compare performance between local and remote systems"
"Check if nginx is running on the remote server"
"Analyze logs on my remote server for error patterns"
```
### Multi-System Analysis
```
"Perform comprehensive health check across all systems"
"Compare configurations between local and remote servers"
"Identify performance differences between environments"
```
## Pros and Cons
### ✅ Pros

View File

@ -2,7 +2,6 @@ import logging
import warnings
from typing import Any, List, Optional, Type, Union
from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field, model_validator
@ -17,9 +16,20 @@ class SSHInput(BaseModel):
description="List of commands to run on the remote server",
)
"""List of commands to run."""
use_sudo: bool = Field(
default=False,
description="Whether to run commands with sudo privileges"
)
"""Whether to run commands with sudo."""
sudo_password: Optional[str] = Field(
default=None,
description="Password for sudo if required (will be used securely)"
)
"""Password for sudo if required."""
@model_validator(mode="before")
@classmethod
def _validate_commands(cls, values: dict) -> Any:
"""Validate commands."""
commands = values.get("commands")
@ -34,39 +44,38 @@ class SSHInput(BaseModel):
class SSHProcess:
"""Persistent SSH connection for command execution."""
def __init__(self, host: str, username: str, port: int = 22,
password: Optional[str] = None, key_filename: Optional[str] = None,
timeout: float = 30.0, return_err_output: bool = True):
**kwargs):
"""Initialize SSH process with connection parameters."""
self.host = host
self.username = username
self.port = port
self.password = password
self.key_filename = key_filename
self.timeout = timeout
self.return_err_output = return_err_output
self.client = None
# Don't connect immediately - connect when needed
def _connect(self):
self._is_connected = False
def connect(self):
"""Establish SSH connection."""
if self._is_connected:
return
try:
import paramiko
except ImportError:
except ImportError as e:
raise ImportError(
"paramiko is required for SSH functionality. "
"Install it with `pip install paramiko`"
)
) from e
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connect_kwargs = {
"hostname": self.host,
"username": self.username,
"port": self.port,
"timeout": self.timeout,
"username": self.username,
}
if self.password:
@ -75,12 +84,14 @@ class SSHProcess:
connect_kwargs["key_filename"] = self.key_filename
self.client.connect(**connect_kwargs)
self._is_connected = True
logger.info(f"SSH connection established to {self.username}@{self.host}:{self.port}")
def run(self, commands: Union[str, List[str]]) -> str:
def run(self, commands: Union[str, List[str]], use_sudo: bool = False,
sudo_password: Optional[str] = None) -> str:
"""Run commands over SSH and return output."""
if not self.client:
self._connect()
if not self._is_connected:
self.connect()
if isinstance(commands, str):
commands = [commands]
@ -88,23 +99,48 @@ class SSHProcess:
outputs = []
for command in commands:
try:
stdin, stdout, stderr = self.client.exec_command(command)
# Prepare command with sudo if needed
if use_sudo:
if sudo_password:
# Use echo to pipe password to sudo -S (read from stdin)
full_command = f"echo '{sudo_password}' | sudo -S {command}"
else:
# Try sudo without password (for passwordless sudo)
full_command = f"sudo {command}"
else:
full_command = command
output = stdout.read().decode('utf-8')
error = stderr.read().decode('utf-8')
stdin, stdout, stderr = self.client.exec_command(full_command)
if error and self.return_err_output:
# For sudo commands with password, we need to handle stdin
if use_sudo and sudo_password:
stdin.write(f"{sudo_password}\n")
stdin.flush()
output = stdout.read().decode()
error = stderr.read().decode()
# Filter out sudo password prompt from error output
if use_sudo and error:
error_lines = error.split('\n')
filtered_error = '\n'.join(
line for line in error_lines
if not line.startswith('[sudo]') and line.strip()
)
error = filtered_error
if error:
outputs.append(f"$ {command}\n{output}{error}")
else:
outputs.append(f"$ {command}\n{output}")
except Exception as e:
outputs.append(f"$ {command}\nError: {str(e)}")
return "\n".join(outputs)
return "\n\n".join(outputs)
def __del__(self):
"""Close SSH connection when object is destroyed."""
if self.client:
if self.client and self._is_connected:
self.client.close()
logger.info(f"SSH connection closed to {self.username}@{self.host}")
@ -119,34 +155,41 @@ class SSHTool(BaseTool):
process: Optional[SSHProcess] = Field(default=None)
"""SSH process with persistent connection."""
# Connection parameters
host: str = Field(..., description="SSH host address")
username: str = Field(..., description="SSH username")
port: int = Field(default=22, description="SSH port")
password: Optional[str] = Field(default=None, description="SSH password")
key_filename: Optional[str] = Field(default=None, description="Path to SSH key")
timeout: float = Field(default=30.0, description="Connection timeout")
# Tool configuration
name: str = "ssh"
"""Name of tool."""
description: str = Field(default="")
"""Description of tool."""
description: str = """
Run shell commands on a remote server via SSH.
This tool maintains a persistent SSH connection and allows executing
commands on the remote server. It supports both regular and privileged
(sudo) command execution.
Use the 'use_sudo' parameter to run commands with sudo privileges.
If sudo requires a password, provide it via 'sudo_password'.
Examples:
- Regular command: {"commands": "ls -la"}
- Sudo command: {"commands": "apt update", "use_sudo": true}
- Multiple commands: {"commands": ["df -h", "free -m", "top -n 1"]}
"""
args_schema: Type[BaseModel] = SSHInput
"""Schema for input arguments."""
model_config = {
"arbitrary_types_allowed": True
}
ask_human_input: bool = False
"""
If True, prompts the user for confirmation (y/n) before executing
commands on the remote server.
"""
def __init__(self, **data):
def __init__(self, **kwargs):
"""Initialize SSH tool and set description."""
super().__init__(**data)
# Set description after initialization
super().__init__(**kwargs)
self.description = f"Run commands on remote server {self.username}@{self.host}:{self.port}"
# Initialize the SSH process (but don't connect yet)
self.process = SSHProcess(
@ -154,32 +197,38 @@ class SSHTool(BaseTool):
username=self.username,
port=self.port,
password=self.password,
key_filename=self.key_filename,
timeout=self.timeout,
return_err_output=True
key_filename=self.key_filename
)
def _run(
self,
commands: Union[str, List[str]],
run_manager: Optional[CallbackManagerForToolRun] = None,
use_sudo: bool = False,
sudo_password: Optional[str] = None,
**kwargs,
) -> str:
"""Run commands on remote server and return output."""
print(f"Executing SSH command on {self.username}@{self.host}:{self.port}") # noqa: T201
print(f"Commands: {commands}") # noqa: T201
try:
if self.ask_human_input:
print(f"Executing SSH command on {self.username}@{self.host}:{self.port}") # noqa: T201
print(f"Commands: {commands}") # noqa: T201
if use_sudo:
print("Running with sudo privileges") # noqa: T201
# Safety check for privileged commands
if use_sudo:
user_input = input("Proceed with sudo command execution? (y/n): ").lower()
if user_input == "y":
return self.process.run(commands, use_sudo=use_sudo, sudo_password=sudo_password)
else:
logger.info("User aborted sudo command execution.")
return "Command execution aborted by user."
else:
user_input = input("Proceed with SSH command execution? (y/n): ").lower()
if user_input == "y":
return self.process.run(commands)
else:
logger.info("Invalid input. User aborted SSH command execution.")
return None # type: ignore[return-value]
else:
return self.process.run(commands)
return "Command execution aborted by user."
except Exception as e:
logger.error(f"Error during SSH command execution: {e}")
return None # type: ignore[return-value]
return f"Error: {str(e)}"

View File

@ -26,10 +26,13 @@ def print_welcome():
print("🤖 Welcome to Pard0x Multi-Agent System Administrator Assistant!")
print("="*80)
print("\nI coordinate a team of specialized agents to help you with system administration tasks:")
print(" • 🖥️ OS Detector - System identification and environment analysis")
print(" • 📊 Logs Analyzer - Log investigation and error diagnosis")
print(" • ⚡ Performance Analyzer - Resource monitoring and optimization")
print(" • 🖥️ OS Detector - System identification and environment analysis (local & remote)")
print(" • 📊 Logs Analyzer - Log investigation and error diagnosis (local & remote)")
print(" • ⚡ Performance Analyzer - Resource monitoring and optimization (local & remote)")
print(" • 🎭 Morale Booster - Motivational poems for tough debugging sessions!")
print("\n🌐 Remote Server Access: My agents can execute commands on both:")
print(" • Local machine via shell commands")
print(" • Remote server via SSH (g@157.90.211.119:8081)")
print("\n" + "-"*80)
@ -39,6 +42,8 @@ def print_examples():
print(" - 'What operating system is this server running?'")
print(" - 'Check the system logs for any errors in the last hour'")
print(" - 'Analyze current system performance and identify bottlenecks'")
print(" - 'Compare disk usage between local and remote server'")
print(" - 'Check if services are running on both systems'")
print(" - 'My web server is down, help me troubleshoot'")
print(" - 'Write me a motivational poem about debugging'")
print("\n" + "-"*80)

View File

@ -1,19 +1,21 @@
# Simple ReAct Agent for Log Analysis
# Simple ReAct Agent for System Administration
This directory contains a simple ReAct (Reasoning and Acting) agent implementation for log analysis and system administration tasks.
This directory contains a simple ReAct (Reasoning and Acting) agent implementation for system administration, log analysis, and remote server management tasks.
## Overview
The simple ReAct agent follows a straightforward pattern:
1. **Receives** user input
2. **Reasons** about what tools to use
3. **Acts** by executing tools when needed
3. **Acts** by executing tools when needed (locally or remotely)
4. **Responds** with the final result
## Features
- **Single Agent**: One agent handles all tasks
- **Shell Access**: Execute system commands safely
- **Local Shell Access**: Execute system commands on the local machine
- **Remote SSH Access**: Execute commands on remote servers via persistent SSH connections
- **System Administration**: Comprehensive system diagnostics and management
- **Log Analysis**: Specialized log analysis capabilities
- **Interactive Chat**: Stream responses with tool usage visibility
- **Conversation History**: Maintains context across interactions
@ -21,7 +23,7 @@ The simple ReAct agent follows a straightforward pattern:
## Architecture
```
User Input → ReAct Agent → Tools (Shell + Log Analyzer) → Response
User Input → ReAct Agent → Tools (Shell + SSH + Tools) → Response
```
## Files
@ -31,12 +33,18 @@ User Input → ReAct Agent → Tools (Shell + Log Analyzer) → Response
## Tools Available
1. **Shell Tool**: Execute system commands
1. **Shell Tool**: Execute system commands on the local machine
- System monitoring (`top`, `ps`, `df`, etc.)
- File operations
- Network diagnostics
2. **Poem Tool**: Generate beautiful poems with different themes:
2. **SSH Tool**: Execute commands on remote servers
- Persistent SSH connections
- Remote system administration
- Cross-platform remote diagnostics
- Secure remote command execution
3. **Poem Tool**: Generate beautiful poems with different themes:
- `nature`: Poems about nature and the environment
- `tech`: Poems about technology and programming
- `motivational`: Inspirational and motivational poems
@ -62,6 +70,11 @@ User: Check disk usage on the system
Agent: 🔧 Using tool: shell
Args: {'command': 'df -h'}
📋 Tool result: Filesystem usage information...
User: Connect to my remote server and check CPU usage
Agent: 🔧 Using tool: configured_ssh_tool
Args: {'command': 'top -bn1 | head -20'}
📋 Tool result: Remote server CPU and process information...
```
## Pros and Cons
@ -82,11 +95,12 @@ Agent: 🔧 Using tool: shell
## When to Use
Choose the simple ReAct agent when:
- You need a straightforward log analysis tool
- You need a straightforward system administration tool
- You want both local and remote server management capabilities
- Your use cases are relatively simple
- You want to understand LangGraph basics
- Resource usage is a concern
- You prefer simplicity over sophistication
- You prefer simplicity over sophisticated multi-agent coordination
## Requirements

View File

@ -7,9 +7,10 @@ from langchain_community.tools.shell.tool import ShellTool
# Pre-configured SSH tool for your server - only connects when actually used
# TODO: Update these connection details for your actual server
configured_ssh_tool = SSHTool(
host="your-server.example.com", # Replace with your server
username="admin", # Replace with your username
key_filename="~/.ssh/id_rsa", # Replace with your key path
host="157.90.211.119", # Replace with your server
port=8081,
username="g", # Replace with your username
key_filename="/Users/ghsioux/.ssh/id_rsa_hetzner", # Replace with your key path
ask_human_input=True # Safety confirmation
)

View File

@ -2,7 +2,6 @@ import logging
import warnings
from typing import Any, List, Optional, Type, Union
from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field, model_validator
@ -17,9 +16,20 @@ class SSHInput(BaseModel):
description="List of commands to run on the remote server",
)
"""List of commands to run."""
use_sudo: bool = Field(
default=False,
description="Whether to run commands with sudo privileges"
)
"""Whether to run commands with sudo."""
sudo_password: Optional[str] = Field(
default=None,
description="Password for sudo if required (will be used securely)"
)
"""Password for sudo if required."""
@model_validator(mode="before")
@classmethod
def _validate_commands(cls, values: dict) -> Any:
"""Validate commands."""
commands = values.get("commands")
@ -34,39 +44,38 @@ class SSHInput(BaseModel):
class SSHProcess:
"""Persistent SSH connection for command execution."""
def __init__(self, host: str, username: str, port: int = 22,
password: Optional[str] = None, key_filename: Optional[str] = None,
timeout: float = 30.0, return_err_output: bool = True):
**kwargs):
"""Initialize SSH process with connection parameters."""
self.host = host
self.username = username
self.port = port
self.password = password
self.key_filename = key_filename
self.timeout = timeout
self.return_err_output = return_err_output
self.client = None
# Don't connect immediately - connect when needed
def _connect(self):
self._is_connected = False
def connect(self):
"""Establish SSH connection."""
if self._is_connected:
return
try:
import paramiko
except ImportError:
except ImportError as e:
raise ImportError(
"paramiko is required for SSH functionality. "
"Install it with `pip install paramiko`"
)
) from e
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connect_kwargs = {
"hostname": self.host,
"username": self.username,
"port": self.port,
"timeout": self.timeout,
"username": self.username,
}
if self.password:
@ -75,12 +84,14 @@ class SSHProcess:
connect_kwargs["key_filename"] = self.key_filename
self.client.connect(**connect_kwargs)
self._is_connected = True
logger.info(f"SSH connection established to {self.username}@{self.host}:{self.port}")
def run(self, commands: Union[str, List[str]]) -> str:
def run(self, commands: Union[str, List[str]], use_sudo: bool = False,
sudo_password: Optional[str] = None) -> str:
"""Run commands over SSH and return output."""
if not self.client:
self._connect()
if not self._is_connected:
self.connect()
if isinstance(commands, str):
commands = [commands]
@ -88,23 +99,48 @@ class SSHProcess:
outputs = []
for command in commands:
try:
stdin, stdout, stderr = self.client.exec_command(command)
# Prepare command with sudo if needed
if use_sudo:
if sudo_password:
# Use echo to pipe password to sudo -S (read from stdin)
full_command = f"echo '{sudo_password}' | sudo -S {command}"
else:
# Try sudo without password (for passwordless sudo)
full_command = f"sudo {command}"
else:
full_command = command
output = stdout.read().decode('utf-8')
error = stderr.read().decode('utf-8')
stdin, stdout, stderr = self.client.exec_command(full_command)
if error and self.return_err_output:
# For sudo commands with password, we need to handle stdin
if use_sudo and sudo_password:
stdin.write(f"{sudo_password}\n")
stdin.flush()
output = stdout.read().decode()
error = stderr.read().decode()
# Filter out sudo password prompt from error output
if use_sudo and error:
error_lines = error.split('\n')
filtered_error = '\n'.join(
line for line in error_lines
if not line.startswith('[sudo]') and line.strip()
)
error = filtered_error
if error:
outputs.append(f"$ {command}\n{output}{error}")
else:
outputs.append(f"$ {command}\n{output}")
except Exception as e:
outputs.append(f"$ {command}\nError: {str(e)}")
return "\n".join(outputs)
return "\n\n".join(outputs)
def __del__(self):
"""Close SSH connection when object is destroyed."""
if self.client:
if self.client and self._is_connected:
self.client.close()
logger.info(f"SSH connection closed to {self.username}@{self.host}")
@ -119,34 +155,41 @@ class SSHTool(BaseTool):
process: Optional[SSHProcess] = Field(default=None)
"""SSH process with persistent connection."""
# Connection parameters
host: str = Field(..., description="SSH host address")
username: str = Field(..., description="SSH username")
port: int = Field(default=22, description="SSH port")
password: Optional[str] = Field(default=None, description="SSH password")
key_filename: Optional[str] = Field(default=None, description="Path to SSH key")
timeout: float = Field(default=30.0, description="Connection timeout")
# Tool configuration
name: str = "ssh"
"""Name of tool."""
description: str = Field(default="")
"""Description of tool."""
description: str = """
Run shell commands on a remote server via SSH.
This tool maintains a persistent SSH connection and allows executing
commands on the remote server. It supports both regular and privileged
(sudo) command execution.
Use the 'use_sudo' parameter to run commands with sudo privileges.
If sudo requires a password, provide it via 'sudo_password'.
Examples:
- Regular command: {"commands": "ls -la"}
- Sudo command: {"commands": "apt update", "use_sudo": true}
- Multiple commands: {"commands": ["df -h", "free -m", "top -n 1"]}
"""
args_schema: Type[BaseModel] = SSHInput
"""Schema for input arguments."""
model_config = {
"arbitrary_types_allowed": True
}
ask_human_input: bool = False
"""
If True, prompts the user for confirmation (y/n) before executing
commands on the remote server.
"""
def __init__(self, **data):
def __init__(self, **kwargs):
"""Initialize SSH tool and set description."""
super().__init__(**data)
# Set description after initialization
super().__init__(**kwargs)
self.description = f"Run commands on remote server {self.username}@{self.host}:{self.port}"
# Initialize the SSH process (but don't connect yet)
self.process = SSHProcess(
@ -154,32 +197,38 @@ class SSHTool(BaseTool):
username=self.username,
port=self.port,
password=self.password,
key_filename=self.key_filename,
timeout=self.timeout,
return_err_output=True
key_filename=self.key_filename
)
def _run(
self,
commands: Union[str, List[str]],
run_manager: Optional[CallbackManagerForToolRun] = None,
use_sudo: bool = False,
sudo_password: Optional[str] = None,
**kwargs,
) -> str:
"""Run commands on remote server and return output."""
print(f"Executing SSH command on {self.username}@{self.host}:{self.port}") # noqa: T201
print(f"Commands: {commands}") # noqa: T201
try:
if self.ask_human_input:
print(f"Executing SSH command on {self.username}@{self.host}:{self.port}") # noqa: T201
print(f"Commands: {commands}") # noqa: T201
if use_sudo:
print("Running with sudo privileges") # noqa: T201
# Safety check for privileged commands
if use_sudo:
user_input = input("Proceed with sudo command execution? (y/n): ").lower()
if user_input == "y":
return self.process.run(commands, use_sudo=use_sudo, sudo_password=sudo_password)
else:
logger.info("User aborted sudo command execution.")
return "Command execution aborted by user."
else:
user_input = input("Proceed with SSH command execution? (y/n): ").lower()
if user_input == "y":
return self.process.run(commands)
else:
logger.info("Invalid input. User aborted SSH command execution.")
return None # type: ignore[return-value]
else:
return self.process.run(commands)
return "Command execution aborted by user."
except Exception as e:
logger.error(f"Error during SSH command execution: {e}")
return None # type: ignore[return-value]
return f"Error: {str(e)}"

View File

@ -146,8 +146,10 @@ def main():
print("Type 'quit', 'exit', or 'q' to exit the chat.")
print("Type 'help' or 'h' for help and examples.")
print("Type 'clear' or 'reset' to clear conversation history.")
print("⚠️ WARNING: This agent has shell access - use with caution!")
print("⚠️ WARNING: This agent has local shell and remote SSH access - use with caution!")
print("🛠️ System Administration Capabilities:")
print(" - Local system diagnostics via shell commands")
print(" - Remote server management via SSH connections")
print(" - Diagnose performance issues (CPU, memory, disk, network)")
print(" - Troubleshoot service and daemon problems")
print(" - Analyze system logs and error messages")
@ -162,6 +164,7 @@ def main():
print("✅ SysAdmin Debugging Agent initialized successfully!")
print("💡 Try asking: 'My system is running slow, can you help?'")
print("💡 Or: 'Check if my web server is running properly'")
print("💡 Or: 'Connect to my remote server and check disk space'")
print("💡 Or: 'Analyze recent system errors'")
print("💡 Need a break? Ask: 'Write me a motivational poem'")
@ -187,7 +190,9 @@ def main():
print("\nSystem Debugging Examples:")
print(" - 'My server is running slow, help me diagnose the issue'")
print(" - 'Check why my Apache/Nginx service won't start'")
print(" - 'Connect to my remote server and check system load'")
print(" - 'Analyze high CPU usage on this system'")
print(" - 'Check disk space on my remote server via SSH'")
print(" - 'Troubleshoot network connectivity problems'")
print(" - 'Check disk space and filesystem health'")
print(" - 'Review recent system errors in logs'")

View File

@ -1,211 +0,0 @@
#!/usr/bin/env python3
"""
Example usage of SSH and Shell tools in both simple-react-agent and multi-agent approaches.
This file demonstrates how to integrate both local shell commands and remote SSH commands.
"""
import os
import warnings
from langchain.chat_models import init_chat_model
from langgraph.prebuilt import create_react_agent
from langchain_core.messages import HumanMessage
# Suppress warnings
warnings.filterwarnings("ignore", message="The shell tool has no safeguards by default. Use at your own risk.")
warnings.filterwarnings("ignore", message="The SSH tool has no safeguards by default. Use at your own risk.")
def demo_simple_react_agent():
"""Demonstrate using both Shell and SSH tools in simple react agent."""
from simple_react_agent.custom_tools import ShellTool, SSHTool, print_poem
print("\n" + "="*60)
print("🤖 Simple React Agent Demo - Shell & SSH Tools")
print("="*60)
# Initialize the chat model
llm = init_chat_model("openai:gpt-4o-mini")
# Create shell tool (local commands)
shell_tool = ShellTool()
# Create SSH tool (uncomment and configure for your server)
# ssh_tool = SSHTool(
# host="your-server.com",
# username="your-username",
# key_filename="~/.ssh/id_rsa", # or use password="your-password"
# ask_human_input=True # Ask for confirmation before SSH commands
# )
# Define tools (SSH tool commented out for demo)
tools = [shell_tool, print_poem] # , ssh_tool]
# Create agent with enhanced system prompt
system_prompt = """You are an expert system administrator with access to both local and remote system tools.
## AVAILABLE TOOLS
1. **Shell Tool (terminal)**: Execute commands on the local machine
2. **SSH Tool (ssh)**: Execute commands on remote servers (when configured)
3. **Poem Tool (print_poem)**: Generate motivational poems for debugging sessions
## SAFETY PROTOCOLS
- Always start with read-only diagnostic commands
- Explain what each command does before executing
- Ask for confirmation before running potentially harmful commands
- Use appropriate tools based on whether you need local or remote system access
## EXAMPLES
- Use shell tool for local system diagnostics: `df -h`, `ps aux`, `top`
- Use SSH tool for remote server management: connecting to production servers
- Use poem tool when users need motivation during difficult troubleshooting
Be helpful, safe, and efficient in your system administration assistance.
"""
# Create the agent
agent = create_react_agent(llm, tools, state_modifier=system_prompt)
# Demo interaction
print("\n📝 Demo Query: 'Check local disk usage and system load'")
response = agent.invoke({
"messages": [
HumanMessage(content="Check local disk usage and system load")
]
})
print("\n🤖 Agent Response:")
for message in response["messages"]:
if hasattr(message, 'content') and message.content:
print(f" {message.content}")
def demo_multi_agent_with_tools():
"""Demonstrate integrating SSH and Shell tools with multi-agent supervisor."""
from multi_agent_supervisor.custom_tools import ShellTool, SSHTool, print_poem
from multi_agent_supervisor.agents import (
create_os_detector_worker,
create_logs_analyzer_worker,
create_performance_analyzer_worker
)
print("\n" + "="*60)
print("🤖 Multi-Agent Supervisor Demo - Enhanced with SSH")
print("="*60)
# Initialize the chat model
llm = init_chat_model("openai:gpt-4o-mini")
# Create tools
shell_tool = ShellTool()
# SSH tool configuration (commented for demo)
# ssh_tool = SSHTool(
# host="production-server.com",
# username="admin",
# key_filename="~/.ssh/production_key",
# ask_human_input=True
# )
# Enhanced tools list
enhanced_tools = [shell_tool, print_poem] # , ssh_tool]
# Create enhanced agents with both local and remote capabilities
os_detector = create_os_detector_worker(
llm=llm,
tools=enhanced_tools,
system_message_suffix="\nYou can use SSH tool to detect OS on remote servers when needed.",
)
logs_analyzer = create_logs_analyzer_worker(
llm=llm,
tools=enhanced_tools,
system_message_suffix="\nYou can analyze logs on both local and remote systems using appropriate tools.",
)
performance_analyzer = create_performance_analyzer_worker(
llm=llm,
tools=enhanced_tools,
system_message_suffix="\nYou can monitor performance on both local and remote systems.",
)
print("\n✅ Multi-agent system enhanced with SSH capabilities!")
print(" - Agents can now work with both local and remote systems")
print(" - SSH tool provides secure remote command execution")
print(" - Shell tool handles local system operations")
def show_tool_usage_examples():
"""Show examples of how to use the tools directly."""
print("\n" + "="*60)
print("📚 Tool Usage Examples")
print("="*60)
print("\n🔧 Shell Tool Usage:")
print("""
from custom_tools import ShellTool
shell_tool = ShellTool(ask_human_input=True) # Ask for confirmation
result = shell_tool.run(commands=["df -h", "free -m", "uptime"])
""")
print("\n🌐 SSH Tool Usage:")
print("""
from custom_tools import SSHTool
# Using SSH key authentication
ssh_tool = SSHTool(
host="server.example.com",
username="admin",
key_filename="~/.ssh/id_rsa",
ask_human_input=True
)
result = ssh_tool.run(commands=["df -h", "systemctl status nginx"])
# Using password authentication
ssh_tool = SSHTool(
host="192.168.1.100",
username="user",
password="secure_password",
port=22
)
result = ssh_tool.run(commands="ps aux | grep python")
""")
print("\n🎭 Poem Tool Usage:")
print("""
from custom_tools import print_poem
result = print_poem("debugging") # Theme: debugging
""")
def main():
"""Main demonstration function."""
print("🚀 SSH + Shell Tools Integration Demo")
print("This demo shows how to use both local shell and remote SSH tools")
# Check if OpenAI API key is set
if not os.getenv("OPENAI_API_KEY"):
print("\n⚠️ Warning: OPENAI_API_KEY not set. Some demos may not work.")
print(" Set your API key: export OPENAI_API_KEY='your-key-here'")
# Show usage examples
show_tool_usage_examples()
# Uncomment to run live demos (requires OpenAI API key)
# demo_simple_react_agent()
# demo_multi_agent_with_tools()
print("\n" + "="*60)
print("✅ Integration Complete!")
print("="*60)
print("\nBoth approaches now support:")
print(" • Local shell command execution (ShellTool)")
print(" • Remote SSH command execution (SSHTool)")
print(" • Motivational poems (print_poem)")
print("\nTo use SSH tools, uncomment and configure the SSH connection parameters.")
print("Make sure to install paramiko: uv add paramiko")
if __name__ == "__main__":
main()