Creating Agent Artifacts
Custom Amorphic agents run on AgentCore Runtime. You upload a zip artifact that contains everything the runtime needs:
- Entry-point handler (for example
main.py) requirements.txt- Python dependencies installed from that requirements file
- Custom user resources (optional files or folders such as data files, configs, or prompts)
This how-to shows a recommended project layout and a reusable generate_agent_artifact.sh script that builds a package for the AgentCore runtime (ARM).
Amorphic Shared Libraries are not attached to AgentCore custom agents. Bundle Python dependencies and any custom resources inside the zip.
Product reference: AI Agents (Create Agent, Agent Package, chat, logs).
Prerequisites
- Python 3.10–3.14 available locally (or Docker, preferred)
- Docker recommended (builds packages that match the AgentCore runtime). Without Docker, the script installs ARM Linux wheels via pip.
zipCLI- An Amorphic environment with AI Core (Agents) enabled and at least one model assigned to Agents
What goes in the artifact
| Item | Required | Notes |
|---|---|---|
Entry-point .py | Yes | Path you set on create (e.g. main.py). Must end with .py. |
requirements.txt | Yes | Listed deps; also copied into the zip |
| Python dependencies | Yes (if listed) | Installed under the zip root (e.g. bedrock-agentcore, strands-agents, boto3) |
| Extra modules | Optional | Additional .py files or packages passed with --entry |
| Custom resources | Optional | Any files/folders via --resources — copied in full (nested trees included) |
Recommended project layout
my-agent/
├── main.py # Agent entry file
├── requirements.txt # Runtime Python dependencies
├── generate_agent_artifact.sh # Artifact build script (from this how-to)
├── resources/ # Optional custom data (bundled in full into the zip)
│ └── data/ # Example nested folder
│ ├── config.json
│ └── ...
├── deployment_package/ # Build folder created by the script (do not edit by hand)
└── agent_artifact.zip # Upload this to Amorphic (name is configurable)
Load bundled files with paths relative to the entry file (same layout inside the zip):
from pathlib import Path
_RESOURCES_DIR = Path(__file__).resolve().parent / "resources" / "data"
_CONFIG_PATH = _RESOURCES_DIR / "config.json"
Minimal entry point
Your package must expose an AgentCore entry point that returns Content / ContentType. Example skeleton:
from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent, tool
app = BedrockAgentCoreApp(debug=True)
@tool
def example_tool(query: str) -> str:
"""
Example tool the agent can call.
Args:
query: User query text
Returns:
Tool result string
"""
return f"Processed: {query}"
agent = Agent(
tools=[example_tool],
system_prompt="You are a helpful assistant. Use tools when they improve accuracy.",
)
@app.entrypoint
def invoke(payload):
"""
Main entrypoint for AgentCore Runtime.
Key steps:
1. Read message and required model_id from the payload
2. Optionally override the agent model
3. Run the agent and return Content/ContentType
Args:
payload: Request dict with "message" and "model_id"
Returns:
dict: Response with Content and ContentType keys
"""
user_message = payload.get("message", "Hello!")
model_id = payload.get("model_id")
if not model_id:
raise Exception("model_id is required")
agent.model.update_config(model_id=model_id)
result = agent(user_message)
return {
"Content": result.message,
"ContentType": "text",
}
if __name__ == "__main__":
app.run()
requirements.txt
Start from a minimal set (add domain libraries as needed):
bedrock-agentcore>=1.0.0
strands-agents>=0.1.0
boto3>=1.28.0
generate_agent_artifact.sh
Save the following as generate_agent_artifact.sh next to main.py, then chmod +x generate_agent_artifact.sh.
#!/bin/bash
# Generate an Amorphic AgentCore agent artifact (zip) for upload.
#
# The zip contains everything needed at runtime:
# 1. Entry-point handler(s) (default: main.py)
# 2. requirements.txt
# 3. Python dependencies installed from requirements.txt (arm64 wheels)
# 4. Any custom user files/folders passed via --resources (copied in full)
#
# Usage:
# ./generate_agent_artifact.sh
# ./generate_agent_artifact.sh --name my_agent.zip --entry main.py --python 312
# ./generate_agent_artifact.sh --resources ./resources
#
set -euo pipefail
SCRIPT_NAME="$(basename "$0")"
OUTPUT_ZIP="agent_artifact.zip"
ENTRY_POINTS=()
REQUIREMENTS="requirements.txt"
PYTHON_VERSION="312"
BUILD_DIR="deployment_package"
RESOURCES=()
usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} [options]
Builds a zip with: entry handler(s) + requirements.txt + Python deps + custom resources.
Options:
-n, --name NAME Output zip filename (default: agent_artifact.zip)
-e, --entry PATH Entry-point or app file/dir to include (repeatable; default: main.py)
-r, --requirements PATH Path to requirements.txt (default: requirements.txt)
-p, --python VER Python ABI version: 310|311|312|313|314 (default: 312)
--resources PATH Custom user file or directory to include (repeatable).
Directories are copied recursively in full; relative paths
are preserved (./resources → resources/ inside the zip).
-d, --build-dir NAME Intermediate build directory (default: deployment_package)
-h, --help Show this help
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--name) OUTPUT_ZIP="$2"; shift 2 ;;
-e|--entry) ENTRY_POINTS+=("$2"); shift 2 ;;
-r|--requirements) REQUIREMENTS="$2"; shift 2 ;;
-p|--python) PYTHON_VERSION="$2"; shift 2 ;;
--resources) RESOURCES+=("$2"); shift 2 ;;
-d|--build-dir) BUILD_DIR="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
esac
done
if [[ ${#ENTRY_POINTS[@]} -eq 0 ]]; then
ENTRY_POINTS=("main.py")
fi
case "${PYTHON_VERSION}" in
310|311|312|313|314) ;;
*) echo "Unsupported --python value: ${PYTHON_VERSION}" >&2; exit 1 ;;
esac
PYTHON_TAG="cp${PYTHON_VERSION}"
PYTHON_IMAGE_TAG="3.${PYTHON_VERSION:1}"
AMORPHIC_PYTHON_ENUM="PYTHON_3_${PYTHON_VERSION:1}"
[[ -f "${REQUIREMENTS}" ]] || { echo "Requirements file not found: ${REQUIREMENTS}" >&2; exit 1; }
for entry in "${ENTRY_POINTS[@]}"; do
[[ -e "${entry}" ]] || { echo "Entry / app path not found: ${entry}" >&2; exit 1; }
done
for resource in "${RESOURCES[@]+"${RESOURCES[@]}"}"; do
[[ -e "${resource}" ]] || { echo "Resource path not found: ${resource}" >&2; exit 1; }
done
echo "Cleaning previous build artifacts..."
rm -rf "${BUILD_DIR}" "${OUTPUT_ZIP}"
mkdir "${BUILD_DIR}"
if docker info >/dev/null 2>&1; then
echo "Installing Python dependencies via Docker (linux/arm64, Python ${PYTHON_IMAGE_TAG})..."
docker run --rm \
--platform linux/arm64 \
-v "$(pwd)":/workspace \
-w /workspace \
"python:${PYTHON_IMAGE_TAG}-slim" \
bash -c "
set -euo pipefail
pip install --upgrade pip && \
pip install \
--platform manylinux2014_aarch64 \
--only-binary=:all: \
--target=/workspace/${BUILD_DIR} \
-r ${REQUIREMENTS}
"
else
echo "Docker unavailable — installing manylinux2014_aarch64 wheels via local pip"
VENV_DIR=".deploy_venv"
rm -rf "${VENV_DIR}"
python3 -m venv "${VENV_DIR}"
# shellcheck disable=SC1091
source "${VENV_DIR}/bin/activate"
pip install --upgrade pip
pip install \
--platform manylinux2014_aarch64 \
--python-version "${PYTHON_VERSION}" \
--implementation cp \
--abi "${PYTHON_TAG}" \
--only-binary=:all: \
--target="./${BUILD_DIR}" \
-r "${REQUIREMENTS}"
deactivate
rm -rf "${VENV_DIR}"
fi
copy_into_package() {
local src="$1"
local rel="${src#./}"
rel="${rel%/}"
src="${src%/}"
if [[ -d "${src}" ]]; then
echo " + dir ${rel}/"
mkdir -p "${BUILD_DIR}/${rel}"
cp -R "${src}/." "${BUILD_DIR}/${rel}/"
elif [[ -f "${src}" ]]; then
echo " + file ${rel}"
local parent
parent="$(dirname "${rel}")"
if [[ "${parent}" != "." ]]; then
mkdir -p "${BUILD_DIR}/${parent}"
fi
cp "${src}" "${BUILD_DIR}/${rel}"
else
echo "Not a file or directory: ${src}" >&2
exit 1
fi
}
echo "Copying entry-point handler(s)..."
for entry in "${ENTRY_POINTS[@]}"; do
copy_into_package "${entry}"
done
echo "Copying requirements file..."
cp "${REQUIREMENTS}" "${BUILD_DIR}/requirements.txt"
if [[ ${#RESOURCES[@]} -gt 0 ]]; then
echo "Copying custom user resources..."
for resource in "${RESOURCES[@]}"; do
copy_into_package "${resource}"
done
fi
echo "Removing bytecode..."
find "${BUILD_DIR}" -depth -type d -name '__pycache__' -exec rm -rf {} \; 2>/dev/null || true
find "${BUILD_DIR}" -type f \( -name '*.pyc' -o -name '*.pyo' -o -name '*.cpython-*.pyc' \) -delete 2>/dev/null || true
echo "Creating zip (handler + requirements + Python deps + custom resources): ${OUTPUT_ZIP}"
(
cd "${BUILD_DIR}"
zip -r "../${OUTPUT_ZIP}" .
)
echo "Agent artifact created: ${OUTPUT_ZIP}"
echo "Package size: $(du -h "${OUTPUT_ZIP}" | cut -f1)"
echo "Upload to Amorphic — Entry Point: ${ENTRY_POINTS[0]} — Python: ${AMORPHIC_PYTHON_ENUM}"
Build the artifact
From your agent project directory:
chmod +x generate_agent_artifact.sh
# Defaults: main.py + requirements.txt + Python deps → agent_artifact.zip (Python 3.12)
./generate_agent_artifact.sh
# Include a full custom resources folder as well
./generate_agent_artifact.sh \
--name my_agent.zip \
--entry main.py \
--python 312 \
--resources ./resources
The script always packages:
| Layer | Source |
|---|---|
| Entry handler(s) | --entry (default main.py) |
requirements.txt | --requirements |
| Python dependencies | Installed from that requirements file into the zip |
| Custom user resources | --resources paths — entire files/folders, nested trees included |
Folder trees keep the same relative path inside the zip as on disk (for example ./resources becomes resources/ next to your entry file). Use the same paths in your code when reading bundled files.
| Local path | Path inside zip |
|---|---|
./resources | resources/... (complete tree) |
./resources/data | resources/data/... |
./resources/data/config.json | resources/data/config.json |
| Flag | Purpose |
|---|---|
--name | Output zip filename (local name only; the upload package name on the platform does not need to match) |
--entry | Handler file(s)/dirs to include; first path is the suggested Entry Point |
--python | 310–314 → Amorphic PYTHON_3_10 … PYTHON_3_14 |
--resources | Custom files or folders copied into the zip in full (relative paths preserved) |
--requirements | Alternate requirements file |
The local zip filename does not matter. After upload, Amorphic stores the package for that agent.
Upload in Amorphic
- Go to AI Services > AI Agents
- Start Create Agent
- Fill create attributes (name, description, model, Python version, entry point)
- Upload the generated zip
- Optionally attach knowledge bases, guardrails, and parameters
- Click Create
Suggested create values when using this script with defaults:
| Attribute | Typical value |
|---|---|
| Python Version | PYTHON_3_12 (match --python 312) |
| Entry Point | main.py (must exist inside the zip) |
| Agent Package | Your generated zip (e.g. agent_artifact.zip) |
Agent Name must be 3–48 characters, start with a letter, and use only letters, numbers, and underscores. Hyphens are not allowed.
Creation is asynchronous. Wait until status is Ready. If create fails, the agent stays disabled until you update or recreate it.
Update an existing agent package
- Rebuild with
generate_agent_artifact.sh - Open the custom agent → edit / upload package
- Upload the new zip and save
- Refresh any open chat session after the update completes
Checklist
- Entry file defines the AgentCore entry point and returns
Content/ContentType -
requirements.txtlists all third-party deps (no Shared Library dependency) - Artifact built for the AgentCore runtime / matching Python version (
--pythonaligns with Amorphic Python Version) - Custom resources (if any) are passed with
--resourcesand match the paths your code opens at runtime - Zip uploads successfully; agent reaches Ready and is enabled before chat
Troubleshooting
| Issue | What to check |
|---|---|
| Import errors at runtime | Dependency missing from zip; rebuild after updating requirements.txt |
| Wrong architecture / native module errors | Prefer the Docker build path so the package matches AgentCore (ARM) |
| Entry point not found | Entry Point on create must match a .py path inside the zip |
| Missing resource file | Confirm --resources included the folder; the path in your code must match the path inside the zip |
| Create failed | Fix package or configuration and update (or recreate); failed agents stay disabled |
Related
- AI Agents — create, chat, logs, system agents
- Guardrails — input/response validation
- Knowledge Bases — attach ACTIVE knowledge bases when needed