Skip to main content

OSSA Canonical Project Structure

The canonical agents/ folder structure that scales from a single agent to thousands of agents

OSSA Canonical Project Structure

OSSA defines a canonical folder structure that scales from a single agent to thousands of agents. This layout is the foundation for agent portability, discovery, and governance.

Canonical Folder Structure

agents/
├── registry.yaml               # Optional: local index (generated)
├── _shared/                    # Optional: shared prompt/tool/policy assets
│   ├── policies/               # Org/team policy bundles (OPA, etc.)
│   ├── prompts/                # Reusable prompt fragments
│   └── tools/                  # Shared tool schemas (OpenAPI/JSON Schema)
├── <agent-name>/
│   ├── agent.ossa.yaml         # REQUIRED: single source of truth
│   ├── system-prompt.md        # Optional: long prompt (referenced by manifest)
│   ├── tools/                  # Optional: agent-specific tool schemas
│   ├── knowledge/              # Optional: RAG sources (docs) or pointers
│   └── tests/                  # Optional: evals + conformance tests

agents-workspace/               # Derived outputs (gitignored)
├── exports/                    # Generated target formats (LangGraph/CrewAI/etc.)
├── state/                      # Runtime checkpoints
└── logs/                       # Traces

[object Object] - Agent Definitions

The agents/ directory is your version-controlled source of truth for all agent definitions. Every agent lives in its own subdirectory with a single required file: agent.ossa.yaml.

Purpose

  • Single source of truth: Each agent is fully defined by its agent.ossa.yaml manifest
  • Scales to thousands: The flat structure with optional registry supports large agent fleets
  • Shareable assets: The _shared/ directory prevents duplication across agents
  • Platform-agnostic: Works with any OSSA-compliant runtime

Agent Directory

Each agent gets its own subdirectory within agents/:

agents/
└── customer-support/
    ├── agent.ossa.yaml         # REQUIRED: single source of truth
    ├── system-prompt.md        # Optional: long system prompt (referenced by manifest)
    ├── tools/
    │   ├── search.yaml         # Agent-specific tool schema (OpenAPI/JSON Schema)
    │   └── create-ticket.yaml  # Agent-specific tool schema
    ├── knowledge/
    │   ├── faq.md              # RAG source documents
    │   └── product-catalog.json # Structured knowledge
    └── tests/
        ├── eval-suite.yaml     # Evaluation test cases
        └── conformance.yaml    # OSSA conformance checks

[object Object] - Shared Assets

The optional _shared/ directory holds assets reused across multiple agents:

agents/_shared/
├── policies/
│   ├── data-access.rego       # OPA policy: data boundary enforcement
│   ├── pii-handling.rego      # OPA policy: PII rules
│   └── cost-limits.yaml       # Budget and rate limit policies
├── prompts/
│   ├── safety-preamble.md     # Common safety instructions
│   ├── output-format.md       # Shared output formatting rules
│   └── brand-voice.md         # Consistent brand tone
└── tools/
    ├── slack-api.yaml         # OpenAPI schema for Slack integration
    ├── database-query.yaml    # Shared database tool schema
    └── email-sender.yaml      # Shared email tool schema

Agents reference shared assets via relative paths in their manifest:

spec: prompts: system: - $ref: "../_shared/prompts/safety-preamble.md" - ./system-prompt.md policies: - $ref: "../_shared/policies/pii-handling.rego"

[object Object] - Local Index

The optional registry.yaml file provides a generated local index of all agents. This enables fast discovery without scanning the filesystem:

# agents/registry.yaml (auto-generated by `ossa registry build`) agents: - name: customer-support version: "1.2.0" role: worker domain: customer-service capabilities: [qa, ticket-creation, escalation] - name: data-analyst version: "0.8.0" role: specialist domain: data-analysis capabilities: [sql-queries, visualization]

[object Object] - The Manifest

The manifest is the single source of truth for each agent. It answers four essential questions:

  1. Who published this? (identity + provenance)
  2. What can it do? (capabilities/skills + interfaces)
  3. What does it need? (runtime + resources + dependencies)
  4. What is it allowed to access? (policy + secrets + data boundaries)
