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
runcoroutine as the universal entry point for all skills. - Leverage shared CLI helpers in
prime-agent-runtime/src/rlm/skill.pyto handle argument parsing withtyro. - Configure skills to read environment variables first, then fall back to persisted
auth.jsoncredentials. - Truncate output using a
max_outputparameter 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →