Skip to main content
 print this page

Creating Agent Artifacts

Custom Amorphic agents run on AgentCore Runtime. You upload a zip artifact that contains everything the runtime needs:

  1. Entry-point handler (for example main.py)
  2. requirements.txt
  3. Python dependencies installed from that requirements file
  4. 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).

warning

Amorphic Shared Libraries are not attached to AgentCore custom agents. Bundle Python dependencies and any custom resources inside the zip.

Related

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.
  • zip CLI
  • An Amorphic environment with AI Core (Agents) enabled and at least one model assigned to Agents

What goes in the artifact

ItemRequiredNotes
Entry-point .pyYesPath you set on create (e.g. main.py). Must end with .py.
requirements.txtYesListed deps; also copied into the zip
Python dependenciesYes (if listed)Installed under the zip root (e.g. bedrock-agentcore, strands-agents, boto3)
Extra modulesOptionalAdditional .py files or packages passed with --entry
Custom resourcesOptionalAny files/folders via --resources — copied in full (nested trees included)
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:

LayerSource
Entry handler(s)--entry (default main.py)
requirements.txt--requirements
Python dependenciesInstalled from that requirements file into the zip
Custom user resources--resources paths — entire files/folders, nested trees included
Keep paths consistent

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 pathPath inside zip
./resourcesresources/... (complete tree)
./resources/dataresources/data/...
./resources/data/config.jsonresources/data/config.json
FlagPurpose
--nameOutput zip filename (local name only; the upload package name on the platform does not need to match)
--entryHandler file(s)/dirs to include; first path is the suggested Entry Point
--python310314 → Amorphic PYTHON_3_10PYTHON_3_14
--resourcesCustom files or folders copied into the zip in full (relative paths preserved)
--requirementsAlternate requirements file
tip

The local zip filename does not matter. After upload, Amorphic stores the package for that agent.

Upload in Amorphic

  1. Go to AI Services > AI Agents
  2. Start Create Agent
  3. Fill create attributes (name, description, model, Python version, entry point)
  4. Upload the generated zip
  5. Optionally attach knowledge bases, guardrails, and parameters
  6. Click Create

Suggested create values when using this script with defaults:

AttributeTypical value
Python VersionPYTHON_3_12 (match --python 312)
Entry Pointmain.py (must exist inside the zip)
Agent PackageYour generated zip (e.g. agent_artifact.zip)
Agent name rules

Agent Name must be 3–48 characters, start with a letter, and use only letters, numbers, and underscores. Hyphens are not allowed.

info

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

  1. Rebuild with generate_agent_artifact.sh
  2. Open the custom agent → edit / upload package
  3. Upload the new zip and save
  4. Refresh any open chat session after the update completes

Checklist

  • Entry file defines the AgentCore entry point and returns Content / ContentType
  • requirements.txt lists all third-party deps (no Shared Library dependency)
  • Artifact built for the AgentCore runtime / matching Python version (--python aligns with Amorphic Python Version)
  • Custom resources (if any) are passed with --resources and match the paths your code opens at runtime
  • Zip uploads successfully; agent reaches Ready and is enabled before chat

Troubleshooting

IssueWhat to check
Import errors at runtimeDependency missing from zip; rebuild after updating requirements.txt
Wrong architecture / native module errorsPrefer the Docker build path so the package matches AgentCore (ARM)
Entry point not foundEntry Point on create must match a .py path inside the zip
Missing resource fileConfirm --resources included the folder; the path in your code must match the path inside the zip
Create failedFix package or configuration and update (or recreate); failed agents stay disabled