Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions examples/gymcoach/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# GymCoach example environment
#
# Copy to .env and fill in the values. Only GROQ_API_KEY is strictly required
# for `agentspec validate`, `health`, and `generate` to succeed in local dev.
# Everything else is consumed by the generated runtime (LangGraph, memory,
# observability) once you run the agent.
#
# Note on format: docker compose reads this file and does NOT support inline
# comments after a value, so all comments here live on their own line.

# ── Model ─────────────────────────────────────────────────────────────────────

# Required. Free tier available at https://console.groq.com
GROQ_API_KEY=

# Optional. Fallback model on Groq rate-limit, timeout, or 5xx.
AZURE_OPENAI_API_KEY=

# ── Memory (started by docker-compose.yml in this directory) ──────────────────

DATABASE_URL=postgresql://gymcoach:gymcoach@localhost:5432/gymcoach
REDIS_URL=redis://localhost:6379/0

# ── Domain config ─────────────────────────────────────────────────────────────

# metric or imperial
UNIT_SYSTEM=metric

# ── Auth (optional for local dev) ─────────────────────────────────────────────

# JWT verification endpoint for the generated REST API.
JWKS_URI=

# ── Observability (optional) ──────────────────────────────────────────────────

LANGFUSE_HOST=https://cloud.langfuse.com
LANGFUSE_PUBLIC_KEY=

# Resolves the manifest reference "secret:langfuse-secret-key".
LANGFUSE_SECRET_KEY=

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
130 changes: 130 additions & 0 deletions examples/gymcoach/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# GymCoach — a production-grade AgentSpec example

A fitness-coaching agent that exercises the full AgentSpec feature surface in one manifest: fallback model, dual-tier memory, sub-agents, input/output guardrails, evaluation datasets with a CI gate, and Langfuse tracing. Use it as a reference when you are migrating a real agent to `agent.yaml`, or as a starting point to copy and prune.

## Architecture at a glance

| Concern | Configured as |
|----------------|-------------------------------------------------------------------------------|
| Model | Groq `llama-3.3-70b-versatile`, with Azure `gpt-4` fallback on rate-limit/5xx |
| Prompts | System prompt from `prompts/system_prompt.txt`, hot-reloadable |
| Tools | 10 function tools (log workout, plan, progress, nutrition, search, ...) |
| MCP | `postgres-db` stdio server |
| Memory | Redis short-term (3600s TTL, 20 turns) + Postgres long-term (90-day TTL) |
| Sub-agents | `observer` (parallel) + `reflector` (sequential), manifests under `agents/` |
| Guardrails | topic-filter, PII detector, prompt-injection in; hallucination/toxicity out |
| Evaluation | deepeval + 2 JSONL datasets (`eval/*.jsonl`), CI gate enabled |
| Observability | Langfuse tracing + OpenTelemetry metrics |
| Compliance | OWASP LLM Top 10 + memory hygiene packs |
| API | REST on port 8000, JWT auth, 60 req/min, streaming enabled |

Full definition: [`agent.yaml`](./agent.yaml) (280 lines).

## Prerequisites

