PraisonAI: Building a Two-Agent AI Workforce

When thinking about AI automation, we picture a single chatbot answering questions. I built something different: two specialized AI workers that coordinate with each other, hand off work between themselves, and produce a finished output without any human involvement in the middle. This is the PraisonAI pattern, and once you see it working, the single-chatbot model feels like a typewriter next to a printing press.


What This Agent Does

This repo uses a PraisonAI-style pattern: multiple specialized agents working in sequence. The example keeps the implementation small by using a YAML configuration file, the OpenAI SDK, and LiteLLM instead of importing a heavy framework directly. Instead of asking one AI to research a topic, analyze it, and write a summary all at once, you assign each job to a specialist. A Researcher agent handles gathering and structuring information. A Writer agent handles turning that information into a clear business summary.

The two agents never overlap. The Researcher does not write. The Writer does not research. Each has a single clear job, and the output of one becomes the input of the other. I used this pattern to automate a workflow that structures the model’s response, passes it to a second agent, and rewrites it for a business audience. The entire process now runs in under two minutes with a single command.


Why Build This

Every AI deployment I have reviewed at the enterprise level eventually hits the same wall. You build a capable AI assistant, it handles simple requests well, and then you give it something complex and it tries to do everything at once. The output suffers. The reasoning gets muddled. The more you ask of a single AI in a single pass, the more it stretches thin across competing demands.

What I discovered in the PraisonAI architecture is a pattern that mirrors how high-performing human teams actually work. You do not ask your best analyst to also be your best writer. You hire for specialization and build handoff systems between specialists. The same logic applies to AI workers. When I separated the research function from the writing function and gave each its own clear instructions, the output quality improved dramatically. Not marginally. Dramatically.

The industry is still largely building single-agent solutions to multi-step problems. If your workflow has more than one distinct cognitive job, it should have more than one agent.


The Architecture: How It Works

The build has three layers.

How To Run It

python agent.py “AI governance risk register”

The topic has to be included because this script requires a topic argument. If you only run python agent.py, Python’s argparse stops the program and shows an error before either agent runs. The words are wrapped in quotes so the full phrase is passed as one topic instead of separate shell arguments.

Layer One: The Configuration File

Everything about the agents is defined in a plain text file called agents.yaml. This file is the steering wheel of the entire system. It defines who each agent is, what their goal is, and what their backstory is. The backstory matters more than it sounds. It is the context that shapes how the agent thinks. A Researcher with a backstory that says “you work for a boutique AI governance consultancy and your output must be fact-dense and ready for a writer” produces a structurally different output than a generic research prompt.

Here is the core of the configuration:

researcher:
  role: "Senior Research Analyst at NosisTech LLC"
  goal: "Gather and structure concise information on the given topic"
  backstory: >
    You are an expert researcher with deep knowledge of AI governance,
    cloud security, and regulatory frameworks. You work for NosisTech LLC,
    a boutique consultancy that advises enterprises on responsible AI
    deployment. Your output must be well-organized, fact-dense, and ready
    for a writer to transform into a clear business summary. Keep your
    response under 500 words, use short bullets instead of tables, and focus
    on the most important points.

writer:
  role: "Technical Writer at NosisTech LLC"
  goal: "Create a clear business summary from the researcher's structured findings"
  backstory: >
    You are a skilled technical communicator at NosisTech LLC who turns
    complex analyses into accessible language for business leaders.
    You never introduce new facts; you only simplify and clarify the
    information provided by the researcher. Your summaries are concise,
    accurate, and highlight actionable insights. Keep your response under
    400 words and use short sections instead of tables.

What This Code Is Actually Doing

The YAML file is not code. It is a configuration document anyone can edit without touching Python. Think of it as a job description document. You are telling the system: here is who this agent is, here is what they are trying to accomplish, and here is the professional context they operate in. The Python code reads this file and uses it to construct the instructions sent to the AI model. Change the YAML, change the agent’s behavior. The engine stays the same. The steering changes.

This is one of the most underrated patterns in agent architecture. The configuration is the product. Non-technical team members can modify agent behavior without ever opening a code file.

Layer Two: The Handoff Engine

The core of agent.py manages the sequential execution. The Researcher runs first. Its output is captured as a string. That string becomes the input to the Writer. The Writer never sees the original topic. It only sees what the Researcher produced.

researcher_prompt = build_system_prompt(researcher_conf)
print("[RESEARCHER] Starting research on: " + topic)
researcher_output = run_agent(client, model_name, researcher_prompt, topic)
print("[RESEARCHER] Complete. Handing off to Writer...")