apiVersion: ossa/v0.4.9 kind: Agent metadata: name: customer-support version: "1.2.0" description: Handles customer support inquiries with escalation labels: team: support-engineering tier: production annotations: ossa.io/owner: "support-team@example.com" ossa.io/sla: "99.9%" spec: # What can it do? role: worker domain: customer-service capabilities: [qa, ticket-creation, escalation, sentiment-analysis] # What does it need? llm: provider: ${LLM_PROVIDER:-anthropic} model: ${LLM_MODEL:-claude-sonnet-4-5-20250929} profile: balanced temperature: 0.3 maxTokens: 8192 fallback_models: - provider: openai model: gpt-4o condition: on_error runtime: execution: sandbox: enabled: true type: container resource_limits: memory_mb: 512 cpu_millicores: 1000 # What is it allowed to access? policies: - $ref: "../_shared/policies/pii-handling.rego" - $ref: "../_shared/policies/cost-limits.yaml" secrets: - name: SUPPORT_API_KEY source: vault path: "secret/support/api-key" data_boundaries: allowed_namespaces: [customer-data, knowledge-base] denied_namespaces: [financial-records, hr-data] # Context window strategy context: strategy: sliding_window max_tokens: 32768 reserved_tokens: system_prompt: 4096 tools: 2048 output: 4096 long_term_memory: provider: qdrant embedding_model: text-embedding-3-small prompts: system: - $ref: "../_shared/prompts/safety-preamble.md" - ./system-prompt.md tools: - name: search_knowledge_base source: ./tools/search.yaml - name: create_support_ticket source: ./tools/create-ticket.yaml - name: send_slack_notification source: ../_shared/tools/slack-api.yaml

[object Object] - Derived Outputs

The agents-workspace/ directory holds derived outputs and runtime state. This folder should NOT be version controlled (add to .gitignore).

Purpose

  • Export targets: Generated agent definitions for specific frameworks (LangGraph, CrewAI, AutoGen, etc.)
  • Runtime state: Checkpoints, session data, and memory snapshots
  • Traces and logs: Execution traces for debugging and observability

Structure

agents-workspace/
├── exports/
│   ├── langgraph/
│   │   └── customer-support.py     # Generated LangGraph agent
│   ├── crewai/
│   │   └── customer-support.yaml   # Generated CrewAI config
│   └── mcp/
│       └── customer-support.json   # Generated MCP server definition
├── state/
│   ├── customer-support/
│   │   ├── checkpoint-2026-02-12.json  # Runtime checkpoint
│   │   └── session-abc123.json         # Active session state
│   └── data-analyst/
│       └── checkpoint-latest.json
└── logs/
    ├── customer-support/
    │   ├── 2026-02-12.jsonl        # Structured trace logs
    │   └── metrics.json            # Performance metrics
    └── data-analyst/
        └── 2026-02-12.jsonl

Why Separate from [object Object]?

  1. Clean version control: Agent definitions are committed; runtime artifacts are not
  2. Security: Sensitive runtime data (session state, user data) stays out of git
  3. Reproducibility: agents/ is portable and reproducible; agents-workspace/ is environment-specific
  4. Export flexibility: Generate target formats on demand without polluting source

[object Object] Recommendation

# OSSA derived outputs (never commit) agents-workspace/ # Keep agent definitions (always commit) # agents/ # <-- NOT ignored, this is your source of truth

What a Perfect Agent Must Declare

At minimum, the agent.ossa.yaml manifest must answer four questions:

1. Who published this? (Identity + Provenance)

metadata: name: customer-support version: "1.2.0" description: Handles customer support inquiries labels: team: support-engineering tier: production annotations: ossa.io/owner: "support-team@example.com" ossa.io/repo: "https://gitlab.com/org/agents"

2. What can it do? (Capabilities/Skills + Interfaces)

spec: role: worker domain: customer-service capabilities: [qa, ticket-creation, escalation, sentiment-analysis] tools: - name: search_knowledge_base source: ./tools/search.yaml - name: create_support_ticket source: ./tools/create-ticket.yaml

3. What does it need? (Runtime + Resources + Dependencies)

spec: llm: provider: ${LLM_PROVIDER:-anthropic} model: ${LLM_MODEL:-claude-sonnet-4-5-20250929} profile: balanced runtime: execution: sandbox: enabled: true type: container resource_limits: memory_mb: 512 cpu_millicores: 1000 dependencies: - name: knowledge-base-service version: ">=2.0.0"

4. What is it allowed to access? (Policy + Secrets + Data Boundaries)

spec: policies: - $ref: "../_shared/policies/pii-handling.rego" - $ref: "../_shared/policies/cost-limits.yaml" secrets: - name: SUPPORT_API_KEY source: vault path: "secret/support/api-key" data_boundaries: allowed_namespaces: [customer-data, knowledge-base] denied_namespaces: [financial-records, hr-data]

Context Windows: Sliding Window + Long-Term Memory

Modern agents operate under hard model context limits. A well-defined agent declares a context strategy that is deterministic enough to audit but flexible enough to evolve.

Declaring Context Strategy

