2026-07-13 10:08 UTC
DANGMUAAI & Developer Tools, Decoded
BackAgents

How to Build a 5-Agent AI Pipeline in Pure Python (No CrewAI)

A developer built a Researcher-Writer-Editor-Reviewer-Publisher pipeline in plain Python and Ollama, skipping CrewAI and AutoGen, step by step.

DangMua EditorialJul 13, 20265 min read

A backend team wants to automate a content pipeline — research, draft, edit, review, publish — and the first three search results all point to CrewAI or AutoGen. Installing either pulls in a dependency tree, an opinionated orchestration layer, and abstractions that hide what actually happens when one agent hands off to the next. For teams that just want to see the mechanics before committing to a framework, there is a simpler starting point.

A developer wrote up exactly that starting point in a how-to: five agents — Researcher, Writer, Editor, Reviewer, Publisher — chained into one pipeline, using nothing but the requests library and a local Ollama model. The write-up frames it as a direct alternative to reaching for a framework on day one, and its core claim is worth stating plainly before the code: the author estimates the whole pipeline at roughly 50 lines, against what he describes as 500 lines to wire up the same flow in CrewAI. That is his own comparison, not an independent benchmark, but it is the reason the pattern is worth understanding.

The pipeline is five agents and one loop

The design has no message bus, no shared memory store, and no planner deciding which agent runs next. Output from one agent becomes the input string for the next, in a fixed order: Researcher → Writer → Editor → Reviewer → Publisher. Everything below builds toward that one for-loop.

Step 1: Define the Agent class

Each agent needs exactly three things to exist: a name for logging, a role label, and a system prompt that tells the model what job it is doing. Nothing else belongs on this class yet.

class Agent:
    def __init__(self, name, role, system_prompt):
        self.name = name
        self.role = role
        self.system_prompt = system_prompt

Step 2: Give the agent a way to think

The process() method is the only place an LLM gets called, which is what makes the whole pipeline swappable to another model provider later. In the source implementation it talks to a local Ollama server.

def process(self, input_text):
    """Call LLM with system prompt + input"""
    import requests
    response = requests.post(
        "http://localhost:11434/api/generate",
        json={
            "model": "llama3.2",
            "prompt": f"{self.system_prompt}\n\nInput: {input_text}",
            "stream": False
        }
    )
    return response.json()["response"]

Because the prompt is just an f-string and the endpoint is a plain HTTP call, pointing this at OpenAI, Groq, or any other model host means changing the URL and payload shape — not the class.

Step 3: Spec the five roles before you write more code

This table is the actual reusable artifact from the source guide — the role and system-prompt combination for each stage of the pipeline.

AgentRoleSystem prompt
ResearcherresearchFind 5 key facts about the topic. Be concise.
WriterwriteWrite a 500-word article from the research.
EditoreditImprove clarity, fix grammar, make it engaging.
ReviewerreviewScore 1-10. List what works and what needs fixing.
PublisherpublishFormat as markdown with title, headings, and CTA.

Step 4: Chain the agents into a Pipeline

Orchestration here is one class and one loop — each agent's output becomes the next agent's input, with a print statement marking progress.

class Pipeline:
    def __init__(self, agents):
        self.agents = agents

    def run(self, topic):
        output = topic
        for agent in self.agents:
            print(f"{agent.name} working...")
            output = agent.process(output)
            print(f"Done ({len(output)} chars)")
        return output

That is the entire orchestration layer the source guide argues you do not need a framework to build: a list of five Agent objects passed into Pipeline, then .run(topic).

What this minimal version does not handle

This part is analysis, not something the source guide addresses — the code above is silent on production concerns, and it is worth being direct about what is missing. There is no retry logic in process(): if the Ollama call times out or the model returns malformed output, the exception propagates straight up and the pipeline stops. There is no parallel fan-out: the for loop in Pipeline.run() processes agents strictly one after another, so a five-agent chain calling a slow local model will run five sequential round trips. And there is no state persistence across runs — nothing in either class writes intermediate output to disk, so a crash after the Editor step loses the Researcher and Writer output along with it. None of that makes the pattern wrong for a first pass; it just means anyone shipping it past a prototype has to add those pieces themselves.

Pro tips before you wire this in

The source guide closes with a short list of practical adjustments worth treating as a checklist rather than an afterthought.

  • Match the model to the task: a code-oriented model like codellama for review-style scoring, llama3.2 for the writing stage.
  • Temperature per role: lower for the Researcher, where factual consistency matters more than variety; higher for the Writer, where some creative range helps.
  • Fallback models: if one model fails, try another.
  • Logging each agent's output: save every stage's response separately so a bad final result can be traced back to the step that introduced it.

The mechanism underneath all of this is deliberately small: one class holding a name, a role, and a prompt; one method making an HTTP call; one loop passing a string from agent to agent. Nothing here requires a framework's scheduler, memory abstraction, or plugin system to function.

The cheapest way to test whether this fits a real workflow is to copy the Agent and Pipeline classes above, point process() at whatever model endpoint is already running, and swap in a system prompt for one real task — before deciding whether CrewAI, AutoGen, or a lean Python script actually solves the problem at hand.

More from DangMua