By Sagar Shankaran, Founder of CallSphere
Learn how to package AI agents into portable, versioned bundles with dependency management, configuration schemas, and reproducible deployments. Build a packaging format that works across teams and environments.
Key takeaways
An AI agent that works on one developer's machine but fails to deploy elsewhere is not a product — it is a prototype. Agent portability requires more than copying Python files. You need to capture the agent's model configuration, tool dependencies, prompt templates, environment requirements, and runtime constraints in a single distributable unit.
Traditional software solved this with package managers and container images. Agents need an equivalent packaging format that captures the unique requirements of LLM-powered systems: model provider configuration, tool schemas, guardrail definitions, and credential requirements.
A well-designed agent package is a directory with a manifest file that declares everything needed to instantiate the agent:
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
import json
import hashlib
from pathlib import Path
from dataclasses import dataclass, field, asdict
from typing import Optional
@dataclass
class ToolDependency:
name: str
version: str
source: str # "builtin", "mcp", "registry"
config_schema: dict = field(default_factory=dict)
@dataclass
class ModelRequirement:
provider: str # "openai", "anthropic", "local"
min_model: str # minimum capable model
recommended_model: str
max_tokens: int = 4096
temperature: float = 0.7
@dataclass
class AgentManifest:
name: str
version: str
description: str
author: str
license: str = "MIT"
model: ModelRequirement = field(
default_factory=lambda: ModelRequirement(
provider="openai",
min_model="gpt-4o-mini",
recommended_model="gpt-4o",
)
)
tools: list[ToolDependency] = field(default_factory=list)
python_dependencies: list[str] = field(default_factory=list)
required_env_vars: list[str] = field(default_factory=list)
config_schema: dict = field(default_factory=dict)
entry_point: str = "agent.py"
min_python_version: str = "3.11"
def to_json(self) -> str:
return json.dumps(asdict(self), indent=2)
@classmethod
def from_json(cls, data: str) -> "AgentManifest":
parsed = json.loads(data)
parsed["model"] = ModelRequirement(**parsed["model"])
parsed["tools"] = [
ToolDependency(**t) for t in parsed["tools"]
]
return cls(**parsed)
The manifest declares the agent's identity, its model requirements, tool dependencies, Python package dependencies, required environment variables, and an entry point. This is everything a deployment system needs to instantiate the agent in any environment.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
Packaging combines the manifest, agent code, prompt templates, and any static assets into a single archive with integrity verification:
import tarfile
import io
from datetime import datetime
class AgentPackager:
def __init__(self, source_dir: str):
self.source_dir = Path(source_dir)
self.manifest_path = self.source_dir / "agent.manifest.json"
def validate(self) -> list[str]:
errors = []
if not self.manifest_path.exists():
errors.append("Missing agent.manifest.json")
return errors
manifest = AgentManifest.from_json(
self.manifest_path.read_text()
)
entry = self.source_dir / manifest.entry_point
if not entry.exists():
errors.append(
f"Entry point {manifest.entry_point} not found"
)
# Check for required prompt templates
prompts_dir = self.source_dir / "prompts"
if prompts_dir.exists():
for f in prompts_dir.iterdir():
if f.suffix not in (".txt", ".md", ".jinja2"):
errors.append(
f"Unexpected prompt file format: {f.name}"
)
return errors
def build(self, output_path: str) -> str:
errors = self.validate()
if errors:
raise ValueError(
f"Validation failed: {'; '.join(errors)}"
)
manifest = AgentManifest.from_json(
self.manifest_path.read_text()
)
archive_name = (
f"{manifest.name}-{manifest.version}.agentpkg.tar.gz"
)
full_output = Path(output_path) / archive_name
with tarfile.open(full_output, "w:gz") as tar:
for file_path in self.source_dir.rglob("*"):
if file_path.is_file() and not self._is_excluded(
file_path
):
arcname = file_path.relative_to(self.source_dir)
tar.add(file_path, arcname=str(arcname))
# Generate checksum
file_hash = self._compute_hash(full_output)
checksum_path = full_output.with_suffix(
full_output.suffix + ".sha256"
)
checksum_path.write_text(file_hash)
return str(full_output)
def _is_excluded(self, path: Path) -> bool:
excludes = {
"__pycache__", ".git", ".env", "node_modules",
".venv", ".pytest_cache",
}
return any(part in excludes for part in path.parts)
def _compute_hash(self, file_path: Path) -> str:
sha = hashlib.sha256()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha.update(chunk)
return sha.hexdigest()
The packager validates the source directory, creates a compressed archive excluding development artifacts, and generates a SHA-256 checksum for integrity verification during distribution.
When installing a packaged agent, the runtime must resolve and install its dependencies — both Python packages and tool connections:
import subprocess
import sys
class AgentInstaller:
def __init__(self, registry_client, tool_manager):
self.registry = registry_client
self.tool_manager = tool_manager
async def install(self, package_path: str, target_dir: str):
extracted = self._extract_package(package_path, target_dir)
manifest = AgentManifest.from_json(
(Path(extracted) / "agent.manifest.json").read_text()
)
# Install Python dependencies
if manifest.python_dependencies:
subprocess.check_call([
sys.executable, "-m", "pip", "install",
"--target", str(Path(extracted) / "vendor"),
*manifest.python_dependencies,
])
# Resolve tool dependencies
for tool in manifest.tools:
if tool.source == "registry":
await self.registry.ensure_installed(
tool.name, tool.version
)
elif tool.source == "mcp":
await self.tool_manager.register_mcp_server(
tool.name, tool.config_schema
)
# Validate environment variables
missing_vars = [
var for var in manifest.required_env_vars
if var not in __import__("os").environ
]
if missing_vars:
raise EnvironmentError(
f"Set these env vars before running: "
f"{', '.join(missing_vars)}"
)
return extracted
def _extract_package(
self, package_path: str, target_dir: str
) -> str:
with tarfile.open(package_path, "r:gz") as tar:
tar.extractall(path=target_dir)
return target_dir
Semantic versioning helps consumers understand upgrade risk. The packaging system should enforce version compatibility rules:
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
from packaging.version import Version
def is_compatible(installed: str, required: str) -> bool:
inst = Version(installed)
req = Version(required)
# Major version must match; installed minor >= required
return inst.major == req.major and inst.minor >= req.minor
Include everything that defines the agent's behavior: code, prompts, tool schemas, and the manifest. Configure environment-specific values at deploy time: API keys, model endpoints, database URLs, and feature flags. The boundary is determinism — if changing a value changes the agent's behavior semantics, it belongs in the package.
Store prompt templates inside the package and version them with the agent. Never load prompts from external sources at runtime unless you also pin and checksum them. Prompt drift is a major source of agent regression bugs.
Yes. An agent designed for GPT-4o-class reasoning will produce garbage output on a smaller model. The manifest should declare both a minimum model and a recommended model, and the installer should warn or block deployment if the target environment cannot meet the minimum requirement.
#AgentPackaging #AgentDistribution #Versioning #DependencyManagement #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
The 2026 desktop AI agent landscape — ServiceNow Project Arc, Anthropic Claude offerings, OpenAI agents, and Google Mariner. A buyer's map.
An agentic-AI perspective on Anthropic Skills system, covering orchestration patterns, tool use, and how agent tooling fits production agent stacks.
Enterprise CIO Guide perspective on Comet's general-availability launch put an agentic browser in front of millions of consumers, and it works better than the demos suggested.
Enterprise CIO Guide perspective on Harvey AI's enterprise rollout numbers show legal agents have moved past the pilot stage at AmLaw 100 firms.
Enterprise CIO Guide perspective on Hippocratic AI's deployment numbers show healthcare voice agents are moving from pilot to production across major US health systems.
An agentic-AI perspective on Claude Agent SDK loops, covering orchestration patterns, tool use, and how agent orchestration fits production agent stacks.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco