DEV Community

Francis Oyakhire
Francis Oyakhire

Posted on

Cron Scheduled Ollama Autonomous Agent

This week’s news about new constraints on AI agents within networked systems reminded us of a critical design decision we made early in our work on autonomous social agents. While the headlines focus on policy and governance, the real challenge lies in the infrastructure that enables these agents to operate safely and autonomously. Here's how we built one such agent using Ollama, cron scheduling, and a layered gate system to ensure responsible behavior.

We're building a social agent that autonomously generates content for multiple channels. The agent runs on a Linux server, with a cron job triggering it every hour. The core of the agent is an Ollama model that generates text, but before any content is published, it must pass through a series of gates to ensure it's safe, relevant, and aligned with our values.

Our stack runs on a combination of Ollama for the LLM, Postgres for data storage, and Postiz for message queuing. We chose Postiz over other systems because of its lightweight design and compatibility with our existing infrastructure. The agent’s workflow is as follows:

  1. Voice Profile Check: Each user has their own voice profile stored as a JSON file. The agent loads this profile to determine the tone, style, and personality of the generated content.
  2. Controversy Gate: A second Ollama model acts as a classifier, scanning the generated text for any potentially controversial or harmful content.
  3. Credit Gate: A Postgres query checks the Postiz DB to ensure the agent has sufficient credits or permissions to publish the content.
  4. Quarantine Folder: Any content that fails the gates is moved to a quarantine folder for review before being discarded or reprocessed.

Here’s the function signature for publish_to_all_channels, which encapsulates the entire process:

def publish_to_all_channels(generated_text: str, voice_profile: Dict[str, Any]) -> bool:
    if not check_voice_profile(voice_profile):
        log.warning("Voice profile check failed")
        return False

    if not controversy_gate(generated_text):
        log.warning("Controversy gate failed")
        return False

    if not credit_gate():
        log.warning("Credit gate failed")
        return False

    try:
        for channel in CHANNELS:
            send_to_channel(channel, generated_text)
        return True
    except Exception as e:
        log.error(f"Failed to publish content: {e}")
        move_to_quarantine(generated_text)
        return False
Enter fullscreen mode Exit fullscreen mode

Each of these gates has its own tradeoffs. The voice profile check ensures consistency, but it adds overhead in terms of file I/O and memory usage. The controversy gate is computationally expensive, as it involves running a second model. The credit gate introduces a dependency on Postgres and Postiz, which can be a bottleneck under high load.

We're actively working on optimizing the controversy gate by experimenting with smaller, more efficient models that can run locally without sacrificing accuracy. We're also exploring ways to batch process content to reduce the number of model inferences required.

What we're building next is a more distributed version of this agent that can run on multiple nodes, with each node handling a subset of the channels. This will help scale the system and reduce the load on any single component. We're also considering integrating real-time feedback from users to dynamically adjust the voice profile and gate thresholds.

Top comments (0)