336 lines
6.4 KiB
Markdown
336 lines
6.4 KiB
Markdown
# Hermes Tool Implementation Guide
|
|
|
|
## Overview
|
|
|
|
This document explains how to create a Hermes tool that integrates with external services (like Pi agent).
|
|
|
|
---
|
|
|
|
## What is a Hermes Tool?
|
|
|
|
A Hermes tool is a Python function that:
|
|
1. **Hermes calls** when the agent decides to use it
|
|
2. **Receives parameters** from the LLM
|
|
3. **Does work** (calls external services, runs commands, etc.)
|
|
4. **Returns a string** that Hermes shows to the agent
|
|
|
|
---
|
|
|
|
## Tool Structure
|
|
|
|
Every Hermes tool needs:
|
|
|
|
```python
|
|
def my_tool(param1: str, param2: Optional[int] = None) -> str:
|
|
"""
|
|
Tool description that LLM sees.
|
|
|
|
Args:
|
|
param1: Description
|
|
param2: Description
|
|
|
|
Returns:
|
|
What the tool returns
|
|
"""
|
|
# Do work here
|
|
return "result"
|
|
|
|
def check_my_tool_requirements() -> bool:
|
|
"""Check if tool can be used (e.g., external service available)."""
|
|
return True
|
|
|
|
# Schema for LLM
|
|
MY_TOOL_SCHEMA = {
|
|
"name": "my_tool",
|
|
"description": "What the tool does",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"param1": {"type": "string", "description": "..."},
|
|
},
|
|
"required": ["param1"]
|
|
}
|
|
}
|
|
|
|
# Register
|
|
registry.register(
|
|
name="my_tool",
|
|
toolset="my_toolset", # Group in Hermes config
|
|
schema=MY_TOOL_SCHEMA,
|
|
handler=lambda args, **kw: my_tool(**args),
|
|
check_fn=check_my_tool_requirements,
|
|
emoji="📦",
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Key Components
|
|
|
|
### 1. Function Handler
|
|
```python
|
|
def my_tool(param1: str, ...) -> str:
|
|
# Work
|
|
return "result as string"
|
|
```
|
|
|
|
### 2. Requirements Check
|
|
```python
|
|
def check_my_tool_requirements() -> bool:
|
|
# Check external service, API key, etc.
|
|
return True # or False if not available
|
|
```
|
|
|
|
### 3. Schema (JSON)
|
|
```python
|
|
MY_TOOL_SCHEMA = {
|
|
"name": "tool_name",
|
|
"description": "What it does (LLM reads this!)",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"param1": {"type": "string", "description": "..."},
|
|
},
|
|
"required": ["param1"]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Registry
|
|
```python
|
|
registry.register(
|
|
name="tool_name",
|
|
toolset="toolset_name", # Enable in config
|
|
schema=SCHEMA,
|
|
handler=lambda args, **kw: my_tool(**args),
|
|
check_fn=check_requirements,
|
|
emoji="📦",
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Example: Pi Agent Tool
|
|
|
|
See `pi_agent_tool.py` for a working example.
|
|
|
|
### Flow
|
|
```
|
|
User: "Fix the bug in auth.py"
|
|
↓
|
|
Hermes Agent decides to use pi_agent tool
|
|
↓
|
|
Calls pi_agent_tool(message="Fix the bug...")
|
|
↓
|
|
Tool calls HTTP server (Level 4)
|
|
↓
|
|
HTTP server runs Pi agent
|
|
↓
|
|
Returns response to Hermes
|
|
↓
|
|
Hermes shows to user
|
|
```
|
|
|
|
---
|
|
|
|
## How to Use
|
|
|
|
### 1. Start Pi Server (Level 4)
|
|
```bash
|
|
npx tsx level4.ts
|
|
```
|
|
|
|
### 2. Add Tool to Hermes
|
|
|
|
Option A: Copy to Hermes tools
|
|
```bash
|
|
cp pi_agent_tool.py ~/.hermes/hermes-agent/tools/
|
|
```
|
|
|
|
Option B: Add to Python path or custom tools directory
|
|
|
|
### 3. Enable in Hermes Config
|
|
|
|
```yaml
|
|
# In config.yaml
|
|
toolset:
|
|
- pi_agent
|
|
```
|
|
|
|
### 4. Use in Conversation
|
|
|
|
```
|
|
User: Can you fix the bug in auth.py?
|
|
|
|
Hermes: *uses pi_agent tool*
|
|
|
|
Tool result: Fixed the bug by changing line 42...
|
|
```
|
|
|
|
---
|
|
|
|
## Tool Best Practices
|
|
|
|
### 1. Always Return a String
|
|
```python
|
|
# Good
|
|
return "Result: found 5 files"
|
|
|
|
# Bad
|
|
return {"result": "found 5"} # JSON must be stringified
|
|
```
|
|
|
|
### 2. Handle Errors Gracefully
|
|
```python
|
|
try:
|
|
# Do work
|
|
return result
|
|
except Exception as e:
|
|
return f"Error: {str(e)}"
|
|
```
|
|
|
|
### 3. Add Requirements Check
|
|
```python
|
|
def check_requirements() -> bool:
|
|
# Check API keys, services, etc.
|
|
return api_key is not None
|
|
```
|
|
|
|
### 4. Write Clear Descriptions
|
|
```python
|
|
# Good - LLM knows when to use
|
|
"""
|
|
Analyze the codebase for security vulnerabilities.
|
|
Use after finding potential issues.
|
|
"""
|
|
|
|
# Bad - LLM confused
|
|
"""Do something"""
|
|
```
|
|
|
|
### 5. Keep Schema Simple
|
|
- Only include needed parameters
|
|
- Mark required parameters
|
|
- Add descriptions for each parameter
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### 1. Test the Function Directly
|
|
```python
|
|
# In Python
|
|
result = pi_agent_tool(message="Say hello")
|
|
print(result)
|
|
```
|
|
|
|
### 2. Test with curl
|
|
```bash
|
|
curl -X POST http://localhost:3000/message \
|
|
-d '{"message": "Hello"}'
|
|
```
|
|
|
|
### 3. Test with Hermes
|
|
- Add to toolset
|
|
- Ask Hermes to use the tool
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Tool Not Found
|
|
- Check tool is in `~/.hermes/hermes-agent/tools/`
|
|
- Check it's in the toolset config
|
|
|
|
### Tool Not Available
|
|
- Check `check_*_requirements()` returns `True`
|
|
- Check external service is running
|
|
|
|
### Tool Called but No Response
|
|
- Check tool returns a string
|
|
- Check for exceptions in handler
|
|
|
|
---
|
|
|
|
## Integration Options: HTTP vs Direct Spawn
|
|
|
|
There are two ways to integrate Pi agent with Hermes:
|
|
|
|
### Option 1: HTTP Server (Current Implementation)
|
|
|
|
```
|
|
Hermes → Python Tool → HTTP Request → Node/TS Server → Pi Agent
|
|
```
|
|
|
|
```python
|
|
# In tool
|
|
import requests
|
|
response = requests.post("http://localhost:3000/message", json={"message": "..."})
|
|
return response.json()["response"]
|
|
```
|
|
|
|
**Pros:**
|
|
- Easy to test/debug (curl, logs)
|
|
- Stateful (agent stays alive between calls)
|
|
- Reuses connections
|
|
- Easier monitoring/rate-limiting
|
|
|
|
**Cons:**
|
|
- More complex (two services)
|
|
- HTTP overhead (~50ms per call)
|
|
- Server must stay running
|
|
|
|
### Option 2: Direct Spawn (Alternative)
|
|
|
|
```
|
|
Hermes → Python Tool → Spawn Process → Pi Wrapper
|
|
```
|
|
|
|
```python
|
|
# In tool
|
|
import subprocess
|
|
process = subprocess.Popen(["npx", "tsx", "pi-wrapper.ts", message],
|
|
stdout=subprocess.PIPE)
|
|
stdout, _ = process.communicate(timeout=300)
|
|
return stdout.decode()
|
|
```
|
|
|
|
**Pros:**
|
|
- Simpler (one process per call)
|
|
- No server to maintain
|
|
- Matches Kugetsu's current pattern
|
|
- Good for low traffic
|
|
|
|
**Cons:**
|
|
- Slow startup (~100-500ms per call)
|
|
- No state between calls
|
|
- Harder to debug
|
|
- Resource heavy under load
|
|
|
|
### Comparison Table
|
|
|
|
| Factor | HTTP Server | Direct Spawn |
|
|
|--------|-------------|--------------|
|
|
| Latency | ~50ms | ~100-500ms |
|
|
| Memory | Persistent (50-100MB) | Per-call |
|
|
| State | Yes | No |
|
|
| Complexity | Higher | Lower |
|
|
| Debugging | Network logs | Process logs |
|
|
| Best For | Production | POC/Simple |
|
|
|
|
### Recommendation
|
|
|
|
- **High load / Production**: HTTP Server
|
|
- **Low load / POC**: Direct Spawn
|
|
- **Matches Kugetsu pattern**: Direct Spawn
|
|
|
|
---
|
|
|
|
## Files in This Project
|
|
|
|
| File | Description |
|
|
|------|-------------|
|
|
| `pi_agent_tool.py` | Working Hermes tool (HTTP approach) |
|
|
| `level4.ts` | HTTP server |
|
|
| `hermes-tool-guide.md` | This document |
|