DEV Community

Francis Oyakhire
Francis Oyakhire

Posted on

Cron Scheduled Ollama Autonomous Agent

This week, the AI infrastructure space saw some eye-opening benchmarks, with modular systems showing dramatic performance gains over alternatives. Amid all the noise, we’ve been quietly iterating on a specific use case that sits at the intersection of autonomous agents and on-device AI: a cron-scheduled Ollama-based social agent that runs on our infrastructure and interacts with users across multiple channels.

We’re building a system where an autonomous agent generates content at regular intervals, processes it through multiple gates, and only publishes it if it passes all checks. The core of this system is Ollama, which we use for its lightweight footprint and ability to run large language models locally. To make this work, we’ve designed a modular architecture that separates concerns: voice profiles are stored as separate files, controversy is checked by a second model, and credit checks are made by querying a Postgres database. Rejected content is quarantined for later review.

Here’s how the publish pipeline looks in code. We’ve abstracted the main function as publish_to_all_channels, which is called by a cron job every 12 hours. The function is responsible for generating content, running it through the gates, and publishing if all checks pass:

from ollama import Client
import psycopg2
import os

def publish_to_all_channels():
    # Step 1: Generate content using Ollama
    client = Client()
    response = client.generate(model="llama3", prompt="Write a social media post about AI ethics")
    generated_content = response["response"].strip()

    # Step 2: Run through voice profile check
    if not check_voice_profile(generated_content):
        log_to_quarantine(generated_content, "voice_profile_failed")
        return

    # Step 3: Run through controversy gate using a second model
    controversy_client = Client(model="controversy-checker")
    controversy_result = controversy_client.generate(prompt=generated_content)
    if controversy_result["response"].lower() in ["controversial", "high_risk"]:
        log_to_quarantine(generated_content, "controversy_gate_failed")
        return

    # Step 4: Run through credit gate by querying Postiz DB
    try:
        conn = psycopg2.connect(
            dbname=os.getenv("POSTIZ_DB_NAME"),
            user=os.getenv("POSTIZ_DB_USER"),
            password=os.getenv("POSTIZ_DB_PASSWORD"),
            host=os.getenv("POSTIZ_DB_HOST"),
            port=os.getenv("POSTIZ_DB_PORT")
        )
        cur = conn.cursor()
        cur.execute("SELECT * FROM credits WHERE content = %s", (generated_content,))
        if not cur.fetchone():
            log_to_quarantine(generated_content, "credit_gate_failed")
            return
    except Exception as e:
        print(f"Database error: {e}")
        log_to_quarantine(generated_content, "database_error")
        return
    finally:
        if conn:
            conn.close()

    # Step 5: Publish the content to all channels
    publish_content(generated_content)
Enter fullscreen mode Exit fullscreen mode

This approach lets us keep the system modular and scalable. Each gate is a self-contained function that can be swapped out or updated independently. For example, we could replace the controversy-checker model with a different one if needed, or even switch to a rule-based system for certain use cases. The quarantine system is equally important - it allows us to review content that failed any gate, and potentially reprocess it later if conditions change.

There are tradeoffs, of course. By using cron jobs, we’re introducing a fixed interval between runs, which may not be ideal for real-time use cases. Also, the current setup assumes a single content generation per run, which might not scale well for high-frequency publishing. We’re actively exploring ways to decouple the scheduling from the processing pipeline, and are looking into event-driven architectures that could allow for more dynamic content generation and publishing.

We’re also working on improving the performance of the controversy gate - the second model check is currently a bit slow, and we’re experimenting with model quantization and caching to reduce latency. What do you think is the right balance between accuracy and speed in this kind of gate? We’d love to hear your thoughts in the comments.

Top comments (0)