How to Structure a Python Project for AI Agents: A Production-Ready Monorepo Architecture

Prime Agent uses a multi-package monorepo that cleanly separates runtime core, AI providers, agent orchestration, and user interfaces while keeping Python-specific components modular and extensible.

If you're building AI agents in Python, organizing your codebase for maintainability and scalability is critical. This article examines how the Prime Intellect team structures PrimeIntellect-ai/prime-agent—a production-grade agent framework—to achieve clean separation of concerns, easy skill development, and provider independence.

The Monorepo Layer Architecture

Prime Agent organizes code into distinct layers, each with a clear responsibility. This structure prevents tight coupling between LLM providers, agent logic, and presentation layers.

Layer Purpose Location
Runtime Core Sub-agent execution, skill management, messaging protocol prime-agent-runtime/src/rlm/
AI Provider Layer LLM backend abstractions (OpenAI, Anthropic, Bedrock) packages/ai/
Agent Orchestration Agent lifecycle, configuration, CLI packages/agent/
Coding Agent Python code execution, tool-running sub-agent packages/coding-agent/
Text-User-Interface (TUI) Interactive terminal UI packages/tui/
Support Scripts Build, release, benchmarking utilities scripts/

The Python-specific runtime lives in prime-agent-runtime/, while TypeScript-based packages share the packages/ directory. This hybrid approach isolates performance-critical Python code from UI and tooling layers.

Core Python Runtime: Skills, Messages, and Harnesses

The runtime layer implements the Remote Call Protocol (RCP)—the message-passing system that lets sub-agents invoke skills asynchronously. Understanding these three files is essential for extending the framework.

Skill Registration (skill.py)

In prime-agent-runtime/src/rlm/skill.py, the SkillBase class defines the contract for all capabilities. Skills are self-registering via a decorator pattern.


# prime-agent-runtime/src/rlm/my_skill.py

from .skill import SkillBase, register_skill

class MySkill(SkillBase):
    """Example skill that echoes a message."""
    name = "my-echo"

    async def run(self, input_text: str) -> str:
        return f"Echo: {input_text}"

register_skill(MySkill)

The register_skill function updates a global registry that the runtime queries at startup. No manual configuration files are required—discovery is automatic.

Test Harness (harness.py)

The SubAgentHarness class in prime-agent-runtime/src/rlm/harness.py spawns a daemon subprocess and drives message exchange. Use it to test skills in isolation without running the full agent stack.

import json
import asyncio
from prime_agent_runtime.src.rlm.harness import SubAgentHarness

async def demo():
    async with SubAgentHarness() as harness:
        response = await harness.call_skill(
            skill_name="my-echo",
            args={"input_text": "Hello, AI!"}
        )
        print(json.loads(response)["result"])

asyncio.run(demo())

The harness handles serialization, daemon lifecycle, and timeout management—your tests focus on skill logic, not plumbing.

Multi-Channel Protocol (mcp_base.py)

For advanced use cases, prime-agent-runtime/src/rlm/mcp_base.py provides Multi-Channel Protocol utilities. This enables skills to stream partial results, handle interruptions, and manage long-running operations across multiple communication channels.

AI Provider Abstraction

The packages/ai/ directory implements provider-agnostic LLM access. According to the codebase, all providers conform to a unified interface exposing a single stream() function returning AssistantMessageEventStream.

Provider-specific modules live in packages/ai/src/providers/. Adding a new LLM backend requires implementing this interface—no changes to agent logic are needed.

import { stream } from "prime-agent/packages/ai";

const events = await stream("gpt-4o-mini", {
  prompt: "Explain the difference between a function and a method."
});

for await (const ev of events) {
  if (ev.type === "text") process.stdout.write(ev.text);
}

This abstraction ensures your agents remain provider-agnostic even as underlying models and APIs evolve.

Agent Orchestration and CLI

The packages/agent/ directory handles high-level concerns: agent lifecycle, command-line argument parsing, and provider display naming. The top-level entry point prime-agent.sh coordinates daemon startup, agent loading, and optional TUI launch.


# Start with interactive terminal UI

./prime-agent.sh --tui

# Run headless with specific configuration

./prime-agent.sh --config agents/default.json

Coding Agent: Specialized Sub-Agent for Code Execution

The packages/coding-agent/ package demonstrates how to build domain-specific sub-agents. It includes:

  • Skill plug-ins (websearch, attach-image, etc.)
  • Extension scaffolding for adding new tools
  • Comprehensive test suites

This pattern—runtime core + specialized agent packages—lets you compose complex agent systems without bloating the core.

Text-User-Interface Layer

The packages/tui/ package renders rich terminal output: syntax-highlighted code, images, and interactive prompts. It connects to the same Python runtime via the daemon protocol, proving that UI and business logic separation works in practice.

Development Environment Setup

The repository includes helper scripts for reproducible environments. Run scripts/setup-kernel-venv.sh to create an isolated Python virtual environment for the runtime:

./scripts/setup-kernel-venv.sh

This ensures consistent dependencies across developer machines and CI pipelines.

Testing Strategy

Each package maintains its own test suite:

  • Python tests: prime-agent-runtime/test/ using pytest
  • TypeScript tests: Vitest suites in relevant packages/

Python tests specifically validate skill registry behavior, message serialization, and harness correctness. Run them with:

cd prime-agent-runtime && pytest test/

Summary

  • Monorepo structure separates runtime, providers, agents, and UI into independent packages
  • Skill-based architecture in prime-agent-runtime/src/rlm/ enables modular capability extension via SkillBase subclasses
  • Automatic discovery through register_skill() eliminates manual configuration
  • Harness testing via SubAgentHarness provides fast, isolated skill validation
  • Provider abstraction in packages/ai/ ensures LLM backend flexibility
  • Sub-agent specialization demonstrated by packages/coding-agent/ shows how to build domain-specific agents

Frequently Asked Questions

How do I add a new skill to Prime Agent?

Create a Python file in prime-agent-runtime/src/rlm/ that subclasses SkillBase, implement the run() coroutine, and call register_skill(YourClass). The runtime discovers it automatically at startup. Test it with SubAgentHarness before integration.

What is the Remote Call Protocol (RCP)?

RCP is Prime Agent's message-passing system for sub-agent communication. It serializes skill invocations and responses as JSON exchanged with a daemon process. The protocol handles async execution, streaming, and error propagation transparently.

Can I use Prime Agent with proprietary LLM providers?

Yes. The packages/ai/ abstraction accepts any provider implementing the streaming interface. Add your provider in packages/ai/src/providers/ and reference it by name in agent configurations—no core runtime changes required.

How does the TUI communicate with Python skills?

The TUI connects to the same daemon as CLI mode. It sends user input as RCP messages, receives rendered output streams, and displays them through a virtual terminal. This shared runtime ensures behavioral parity between headless and interactive modes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →