Skip to main content

Agents

An agent is the central deployable unit in Vystak. An agent definition declares which model to use, what tools the agent has, how it persists state, and what backing services it needs. Channels (Slack, chat, Discord, webhook) are top-level deployables that route to one or more agents — they're not declared on the agent itself.

This page walks through the full agent schema using a working example.

A complete agent​

Here's a chatbot with persistent conversation memory backed by Postgres:

name: sessions-agent
instructions: |
You are a helpful assistant with persistent memory of our conversation.
If the user has told you something before, remember it.
Refer back to earlier parts of the conversation when relevant.
default_model:
name: minimax
provider:
name: anthropic
type: anthropic
model_name: MiniMax-M2.7
parameters:
temperature: 0.7
anthropic_api_url: https://api.minimax.io/anthropic
platform:
name: docker
type: docker
provider:
name: docker
type: docker
sessions:
type: postgres
provider:
name: docker
type: docker
secrets:
- name: ANTHROPIC_API_KEY
port: 8091

Run it with vystak apply and Vystak will:

  1. Provision a Postgres container.
  2. Build the agent image with the Postgres checkpointer wired up.
  3. Run the agent container on the shared vystak-net Docker network, exposing the A2A endpoint on port.

Conversations persist across restarts. Send the same session_id and the agent picks up where it left off.

Required fields​

The minimum agent has just two fields — name and default_model:

name: bare-bot
default_model:
name: claude
provider:
name: anthropic
type: anthropic
model_name: claude-sonnet-4-20250514
  • name — used as the container/app name and as the OpenAI-compatible model ID
  • default_model — which LLM to call. See Models

Everything else is optional. Every agent automatically exposes an A2A (agent-to-agent) JSON-RPC endpoint on its HTTP port; no channel declaration is required to make the agent reachable in-cluster.

Model pool​

An agent can carry additional models beyond the default in models:. Names must be unique across default_model and models. Callers can pick a model per turn (and the choice is pinned to the session), which is how heartbeats and cost-tiered routing select cheaper or stronger models:

default_model:
name: sonnet
provider: {name: anthropic, type: anthropic}
model_name: claude-sonnet-4-20250514
models:
- name: haiku
provider: {name: anthropic, type: anthropic}
model_name: claude-haiku-4-5-20251001

Adding skills​

A skill is a named capability. It comes in two forms:

  • a folder skill — packaged instructions in skills/<name>/SKILL.md next to your vystak.yaml, loaded by the agent on demand
  • an inline skill — a named bundle of tools (Python functions the agent can call), optionally with a short prompt
skills:
- research # folder skill: skills/research/SKILL.md
- name: ops # inline skill: tool bundle
tools:
- lookup_order
- process_refund
prompt: Always verify the order before processing refunds.

Folder skills​

A folder skill lives at skills/<name>/SKILL.md with YAML frontmatter:

---
name: research
description: Product-research workflow — how to compare and cite sources.
tools: [web_search] # optional, resolved from tools/
---
When asked to research a topic, follow this process...

description is required — it is the only thing the agent sees before deciding to use the skill. The folder can hold extra resource files (reference docs, templates) alongside SKILL.md.

At runtime the agent gets progressive disclosure: its system prompt lists each skill's name and description, and two auto-provided tools — load_skill(name) and read_skill_file(skill, path) — fetch the full instructions and resource files only when needed. Editing any file in the skill folder changes the agent's content hash, so vystak plan shows a redeploy.

Inline skills and tools​

Tools are Python functions that live in a tools/ directory next to your vystak.yaml; each tool name maps to tools/<name>.py exporting a function of the same name.

# tools/lookup_order.py
from langchain_core.tools import tool

@tool
def lookup_order(order_id: str) -> str:
"""Look up an order by ID."""
return f"Order {order_id}: shipped"

An inline skill's prompt field is appended to the agent's system prompt.

See examples/docker-skills/ for a working project using both forms.

Adding sessions (conversation memory)​

Vystak supports three session backends:

EngineWhen to use
(none — default)Stateless agents; in-memory state lost on restart
sqliteSingle-instance agents that need persistence; backed by a Docker volume
postgresProduction; multi-instance and survives container replacement
sessions:
type: postgres
provider:
name: docker
type: docker

The Docker provider auto-provisions a Postgres container the first time. Connection string is injected into the agent as SESSION_STORE_URL.

To bring your own Postgres (e.g., a managed instance) instead of letting Vystak provision one:

sessions:
type: postgres
connection_string_env: DATABASE_URL

The agent then reads DATABASE_URL from its environment.

Adding long-term memory​

