python-library.md
1 --- 2 sidebar_position: 5 3 title: "Using Hermes as a Python Library" 4 description: "Embed AIAgent in your own Python scripts, web apps, or automation pipelines — no CLI required" 5 --- 6 7 # Using Hermes as a Python Library 8 9 Hermes isn't just a CLI tool. You can import `AIAgent` directly and use it programmatically in your own Python scripts, web applications, or automation pipelines. This guide shows you how. 10 11 --- 12 13 ## Installation 14 15 Install Hermes directly from the repository: 16 17 ```bash 18 pip install git+https://github.com/NousResearch/hermes-agent.git 19 ``` 20 21 Or with [uv](https://docs.astral.sh/uv/): 22 23 ```bash 24 uv pip install git+https://github.com/NousResearch/hermes-agent.git 25 ``` 26 27 You can also pin it in your `requirements.txt`: 28 29 ```text 30 hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git 31 ``` 32 33 :::tip 34 The same environment variables used by the CLI are required when using Hermes as a library. At minimum, set `OPENROUTER_API_KEY` (or `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` if using direct provider access). 35 ::: 36 37 --- 38 39 ## Basic Usage 40 41 The simplest way to use Hermes is the `chat()` method — pass a message, get a string back: 42 43 ```python 44 from run_agent import AIAgent 45 46 agent = AIAgent( 47 model="anthropic/claude-sonnet-4", 48 quiet_mode=True, 49 ) 50 response = agent.chat("What is the capital of France?") 51 print(response) 52 ``` 53 54 `chat()` handles the full conversation loop internally — tool calls, retries, everything — and returns just the final text response. 55 56 :::warning 57 Always set `quiet_mode=True` when embedding Hermes in your own code. Without it, the agent prints CLI spinners, progress indicators, and other terminal output that will clutter your application's output. 58 ::: 59 60 --- 61 62 ## Full Conversation Control 63 64 For more control over the conversation, use `run_conversation()` directly. It returns a dictionary with the full response, message history, and metadata: 65 66 ```python 67 agent = AIAgent( 68 model="anthropic/claude-sonnet-4", 69 quiet_mode=True, 70 ) 71 72 result = agent.run_conversation( 73 user_message="Search for recent Python 3.13 features", 74 task_id="my-task-1", 75 ) 76 77 print(result["final_response"]) 78 print(f"Messages exchanged: {len(result['messages'])}") 79 ``` 80 81 The returned dictionary contains: 82 - **`final_response`** — The agent's final text reply 83 - **`messages`** — The complete message history (system, user, assistant, tool calls) 84 - **`task_id`** — The task identifier used for VM isolation 85 86 You can also pass a custom system message that overrides the ephemeral system prompt for that call: 87 88 ```python 89 result = agent.run_conversation( 90 user_message="Explain quicksort", 91 system_message="You are a computer science tutor. Use simple analogies.", 92 ) 93 ``` 94 95 --- 96 97 ## Configuring Tools 98 99 Control which toolsets the agent has access to using `enabled_toolsets` or `disabled_toolsets`: 100 101 ```python 102 # Only enable web tools (browsing, search) 103 agent = AIAgent( 104 model="anthropic/claude-sonnet-4", 105 enabled_toolsets=["web"], 106 quiet_mode=True, 107 ) 108 109 # Enable everything except terminal access 110 agent = AIAgent( 111 model="anthropic/claude-sonnet-4", 112 disabled_toolsets=["terminal"], 113 quiet_mode=True, 114 ) 115 ``` 116 117 :::tip 118 Use `enabled_toolsets` when you want a minimal, locked-down agent (e.g., only web search for a research bot). Use `disabled_toolsets` when you want most capabilities but need to restrict specific ones (e.g., no terminal access in a shared environment). 119 ::: 120 121 --- 122 123 ## Multi-turn Conversations 124 125 Maintain conversation state across multiple turns by passing the message history back in: 126 127 ```python 128 agent = AIAgent( 129 model="anthropic/claude-sonnet-4", 130 quiet_mode=True, 131 ) 132 133 # First turn 134 result1 = agent.run_conversation("My name is Alice") 135 history = result1["messages"] 136 137 # Second turn — agent remembers the context 138 result2 = agent.run_conversation( 139 "What's my name?", 140 conversation_history=history, 141 ) 142 print(result2["final_response"]) # "Your name is Alice." 143 ``` 144 145 The `conversation_history` parameter accepts the `messages` list from a previous result. The agent copies it internally, so your original list is never mutated. 146 147 --- 148 149 ## Saving Trajectories 150 151 Enable trajectory saving to capture conversations in ShareGPT format — useful for generating training data or debugging: 152 153 ```python 154 agent = AIAgent( 155 model="anthropic/claude-sonnet-4", 156 save_trajectories=True, 157 quiet_mode=True, 158 ) 159 160 agent.chat("Write a Python function to sort a list") 161 # Saves to trajectory_samples.jsonl in ShareGPT format 162 ``` 163 164 Each conversation is appended as a single JSONL line, making it easy to collect datasets from automated runs. 165 166 --- 167 168 ## Custom System Prompts 169 170 Use `ephemeral_system_prompt` to set a custom system prompt that guides the agent's behavior but is **not** saved to trajectory files (keeping your training data clean): 171 172 ```python 173 agent = AIAgent( 174 model="anthropic/claude-sonnet-4", 175 ephemeral_system_prompt="You are a SQL expert. Only answer database questions.", 176 quiet_mode=True, 177 ) 178 179 response = agent.chat("How do I write a JOIN query?") 180 print(response) 181 ``` 182 183 This is ideal for building specialized agents — a code reviewer, a documentation writer, a SQL assistant — all using the same underlying tooling. 184 185 --- 186 187 ## Batch Processing 188 189 For running many prompts in parallel, Hermes includes `batch_runner.py`. It manages concurrent `AIAgent` instances with proper resource isolation: 190 191 ```bash 192 python batch_runner.py --input prompts.jsonl --output results.jsonl 193 ``` 194 195 Each prompt gets its own `task_id` and isolated environment. If you need custom batch logic, you can build your own using `AIAgent` directly: 196 197 ```python 198 import concurrent.futures 199 from run_agent import AIAgent 200 201 prompts = [ 202 "Explain recursion", 203 "What is a hash table?", 204 "How does garbage collection work?", 205 ] 206 207 def process_prompt(prompt): 208 # Create a fresh agent per task for thread safety 209 agent = AIAgent( 210 model="anthropic/claude-sonnet-4", 211 quiet_mode=True, 212 skip_memory=True, 213 ) 214 return agent.chat(prompt) 215 216 with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor: 217 results = list(executor.map(process_prompt, prompts)) 218 219 for prompt, result in zip(prompts, results): 220 print(f"Q: {prompt}\nA: {result}\n") 221 ``` 222 223 :::warning 224 Always create a **new `AIAgent` instance per thread or task**. The agent maintains internal state (conversation history, tool sessions, iteration counters) that is not thread-safe to share. 225 ::: 226 227 --- 228 229 ## Integration Examples 230 231 ### FastAPI Endpoint 232 233 ```python 234 from fastapi import FastAPI 235 from pydantic import BaseModel 236 from run_agent import AIAgent 237 238 app = FastAPI() 239 240 class ChatRequest(BaseModel): 241 message: str 242 model: str = "anthropic/claude-sonnet-4" 243 244 @app.post("/chat") 245 async def chat(request: ChatRequest): 246 agent = AIAgent( 247 model=request.model, 248 quiet_mode=True, 249 skip_context_files=True, 250 skip_memory=True, 251 ) 252 response = agent.chat(request.message) 253 return {"response": response} 254 ``` 255 256 ### Discord Bot 257 258 ```python 259 import discord 260 from run_agent import AIAgent 261 262 client = discord.Client(intents=discord.Intents.default()) 263 264 @client.event 265 async def on_message(message): 266 if message.author == client.user: 267 return 268 if message.content.startswith("!hermes "): 269 query = message.content[8:] 270 agent = AIAgent( 271 model="anthropic/claude-sonnet-4", 272 quiet_mode=True, 273 skip_context_files=True, 274 skip_memory=True, 275 platform="discord", 276 ) 277 response = agent.chat(query) 278 await message.channel.send(response[:2000]) 279 280 client.run("YOUR_DISCORD_TOKEN") 281 ``` 282 283 ### CI/CD Pipeline Step 284 285 ```python 286 #!/usr/bin/env python3 287 """CI step: auto-review a PR diff.""" 288 import subprocess 289 from run_agent import AIAgent 290 291 diff = subprocess.check_output(["git", "diff", "main...HEAD"]).decode() 292 293 agent = AIAgent( 294 model="anthropic/claude-sonnet-4", 295 quiet_mode=True, 296 skip_context_files=True, 297 skip_memory=True, 298 disabled_toolsets=["terminal", "browser"], 299 ) 300 301 review = agent.chat( 302 f"Review this PR diff for bugs, security issues, and style problems:\n\n{diff}" 303 ) 304 print(review) 305 ``` 306 307 --- 308 309 ## Key Constructor Parameters 310 311 | Parameter | Type | Default | Description | 312 |-----------|------|---------|-------------| 313 | `model` | `str` | `"anthropic/claude-opus-4.6"` | Model in OpenRouter format | 314 | `quiet_mode` | `bool` | `False` | Suppress CLI output | 315 | `enabled_toolsets` | `List[str]` | `None` | Whitelist specific toolsets | 316 | `disabled_toolsets` | `List[str]` | `None` | Blacklist specific toolsets | 317 | `save_trajectories` | `bool` | `False` | Save conversations to JSONL | 318 | `ephemeral_system_prompt` | `str` | `None` | Custom system prompt (not saved to trajectories) | 319 | `max_iterations` | `int` | `90` | Max tool-calling iterations per conversation | 320 | `skip_context_files` | `bool` | `False` | Skip loading AGENTS.md files | 321 | `skip_memory` | `bool` | `False` | Disable persistent memory read/write | 322 | `api_key` | `str` | `None` | API key (falls back to env vars) | 323 | `base_url` | `str` | `None` | Custom API endpoint URL | 324 | `platform` | `str` | `None` | Platform hint (`"discord"`, `"telegram"`, etc.) | 325 326 --- 327 328 ## Important Notes 329 330 :::tip 331 - Set **`skip_context_files=True`** if you don't want `AGENTS.md` files from the working directory loaded into the system prompt. 332 - Set **`skip_memory=True`** to prevent the agent from reading or writing persistent memory — recommended for stateless API endpoints. 333 - The `platform` parameter (e.g., `"discord"`, `"telegram"`) injects platform-specific formatting hints so the agent adapts its output style. 334 ::: 335 336 :::warning 337 - **Thread safety**: Create one `AIAgent` per thread or task. Never share an instance across concurrent calls. 338 - **Resource cleanup**: The agent automatically cleans up resources (terminal sessions, browser instances) when a conversation ends. If you're running in a long-lived process, ensure each conversation completes normally. 339 - **Iteration limits**: The default `max_iterations=90` is generous. For simple Q&A use cases, consider lowering it (e.g., `max_iterations=10`) to prevent runaway tool-calling loops and control costs. 340 :::