Logfire Infrastructure
pydantic/skills
Monitor hosts, Docker containers, Kubernetes clusters, database/queue/cache servers, and cloud-provider metrics with Pydantic Logfire — no application code required.
Deploy an Agent Kernel project to AWS, Azure, or GCP using Terraform modules, or to any Kubernetes cluster (on-prem, baremetal, EKS) using the official Helm chart.
$ npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install yaalalabs/agent-kernel ak-cloud-deploy --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/yaalalabs/agent-kernel.git skills-src && mkdir -p .claude/skills && cp -r skills-src/ak-py/src/agentkernel/skills/ak-cloud-deploy .claude/skills/ak-cloud-deploy && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "ak-cloud-deploy" agent skill from https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deploy into .claude/skills/ak-cloud-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ak-cloud-deploy", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deployType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install yaalalabs/agent-kernel ak-cloud-deploy --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaalalabs/agent-kernel.git skills-src && mkdir -p .agents/skills && cp -r skills-src/ak-py/src/agentkernel/skills/ak-cloud-deploy .agents/skills/ak-cloud-deploy && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "ak-cloud-deploy" agent skill from https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deploy into .agents/skills/ak-cloud-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ak-cloud-deploy", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install yaalalabs/agent-kernel ak-cloud-deploy --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaalalabs/agent-kernel.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/ak-py/src/agentkernel/skills/ak-cloud-deploy .cursor/skills/ak-cloud-deploy && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "ak-cloud-deploy" agent skill from https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deploy into .cursor/skills/ak-cloud-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ak-cloud-deploy", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/yaalalabs/agent-kernel.git --path ak-py/src/agentkernel/skills/ak-cloud-deploy--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install yaalalabs/agent-kernel ak-cloud-deploy --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaalalabs/agent-kernel.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/ak-py/src/agentkernel/skills/ak-cloud-deploy .gemini/skills/ak-cloud-deploy && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "ak-cloud-deploy" agent skill from https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deploy into .gemini/skills/ak-cloud-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ak-cloud-deploy", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install yaalalabs/agent-kernel ak-cloud-deployInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/yaalalabs/agent-kernel.git skills-src && mkdir -p .github/skills && cp -r skills-src/ak-py/src/agentkernel/skills/ak-cloud-deploy .github/skills/ak-cloud-deploy && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "ak-cloud-deploy" agent skill from https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deploy into .github/skills/ak-cloud-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ak-cloud-deploy", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install yaalalabs/agent-kernel ak-cloud-deploy --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaalalabs/agent-kernel.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/ak-py/src/agentkernel/skills/ak-cloud-deploy .opencode/skills/ak-cloud-deploy && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "ak-cloud-deploy" agent skill from https://github.com/yaalalabs/agent-kernel/tree/develop/ak-py/src/agentkernel/skills/ak-cloud-deploy into .opencode/skills/ak-cloud-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ak-cloud-deploy", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
ak-cloud-deployDeploy an Agent Kernel project to AWS, Azure, or GCP using Terraform modules, or to any Kubernetes cluster (on-prem, baremetal, EKS) using the official Helm chart.
Ak Cloud Deploy is an agent skill from yaalalabs/agent-kernel. Deploy an Agent Kernel project to AWS, Azure, or GCP using Terraform modules, or to any Kubernetes cluster (on-prem, baremetal, EKS) using the official Helm chart. Supports serverless and containerized modes for all three clouds. AWS supports execution modes (restsync, restasync, async, stream), queue-based scalable processing, custom API Gateway authorizers, EventBridge Scheduler for scheduled tasks, and SSM Parameter Store secret resolution (ssmenabled). GCP supports Cloud Run serverless (scale-to-zero) and…
Its SKILL.md is about 14k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `evals/evals.json`).
It sits in Backend & APIs, covering Container orchestration, Serverless and Infrastructure as code. It works with Amazon Web Services, Google Cloud, Microsoft Azure and Kubernetes. The repository describes itself as: The Operating System for Scalable Enterprise AI Agents - Run, orchestrate, and deploy Compliant Enterprise AI Agents at scale across frameworks, without lock-in, rewrites or… The licence is Apache-2.0.
4 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit e03a602. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
helmterraformawskubectlcurlazgcloudFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
valkey.ioFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
OPENAI_API_KEYJWT_SECRETSOME_OTHER_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Ak Cloud Deploy loads about 14k tokens when it runs. Until then it costs about 184 tokens; SKILL.md has 3,539 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from yaalalabs/agent-kernel at commit e03a602, republished under its Apache-2.0 licence (© yaalalabs). 3,539 words, ~13,569 tokens.
.claude/skills/ak-cloud-deploy/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Use this skill to deploy your Agent Kernel project to AWS, Azure, or GCP.
When the user wants cloud deployment, follow this workflow.
Check for:
pyproject.toml with agentkernel dependencyapp.py, lambda.py, or similar)config.yamlIf missing, suggest ak-init first.
rest_sync, supports standard or queue/scalable mode; AWS serverless or containerized)rest_async, queue/scalable mode; AWS serverless or containerized)async, works with or without queue mode — queue_mode = false runs the agent inline, queue_mode = true enqueues to a separately-scalable Agent Runner) — AWS serverless or AWS containerized/ECSstream, works with or without queue mode, same as async above) — AWS serverless or AWS containerized/ECS; also available via SSE (POST /api/v1/chat with execution.mode: stream, no Terraform changes required) wherever the built-in FastAPI REST server runs: local/self-hosted, AWS ECS single-container REST, Azure Container Apps, GCP Cloud Run — not on AWS Lambda or Azure Functions, which use WebSocket insteadprefix applied to every resource name (e.g. myapp-dev-chat).Use official modules:
yaalalabs/ak-serverless/awsyaalalabs/ak-containerized/awsyaalalabs/ak-serverless/azurermyaalalabs/ak-containerized/azurermyaalalabs/ak-serverless/googleyaalalabs/ak-containerized/googleUse current module version (0.9.3) unless user requests another.
Kubernetes does not use Terraform: the Helm chart lives at ak-deployment/ak-k8s/chart in the
Agent Kernel repository and is published as an OCI artifact
(oci://ghcr.io/yaalalabs/charts/agent-kernel, versioned with each release). Install the
published chart, as in the Kubernetes section below, not a repository checkout.
All modules are provider-agnostic: they declare required_providers but do not configure them internally. Configure each provider (aws/docker, azurerm, or google/google-beta/docker) in the root module and pass it explicitly via the module's providers = { ... } argument, as shown in the examples below. Azure's containerized module builds and pushes its image via a nested submodule with its own internal docker provider, so no docker provider needs to be configured or passed by the caller there.
AWS-only features in this skill:
execution_modequeue_modeauthorizerAzure examples use the provider module's standard top-level inputs (function_name, package_path, container_port, publisher_email) rather than AWS serverless execution modes.
GCP modules use Cloud Run for both serverless (scale-to-zero, min_instance_count=0) and containerized (always-on, min_instance_count≥1) modes. Both use agentkernel.gcp.CloudRun as the entry point.
When the user selects a session store, always update both app dependencies and config.yaml.
redisdependencies = [
"agentkernel[openai,api,redis]>=0.9.3"
]config.yaml session block:session:
type: redis
namespace: chat
redis:
host: ${REDIS_HOST}
port: 6379
db: 0Valkey is the open-source, Linux Foundation-governed fork of Redis. It is wire-compatible with Redis and available on AWS ElastiCache at a lower price point than the Redis OSS engine. Agent Kernel treats it as a first-class session and response store backend on AWS.
valkeydependencies = [
"agentkernel[openai,api,aws,valkey]>=0.9.3"
]config.yaml session block:session:
type: valkey
valkey:
url: ${AK_SESSION__VALKEY__URL} # valkeys:// for SSL
ttl: 604800
prefix: "ak:sessions:"create_valkey_cluster = true (serverless and
containerized) and injects AK_SESSION__VALKEY__URL — session.type: valkey must still be set
in config.yaml since the type itself is never injected.create_valkey_response_store = true (serverless only) and
execution.response_store.type: valkey in config.yaml.awsdependencies = [
"agentkernel[openai,api,aws]>=0.9.3"
]config.yaml session block:session:
type: dynamodb
dynamodb:
table_name: ${DYNAMODB_MEMORY_TABLE}
region: ${AWS_REGION}azuredependencies = [
"agentkernel[openai,api,azure]>=0.9.3"
]config.yaml session block:session:
type: cosmosdb
cosmosdb:
endpoint: ${AZURE_COSMOS_ENDPOINT}
database_name: ${AZURE_COSMOS_DATABASE}
container_name: ${AZURE_COSMOS_CONTAINER}gcpdependencies = [
"agentkernel[openai,api,gcp]>=0.9.3"
]config.yaml session block:session:
type: firestore
firestore:
collection_name: ${AK_SESSION__FIRESTORE__COLLECTION_NAME}
project_id: ${PROJECT_ID}
ttl: 604800AK_SESSION__TYPE=firestore and AK_SESSION__FIRESTORE__COLLECTION_NAME automatically when Firestore is enabled.expiry_time field for automatic document expiry.If the Terraform module creates the backing store (for example create_redis_cluster = true, create_valkey_cluster = true, or create_dynamodb_memory_table = true), make sure output values are wired to the app environment variables used by config.yaml.
Conversation threads (ak-add-capabilities) deploy the same way sessions do: the app mounts
AgentThreadRequestHandler (which is what enables the feature; the self-hosted REST API is the only
surface that records threads — queue-mode runners and the other deployment adapters do not) and
declares thread.type in config.yaml, and Terraform provisions the backend, injecting only the
connection detail. Terraform never sets AK_THREAD__TYPE itself.
create_dynamodb_thread_table = true provisions a DynamoDB
table (partition session_id, sort sk, TTL on expiry_time) and injects
AK_THREAD__DYNAMODB__TABLE_NAME.create_firestore_thread_collection = true requires
create_firestore_database = true, reuses that database, and injects
AK_THREAD__FIRESTORE__COLLECTION_NAME, __PROJECT_ID, and __DATABASE_ID. No new resource is
provisioned — the collection is created on first write.create_redis_cluster /
create_valkey_cluster already provisions and declare thread: {type: redis} (or valkey) with
the cluster URL in config.yaml.Setting the Terraform flag without declaring thread.type in config.yaml silently leaves
threads on the non-durable in-memory backend (any AK_THREAD__* var materialises AKConfig.thread,
but type still defaults to in_memory) — always pair the flag with the matching thread.type.
Scheduled tasks (ak-add-capabilities) deploy on the same two-step split: the app declares
schedule.provider.type and schedule.store.type in config.yaml, and Terraform provisions the
backends, injecting only the coordinates. Terraform never sets AK_SCHEDULE__PROVIDER__TYPE or
AK_SCHEDULE__STORE__TYPE.
Scheduling requires queue mode — occurrences are delivered into the input queue, so
queue_mode = true is mandatory on AWS. enable_scheduling without it is rejected.
enable_scheduling = true provisions an EventBridge Scheduler
schedule group and the execution role Scheduler assumes to deliver triggers to the input queue,
grants both the request/REST role and the agent-runner role scheduler:CreateSchedule|UpdateSchedule|DeleteSchedule|GetSchedule
plus iam:PassRole on that role, and injects
AK_SCHEDULE__PROVIDER__EVENTBRIDGE__GROUP_NAME, __ROLE_ARN and __QUEUE_ARN. It also flips the
input queue to content-based deduplication, because Scheduler cannot set a
MessageDeduplicationId (application senders are unaffected — their explicit id takes precedence).create_dynamodb_schedule_table = true provisions a DynamoDB
table (partition task_id, no sort key, no GSI, TTL on expiry_time) and injects
AK_SCHEDULE__STORE__DYNAMODB__TABLE_NAME.create_redis_cluster /
create_valkey_cluster already provisions and declare schedule: {store: {type: redis}} (or
valkey) with the cluster URL in config.yaml.local provider only works
in a single always-on process with the in_memory transport and store, which no cloud deployment
mode gives you.queue_mode = true
enable_scheduling = true
create_dynamodb_schedule_table = trueschedule:
provider:
type: eventbridge
store:
type: dynamodbSetting the Terraform flags without declaring both types in config.yaml silently leaves scheduling
on the in-process local provider and in_memory store (any AK_SCHEDULE__* var materialises
AKConfig.schedule, but the types still default), while the provisioned group and table sit unused —
always pair the flags with the matching types. The reverse is safe: declaring the types without the
flags fails at startup with an AKConfigError on the missing group_name / role_arn / queue_arn.
The blocks above enable deferring and the agent tools. The /api/v1/schedules management routes are
not mounted from config — on ECS queue mode, pass the handler to the IO container's entrypoint,
where it is served alongside the queue-producing chat route:
# app_rest_service.py
from agentkernel.aws import ECSIOHandler
from agentkernel.schedule import ScheduleRESTRequestHandler
if __name__ == "__main__":
# add authoriser=... to scope listings to the caller; without one the routes are open
ECSIOHandler.run(handlers=[ScheduleRESTRequestHandler()])Remember to list the schedule paths in gateway_endpoints — the gateway only proxies paths it is
told about.
New outputs on both AWS stacks (null unless the matching flag is set): schedule_group_name,
schedule_group_arn, scheduler_execution_role_arn, schedule_table_name, schedule_table_arn.
Secret resolution (ak-add-capabilities) follows the same split: the app declares
secret.provider.type: aws_ssm in config.yaml and resolves keys with
SecretManager.current().get("OPENAI_API_KEY"); Terraform grants read access and injects only the
deployment scope. Terraform never sets AK_SECRET__PROVIDER__TYPE.
ssm_enabled = true grants only the tier that runs the agent
(the agent runner when queue_mode = true; otherwise the serverless request handler / containerized
REST service) ssm:GetParameter on arn:aws:ssm:<region>:<account>:parameter/ak/<prefix>/* and
injects AK_SECRET__PREFIX = <prefix> (the module's own prefix). The response handler and
WebSocket connection handler never get SSM access, so resolve secrets in agent-runner code only.SecureString
(AWS-managed alias/aws/ssm key) at /ak/<prefix>/<key lowercased>, e.g.
aws ssm put-parameter --name /ak/<prefix>/openai_api_key --type SecureString --value sk-....environment_variables: a set, non-empty environment variable always wins over
SSM, so leaving OPENAI_API_KEY there bypasses the store.aws extra (agentkernel[aws]) for the aws_ssm provider.ssm_enabled = truesecret:
provider:
type: aws_ssm
# prefix is injected by Terraform as AK_SECRET__PREFIXfrom agentkernel.secret import SecretManager
from agents import set_default_openai_key
set_default_openai_key(SecretManager.current().get("OPENAI_API_KEY"))Declaring aws_ssm without ssm_enabled (and without setting secret.prefix yourself) fails at
startup with an AKConfigError on the missing prefix; with a prefix but no IAM grant, lookups raise
SecretError (AccessDenied). Setting ssm_enabled without declaring the provider leaves resolution on
the env provider. See examples/aws-serverless/openai and
examples/aws-containerized/openai-dynamodb-scalable.
Use Lambda handler entrypoint:
from agentkernel.aws import Lambda
from agentkernel.openai import OpenAIModule
# Register agents with your framework module
OpenAIModule([...])
handler = Lambda.handlerrest_sync)Use this for straightforward request/response APIs.
This is the single-Lambda pattern: use request_handler plus any gateway_endpoints. Do not add agent_runner or response_handler unless you are using queue mode.
module "serverless_agents" {
source = "yaalalabs/ak-serverless/aws"
version = "0.9.3"
prefix = var.prefix
product_display_name = "AK Serverless"
region = var.region
execution_mode = "rest_sync"
request_handler = {
function_name = "chat-handler"
function_description = "AK request handler"
handler_path = "lambda.handler"
package_type = "Image"
package_path = "../dist"
timeout = 45
memory_size = 256
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
gateway_endpoints = [
{ path = "app", method = "GET" },
{ path = "app_info", method = "POST" }
]
}rest_sync or rest_async)Use this for high throughput and long-running requests.
This is the multi-artifact pattern used by the scalable example: a request handler zip, an agent-runner image, and a response-handler zip.
Each Lambda can use one of three package_type values:
package_type | Artifact source | Required field(s) | What Terraform does |
|---|---|---|---|
LocalZip | Local ZIP file or source directory | package_path | Uses the Lambda module's local packaging support. No shared source bucket is managed by this module. |
S3Zip | ZIP artifact stored in S3 | Either package_path or lambda_package_s3 | If package_path is provided, this module creates/uses a shared source bucket, uploads the ZIP, and deploys from S3. If lambda_package_s3 ({ bucket, key, version_id? }) is provided, Terraform uses the existing S3 object directly; set version_id (from a versioned bucket) so re-uploading changed code redeploys the function. |
Image | Container image in ECR | Either ecr_image_uri or package_path | If ecr_image_uri is provided, Terraform uses the existing image. If package_path is provided, Terraform builds and pushes an image to ECR and deploys it. |
package_path and lambda_package_s3/ecr_image_uri are mutually exclusive — set only one.
Development / local build (build artifacts locally before running terraform apply):
module "serverless_agents" {
source = "yaalalabs/ak-serverless/aws"
version = "0.9.3"
prefix = var.prefix
region = var.region
product_display_name = "AK Scalable REST"
queue_mode = true
execution_mode = "rest_sync" # can also be "rest_async"
create_dynamodb_memory_table = true
create_dynamodb_response_store = true
# Uncomment with `schedule: {provider: {type: eventbridge}, store: {type: dynamodb}}` in config.yaml
# enable_scheduling = true
# create_dynamodb_schedule_table = true
request_handler = {
function_name = "request-handler"
function_description = "Receives REST requests"
handler_path = "lambda_request_handler.handler"
package_type = "LocalZip"
package_path = "../dist_request_handler.zip"
timeout = 45
memory_size = 256
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
agent_runner = {
function_name = "agent-runner"
function_description = "Processes queued requests"
handler_path = "lambda_agent_runner.handler"
package_type = "Image"
package_path = "../dist_agent_runner"
timeout = 45
memory_size = 512
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
response_handler = {
function_name = "response-handler"
function_description = "Handles async response completion"
handler_path = "lambda_response_handler.handler"
package_type = "LocalZip"
package_path = "../dist_response_handler.zip"
timeout = 45
memory_size = 256
}
queue_config = {
input_queue_visibility_timeout = 60
input_queue_max_receive_count = 3
input_queue_create_dlq = false
input_queue_message_retention_seconds = 300
output_queue_visibility_timeout = 60
output_queue_max_receive_count = 3
output_queue_create_dlq = false
output_queue_message_retention_seconds = 300
batch_size = 10
maximum_batching_window_in_seconds = 0
}
}Production: external artifact sources (S3 / ECR)
Build and push artifacts in CI/CD, then point Terraform at them so terraform apply contains no local build step:
request_handler = {
function_name = "request-handler"
handler_path = "lambda_request_handler.handler"
package_type = "S3Zip"
lambda_package_s3 = {
bucket = "my-lambda-packages-bucket"
key = "dist_request_handler.zip"
version_id = "<object-version-id>" # from your versioned bucket → redeploys work (#548)
}
timeout = 45
memory_size = 256
environment_variables = { OPENAI_API_KEY = var.openai_api_key }
}
agent_runner = {
function_name = "agent-runner"
handler_path = "lambda_agent_runner.handler"
package_type = "Image"
ecr_image_uri = "123456789012.dkr.ecr.us-west-2.amazonaws.com/agent-runner:latest"
timeout = 45
memory_size = 512
environment_variables = { OPENAI_API_KEY = var.openai_api_key }
}
response_handler = {
function_name = "response-handler"
handler_path = "lambda_response_handler.handler"
package_type = "S3Zip"
lambda_package_s3 = {
bucket = "my-lambda-packages-bucket"
key = "dist_response_handler.zip"
version_id = "<object-version-id>" # from your versioned bucket → redeploys work (#548)
}
timeout = 45
memory_size = 256
}See examples/aws-serverless/scalable-openai for a complete working example of this pattern.
Terraform only replaces a Lambda's code when s3_bucket, s3_key, s3_object_version, or source_code_hash changes. If you re-upload to the same S3 key in an unversioned bucket, none of those change and the function keeps running the old code. To make S3Zip redeploys reliable when pointing at your own artifact (lambda_package_s3): enable versioning on the bucket holding the ZIPs, then pass the uploaded object's version through lambda_package_s3.version_id.
Queue mode config.yaml (bundled into every Lambda package — execution.mode, queue URLs, table names, and max_receive_count are all injected automatically by Terraform as environment variables; only set values that are NOT injected):
# For rest_sync or rest_async
execution:
response_store:
type: dynamodb # or redis / valkey — not injected, must be set here
retry_count: 5
delay: 5
session:
type: dynamodb # or redis / valkey — not injected, must be set hererest_sync: request handler sends to queue, polls the response store until the result is available, then returns it synchronously.rest_async: request handler returns {status: "ACCEPTED", request_id} immediately; the client polls GET /api/{version}/{endpoint} with {"request_id": "<id>"} in the JSON body.Required pyproject.toml extras for queue mode:
dependencies = [
"agentkernel[openai,api,aws]>=0.9.3" # include 'redis' if using Redis, or 'valkey' if using Valkey session/response store
]async)Use this for realtime bidirectional interactions.
This follows the current websocket example shape: the request handler stays on the module's top-level Lambda inputs, then queue workers and WebSocket handlers are added via nested blocks.
module "serverless_agents" {
source = "yaalalabs/ak-serverless/aws"
version = "0.9.3"
prefix = var.prefix
region = var.region
product_display_name = "AK WebSocket Serverless Example"
queue_mode = true
execution_mode = "async"
create_redis_cluster = true
create_dynamodb_response_store = true
request_handler = {
function_name = "request-handler"
function_description = "Receives WebSocket requests"
handler_path = "lambda_request_handler.handler"
package_type = "LocalZip"
package_path = "../dist_request_handler.zip"
timeout = 45
memory_size = 256
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
agent_runner = {
function_name = "agent-runner"
function_description = "Processes chat tasks"
handler_path = "lambda_agent_runner.handler"
package_type = "Image"
package_path = "../dist_agent_runner"
timeout = 45
memory_size = 512
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
response_handler = {
function_name = "response-handler"
function_description = "Sends responses to connections"
handler_path = "lambda_response_handler.handler"
package_type = "LocalZip"
package_path = "../dist_response_handler.zip"
timeout = 45
memory_size = 256
}
ws_connection_handler = {
function_name = "ws-connection-handler"
function_description = "Handles $connect/$disconnect"
handler_path = "lambda_ws_connection_handler.handler"
package_path = "../dist_ws_connection_handler.zip"
timeout = 45
memory_size = 256
}
ws_routes = [
{ route = "app" },
{ route = "app_info" }
]
}WebSocket lambda_ws_connection_handler.py — implement AuthValidator to validate the bearer token on $connect:
import jwt
import os
from agentkernel.auth import AuthValidator, ValidationResult
from agentkernel.aws import WebsocketConnectionHandler
class MyAuthValidator(AuthValidator):
def validate(self, token: str) -> ValidationResult:
try:
payload = jwt.decode(
token,
os.environ["JWT_SECRET"],
algorithms=["HS256"],
issuer=os.environ["JWT_ISSUER"],
audience=os.environ["JWT_AUDIENCE"],
)
user_id = payload.get("userId", "")
if user_id:
return ValidationResult(is_valid=True, claims={"userId": user_id})
return ValidationResult(is_valid=False, error_msg="userId claim missing")
except Exception as e:
return ValidationResult(is_valid=False, error_msg=str(e))
handler = WebsocketConnectionHandler.set_auth_validator(MyAuthValidator()).handlerWebSocket lambda_request_handler.py — routes are registered by route name (no leading slash, no HTTP method) because the WebSocket API uses $request.body.route for dispatch:
import json
from agentkernel.aws import Lambda
@Lambda.register("app") # WebSocket route name, no slash/method
def app_handler(event, context):
return {"response": "Hello from app route"}
@Lambda.register("app_info")
def app_info_handler(event, context):
payload = json.loads(event.get("body") or "{}")
return {"response": "Hello from app_info route"}
handler = Lambda.handlerContrast with REST mode where routes use a leading slash and HTTP method:
@Lambda.register("/app", method="GET") # REST mode: path + methodWebSocket config.yaml (bundled into every Lambda package — execution.mode, websocket_api.chat_route, queue URLs, and connection table name are all injected automatically by Terraform; only set values that are NOT injected):
session:
type: redis # or valkey / dynamodb — not injected, must be set here
redis:
prefix: "ak:myapp:"AuthValidator.validate() must return claims["userId"]; connections without a valid token are rejected at $connect.authorizer Terraform block is not supported in WebSocket mode — leave it unset.LocalZip is supported for ws_connection_handler.package_path.ws_routes custom routes must have a matching @Lambda.register("route_name") entry. ws_chat_route is the built-in AI chat route — it is handled internally by the framework and does not need a @Lambda.register entry.Required pyproject.toml extras for WebSocket mode:
dependencies = [
"agentkernel[openai,api,aws,redis,auth]>=0.9.3"
]stream)Use this when the client should receive each stream event as soon as it is produced, instead of waiting for the full response.
Same Terraform shape as WebSocket Async (request_handler, agent_runner, response_handler, ws_connection_handler, ws_routes) — only execution_mode changes:
module "serverless_agents" {
source = "yaalalabs/ak-serverless/aws"
version = "0.9.3"
prefix = var.prefix
region = var.region
product_display_name = "AK Streaming WebSocket Example"
queue_mode = true
execution_mode = "stream"
create_redis_cluster = true
# create_redis_response_store, create_valkey_response_store, and create_dynamodb_response_store must stay false/unset:
# WebSocket modes (async/stream) push responses over the connection and Terraform
# validation fails if a response store is enabled for them.
request_handler = { ... } # same shape as async mode
agent_runner = { ... } # runs ServerlessStreamAgentRunner, streams chunks to output queue
response_handler = { ... } # broadcasts each chunk as a STREAM_CHUNK message
ws_connection_handler = { ... }
ws_routes = [ { route = "app" }, { route = "app_info" } ]
}config.yaml — the only required setting beyond WebSocket async mode is the execution mode itself:
execution:
mode: streamThis makes ServerlessAgentRunner.handle() dispatch to ServerlessStreamAgentRunner (queue mode) — no code change needed in the agent runner Lambda beyond the standard Lambda.handler entrypoint.
Message format — clients receive a sequence of STREAM_CHUNK messages instead of one CHAT_RESPONSE:
{"type": "STREAM_CHUNK", "delta": "Hello", "done": false, "session_id": "user-1"}
{"type": "STREAM_CHUNK", "delta": " world", "done": false, "session_id": "user-1"}
{"type": "STREAM_CHUNK", "delta": "!", "done": true, "session_id": "user-1"}On an unrecoverable error, the final chunk carries error instead of delta, with done: true.
queue_mode = false): the request handler Lambda streams tokens directly to the WebSocket client without SQS.create_redis_response_store / create_valkey_response_store / create_dynamodb_response_store must be false for stream (same constraint as async) — Terraform validation enforces this.Containerized / direct streaming (no Terraform WebSocket setup): wherever the built-in FastAPI REST server runs — local/self-hosted, AWS ECS single-container REST, Azure Container Apps, GCP Cloud Run — SSE event streaming can be enabled by setting execution.mode: stream in config.yaml. POST /api/v1/chat and /api/v1/chat-multipart then return text/event-stream responses instead of JSON — no queue or WebSocket infrastructure is required for this mode. Not available on AWS Lambda or Azure Functions (serverless), which use WebSocket for streaming instead.
If the user needs token verification, include authorizer block:
authorizer = {
description = "API Gateway Lambda Authorizer"
function_name = "gateway-authorizer"
handler_path = "lambda_auth.handler"
package_path = "../dist_auth.zip"
package_type = "LocalZip"
result_ttl_in_seconds = 0
environment_variables = {
SOME_OTHER_KEY = "Some Other Value"
}
}from agentkernel.api import RESTAPI
from agentkernel.openai import OpenAIModule
OpenAIModule([...])
if __name__ == "__main__":
RESTAPI.run()module "containerized_agents" {
source = "yaalalabs/ak-containerized/aws"
version = "0.9.3"
prefix = var.prefix
region = var.region
product_display_name = "AK ECS Deployment"
rest_service = {
package_path = "../dist"
container_port = 8000
desired_count = 2
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
create_dynamodb_memory_table = true
}Use this for high-throughput or long-running agents. Two separate ECS services share SQS queues:
ECSIOHandler) — Thread 1: FastAPI REST API; Thread 2: output queue consumer → DynamoDB / WebSocket. Thread 2 is actually queues.output.no_of_consumers (default 5) parallel polling threads.ECSAgentRunner) — runs queues.input.no_of_consumers (default 5) parallel threads, each polling the input queue, executing the agent, and putting the result on the output queue.app_rest_service.py (IO container entrypoint — NO agent definitions here):
from agentkernel.aws import ECSIOHandler
runner = ECSIOHandler.run
if __name__ == "__main__":
runner()Pass auth_validator=MyAuthValidator() to secure the REST routes: ECSIOHandler.run binds it
onto every REST route via AWSRestAPI.add_auth_handlers (the same Bearer-token mechanism as
RESTAPI.add_auth_handlers in Basic Mode); omit it to leave the REST routes unauthenticated.
app_agent_runner.py (Agent Runner container entrypoint):
from agentkernel.aws import ECSAgentRunner
from agentkernel.openai import OpenAIModule
OpenAIModule([...]) # register agents here only
handler = ECSAgentRunner.run
if __name__ == "__main__":
handler()config.yaml (same file included in both images — queue URLs and table names are injected by Terraform; batch_size is Terraform-only, never set here):
execution:
queues:
type: sqs # mandatory in a declared queues block: it is what selects the transport
input:
no_of_consumers: 5 # parallel input-poll threads in the Agent Runner container (default 5)
output:
no_of_consumers: 5 # parallel output-poll threads in the IO container (default 5)
response_store:
type: dynamodb
retry_count: 30
delay: 2
session:
type: dynamodb # or redis / valkey (create_redis_cluster / create_valkey_cluster)Terraform:
module "containerized_agents" {
source = "yaalalabs/ak-containerized/aws"
version = "0.9.3"
prefix = var.prefix
region = var.region
rest_service = {
package_path = "../dist-rest-service"
cpu = 512
memory = 1024
desired_count = 2
command = ["python", "app_rest_service.py"]
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
queue_mode = true
execution_mode = "rest_sync" # or "rest_async"
# Uncomment with `schedule: {provider: {type: eventbridge}, store: {type: dynamodb}}` in config.yaml
# enable_scheduling = true
# create_dynamodb_schedule_table = true
queue_config = {
input_queue_visibility_timeout = 120
output_queue_visibility_timeout = 60
input_queue_create_dlq = true
output_queue_create_dlq = true
}
# Agent Runner container — separate image with agent definitions
agent_runner = {
cpu = 1024
memory = 2048
desired_count = 1
package_path = "../dist-agent-runner"
command = ["python", "app_agent_runner.py"]
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
# Optional: auto-scale Agent Runner based on Input Queue depth
scaling_config = {
enabled = true
min_count = 1
max_count = 10
backlog_target = 5
scale_in_cooldown = 180
scale_out_cooldown = 60
}
create_dynamodb_memory_table = true
}Required pyproject.toml extras:
dependencies = [
"agentkernel[openai,api,aws]>=0.9.3"
]Key rules:
OpenAIModule([...])) go in app_agent_runner.py only — never in app_rest_service.py.ECSIOHandler starts two threads via ThreadRunner; if the output-consumer pool crashes, sibling consumer threads finish their in-flight message first (graceful drain via a shared shutdown_event), then the container exits (os._exit(1)) so ECS can restart it. The REST API thread doesn't participate in the drain — it's just terminated at that point.ECSAgentRunner and ECSOutputConsumer both extend ECSSQSConsumer (itself a RawQueueConsumer, the same base LambdaSQSConsumer extends); extend either class to customise message processing.async / stream)A WebSocket API Gateway proxies frames to the ECS REST service via a VPC Link V1 + internal NLB
in front of the existing ALB. Supports both direct (queue_mode = false, one ECS service,
agent runs inline) and queue (queue_mode = true, same two-container split as section B,
with the REST/IO service enqueueing chat frames and its output-queue consumer pushing replies
back over the socket) variants. execution_mode = "async" delivers the full reply as one
CHAT_RESPONSE push; execution_mode = "stream" delivers it token-by-token as a sequence of
STREAM_CHUNK pushes (terminated by a chunk with "done": true) — in queue mode
ECSAgentRunner.run() dispatches to ECSStreamAgentRunner, which fans out one Output Queue
message per chunk instead of one for the full reply; in direct mode the chat route runs
ChatService.process_stream_chat_async() and broadcasts each chunk inline. See
examples/aws-containerized/openai-stream (direct mode) or
examples/aws-containerized/openai-stream-queue-mode (queue mode) for full streaming examples.
app.py (direct/single-container variant; register custom routes before calling run()):
from agentkernel.aws import AWSWebsocketAPI
from agentkernel.auth import AuthValidator, ValidationContext, ValidationResult
from agentkernel.openai import OpenAIModule
from typing import Optional
OpenAIModule([...])
class CustomAuthValidator(AuthValidator):
def validate(self, token: str, context: Optional[ValidationContext] = None) -> ValidationResult:
# userId claim is mandatory — it's how a connection maps to a user for reply routing
...
return ValidationResult(is_valid=True, claims={"userId": "userId-claim-value"})
@AWSWebsocketAPI.register("status") # bare route name, no leading slash, no HTTP verb
async def status(ctx: dict) -> dict:
# ctx = {"message": <raw JSON body dict>, "user_id": ...} — no BaseRequest schema imposed
return {"status": "OK", "user_id": ctx["user_id"]} # dict return -> broadcast as SYSTEM_RESPONSE; None -> no broadcast
if __name__ == "__main__":
# Direct mode calls AWSWebsocketAPI directly — NOT ECSIOHandler, which always starts a
# second thread polling an output queue that doesn't exist in direct (non-queue) mode.
AWSWebsocketAPI.set_auth_handler(auth_validator=CustomAuthValidator()).run()Queue mode is different: it splits into app_rest_service.py (ECSIOHandler.run(auth_validator=...)
— correct here, since ECSIOHandler also starts the output-queue consumer thread — custom routes
registered here, no agent definitions) and app_agent_runner.py (ECSAgentRunner.run,
OpenAIModule([...]) here only) — identical shape to section B.
Terraform:
module "containerized_agents" {
source = "yaalalabs/ak-containerized/aws"
version = "0.9.3"
providers = { aws = aws, docker = docker }
prefix = var.prefix
region = var.region
vpc_id = var.vpc_id
private_subnet_ids = var.private_subnet_ids
rest_service = {
package_path = "../dist"
container_port = 8000
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}
queue_mode = false # or true for the two-container queue variant (see section B)
execution_mode = "async" # "stream" delivers the reply as STREAM_CHUNK messages instead
ws_chat_route = "chat" # optional, defaults to "chat"
ws_routes = [ # every @AWSWebsocketAPI.register(...) route must be listed here too
{ route = "status" },
]
create_dynamodb_memory_table = true
}Key rules:
AuthValidator.validate() must resolve a userId claim, or the framework
raises at $connect construction time. There is no API Gateway authorizer for WebSocket mode.@AWSWebsocketAPI.register("name") decorator and a
matching Terraform ws_routes = [{ route = "name" }] entry — Python cannot create the API
Gateway route/integration, so both sides must declare it. ws_chat_route is Terraform-only —
it just names the API Gateway route key that forwards to the container's hardcoded /ws/chat
endpoint; the container never reads it from config, and renaming it needs no Python change.response_store
input to set) — replies are always pushed over the connection instead. gateway_endpoints
specifically has a Terraform validation rule rejecting it in async/stream modes.{"route": "chat", "body": {...}} — the WebSocket API's
route_selection_expression is $request.body.route.from agentkernel.azure import AzureFunctions
from agentkernel.openai import OpenAIModule
OpenAIModule([...])
handler = AzureFunctions.handlermodule "serverless_agents" {
source = "yaalalabs/ak-serverless/azurerm"
version = "0.9.3"
prefix = var.prefix
region = var.region
resource_group_name = var.resource_group_name
publisher_email = var.publisher_email
product_display_name = "AK Azure Functions Example"
function_name = "openai-agents"
function_description = "Agent Kernel OpenAI Sample Azure Function"
module_type = "python"
package_path = "../dist.zip"
create_redis_cluster = true
create_cosmosdb_cluster = false
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
gateway_endpoints = [
{
function_name = "AgentFunction"
path = "/chat"
method = "POST"
},
{
function_name = "CustomFunction"
path = "/custom"
method = "POST"
}
]
}module "containerized_agents" {
source = "yaalalabs/ak-containerized/azurerm"
version = "0.9.3"
prefix = var.prefix
region = var.region
resource_group_name = var.resource_group_name
publisher_email = var.publisher_email
package_path = "../dist"
product_display_name = "AK Azure Container Apps"
container_port = 8000
create_cosmosdb_cluster = true
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}from agentkernel.gcp import CloudRun
from agentkernel.openai import OpenAIModule
OpenAIModule([...])
@CloudRun.register("/app", method="GET")
def app_handler() -> dict:
return {"status": "ok"}
def main() -> None:
CloudRun.run()module "serverless_agent" {
source = "yaalalabs/ak-serverless/google"
version = "0.9.3"
providers = { google = google, google-beta = google-beta, docker = docker }
project_id = var.project_id
region = var.region
prefix = var.prefix
product_display_name = "AK GCP Serverless"
package_path = "${path.module}/../dist"
create_redis_cluster = true
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
gateway_endpoints = [
{ path = "app", method = "GET", overwrite_path = "/app" },
{ path = "app_info", method = "POST", overwrite_path = "/app_info" }
]
}module "serverless_agent" {
source = "yaalalabs/ak-serverless/google"
version = "0.9.3"
providers = { google = google, google-beta = google-beta, docker = docker }
project_id = var.project_id
region = var.region
prefix = var.prefix
product_display_name = "AK GCP Serverless Firestore"
package_path = "${path.module}/../dist"
create_firestore_db = true
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}The module injects AK_SESSION__TYPE=firestore and AK_SESSION__FIRESTORE__COLLECTION_NAME automatically when create_firestore_db = true.
Required pyproject.toml extras:
dependencies = [
"agentkernel[openai,api,gcp]>=0.9.3" # for Firestore sessions
# or: "agentkernel[openai,api,redis]>=0.9.3" # for Redis sessions
]Same as GCP Serverless — use agentkernel.gcp.CloudRun:
from agentkernel.gcp import CloudRun
from agentkernel.openai import OpenAIModule
OpenAIModule([...])
def main() -> None:
CloudRun.run()module "containerized_agent" {
source = "yaalalabs/ak-containerized/google"
version = "0.9.3"
providers = { google = google, google-beta = google-beta, docker = docker }
project_id = var.project_id
region = var.region
prefix = var.prefix
product_display_name = "AK GCP Containerized"
package_path = "${path.module}/../dist"
min_instance_count = 1 # always-on — no cold starts
container_port = 8000
create_redis_cluster = true
environment_variables = {
OPENAI_API_KEY = var.openai_api_key
}
}Key difference from GCP Serverless: min_instance_count = 1 keeps at least one instance running at all times, eliminating cold starts.
Deploys the queue pipeline to any Kubernetes cluster: an io-handler Deployment (REST API +
Response Handler), an agent-runner Deployment (the consumers executing the agents), and an
optional ws-gateway Deployment for WebSocket modes. Backing services (Valkey, NATS) are
condition-gated dependencies of the chart; flavors (dev, baremetal, EKS) are values files.
Reference: the chart at ak-deployment/ak-k8s/ and the end-to-end example at
examples/k8s/openai-queue-mode/ in the Agent Kernel repository.
Entry files (one image per component; the chart's command defaults match these names):
# app_io_handler.py: the io-handler image
from agentkernel.pipeline import IOHandler
def main():
IOHandler.run()
if __name__ == "__main__":
main()# app_agent_runner.py: the agent-runner image (registers the agent modules)
from agentkernel.openai import OpenAIModule
from agentkernel.pipeline import AgentRunner
from agents import Agent
# ... define agents ...
OpenAIModule([...])
def main():
AgentRunner.run()
if __name__ == "__main__":
main()# app_ws_gateway.py: only for async/stream modes
from agentkernel.pipeline import WebSocketGateway
def main():
WebSocketGateway.run(auth_validator=YourAuthValidator()) # claims must include 'userId'
if __name__ == "__main__":
main()Config split: the image's config.yaml declares WHAT runs (execution.mode, agents,
logging); the chart injects WHERE it runs (broker, response store, session store) as AK_*
env vars that override matching config.yaml fields. Point execution.queues.type at
nats (recommended on-prem), kafka, or sqs, and use a shared response/session store
(valkey) on any multi-pod topology.
Images: python:3.12-slim base, dependencies staged with pip's --target layout, one
Dockerfile per component (mirror examples/k8s/openai-queue-mode/deploy/package.sh, which
also cross-installs Linux wheels so builds work from macOS).
Install:
helm pull oci://ghcr.io/yaalalabs/charts/agent-kernel --version 0.9.3 --untar # unpacks the flavor values files
helm install ak oci://ghcr.io/yaalalabs/charts/agent-kernel --version 0.9.3 \
-f agent-kernel/values-dev.yaml \
--set ioHandler.image.repository=<io image> \
--set agentRunner.image.repository=<runner image> --set image.tag=<tag> \
--set 'extraEnv[0].name=OPENAI_API_KEY' \
--set 'extraEnv[0].valueFrom.secretKeyRef.name=openai' \
--set 'extraEnv[0].valueFrom.secretKeyRef.key=api-key'The chart version tracks the Agent Kernel release (the pin above is the current one); the
flavor values files ship inside the chart, so no repository checkout is needed.
ak-deployment/ak-k8s/chart in the repository is for developing the chart itself.
values-dev.yaml: micro-clusters (k3d/kind/microk8s/k3s), single replicas, auto-provisioned
JetStream, port-forward entry; also documents the single-process profile (one pod,
in_memory transport, zero backing services).values-baremetal.yaml: Envoy Gateway + MetalLB + cert-manager, NACK-managed JetStream
objects (autoProvision: false), OpenEBS hostpath storage.values-eks.yaml: AWS Load Balancer Controller gateway classes, EBS gp3, Pod Identity;
sqs, kafka, and nats all valid.--set execution.mode=stream --set wsGateway.enabled=true plus a
wsGateway.auth.token; the gateway needs a shared session store for its connection table.keda.enabled=true scales the agent-runner on queue depth (KEDA prerequisite).Verify:
kubectl port-forward service/ak-agent-kernel-io 8000:80
curl -s -X POST http://localhost:8000/api/v1/chat \
-H 'Content-Type: application/json' \
-d '{"prompt": "Hello", "session_id": "s1", "agent": "<agent>"}'Teardown: helm uninstall ak (cluster prerequisites like Gateway API CRDs, KEDA, NACK,
and Strimzi are installed per cluster, never by the chart, so they remain).
For AWS async/scalable deployments, build and package separately:
LocalZip, S3Zip, or ImageLocalZip, S3Zip, or Image (typically Image for heavier dependencies)LocalZip, S3Zip, or ImageLocalZip only (WebSocket mode; no package_type field, only package_path)Azure serverless uses a single zip package_path for the Functions deployment.
Azure containerized uses package_path as the Docker build context for Container Apps.
Each Lambda package must include a config.yaml. Most runtime values (execution.mode, queue URLs, table names, max_receive_count, websocket_api.chat_route) are injected automatically by Terraform as environment variables and do not need to be set in config.yaml. Only the values in the table below must be configured manually:
| Config key | Required in | Notes |
|---|---|---|
execution.response_store.type | request-handler, response-handler (queue modes) | redis, valkey, or dynamodb — not injected |
execution.response_store.retry_count | request-handler (rest_sync) | how many times to poll for response |
execution.response_store.delay | request-handler (rest_sync) | seconds between polls |
session.type | agent-runner | redis, valkey, or dynamodb — not injected |
session.redis.prefix | agent-runner (Redis sessions) | key namespace prefix |
session.valkey.prefix | agent-runner (Valkey sessions) | key namespace prefix |
input_queue_visibility_timeout must be >= agent runner Lambda timeout.output_queue_visibility_timeout must be >= response handler Lambda timeout.message_group_id maps to session ID.aws configure)package_type = "Image")az login)gcloud auth application-default login)cd deploy
terraform init
terraform plan
terraform applyKubernetes deployments use helm install / helm upgrade instead; see the
On-Prem / Kubernetes section above.
cd deploy
terraform destroyKubernetes: helm uninstall <release>.
ak-add-capabilities) before production rollout.ak-test) against deployed endpoints.ak-add-integration) for Slack/WhatsApp/Telegram/Teams.ak-build) and re-run terraform apply (or helm upgrade).© yaalalabs, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in ak-py/src/agentkernel/skills/ak-cloud-deploy of yaalalabs/agent-kernel.
Open the folder on GitHubat commit e03a602
Ak Cloud Deploy next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Ak Cloud Deploy this skillyaalalabs/agent-kernel | 191 | — | ~14k | Automated safety check: Pass | Apache-2.0 | |
| Logfire Infrastructurepydantic/skills | 140 | — | ~1.8k | Automated safety check: Pass | MIT | |
| Cloud Devopsdavila7/claude-code-templates | 32k | 4 repos | ~1.4k | Automated safety check: Pass | MIT | |
| Heroku To AWSaws/agent-toolkit-for-aws | 2.8k | — | ~7.2k | Automated safety check: Pass | Apache-2.0 | |
| Agent Bom Scan InfraLeoYeAI/openclaw-master-skills | 2.2k | — | ~1.5k | Automated safety check: Pass | Apache-2.0 | |
| Discover Infrarand/cc-polymath | 181 | — | ~783 | Automated safety check: Pass | MIT |
pydantic/skills
Monitor hosts, Docker containers, Kubernetes clusters, database/queue/cache servers, and cloud-provider metrics with Pydantic Logfire — no application code required.
davila7/claude-code-templates
Cloud infrastructure and DevOps workflow covering AWS, Azure, GCP, Kubernetes, Terraform, CI/CD, monitoring, and cloud-native development.
aws/agent-toolkit-for-aws
Migrate workloads from Heroku to AWS. An agent skill from aws/agent-toolkit-for-aws.
LeoYeAI/openclaw-master-skills
Scan infrastructure-as-code, cloud configurations, and find secrets.
rand/cc-polymath
Automatically discover cloud, infrastructure, deployment, and container skills when working with AWS, GCP, Azure, Docker, Kubernetes, Terraform, Netlify, Heroku, serverless, or IaC
alirezarezvani/claude-skills
Design AWS architectures for startups using serverless patterns and IaC templates.
yaalalabs/agent-kernel
Code quality standards, formatting, Python style rules (classes over script-style functions, configuration-field rules), commit conventions, and PR workflow for Agent Kernel development.
yaalalabs/agent-kernel
Step-by-step guide for adding a new built-in test evaluator provider to Agent Kernel (beyond DeepEval, Opik and JEV).
yaalalabs/agent-kernel
Step-by-step guide for adding a new guardrail provider to Agent Kernel.
yaalalabs/agent-kernel
Step-by-step guide for adding a new knowledge base backend to Agent Kernel.
yaalalabs/agent-kernel
Step-by-step guide for adding a new messaging platform integration to Agent Kernel.
yaalalabs/agent-kernel
Step-by-step guide for adding a new multimodal attachment storage backend to Agent Kernel.
Categories
Deploy an Agent Kernel project to AWS, Azure, or GCP using Terraform modules, or to any Kubernetes cluster (on-prem, baremetal, EKS) using the official Helm chart. Ak Cloud Deploy is an agent skill from yaalalabs/agent-kernel. Deploy an Agent Kernel project to AWS, Azure, or GCP using Terraform modules, or to any Kubernetes cluster (on-prem, baremetal, EKS) using the official Helm chart.
Ak Cloud Deploy fits situations like: tasks that involve Container orchestration; tasks that involve Serverless; tasks that involve Infrastructure as code.
Run `npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a claude-code`. Or copy the skill folder (ak-py/src/agentkernel/skills/ak-cloud-deploy in yaalalabs/agent-kernel) into .claude/skills/ak-cloud-deploy in your project. Claude Code loads it when a task matches its description.
Run `npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a codex`. Or copy the skill folder (ak-py/src/agentkernel/skills/ak-cloud-deploy in yaalalabs/agent-kernel) into .agents/skills/ak-cloud-deploy in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add yaalalabs/agent-kernel --skill ak-cloud-deploy -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ak-cloud-deploy, .gemini/skills/ak-cloud-deploy, .github/skills/ak-cloud-deploy and .opencode/skills/ak-cloud-deploy in your project.
Going by SKILL.md and its folder, Ak Cloud Deploy needs the command-line tools its instructions call (helm, terraform, aws, kubectl, curl and az) and credentials named OPENAI_API_KEY, JWT_SECRET and SOME_OTHER_KEY. Our summary lists: Docker.
SKILL.md names 1 domain. As links in the text: valkey.io. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Ak Cloud Deploy is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 14k tokens (SKILL.md is roughly 54k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Ak Cloud Deploy: Logfire Infrastructure (pydantic/skills, 140 stars), Cloud Devops (davila7/claude-code-templates, 32k stars), Heroku To AWS (aws/agent-toolkit-for-aws, 2.8k stars) and Agent Bom Scan Infra (LeoYeAI/openclaw-master-skills, 2.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
yaalalabs (a GitHub organization) maintains it in yaalalabs/agent-kernel, which has 191 GitHub stars. The repository holds 23 skills in this directory. The repository was last updated on October 8, 2026.
Source: yaalalabs/agent-kernel on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.