To deploy a Python AI agent without Kubernetes, keep the agent definition, Python tools, and release tests in a repository, then run that project on a managed agent runtime. The runtime owns packaging, execution, scaling, deployment, and traces. Your team still owns the agent's behavior and business logic.
The example below is deliberately complete and small. It uses one current EU-hosted connic/* model and one pure-Python tool, so it needs no model-provider account, external API, Dockerfile, Helm chart, or cluster. You can copy the files as written and then replace the sample delivery table with your own service.
What does “without Kubernetes” actually mean?
Your application team delegates the container orchestration layer to a managed runtime. Infrastructure still exists; the platform operator runs it. The team deploys an agent Project instead of managing the underlying containers.
Kubernetes is a capable general-purpose orchestrator. Its official documentation explains how you reference a built container image, then use a Deployment to manage Pods and rollouts. That is useful infrastructure, but it stops below the agent-specific layer: prompts, model calls, tool traces, evaluation, and release assertions are still yours to assemble. Review how Kubernetes describes preparing and referencing container images and managing application rollouts with Deployments.
| Production concern | Kubernetes-based service | Managed agent runtime |
|---|---|---|
| Packaging | Build, scan, tag, and publish an image | Build the repository into the managed runtime |
| Execution | Choose and configure a workload object | Run the named agent from its YAML definition |
| Scaling | Configure HPA and the required metrics path | Set concurrency controls; runtime scales execution |
| Secrets | Configure Secrets, encryption, and access policy | Set environment-scoped variables in the Project |
| Release quality | Add agent tests to your CI/CD pipeline | Run declarative tests as the deploy gate |
| Debugging | Export logs and add agent-aware telemetry | Inspect inputs, outputs, model calls, tools, and cost |
Kubernetes horizontal autoscaling depends on resource, custom, or external metrics; queue depth or agent latency normally requires an explicit metrics path. Kubernetes Secrets are stored unencrypted in etcd by default unless you enable protection. Durable log storage and querying also require a cluster-level backend because Kubernetes does not include one. Check the official guidance for configuring Horizontal Pod Autoscaling, hardening Kubernetes Secrets, and adding cluster-level logging. A managed Kubernetes service can remove control-plane work, but these application-layer choices do not disappear.
The complete Python agent project
This project answers delivery questions from a small, deterministic data table. The model must call the tool before it quotes a delivery window, and the test suite verifies that the tool call remains in the execution path.
delivery-agent/
├── agents/
│ └── delivery-assistant.yaml
├── tools/
│ └── shipping.py
├── tests/
│ └── delivery-assistant.yaml
└── .gitignore1. Scaffold the repository
The Composer SDK requires Python 3.10 or newer. Install the package and create a project directory:
python -m pip install connic-composer-sdk
connic init delivery-agent
cd delivery-agentCreate a Connic Project in the dashboard, select an EU deployment region, and authenticate the local directory with connic login. That command writes Project credentials to .connic. Keep the file out of Git:
.connic
.venv/
__pycache__/2. Define the agent in YAML
The YAML is the operational contract for the agent: model, prompt, callable tools, and guardrails stay visible in code review. This example uses the currentconnic/glm-5.2 model, which needs no separate provider key.
version: "1.0"
name: delivery-assistant
type: llm
model: connic/glm-5.2
description: "Answer delivery-window questions with verified data"
system_prompt: |
You answer questions about delivery times.
Always call shipping.estimate_delivery before giving an estimate.
If the user does not name a country, ask for one without calling the tool.
Pass an uppercase ISO country code and either standard or express.
If the tool reports available false, say: "This route is unavailable."
Never invent a delivery window.
tools:
- shipping.estimate_delivery
guardrails:
input:
- type: prompt_injection
mode: block
- type: pii
mode: redact
output:
- type: moderation
mode: block
- type: system_prompt_leakage
mode: blockThe four guardrails are a practical production baseline: block prompt injection, redact input PII, moderate output, and block system-prompt leakage. Adjust them to the application rather than treating any default as a complete safety policy. You can review every guardrail mode and execution rule before shipping sensitive workflows.
3. Write the tool as plain Python
A custom tool is an ordinary function under tools/. Type hints define the input schema and the docstring tells the model when the function is useful. Connic discovers the function directly from the file.
DELIVERY_WINDOWS = {
("DE", "standard"): (2, 4),
("DE", "express"): (1, 2),
("FR", "standard"): (3, 5),
("FR", "express"): (1, 3),
("NL", "standard"): (2, 4),
("NL", "express"): (1, 2),
}
def estimate_delivery(country_code: str, service: str = "standard") -> dict:
"""Return the delivery window for a supported country and service."""
country = country_code.upper()
normalized_service = service.lower()
window = DELIVERY_WINDOWS.get((country, normalized_service))
if window is None:
return {
"available": False,
"country_code": country,
"service": normalized_service,
}
minimum_days, maximum_days = window
return {
"available": True,
"country_code": country,
"service": normalized_service,
"minimum_business_days": minimum_days,
"maximum_business_days": maximum_days,
}This implementation has no network dependency, which makes the tutorial reproducible. In a real Project, replace the dictionary lookup with your API or database call and read credentials from environment variables. If the service is private, use Connic Bridge instead of opening an inbound firewall rule. Follow the documentation to implement and expose Python tools or connect the runtime to private services.
4. Add a release-gating test
This agent could return a plausible number after checking the wrong route. The suite asserts that the run completes and that shipping.estimate_delivery appears in the trace with the requested country and service. It also checks the quoted window, the unavailable-route response, and the no-country branch.
version: "1.0"
defaults:
runs: 1
timeout_s: 60
tests:
- name: estimates_standard_delivery_to_germany
payload: "How many business days does standard delivery to DE take?"
expected_result: status == "completed" and "2" in output and "4" in output
expected_tool_calls:
- shipping.estimate_delivery: params.country_code == "DE" and params.service == "standard" and invocations == 1
- name: checks_an_unsupported_route
payload: "Can you ship express to the US?"
expected_result: status == "completed" and "route is unavailable" in output
expected_tool_calls:
- shipping.estimate_delivery: params.country_code == "US" and params.service == "express" and invocations == 1
- name: asks_for_a_country_before_lookup
payload: "How long does standard delivery take?"
expected_result: status == "completed"
expected_no_tool_calls:
- shipping.estimate_deliveryStart with one run per case so the deployment gate stays fast. For behavior that is genuinely stochastic, increase runs and set a success threshold below 100. The test reference shows how to configure repeated runs and tool-call assertions.
5. Lint and run in the managed development runner
These commands validate file discovery and start an isolated cloud development runner that uses the same image as production:
connic lint
connic devInside the interactive dev session, press t to run the synced suites against that active environment and q to exit. Then run connic test for a fresh ad-hoc test run against the configured environment. You can review the managed development loop before connecting Git.
shipping.get_quote, then run connic test --filter estimates_standard_delivery_to_germany. The case should fail because the trace contains shipping.estimate_delivery, not the invented tool. Restore the assertion and rerun the suite before you deploy.6. Deploy through Git
Connect the repository to the Connic Project and map a branch to the target environment. A push to that branch starts the managed Build → Tests → Deploy pipeline. The built image is tested before release, and any failed case stops promotion. Git-connected Projects deploy through this push flow; a Project that is not connected to Git can use connic deploy.
git add agents tools tests .gitignore
git commit -m "Add delivery assistant"
git push origin mainThe same repository can map different branches to development, staging, and production environments, each with isolated configuration and run history. Read how to connect branches and deploy a Project and inspect the deployment test gate.
See how YAML, Python, Git deployment, EU-hosted models, observability, approvals, and governance fit together beyond this example.
Explore the code-first EU platformWhat the managed runtime now owns
A container log can tell you that a process wrote an error. An agent trace can show the input, the model step that selected a tool, the exact tool arguments and result, and the final response. That is the useful debugging boundary for agent behavior. See exactly what is captured when you inspect runs and hierarchical traces.
What does this example cost to run?
Separate platform execution from model tokens when you estimate cost. At the published rates checked on August 12, 2026, 1,000 monthly runs averaging 15 seconds produce this platform subtotal:
| Line item | Calculation | Monthly |
|---|---|---|
| Runs | 1,000 × €0.047 | €47.00 |
| Compute | 15,000 seconds × €0.00042 | €6.30 |
| Platform subtotal | Before model, storage, and Retrieval usage | €53.30 |
The table is arithmetic only. Actual duration and token use depend on the prompt, model, tool calls, and traffic. Check the latest numbers before budgeting. You can review current Project pricing and usage rates and verify current managed-model IDs and token rates.
Where does the data run?
This sample keeps the Connic-operated path in the EU: select an EU Project region, use a connic/* model that runs on EU inference capacity, and execute the pure-Python tool in the managed runtime. That boundary changes when you add an external component. A BYOK provider, custom API, judge, guardrail, or result destination can send data elsewhere if you configure it to do so.
Residency follows the complete request path. The model, tools, storage, traces, and outbound destinations all affect it. Use the detailed boundary to verify which parts of a code-first Project stay in the EU.
When should you still choose Kubernetes?
Use Kubernetes when direct infrastructure control is a product or policy requirement. It remains a strong fit when a platform team already operates a mature cluster, the workload needs custom scheduling or network primitives, policy requires execution inside infrastructure you directly control, or a measured high-volume workload has a better self-hosted total cost.
Choose a managed agent runtime when the differentiating work is the agent: its tools, behavior, integrations, quality, and user experience. The trade is less infrastructure control in exchange for an agent-native release and operations layer. If you already have a framework prototype, you can also see what changes when moving LangChain or ADK code into production.
Methodology and disclosure
Connic publishes this tutorial and is the managed runtime used in the example. Kubernetes claims were checked against the official Kubernetes documentation; Connic syntax, behavior, and pricing were checked against current product code and documentation on August 12, 2026. No performance comparison was run, and Kubernetes can run AI agents. The cost example excludes model tokens, storage, Retrieval, engineering labor, and any external service fees.