feat: agent with loop strategy

This commit is contained in:
Daniel Eder 2025-09-28 11:14:44 +02:00
parent 905e3c91ab
commit a2b7feec16
2 changed files with 45 additions and 2 deletions

View file

@ -8,7 +8,7 @@ Python-based Telnet helper for connecting to MUD servers, handling login flows,
- Loads credentials and connection settings from a local `.env` file.
- Interactive console session that mirrors server output and lets you type commands directly.
- Optional always-on tool mode plus an on-demand `#execute <tool>` escape hatch for ad-hoc automations.
- Higher-level agents (`FixedStrategyAgent` so far) that can string multiple tools together via `#agent <spec>`.
- Higher-level agents (`fixed`, `loop`) that can string multiple tools together via `#agent <spec>`.
- Built-in tools (`SimpleTool`, `ExploreTool`, `CommunicationTool`, `MovementTool`, `IntelligentCommunicationTool`) with a pluggable interface for custom behaviours.
## Requirements
@ -50,6 +50,14 @@ Python-based Telnet helper for connecting to MUD servers, handling login flows,
This example uses the fixed strategy agent to run `move` and then `explore` once. The first token after `#agent` selects the agent type (`fixed` today, more to come), and any remaining text is passed as that agent's configuration.
7. To run a looping agent that repeats tools, use:
```text
#agent loop move,explore
```
Append `:delay` to pause between iterations, e.g. `#agent loop move,explore:2.5`.
## Environment Variables
All variables can be placed in the `.env` file (one `KEY=value` per line) or provided through the shell environment.

View file

@ -2,9 +2,10 @@ from __future__ import annotations
import sys
from abc import ABC, abstractmethod
import itertools
from dataclasses import dataclass
from threading import Event
from typing import Callable, Iterable
from typing import Callable
ToolInvoker = Callable[[str], bool]
@ -39,6 +40,30 @@ class FixedStrategyAgent(Agent):
break
@dataclass
class LoopAgent(Agent):
"""Continuously execute a fixed strategy until stopped."""
plan: str
delay: float = 0.0
def run(self, *, invoke_tool: ToolInvoker, stop_event: Event) -> None:
steps = [part.strip() for part in self.plan.split(",") if part.strip()]
if not steps:
print("[Agent] No tools configured for loop strategy", file=sys.stderr)
return
for step in itertools.cycle(steps):
if stop_event.is_set():
break
success = invoke_tool(step)
if not success:
break
if self.delay > 0:
if stop_event.wait(self.delay):
break
def build_agent(spec: str) -> Agent:
normalized = spec.strip()
if not normalized:
@ -50,5 +75,15 @@ def build_agent(spec: str) -> Agent:
if kind in {"fixed", "strategy", "fixedstrategy"}:
return FixedStrategyAgent(config)
if kind in {"loop", "cycle"}:
delay = 0.0
if ":" in config:
plan, delay_str = config.split(":", 1)
config = plan.strip()
try:
delay = float(delay_str.strip())
except ValueError:
print(f"[Agent] Invalid delay '{delay_str}', defaulting to 0")
return LoopAgent(config, delay=delay)
raise RuntimeError(f"Unknown agent type '{kind}'")