Skip to main content

Overview

AgentSystem is the abstract base class that all agents in HyperAgents inherit from. It lives in agent/base_agent.py and provides two things every agent needs: a configured LLM model identifier and a thread-safe logger that writes conversation history to a markdown file. Both MetaAgent and TaskAgent extend AgentSystem and implement its single abstract method, forward.

Source

agent/base_agent.py
from abc import ABC, abstractmethod
from agent.llm import OPENAI_MODEL
from utils.thread_logger import ThreadLoggerManager


class AgentSystem(ABC):
    def __init__(
        self,
        model=OPENAI_MODEL,
        chat_history_file='./outputs/chat_history.md',
    ):
        self.model = model

        # Initialize logger and store it in thread-local storage
        self.logger_manager = ThreadLoggerManager(log_file=chat_history_file)
        self.log = self.logger_manager.log

        # Clear the log file
        with open(chat_history_file, 'w') as f:
            f.write('')

    @abstractmethod
    def forward(self, *args, **kwargs):
        pass

Constructor

AgentSystem(model=OPENAI_MODEL, chat_history_file='./outputs/chat_history.md')
model
str
default:"openai/gpt-4o"
The LLM model identifier passed to LiteLLM. Uses the provider/model-name format. The default constant OPENAI_MODEL resolves to "openai/gpt-4o".
chat_history_file
str
default:"./outputs/chat_history.md"
Path to the file where the full conversation history (all LLM inputs and outputs) will be written. The file is cleared on every instantiation — each agent run starts with a fresh log. The parent directory is created automatically by ThreadLoggerManager if it does not exist.

Instance Attributes

AttributeTypeDescription
self.modelstrThe model identifier string passed to the constructor.
self.logger_managerThreadLoggerManagerThe logger manager instance. Keyed internally by (thread_id, log_file) so multiple agents running in threads write to separate files without locking.
self.logcallableShortcut to self.logger_manager.log(message, level=logging.INFO). Call this inside forward to record any string to the chat history file.

Abstract Method

@abstractmethod
def forward(self, *args, **kwargs):
    pass
Every subclass must implement forward. The signature is unconstrained at the base class level — concrete agents define their own parameter lists. Calling AgentSystem() directly or subclassing without implementing forward raises TypeError.

ThreadLoggerManager

ThreadLoggerManager (in utils/thread_logger.py) wraps Python’s standard logging module with thread-safety. It is used internally by AgentSystem but can also be used standalone.
utils/thread_logger.py
class ThreadLoggerManager:
    _loggers = {}
    _lock = threading.Lock()

    def __init__(self, log_file='./chat_history.md', level=logging.INFO):
        ...

    def get_logger(self):
        """Get or create a logger specific to (thread_id, log_file)."""
        ...

    def log(self, message, level=logging.INFO):
        logger = self.get_logger()
        logger.log(level, message)
Key implementation details:
  • Loggers are stored in a class-level dict keyed by (thread_id, log_file). A ThreadPoolExecutor running five TaskAgent instances will therefore produce five independent log files with no interleaving.
  • The underlying RotatingFileHandler caps each file at 10 MB with up to 5 rotating backups.
  • Log entries are written with the format string '%(message)s' — just the bare message, no timestamps or level prefixes — which keeps the chat history files human-readable as markdown.

Subclassing Pattern

The pattern used throughout HyperAgents is to override only forward and delegate all LLM calls to chat_with_agent, passing self.model and self.log:
from agent.base_agent import AgentSystem
from agent.llm_withtools import chat_with_agent

class MyAgent(AgentSystem):
    def forward(self, inputs):
        instruction = f"Do something with: {inputs}"
        new_msg_history = chat_with_agent(
            instruction,
            model=self.model,
            msg_history=[],
            logging=self.log,
        )
        return new_msg_history
The constructor is inherited as-is. Instantiate your agent with:
from agent.llm import CLAUDE_MODEL

agent = MyAgent(
    model=CLAUDE_MODEL,
    chat_history_file="./outputs/my_agent_chat.md",
)
agent.forward(inputs)
AgentSystem.__init__ opens chat_history_file in write mode ('w') every time a new instance is created, which truncates any existing content. If you need to preserve logs across runs, copy the file before creating a new agent instance.

Build docs developers (and LLMs) love