Best Practices for Organizing Agent Code in Python: A Prime Agent Guide

Organize Python agent skills as self-contained async modules with a standard run entry point, shared CLI helpers, and environment-aware configuration to ensure seamless integration with the agent runtime.

PrimeIntellect-ai/prime-agent demonstrates a scalable approach to organizing agent code in Python by treating each capability as an isolated, importable package. This architecture separates concerns between the runtime infrastructure and individual skills, making it straightforward to add new tools without modifying the core agent logic. By following these conventions, developers can build modular, testable components that integrate cleanly with the RLM REPL.

One-Module Skill Layout

Each skill in Prime Agent lives as an independent Python package following a strict directory convention.

Directory Structure Convention

Place skill code under packages/coding-agent/skills/<skill-name>/src/<skill-name>/. This nested structure ensures the package name matches the import path expected by the runtime. For example, the websearch skill resides at packages/coding-agent/skills/websearch/src/websearch/, allowing the runtime to import it predictably.

The Run Entry Point

Every skill must expose an async coroutine named run that serves as the entry point for the RLM REPL. This function receives parameters parsed from the CLI and returns a value to the agent. The minimal implementation in packages/coding-agent/test/fixtures/skills/python-skill/src/python_skill/__init__.py demonstrates this pattern with a four-line function:

async def run(message: str = "hello") -> str:
    """Return the provided message."""
    return message

Async-First Architecture

Skills should perform I/O without blocking the agent’s event loop.

Non-Blocking I/O Patterns

Write all skills as async def run(...) coroutines. The websearch skill in packages/coding-agent/skills/websearch/src/websearch/websearch.py uses httpx.AsyncClient for HTTP requests, allowing concurrent network operations:

import httpx

async def run(query: str, *, max_output: int = 8192) -> str:
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            "https://google.serper.dev/search",
            json={"q": query},
            headers={"X-API-KEY": api_key},
        )
        resp.raise_for_status()
        data = resp.json()
    return data

Cancellation and Timeouts

Respect the agent’s lifecycle by handling cancellation gracefully. The websearch implementation configures httpx.AsyncClient with appropriate timeouts, ensuring that slow network calls do not hang the REPL indefinitely.

Shared Infrastructure and CLI Helpers

Centralize command-line plumbing to reduce boilerplate in individual skills.

Centralized CLI Parsing

The runtime provides shared CLI helpers in prime-agent-runtime/src/rlm/skill.py. The cli() function discovers the skill module by matching the console-script name to the import name, then invokes run_cli which parses arguments using tyro and prints any non-None result.

Entry Point Registration

Register skills as console scripts in pyproject.toml or setup.cfg, pointing to the shared CLI handler:

[options.entry_points]
console_scripts =
    echo = echo:cli

Inside the skill package, create a minimal cli.py that delegates to the runtime:

from prime_agent_runtime.rlm.skill import cli

if __name__ == "__main__":
    cli()

Environment-Aware Configuration

Handle credentials and settings without hardcoding sensitive values.

Credential Resolution

Implement helper functions like _resolve_api_key to check environment variables before falling back to persisted storage. The websearch skill first checks SERPER_API_KEY, then consults the agent’s auth.json file, enabling seamless operation after a live /login command.

def _resolve_api_key():
    import os
    return os.getenv("SERPER_API_KEY") or _load_from_auth_json()

Graceful Degradation

When configuration is missing, return explicit, user-focused messages instead of raising cryptic exceptions. This allows the agent to report actionable feedback:

api_key = _resolve_api_key()
if not api_key:
    return "Web search not configured – see the skill doc for setup."

Robust Output Handling

Manage response sizes to prevent context window overflow.

Truncation Strategies

Accept a ** max_output parameter** in the run signature to limit response length. The websearch skill truncates results safely, preserving the most relevant content while respecting the boundary:

return formatted_results[:max_output]

Error Message Best Practices

Structure error messages to indicate exactly what resource is missing and how to configure it. This convention appears throughout the codebase, ensuring users receive clear guidance rather than stack traces.

Testing Isolation

Maintain test fixtures under packages/coding-agent/test/fixtures/skills/, mirroring the production layout. This structure allows unit tests to import skills directly without initializing the entire agent runtime, providing fast, isolated feedback during development.

Summary

  • Organize agent code in Python as self-contained packages under packages/coding-agent/skills/<name>/src/<name>/ to ensure predictable imports.
  • Implement an async run coroutine as the universal entry point for all skills.
  • Leverage shared CLI helpers in prime-agent-runtime/src/rlm/skill.py to handle argument parsing with tyro.
  • Configure skills to read environment variables first, then fall back to persisted auth.json credentials.
  • Truncate output using a max_output parameter to respect context limits.
  • Place test fixtures in packages/coding-agent/test/fixtures/skills/ to enable isolated testing.

Frequently Asked Questions

What is the required function signature for a Prime Agent skill?

Every skill must define an async coroutine named run that accepts typed parameters and returns a value. The signature async def run(message: str = "hello") -> str in packages/coding-agent/test/fixtures/skills/python-skill/src/python_skill/__init__.py demonstrates the minimal viable implementation expected by the runtime.

How does Prime Agent handle asynchronous operations in skills?

Skills must be declared as async def functions. The runtime executes these coroutines within the agent’s event loop, allowing non-blocking I/O. As shown in packages/coding-agent/skills/websearch/src/websearch/websearch.py, use httpx.AsyncClient for network requests and implement timeouts to respect cancellation signals.

Where should API keys and secrets be stored for agent skills?

Store sensitive configuration in environment variables (e.g., SERPER_API_KEY) or the agent’s persisted auth.json file. Helper functions like _resolve_api_key check these sources in order, keeping credentials out of source code while maintaining usability after authentication commands.

How can I test individual skills without loading the entire agent?

Place test fixtures under packages/coding-agent/test/fixtures/skills/ using the same directory structure as production skills. This allows you to import and unit test the run function directly without initializing the full Prime Agent runtime, enabling fast, isolated development cycles.

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 →