Python Package Layout
Python Package Layout
There are two common layouts for structuring a Python project.
Flat Layout (typical starting point)
aiservice/
├── api/
├── services/
├── test_interface/
├── models/
├── prompts/
├── jre/
├── data/
└── pyproject.toml
Everything sits at the top level. The problem: setuptools sees all folders and can't tell which are Python code vs data. It finds api/, services/, jre/, models/, prompts/, data/, test_interface/ and panics because it doesn't know what to include.
Src Layout (the proper way)
aiservice/
├── src/
│ └── epic_aiservice/
│ ├── api/
│ ├── services/
│ └── test_interface/
├── models/
├── prompts/
├── jre/
├── data/
└── pyproject.toml
Python code lives inside src/, data lives outside. No ambiguity.
Src Layout with AI Agents
If your project includes AI agents, MCP, guardrails, or multi-agent orchestration, extend the src layout with dedicated folders for each concern:
aiservice/
├── src/
│ └── epic_aiservice/
│ ├── api/ # REST/WebSocket endpoints
│ ├── services/ # Business logic
│ ├── test_interface/ # Dev/test UI
│ │
│ ├── agents/ # ── Agent definitions ──
│ │ ├── __init__.py
│ │ ├── base.py # Base agent class/interface
│ │ ├── research_agent.py # One file per agent
│ │ ├── coding_agent.py
│ │ └── ...
│ │
│ ├── tools/ # ── Tool functions ──
│ │ ├── __init__.py
│ │ ├── base.py # Base tool class/interface
│ │ ├── web_search.py # One file per tool
│ │ ├── file_reader.py
│ │ └── ...
│ │
│ ├── guardrails/ # ── Input/output validation ──
│ │ ├── __init__.py
│ │ ├── input_validators.py # Prompt injection, PII detection, etc.
│ │ ├── output_validators.py # Hallucination, toxicity checks, etc.
│ │ └── content_filters.py # Blocked topics, profanity, etc.
│ │
│ ├── mcp/ # ── Model Context Protocol ──
│ │ ├── clients/
│ │ │ ├── __init__.py
│ │ │ └── client.py # MCP client connections
│ │ └── servers/
│ │ ├── __init__.py
│ │ ├── tools_server.py # Expose tools via MCP
│ │ └── resources_server.py # Expose data via MCP
│ │
│ ├── orchestrator/ # ── Multi-agent coordination ──
│ │ ├── __init__.py
│ │ ├── router.py # Routes tasks → agents
│ │ └── workflow.py # Multi-agent workflows
│ │
│ └── config/ # ── Configuration ──
│ ├── settings.py # Env vars, model configs
│ ├── agent_registry.py # Maps agent names → classes
│ └── tool_registry.py # Maps tool names → classes
│
├── prompts/ # ── Prompt templates (not Python) ──
│ ├── system/
│ │ ├── research_agent.md # System prompt per agent (.md for long prose)
│ │ ├── coding_agent.md
│ │ └── ...
│ ├── templates/
│ │ ├── summarize.md # Reusable prompt templates
│ │ └── ...
│ └── configs/
│ ├── research_agent.yaml # Prompt configs (.yaml for structured data)
│ ├── few_shot_examples.yaml # Few-shot examples with variables
│ └── ...
│
├── models/ # ML model files
├── data/ # Static data, embeddings, etc.
├── jre/ # Java runtime (if needed)
└── pyproject.toml
When to Graduate: Flat vs Folder-per-Agent
The layout above uses a flat agents folder (one .py file per agent). This is fine for prototypes and small teams. When you need independent deployment, scaling, or dependencies per agent, switch to folder-per-agent:
Flat (prototype / small team):
agents/
├── __init__.py
├── base.py
├── research_agent.py ← just a file
├── coding_agent.py
└── ...
Folder-per-agent (production / scaling):
agents/
├── shared/ # ── Shared across all agents ──
│ ├── base.py # Base agent class/interface
│ └── utils.py
│
├── research_agent/ # ── Each agent = its own deployable unit ──
│ ├── agent.py # The agent class
│ ├── lambda_handler.py # AWS Lambda entry point
│ ├── Dockerfile # Container for deployment
│ ├── pyproject.toml # Dependencies for THIS agent only
│ ├── uv.lock # Locked dependencies
│ ├── tests/
│ │ ├── test_agent.py
│ │ └── test_integration.py
│ └── prompts/ # Prompts specific to this agent
│ ├── system.md
│ └── config.yaml
│
├── coding_agent/
│ ├── agent.py
│ ├── lambda_handler.py
│ ├── Dockerfile
│ ├── pyproject.toml
│ ├── uv.lock
│ ├── tests/
│ │ └── test_agent.py
│ └── prompts/
│ ├── system.md
│ └── config.yaml
│
└── ...
| Flat | Folder-per-agent | |
|---|---|---|
| Deploy | All agents deploy together | Each agent deploys independently |
| Dependencies | Shared pyproject.toml |
Each agent has its own |
| Scaling | Scale everything or nothing | Scale individual agents |
| Team | One person / small team | Different people own different agents |
| Testing | Run all tests at once | Test each agent in isolation |
| Use when | Prototype, MVP, < 3 agents | Production, > 3 agents, different infra needs |
Note: In the folder-per-agent layout, agent-specific prompts live inside the agent folder. Shared prompts (used by multiple agents) still live in the top-level
prompts/folder.
This pattern works for any platform — web apps, desktop apps, mobile. The agents always run server-side (Lambda, Cloud Run, ECS, etc.) and the frontend calls them via API. The frontend technology is irrelevant to how you structure your agents.
Why This Structure
| Folder | What goes here | Why separate |
|---|---|---|
agents/ |
One class per agent with its system prompt, tools list, and loop | Each agent is independently testable |
tools/ |
Pure functions that agents call (API wrappers, DB queries, etc.) | Tools are shared across agents |
guardrails/ |
Input/output validators attached to agents | Security/safety is a cross-cutting concern |
mcp/clients/ |
Code that connects to external MCP servers | Clients consume tools from other services |
mcp/servers/ |
Code that exposes your tools/resources via MCP | Servers share your tools with other apps |
orchestrator/ |
Router that picks the right agent, workflow pipelines | Multi-agent coordination lives here |
config/ |
Registries that map names → classes, env vars, model selection | Avoids hardcoded imports everywhere |
prompts/ |
Markdown files for system prompts and templates | Outside src/ — not Python code, just text data |
Key rule:
prompts/stays outsidesrc/(likemodels/anddata/) because prompts are text data, not Python packages. Everything insidesrc/is importable Python code.
.mdvs.yamlfor prompts:
.md— best for long system prompts (readable prose, supports headings/formatting).yaml— best for structured prompts with variables, few-shot examples, model params (role, content, temperature, etc.)