- [`pnpm`](https://pnpm.io) 9+ and Node 20+ (to run the `agentspec` CLI from this monorepo)
- [Docker](https://docs.docker.com/get-docker/) (for the Postgres + Redis services used by memory health checks)
- A free Groq API key from <https://console.groq.com> (the only env var the example strictly requires)

Optional:
- Azure OpenAI key for fallback (`AZURE_OPENAI_API_KEY`)
- Langfuse account for tracing (`LANGFUSE_*`)

## Quick start

From the repo root, after `pnpm install && pnpm -r build`:

```bash
cd examples/gymcoach
cp .env.example .env # fill in GROQ_API_KEY at minimum

# Export every line of .env into the current shell. The agentspec CLI reads
# variables from the process environment, not the .env file itself.
set -a; . ./.env; set +a

# 1. Start the declared services (Postgres + Redis) in the background
docker compose up -d
docker compose ps # wait until both services report "healthy"

# 2. Schema-check the manifest (no I/O)
agentspec validate agent.yaml

# 3. Runtime health — probes the live Groq endpoint + sub-agent manifests
agentspec health agent.yaml

# 4. Generate runnable LangGraph code from the manifest
agentspec generate agent.yaml --framework langgraph --output ./agent
```

`agentspec generate` writes `agent.py`, `requirements.txt`, `.env.example`, and a `guardrails.py` module into `./agent/`. Install the Python deps and run it per the generated README:

```bash
cd agent
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python agent.py
```

When you are done, tear the services down:

```bash
docker compose down -v
```

## What the manifest references

### Required env vars

| Variable | Used for | Required? |
|-------------------|--------------------------------------------|----------------------------------|
| `GROQ_API_KEY` | Primary model | yes |
| `DATABASE_URL` | Long-term memory (Postgres) | yes (docker-compose default set) |
| `REDIS_URL` | Short-term memory | yes (docker-compose default set) |
| `UNIT_SYSTEM` | Injected into the system prompt template | yes (`metric` or `imperial`) |

### Optional env vars

| Variable | Used for |
|--------------------------------|--------------------------------------------------|
| `AZURE_OPENAI_API_KEY` | Fallback model on Groq rate-limit / 5xx |
| `JWKS_URI` | JWT verification on the generated REST API |
| `LANGFUSE_HOST` | Langfuse tracing endpoint |
| `LANGFUSE_PUBLIC_KEY` | Langfuse public key |
| `LANGFUSE_SECRET_KEY` | Resolves `$secret:langfuse-secret-key` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry metrics exporter |

See [`.env.example`](./.env.example) for the full list with local-dev defaults.

### Required services

`docker compose up -d` in this directory starts:

- **Postgres 16** on `127.0.0.1:5432` (user/password/db all `gymcoach`) for long-term memory
- **Redis 7** on `127.0.0.1:6379` for short-term memory

Both have healthchecks, so `docker compose ps` will report `healthy` once they are ready to serve.

## What is in this directory

| Path | Purpose |
|-----------------------------------|----------------------------------------------------------------------|
| [`agent.yaml`](./agent.yaml) | Top-level manifest consumed by the CLI, SDK, and operator |
| [`prompts/system_prompt.txt`](./prompts/system_prompt.txt) | System prompt with `{{ unit_system }}` + `{{ current_date }}` placeholders |
| [`tools/tool_implementations.py`](./tools/tool_implementations.py) | 10 Python tool stubs referenced by `spec.tools[].module` |
| [`agents/observer.yaml`](./agents/observer.yaml) | Parallel sub-agent that flags injury risk and scope drift |
| [`agents/reflector.yaml`](./agents/reflector.yaml) | Sequential sub-agent that writes a post-session retrospective |
| [`eval/workout-qa.jsonl`](./eval/workout-qa.jsonl) | Eval dataset for general workout Q&A |
| [`eval/exercise-advice.jsonl`](./eval/exercise-advice.jsonl) | Eval dataset for exercise-specific advice |
| [`docker-compose.yml`](./docker-compose.yml) | Local Postgres + Redis for memory and health checks |
| [`.env.example`](./.env.example) | Required and optional env vars |

## A note on the tool stubs

[`tools/tool_implementations.py`](./tools/tool_implementations.py) contains **stub functions** that return canned data. They exist so the manifest resolves `$file:tools/tool_implementations.py` to a real symbol and so `agentspec generate` has something to wire up. Replace each function with real database queries, HTTP calls, or domain logic before deploying the generated agent.

## See also

- [Migration guide: GymCoach](../../docs/guides/migrate-gymcoach.md) — walks through how this manifest was derived from an existing FastAPI + Groq codebase.
- [Concept: the manifest](../../docs/concepts/manifest.md) — full schema reference.
- [Concept: health checks](../../docs/concepts/health-checks.md) — how `agentspec health` probes each declared dependency.
- [Concept: compliance](../../docs/concepts/compliance.md) — how `agentspec audit` scores this manifest against OWASP LLM Top 10 and memory hygiene rules.
31 changes: 31 additions & 0 deletions examples/gymcoach/agents/observer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: agentspec.io/v1
kind: AgentSpec

metadata:
name: gymcoach-observer
version: 1.0.0
description: "Sub-agent that monitors GymCoach sessions in parallel and flags anomalies (injury risk, overtraining, bad form)."
tags: [fitness, monitoring, sub-agent]
author: "AgentBoot"
license: MIT

spec:
model:
provider: groq
id: llama-3.3-70b-versatile
apiKey: $env:GROQ_API_KEY
parameters:
temperature: 0.1
maxTokens: 200

prompts:
system: |
You are the GymCoach observer. You receive each assistant turn in parallel
and must flag any of the following in a single concise line, or return "ok"
when nothing applies:
- injury-risk cues (sharp pain, numbness, joint locking)
- overtraining signs (no rest days, declining performance)
- unsafe form recommendations
- scope drift (topics outside fitness, training, exercise, nutrition)

tools: []
31 changes: 31 additions & 0 deletions examples/gymcoach/agents/reflector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: agentspec.io/v1
kind: AgentSpec

metadata:
name: gymcoach-reflector
version: 1.0.0
description: "Sub-agent that runs after a GymCoach session and writes a short retrospective to long-term memory."
tags: [fitness, reflection, sub-agent]
author: "AgentBoot"
license: MIT

spec:
model:
provider: groq
id: llama-3.3-70b-versatile
apiKey: $env:GROQ_API_KEY
parameters:
temperature: 0.2
maxTokens: 400

prompts:
system: |
You are the GymCoach reflector. You run sequentially after each user
session ends. Read the full conversation and produce a 3-5 bullet
retrospective covering:
- what the user asked for
- what the coach recommended
- anything the coach should remember next time (preferences, constraints, progress)
Keep each bullet under 20 words. Return only the bullets.

tools: []
47 changes: 47 additions & 0 deletions examples/gymcoach/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Local services for the GymCoach example.
#
# Starts the Postgres + Redis dependencies declared in agent.yaml so you can
# run `agentspec health agent.yaml` and any generated LangGraph code locally
# without setting up databases by hand.
#
# docker compose up -d
# docker compose ps # wait until both services are "healthy"
# docker compose down -v # tear down + remove volumes

services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-gymcoach}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-gymcoach}
POSTGRES_DB: ${POSTGRES_DB:-gymcoach}
ports:
- "127.0.0.1:5432:5432"
volumes:
- gymcoach_postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-gymcoach} -d ${POSTGRES_DB:-gymcoach}"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
restart: unless-stopped

redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
ports:
- "127.0.0.1:6379:6379"
volumes:
- gymcoach_redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
start_period: 3s
restart: unless-stopped

volumes:
gymcoach_postgres:
gymcoach_redis:
Loading