Sessions remember a single conversation. Memory persists facts across all conversations for a given user:

memory:
type: postgres
provider:
name: docker
type: docker

When memory is set, the generated agent gets two extra tools: save_memory and forget_memory. The agent learns to use them based on context (you can also nudge it via instructions).

Adding services​

Use services for any other backing infrastructure:

services:
- name: cache
type: redis
provider:
name: docker
type: docker
- name: vectors
type: qdrant
provider:
name: docker
type: docker

Each service gets a connection string in the agent's environment (<NAME>_URL).

See Services for the full list of supported types.

Adding a workspace​

When an agent needs file I/O, shell access, or git operations, declare a workspace — a standalone container the agent drives over SSH (JSON-RPC via an SSH subsystem), keeping tool execution isolated from the agent process:

workspace:
name: dev
image: python:3.12-slim
provision:
- apt-get update && apt-get install -y git ripgrep
persistence: volume
skills:
- name: editing
tools: [fs.readFile, fs.writeFile, fs.edit, exec.run, git.status]

Built-in fs.*, exec.*, git.*, and search_project tools become available — see Workspaces. Workspaces run on Docker and Azure Container Apps; on ACA, persistence: volume is backed by Azure Files (bind is rejected — there's no host filesystem).

Adding subagents​

An agent can declare subagents — full agent definitions it can delegate to. The parent gets a call tool per subagent, and calls flow over the same A2A transport as any agent-to-agent traffic:

agents:
- name: orchestrator
default_model: sonnet
platform: local
subagents: [researcher, writer]
- name: researcher
default_model: sonnet
platform: local
- name: writer
default_model: sonnet
platform: local

Subagent names must be unique and an agent cannot reference itself. See Multi-agent for routing, session continuity, and restrictive-routing patterns.

Exposing the agent through a channel​

Channels are top-level deployables that route inbound traffic to one or more agents. They are siblings of agents in the multi-document layout, not nested fields. Here's a Slack channel routing to a single agent:

providers:
docker: {type: docker}
anthropic: {type: anthropic}

platforms:
local: {type: docker, provider: docker}

models:
sonnet:
provider: anthropic
model_name: claude-sonnet-4-20250514

agents:
- name: support-bot
instructions: You are a customer support agent.
default_model: sonnet
platform: local
secrets:
- {name: ANTHROPIC_API_KEY}

channels:
- name: slack-main
type: slack
platform: local
secrets:
- {name: SLACK_BOT_TOKEN}
- {name: SLACK_APP_TOKEN}
agents: [support-bot]

A channel with multiple agents: enables self-serve routing — the channel's runtime resolves which agent answers per Slack channel, with optional default_agent, channel_overrides, and group_policy/dm_policy gates.

See Channels overview for the full list of channel types (slack, chat, discord, api, webhook, voice, cron, widget).

Multiline instructions​

The instructions field is the agent's system prompt. You can use multiline strings, template variables, and reference per-skill prompts that get appended automatically.

instructions: |
You are a customer support agent for ACME Corp.
Be concise and friendly.
When handling refunds, follow the company refund policy.

Python definition​

YAML is the simple on-ramp. For programmatic agents, define them in Python in vystak.py:

import vystak

anthropic = vystak.Provider(name="anthropic", type="anthropic")
docker = vystak.Provider(name="docker", type="docker")

model = vystak.Model(
name="claude",
provider=anthropic,
model_name="claude-sonnet-4-20250514",
)

agent = vystak.Agent(
name="support-bot",
instructions="You are a helpful support agent.",
default_model=model,
platform=vystak.Platform(name="docker", type="docker", provider=docker),
sessions=vystak.Postgres(provider=docker),
skills=[vystak.Skill(name="support", tools=["lookup_order", "process_refund"])],
)

Vystak picks up vystak.yaml, vystak.yml, or vystak.py automatically, and collects every module-level Agent in the Python form — so one file can define a whole fleet. The Python form earns you loops, conditionals, type checking, and reusable agent factories.

Hash-based change detection​

Vystak content-hashes your agent definition. vystak apply compares the new hash to the deployed hash and skips deploys that wouldn't change anything. To force a redeploy:

vystak apply --force

What's next​

  • Workspaces — standalone execution environments and built-in fs.*/exec.*/git.* tools
  • Multi-agent — subagents, delegation, and routing
  • Models — supported model providers and parameters
  • Services — backing infrastructure types
  • Channels — Slack, chat, Discord, and more
  • Transport — east-west messaging (HTTP, NATS) for A2A calls
  • Examples — agents from minimal to multi-agent collaboration
  • Deploying to Docker — how vystak apply works under the hood