spec: context: strategy: sliding_window # How to manage the context window max_tokens: 32768 # Hard limit for the context window reserved_tokens: system_prompt: 4096 # Reserved for system instructions tools: 2048 # Reserved for tool definitions output: 4096 # Reserved for generation overflow: summarize # What to do when window is full long_term_memory: provider: qdrant # Vector store for persistent recall embedding_model: text-embedding-3-small dimensions: 1536 retrieval: top_k: 10 # Number of memories to retrieve score_threshold: 0.7 # Minimum relevance score

Context Strategies

StrategyBehaviorBest For
sliding_windowRemove oldest messages first (FIFO)General-purpose agents
summarizeCompress old messages into summariesLong conversations
importance_basedKeep high-importance messages longerComplex multi-step tasks
hybridSliding window + summarize on overflowProduction workloads

Token Budget Breakdown

The context window is divided into reserved zones:

┌──────────────────────────────────────────┐
│  System Prompt          (reserved: 4096) │
├──────────────────────────────────────────┤
│  Tool Definitions       (reserved: 2048) │
├──────────────────────────────────────────┤
│  Retrieved Memories     (dynamic)        │
├──────────────────────────────────────────┤
│  Conversation History   (sliding window) │
├──────────────────────────────────────────┤
│  Output Generation      (reserved: 4096) │
└──────────────────────────────────────────┘

The remaining tokens after reservations are split between retrieved long-term memories and the sliding conversation window. This ensures the agent never exceeds its context limit while maintaining access to both recent context and relevant historical knowledge.

Agent Taxonomy

OSSA uses a taxonomy system to classify and organize agents within the manifest:

spec: role: worker | supervisor | coordinator | specialist domain: customer-service | data-analysis | content-creation | ... capabilities: [capability-1, capability-2, ...] labels: tags: [optional, custom, tags]

Roles

  • worker: Executes specific tasks (e.g., customer support agent, data fetcher)
  • supervisor: Manages and coordinates workers (e.g., team lead, orchestrator)
  • coordinator: Routes tasks between agents (e.g., dispatcher, load balancer)
  • specialist: Domain expert for complex tasks (e.g., legal advisor, data scientist)

Using Taxonomy for Routing

Taxonomy enables intelligent routing in multi-agent systems:

// Find all customer service workers const supportAgents = await ossa.findAgents({ role: 'worker', domain: 'customer-service' }); // Find agents with specific capability const escalationHandlers = await ossa.findAgents({ capabilities: ['escalation-handling'] }); // Route task to appropriate agent const agent = await ossa.routeTask(task, { requiredCapabilities: ['sql-queries', 'data-visualization'] });

Project Initialization

When you run ossa init, OSSA creates the canonical structure:

$ ossa init my-project ✅ Created agents/ directory (version controlled) ✅ Created agents-workspace/ directory (gitignored) ✅ Created agents/_shared/ with starter policies and prompts ✅ Created .gitignore with agents-workspace/

To add a new agent:

$ ossa agent create customer-support --role worker --domain customer-service ✅ Created agents/customer-support/agent.ossa.yaml ✅ Created agents/customer-support/system-prompt.md ✅ Created agents/customer-support/tools/ ✅ Created agents/customer-support/tests/

Best Practices

✅ DO

  • Commit agents/ to version control (git)
  • Ignore agents-workspace/ in .gitignore
  • Use _shared/ for policies, prompts, and tools reused across agents
  • Answer all four questions in every agent.ossa.yaml (identity, capabilities, runtime, access)
  • Declare a context strategy so token usage is auditable and predictable
  • Include tests/ with evaluation suites and conformance checks
  • Version your manifests (semver recommended)

❌ DON'T

  • Don't commit agents-workspace/ (contains runtime state, exports, logs)
  • Don't hardcode secrets in agent manifests (use ${ENV_VAR} substitution or vault references)
  • Don't mix runtime state with agent definitions
  • Don't duplicate shared assets across agents (use _shared/ with $ref)
  • Don't skip context strategy (undefined context behavior leads to unpredictable token usage)

Migration from Other Frameworks

When migrating from other frameworks (LangChain, CrewAI, etc.), use ossa migrate:

# Migrate LangChain agents to OSSA canonical structure $ ossa migrate --from langchain --input ./langchain-agents --output ./agents ✅ Created agents/ with migrated agent definitions (agent.ossa.yaml) ✅ Created agents-workspace/ for runtime exports ✅ Generated agents/registry.yaml index

See Migration Guides for framework-specific instructions.

Summary

FolderPurposeVersion ControlContains
agents/Agent definitions✅ YESagent.ossa.yaml, prompts, tool schemas, knowledge, tests
agents/_shared/Shared assets✅ YESPolicies, reusable prompts, shared tool schemas
agents-workspace/Derived outputs❌ NOExports, runtime state, traces/logs

Key Takeaway: agents/ is the single source of truth (commit to git). agents-workspace/ holds generated outputs and runtime state (never commit).

Next Steps