writer_prompt = build_system_prompt(writer_conf)
print("[WRITER] Creating clear business summary...", flush=True)
final_summary = run_agent(client, model_name, writer_prompt, researcher_output)

What This Code Is Actually Doing

The first three lines run the Researcher. The function build_system_prompt takes the YAML configuration and turns it into a set of instructions the AI receives before it sees the topic. Think of it as the briefing a manager gives an employee before they start an assignment. The run_agent function then sends those instructions plus the topic to the AI model and waits for the response.

The next three lines repeat the process for the Writer, but notice the last argument. Instead of passing the original topic, it passes researcher_output. The Writer receives the Researcher’s complete report as its starting point. It has no access to the original topic. It cannot go off in a different direction. It can only work with what the Researcher produced. That constraint is intentional and important. It enforces the division of labor at the code level, not just at the instruction level.

Layer Three: The Model-Agnostic Router

Every API call routes through LiteLLM. The model name, the endpoint, and the API key are all pulled from environment variables. No model name appears anywhere in the code.

client = OpenAI(base_url=base_url, api_key=api_key, timeout=60)

The timeout matters because if the upstream model or proxy stalls, the script fails clearly instead of hanging silently.

What This Code Is Actually Doing

This single line is doing something significant. It creates an AI client that looks like an OpenAI connection but points at whatever URL you put in your LITELLM_BASE_URL environment variable. LiteLLM sits at that URL and translates the request into whatever format the actual AI provider requires. Claude, DeepSeek, Gemini, a local Ollama model running on your own hardware: all of them receive the same request format. The code never needs to know which one is running.


The Build: What I Tested and What I Found

For this verification, I ran the workflow through DeepSeek V4 Pro behind the local LiteLLM proxy. The important result was not just that the model answered. The handoff worked: the Researcher produced structured findings, the Writer transformed those findings, and the script completed successfully from one command.

The first test also exposed a practical issue: running python agent.py without a topic fails immediately because the script requires a topic argument. That is why the correct command is:

python agent.py “AI governance risk register”

That small detail matters. The agent cannot research an empty assignment.


What I Would Do Differently

This build demonstrates the pattern cleanly, but a production deployment for an enterprise client would require several additions that this version does not implement.

The most significant gap is immutable audit trails. This system prints what each agent produces to the console, but it does not create immutable audit logs. An enterprise deploying autonomous AI workers needs cryptographically signed records of every agent decision, every handoff, and every output. Without that, you cannot prove to a regulator, an auditor, or a client what the agent actually did.

The second gap is dynamic cost routing. Both agents use the same model for every run. In a production system processing thousands of workflows, you would route simpler tasks to cheaper models and reserve the expensive, high-capability models for complex reasoning steps. A FinOps layer that makes those routing decisions in real time could cut enterprise inference costs significantly while maintaining output quality where it matters.

The third gap is role-based access control at the agent level. In a production version, if the Researcher agent had access to a database tool, it would need access controls that limit exactly what it can see. A production system needs agents to inherit the precise clearance level of the human user who triggered the workflow. Zero-trust security applied to AI agents is an architectural pattern that almost no current framework implements correctly, and it is the first thing an enterprise security team will ask about.

The fourth gap is human-in-the-loop escalation with SLA management. This build runs to completion automatically. A production version needs defined triggers: if the Researcher produces output below a confidence threshold, pause and ask a human before the Writer runs. If the final summary contains a compliance flag, route to a reviewer before delivery. And if that reviewer does not respond within a defined window, escalate automatically. This demo does not include that SLA management layer.


What This Means

If you run a business that produces any kind of research, analysis, or written output on a recurring basis, the pattern in this post is directly applicable to your operation.

Market research that currently takes a team member four hours to produce can be restructured as a two-agent workflow. One agent gathers and structures the data. Another agent writes the client-facing summary. The human reviews the output and makes judgment calls. The mechanical work is automated. The strategic work stays with people.

The YAML configuration means your team can modify the agents’ behavior without involving a developer. Change the Researcher’s backstory to focus on a specific industry. Change the Writer’s goal to produce output in a specific format. The system adapts. The code does not change.

The model-agnostic layer means you are not locked into any single AI provider. As better models become available, you update one line in a configuration file. Your workflow continues without interruption and immediately benefits from the improved capability.

The businesses that build these patterns now will have a structural advantage that compounds over time. The longer these systems run, the more refined the configurations become, and the wider the gap grows between organizations that automate at this level and those that are still doing it manually.


Tools Used


(c) 2026 NosisTech LLC. Licensed under CC BY 4.0. Use freely, just credit us.

WEEKLY BUILD NOTES

One documented agent build in your inbox, every week

Real systems, real code, the errors left in. No spam, unsubscribe anytime.