1 unstable release
| 0.1.10 | May 28, 2026 |
|---|
#416 in Artificial intelligence
Used in 9 crates
(8 directly)
180KB
4.5K
SLoC
AgentForge
One file in. A better agent out.
AgentForge is a self-improving AI agent optimization platform written in Rust. Feed it a single agent file — a declarative spec describing your AI agent's system prompt, tools, output schemas, and behavioral constraints — and it autonomously generates test scenarios, runs the agent, scores every execution trace, and iterates on the specification until it converges on a measurably better version.
Table of Contents
- Overview
- Architecture
- Project Structure
- Quick Start
- LLM Providers
- Agent File Format
- CLI Usage
- REST API
- Scoring Dimensions
- Promotion Gatekeeper
- Multi-Agent Testing
- Benchmark Suites
- Configuration
- Running Tests
- GitHub Actions Marketplace
- CI/CD Integration
- Contributing
- Roadmap
Overview
AI agent development has a painful quality gap: teams ship prompts and tool definitions with little systematic testing, and improvements are made based on anecdote rather than measurement. AgentForge removes the manual burden by orchestrating a fully automated improvement loop:
parse → generate tests → run → trace → score → optimize → gate → promote
Humans set the quality bar. The platform handles the repetitive evaluation and iteration work.
Core Features (MVP)
| Feature | Description |
|---|---|
| F-01 Agent Loader | Parses YAML/JSON/Markdown/Copilot .agent.md agent files, validates against schema, SHA-based version store |
| F-02 Scenario Generator | Generates N test scenarios via schema-derived, adversarial, and domain-seeded strategies |
| F-03 Agent Runner | Parallel execution with full trace capture, retry logic, and token usage tracking |
| F-04 Trace Scorer | Six-dimension weighted scoring via deterministic assertions + LLM-as-judge |
| F-05 Optimizer | Iterative self-improvement loop: generates candidate variants, quick-evals on a 25-scenario subset, saves best; terminates on converged / no_improvement / max_iterations / failed |
| F-06 Gatekeeper | Three-gate promotion logic: score gate + regression gate + stability gate |
| F-07 REST API | Axum-based API with endpoints for agents, runs, diffs, and results; GET /agents?name= filter for version history |
| F-08 CLI | agentforge run, diff, promote commands with GitHub Actions support |
| F-09 Web Dashboard | React UI: agents list grouped by name with version count badge; agent detail page with Version History card; LCS-based unified diff viewer |
Architecture
┌──────────────────────────────────────────────────────────────────────┐
│ AGENTFORGE PLATFORM │
│ │
│ INPUT: Agent File (YAML / JSON / MD) │
│ system_prompt · tools[] · output_schema │
│ constraints[] · model · sampling_config │
│ │ │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ F-01: AGENT LOADER │ │
│ │ Parser → Schema Validator │ │
│ │ → Version Store (SHA-based) │ │
│ └────────────────┬───────────────┘ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ F-02: SCENARIO GENERATOR │ │
│ │ Schema-derived (50%) │ │
│ │ Adversarial (30%) │ │
│ │ Domain-seeded (20%) │ │
│ └────────────────┬──────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ F-03: AGENT RUNNER (parallel workers, full trace capture) │ │
│ └────────────────────────────────┬───────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ F-04: TRACE ANALYZER & SCORER │ │
│ │ Deterministic assertions + LLM-as-judge │ │
│ │ Weighted aggregate score + Failure cluster report │ │
│ └─────────────────────────────────┬──────────────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ F-05: OPTIMIZER │ │
│ │ Prompt rewrite · Tool desc rewrite │ │
│ │ Schema tighten · Example inject │ │
│ │ → 5–20 Candidate Variants │ │
│ └──────────────────────┬───────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ F-06: PROMOTION GATEKEEPER │ │
│ │ Score Gate (+3% over champion) │ │
│ │ Regression Gate (≥99% pass) │ │
│ │ Stability Gate (3 seeds) │ │
│ └─────────────────────┬───────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ PROMOTED AGENT FILE │ │
│ │ (versioned, diffed, changelog) │ │
│ └───────────────────────────────────────┘ │
│ │
│ F-07: REST API │ F-08: CLI / GitHub Actions │
└──────────────────────────────────────────────────────────────────────┘
Project Structure
This is a Cargo workspace with 16 crates:
agentforge/
├── Cargo.toml # Workspace root
├── Cargo.lock
├── docker-compose.yml # PostgreSQL for local dev
├── Dockerfile
├── .env.example # Environment variable template
├── migrations/ # SQLx database migrations
│ ├── 001_agent_versions.sql
│ ├── 002_eval_runs.sql
│ ├── 003_scenarios.sql
│ ├── 004_traces.sql
│ ├── 005_shadow_runs.sql
│ ├── 006_finetune_exports.sql
│ ├── 007_benchmarks.sql
│ ├── 008_trace_cost.sql
│ ├── 009_failure_cluster_api_error.sql
│ └── 010_opt_tracking.sql
├── fixtures/
│ ├── customer-support-agent.yaml # Native YAML example
│ └── agentforge-evaluator.agent.md # Copilot .agent.md example
└── crates/
├── agentforge-core/ # Shared types, errors, traits (AgentFile, EvalRun, Trace, Scenario…)
├── agentforge-parser/ # Agent file parsing (YAML, JSON, Markdown/Copilot frontmatter)
├── agentforge-scenarios/ # Scenario generation (schema-derived, adversarial, domain-seeded)
├── agentforge-runner/ # Parallel agent execution + full trace capture
├── agentforge-scorer/ # Deterministic assertions + LLM-as-judge scoring
├── agentforge-optimizer/ # Variant generation + self-improvement loop
├── agentforge-gatekeeper/ # Three-gate promotion logic
├── agentforge-db/ # PostgreSQL repository layer (SQLx)
├── agentforge-api/ # REST API (Axum 0.8)
├── agentforge-cli/ # CLI binary (Clap 4)
├── agentforge-benchmarks/ # Standard benchmark comparison suite
├── agentforge-finetune/ # Fine-tune dataset exporter (JSONL)
├── agentforge-multiagent/ # Multi-agent composition testing
├── agentforge-observability/ # OTLP trace export hooks
├── agentforge-online-eval/ # Shadow-mode real-traffic comparison
└── agentforge-redteam/ # Adversarial safety red-team probes
Quick Start
Prerequisites
- Rust 1.83+ (install via rustup)
- Docker + Docker Compose
- One of: OpenAI API key, Anthropic API key, NVIDIA NIM API key, or Ollama running locally (no key required)
- Node.js 20+ and npm (only required if developing the
web/dashboard locally; production deployment uses the pre-built Docker image)
1. Clone and start infrastructure
git clone https://github.com/bhavinkotak/agentforge.git
cd agentforge
# Start PostgreSQL and Redis
docker-compose up -d
# Copy and configure environment
cp .env.example .env
Edit .env and add credentials for your chosen LLM provider:
OpenAI (default)
OPENAI_API_KEY=sk-...
AGENTFORGE_JUDGE_PROVIDER=openai
AGENTFORGE_JUDGE_MODEL=gpt-4o
Anthropic
ANTHROPIC_API_KEY=sk-ant-...
AGENTFORGE_JUDGE_PROVIDER=anthropic
AGENTFORGE_JUDGE_MODEL=claude-3-5-haiku-20241022
NVIDIA NIM (free tier at build.nvidia.com — no credit card for free models)
NVIDIA_API_KEY=nvapi-...
AGENTFORGE_JUDGE_PROVIDER=nvidia
AGENTFORGE_NVIDIA_MODEL=meta/llama-3.1-8b-instruct
AGENTFORGE_JUDGE_MODEL=meta/llama-3.1-8b-instruct
Ollama (fully local, no API key needed)
# Install Ollama: https://ollama.com/download
ollama serve
ollama pull llama3.2:3b # or any model you prefer
# In .env:
AGENTFORGE_JUDGE_PROVIDER=ollama
AGENTFORGE_JUDGE_MODEL=llama3.2:3b
AGENTFORGE_OLLAMA_MODEL=llama3.2:3b
AGENTFORGE_OLLAMA_BASE_URL=http://localhost:11434/v1
Running inside Docker with Ollama on the host? Set
AGENTFORGE_OLLAMA_BASE_URL=http://host.docker.internal:11434/v1so the container can reach the Ollama process running on your machine.
2. Run database migrations
export DATABASE_URL="postgres://agentforge:agentforge@localhost:5432/agentforge"
cargo install sqlx-cli --no-default-features --features postgres
sqlx migrate run
3. Build and run the API server
cargo build --release
DATABASE_URL=$DATABASE_URL ./target/release/agentforge-api
# Server starts on http://127.0.0.1:8080
4. Run your first eval via CLI
# Run a full evaluation cycle
./target/release/agentforge run \
--agent fixtures/customer-support-agent.yaml \
--scenarios 50
# Show a scorecard diff between two versions
./target/release/agentforge diff <version-id-1> <version-id-2>
# Promote the winning version (pass the run-id returned by `agentforge run`)
./target/release/agentforge promote <run-id>
LLM Providers
AgentForge uses two independent LLM roles: the agent (the model being evaluated) and the judge (scores traces). Both are configured separately so you can mix providers — e.g., evaluate an Ollama agent with an OpenAI judge, or evaluate a NVIDIA NIM agent with an Anthropic judge. The API enforces that the two providers differ to prevent circular bias.
Set AGENTFORGE_JUDGE_PROVIDER to route the judge (scorer + optimizer) to the correct backend. Set provider on a POST /api/v1/runs request (or --provider via CLI) to route the agent itself.
OpenAI
OPENAI_API_KEY=sk-...
AGENTFORGE_JUDGE_PROVIDER=openai # default
AGENTFORGE_JUDGE_MODEL=gpt-4o # default
The AGENTFORGE_JUDGE_BASE_URL variable overrides the base URL for any OpenAI-compatible endpoint (useful for Azure OpenAI, LM Studio, or other proxies):
AGENTFORGE_JUDGE_BASE_URL=https://myazure.openai.azure.com/openai/deployments/gpt-4o
AGENTFORGE_JUDGE_API_KEY=<azure-key>
Anthropic
ANTHROPIC_API_KEY=sk-ant-...
AGENTFORGE_JUDGE_PROVIDER=anthropic
AGENTFORGE_JUDGE_MODEL=claude-3-5-haiku-20241022
NVIDIA NIM
Sign up for a free API key at build.nvidia.com. Many models are available with no credits required.
NVIDIA_API_KEY=nvapi-...
AGENTFORGE_JUDGE_PROVIDER=nvidia
AGENTFORGE_NVIDIA_MODEL=meta/llama-3.1-8b-instruct # model for agent runs
AGENTFORGE_JUDGE_MODEL=meta/llama-3.1-8b-instruct # model for judge/scorer
Free-tier models (no credits required):
| Model ID | Notes |
|---|---|
meta/llama-3.1-8b-instruct |
Fast, good default for evals |
meta/llama-3.1-70b-instruct |
Higher quality, slower |
mistralai/mistral-7b-instruct-v0.3 |
Compact, general purpose |
mistralai/mistral-small-4-119b-2603 |
Larger Mistral, default if AGENTFORGE_NVIDIA_MODEL unset |
nvidia/nemotron-mini-4b-instruct |
NVIDIA-tuned, very fast |
microsoft/phi-3-mini-4k-instruct |
Efficient small model |
NVIDIA NIM enforces single tool calls per request (
parallel_tool_calls=false). The base URL is alwayshttps://integrate.api.nvidia.com/v1— it cannot be overridden.
Ollama (fully local)
Install Ollama, then pull a model and start the server:
ollama pull llama3.2:3b # ~2 GB
ollama serve # starts on http://localhost:11434
Configure AgentForge to use it:
AGENTFORGE_JUDGE_PROVIDER=ollama
AGENTFORGE_JUDGE_MODEL=llama3.2:3b
AGENTFORGE_OLLAMA_MODEL=llama3.2:3b # model used for agent runs
AGENTFORGE_OLLAMA_BASE_URL=http://localhost:11434/v1
Ollama requires no API key. At runtime, any model field in an agent YAML file is transparently overridden with AGENTFORGE_OLLAMA_MODEL, so cloud-targeted agent files work without modification.
Docker + Ollama on host: replace
localhostwithhost.docker.internal:AGENTFORGE_OLLAMA_BASE_URL=http://host.docker.internal:11434/v1
AWS Bedrock
AWS Bedrock provides managed access to foundation models from Anthropic, Meta, Mistral, and others. Because Bedrock uses SigV4 request signing rather than API keys, it requires a self-hosted runner (GitHub-hosted runners do not have AWS credentials).
Prerequisites:
- An AWS account with Bedrock access enabled for the model you want to use.
- IAM credentials with the
bedrock:InvokeModelpermission. - A self-hosted GitHub Actions runner (or local/cloud VM) with AWS credentials available via environment variables or an instance role.
CLI
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1
./target/release/agentforge run \
--agent fixtures/customer-support-agent.yaml \
--provider bedrock \
--judge-provider openai # judge must be a different provider
Environment variables
# AWS credentials (can also be supplied via instance role / IRSA — no env vars needed in that case)
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_SESSION_TOKEN=... # only required for temporary credentials
AWS_REGION=us-east-1
# Bedrock model for agent runs (defaults to anthropic.claude-3-haiku-20240307-v1:0)
AGENTFORGE_BEDROCK_MODEL=anthropic.claude-3-5-sonnet-20241022-v2:0
Supported Bedrock model IDs
| Model | ID |
|---|---|
| Claude 3.5 Sonnet v2 | anthropic.claude-3-5-sonnet-20241022-v2:0 |
| Claude 3.5 Haiku | anthropic.claude-3-5-haiku-20241022-v1:0 |
| Claude 3 Haiku | anthropic.claude-3-haiku-20240307-v1:0 |
| Llama 3.1 8B Instruct | meta.llama3-1-8b-instruct-v1:0 |
| Llama 3.1 70B Instruct | meta.llama3-1-70b-instruct-v1:0 |
| Mistral 7B Instruct | mistral.mistral-7b-instruct-v0:2 |
The Bedrock base URL (
https://bedrock-runtime.<region>.amazonaws.com) is derived automatically fromAWS_REGION. It cannot be overridden. Cross-region inference profiles are supported by settingAWS_REGIONto the inference profile region.
Agent File Format
AgentForge accepts agent files in the following formats:
- AgentForge native YAML (recommended)
- GitHub Copilot
.agent.md— YAML frontmatter + Markdown system prompt body - OpenAI Assistants API JSON
- Anthropic Claude system prompt + tool block JSON
- LangChain / LangGraph agent YAML
- CrewAI agent definition YAML
GitHub Copilot .agent.md Format
Compatible with agents from github/awesome-copilot. The frontmatter holds metadata; the Markdown body becomes the system prompt.
---
name: 'Code Review Expert'
description: 'Specialist in reviewing code for security and maintainability'
model: GPT-4.1
tools: ['read', 'search/codebase', 'github/*']
---
# Code Review Expert
You are an expert code reviewer specializing in security, performance,
and maintainability.
## Review Focus Areas
- **Security**: Check for injection vulnerabilities and data exposure
- **Performance**: Identify N+1 queries and unnecessary allocations
- **Maintainability**: Evaluate clarity and SOLID principles
Frontmatter fields:
| Field | Type | Description |
|---|---|---|
name |
string | Agent display name |
description |
string | Short description (stored in metadata) |
model |
string | LLM model ID — infers provider from name (e.g. GPT-4.1 → OpenAI, claude-* → Anthropic) |
tools |
string[] | Capability references like "github/*", "read", "context7/*" |
argument-hint |
string | Hint for the argument the agent expects (stored in metadata) |
mcp-servers |
object | MCP server configurations (stored in metadata) |
Copilot tool capability references are mapped to ToolDefinition entries so AgentForge can reason about them during scenario generation and scoring.
AgentForge Native YAML Schema
# agent.yaml — AgentForge native schema v1
agentforge_schema_version: "1"
name: "customer-support-agent"
version: "2.1.0"
model:
provider: openai # openai | anthropic | nvidia_nim | ollama | bedrock
model_id: gpt-4o
temperature: 0.2
max_tokens: 2048
system_prompt: |
You are a helpful customer support agent for Acme Corp.
Always greet the user by name if known.
Never share pricing without verifying entitlement first.
tools:
- name: get_order_status
description: "Retrieve status of a customer order by order ID."
parameters:
type: object
properties:
order_id:
type: string
description: "The order identifier, format: ORD-XXXXXXXX"
required: [order_id]
output_schema:
type: object
properties:
response:
type: string
action_taken:
type: string
enum: [escalate, resolved, needs_followup, no_action]
confidence:
type: number
minimum: 0
maximum: 1
required: [response, action_taken]
constraints:
- "Never mention competitor products."
- "Do not provide refunds without running check_refund_eligibility first."
- "Always confirm order ID before calling get_order_status."
eval_hints:
domain: customer_support
typical_turns: 3
critical_tools: [get_order_status, check_refund_eligibility]
pass_threshold: 0.85 # minimum aggregate score to promote
scenario_count: 200
CLI Usage
agentforge <COMMAND> [OPTIONS]
Commands:
run Run a full eval cycle (parse → generate → run → score → optimize → gate)
diff Show scorecard diff between two agent versions
Usage: agentforge diff <version-id-1> <version-id-2>
promote Promote a candidate version to champion
Usage: agentforge promote <run-id>
help Print help
Options for `run`:
--agent <FILE> Path to agent YAML/JSON file (required)
--scenarios <N> Number of scenarios to generate (default: 100)
--concurrency <N> Parallel workers (default: 10)
--seed <N> Random seed for reproducibility (default: 42)
--provider <NAME> Agent LLM provider: openai | anthropic | nvidia | ollama | bedrock (default: openai)
(ollama requires `ollama serve` running locally; bedrock requires AWS credentials and a self-hosted runner)
--judge-provider <NAME> Judge LLM provider (must differ from --provider; default: anthropic)
--threshold <F> Pass threshold 0.0–1.0 (default: 0.85)
--output-json <FILE> Write full scorecard JSON to FILE (used by the GitHub Action)
--dry-run Validate agent file and preview scenario count; no LLM calls made
--max-cost <USD> Abort if estimated cost exceeds USD (e.g. --max-cost 5.00); 0 = no cap
--agent-format <FMT> Override format detection: native_yaml | openai_json | anthropic_json | langchain_yaml | crewai_yaml | copilot_agent_md
--weight-task <F> Override task-completion weight (default: 0.35)
--weight-tool <F> Override tool-selection weight (default: 0.20)
--weight-args <F> Override argument-correctness weight (default: 0.20)
--weight-schema <F> Override schema-compliance weight (default: 0.15)
--weight-instr <F> Override instruction-adherence weight (default: 0.07)
--weight-path <F> Override path-efficiency weight (default: 0.03)
--red-team Append adversarial red-team probes to standard scenarios
--cost-optimize After eval, recommend cheaper model alternatives
--watch Re-run the evaluation automatically when the agent file is saved.
Polls for file changes every 500 ms. Press Ctrl-C to stop.
Exit codes:
0 — All gates passed, version promoted (or no promotion needed)
1 — Gatekeeper blocked promotion
2 — Error (parse failure, DB connection, etc.)
REST API
The API server runs on http://0.0.0.0:8080 by default.
An OpenAPI 3.1 spec is available at docs/openapi.yaml and can be loaded into Swagger UI, Postman, or any OpenAPI-compatible tool.
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/agents |
List all registered agent versions (paginated: ?limit=50&offset=0); filter by name with ?name=<agent-name> |
POST |
/api/v1/agents |
Upload and register a new agent version |
GET |
/api/v1/agents/:id |
Get agent version by ID |
PATCH |
/api/v1/agents/:id |
Update agent metadata (is_champion, changelog) |
DELETE |
/api/v1/agents/:id |
Delete an agent version (blocked if it is the current champion) |
GET |
/api/v1/agents/:id/scenarios |
List generated test scenarios for an agent version (paginated: ?limit=50, max 500) |
GET |
/api/v1/agents/:id/runs |
List all eval runs for a specific agent version, newest first (paginated: ?limit=20) |
GET |
/api/v1/runs |
List all eval runs (paginated: ?limit=50&offset=0) |
POST |
/api/v1/runs |
Start a new eval run (rate-limited by AGENTFORGE_MAX_CONCURRENT_RUNS) |
GET |
/api/v1/runs/:id |
Get run status and results |
DELETE |
/api/v1/runs/:id |
Cancel a pending/running eval run (sets status → cancelled) |
GET |
/api/v1/runs/:id/scorecard |
Full scorecard with per-dimension scores and failure clusters |
GET |
/api/v1/runs/:id/traces |
List traces for a run (paginated: ?limit=100&offset=0, max 500) |
GET |
/api/v1/runs/:id/progress |
Server-Sent Events stream of live run progress (emits every ~2 s until terminal) |
GET |
/api/v1/diff |
Scorecard diff between two versions (?v1=<uuid>&v2=<uuid>) |
POST |
/api/v1/promote/:run_id |
Promote version to champion (runs all three gatekeeper gates) |
POST |
/api/v1/shadow-runs |
Start a shadow run comparing champion and candidate on live traffic |
GET |
/api/v1/shadow-runs/:id |
Get shadow run status and comparison result |
POST |
/api/v1/exports/finetune |
Export passing trace pairs from a completed eval run as JSONL |
GET |
/api/v1/exports/finetune/:id |
Get fine-tune export job status and download path |
POST |
/api/v1/benchmarks |
Run an agent against a standard benchmark suite (GAIA, AgentBench, or WebArena) |
GET |
/api/v1/benchmarks/:id |
Get benchmark run status, accuracy, and percentile rank |
GET |
/health |
Liveness probe — exempt from API key authentication |
Concurrency limit on
POST /runs: to prevent accidental LLM cost floods, the server rejects new eval-run requests with HTTP 429 whenAGENTFORGE_MAX_CONCURRENT_RUNSactive background tasks are already running (default:10). For high-throughput CI, raise this value in your deployment env. For per-client rate limiting, place a reverse proxy (nginx / Cloudflare) in front of the API.
Scenario count limit on
POST /runs:scenario_countmust not exceedAGENTFORGE_MAX_SCENARIOS(default:2000). Requests that exceed this value are rejected with HTTP 400.
auto_optimizeonPOST /runs: set"auto_optimize": trueto enable the iterative self-improvement loop. After the eval completes, if the aggregate score is below thethreshold(default: 0.92), the optimizer enters a round-based loop. Each round it generates candidate variants (prompt rewrites, tool-description rewrites, example injections, constraint tightenings), quick-evaluates each on a 25-scenario subset, and saves the best-performing variant if it improves by more than 1 percentage point. The loop terminates with a status ofconverged(score ≥ threshold),no_improvement(no variant helped),max_iterations(hitmax_opt_iterations, default 5), orfailed(unrecoverable error). The parent SHA is recorded on every saved variant for full lineage tracking. Theopt_status,opt_rounds,opt_best_score, andopt_best_agent_idfields on the run record reflect the final loop state.
Authentication: set
AGENTFORGE_API_KEYto require a Bearer token on all/api/v1/*endpoints. Requests without a validAuthorization: Bearer <key>header will receive HTTP 401. The/healthendpoint is always unauthenticated. When the env var is unset the server runs in unauthenticated development mode.
Example: Start an eval run
# Upload agent file (YAML or JSON; Content-Type should match the file format)
curl -X POST http://localhost:8080/api/v1/agents \
-H "Content-Type: application/yaml" \
--data-binary @fixtures/customer-support-agent.yaml
# Start eval run (all fields except agent_id are optional)
# Add -H "Authorization: Bearer $AGENTFORGE_API_KEY" when authentication is enabled
curl -X POST http://localhost:8080/api/v1/runs \
-H "Content-Type: application/json" \
-d '{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"scenario_count": 100,
"concurrency": 10,
"seed": 42,
"threshold": 0.92,
"provider": "openai",
"judge_provider": "anthropic",
"auto_optimize": true,
"max_opt_iterations": 5
}'
# Poll for run results (replace with the run UUID returned above)
curl http://localhost:8080/api/v1/runs/7dc53df0-c5fa-4f6c-b6b5-8d2d19afe5b1
Example: Shadow run (online eval)
Shadow runs let you compare a candidate agent against the current champion on a sample of real traffic without touching your production path. The champion and candidate run in parallel on the same sampled scenarios; a comparison_result summarising win/lose/tie counts and per-dimension deltas is saved when the run completes.
# Start a shadow run with 10 % traffic routed to the candidate
curl -X POST http://localhost:8080/api/v1/shadow-runs \
-H "Content-Type: application/json" \
-d '{
"champion_agent_id": "550e8400-e29b-41d4-a716-446655440000",
"candidate_agent_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"traffic_percent": 10
}'
# Poll for results
curl http://localhost:8080/api/v1/shadow-runs/<shadow-run-id>
Example: Export fine-tune data
After a completed eval run, export passing trace pairs as a JSONL file for supervised fine-tuning. The three supported formats map to OpenAI, Anthropic, and HuggingFace datasets-compatible schemas.
# Enqueue an export job (format defaults to "openai")
curl -X POST http://localhost:8080/api/v1/exports/finetune \
-H "Content-Type: application/json" \
-d '{
"run_id": "7dc53df0-c5fa-4f6c-b6b5-8d2d19afe5b1",
"format": "openai"
}'
# Poll until status == "complete", then read file_path for the JSONL location
curl http://localhost:8080/api/v1/exports/finetune/<export-id>
Supported format values: "openai" (chat completion pairs), "anthropic" (messages API pairs), "huggingface" (HuggingFace datasets-compatible JSONL).
Example: Run a standard benchmark
Benchmark runs evaluate an agent against a fixed task set from a public benchmark suite and return an accuracy score plus a percentile rank against the baseline public leaderboard.
# Start a GAIA benchmark run
curl -X POST http://localhost:8080/api/v1/benchmarks \
-H "Content-Type: application/json" \
-d '{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"suite": "gaia"
}'
# Poll for results (accuracy + percentile_rank populated when complete)
curl http://localhost:8080/api/v1/benchmarks/<benchmark-run-id>
Supported suite values: "gaia", "agentbench", "webarena". See Benchmark Suites for details.
Scoring Dimensions
Every execution trace is scored across six dimensions:
| Dimension | Weight | Scoring Method | What is Measured |
|---|---|---|---|
| Task completion | 35% | Deterministic + LLM judge | Did the agent achieve the stated goal? |
| Tool selection accuracy | 20% | Exact match | Were the correct tools called? |
| Argument correctness | 20% | JSON schema + semantic | Were tool arguments valid and semantically correct? |
| Output schema compliance | 15% | JSON schema strict | Does output match the declared schema? |
| Instruction adherence | 7% | LLM judge with rubric | Did the agent follow all behavioral constraints? |
| Path efficiency | 3% | Step count vs. optimal | Was the shortest valid path taken? |
Weights are configurable via environment variables (AGENTFORGE_WEIGHT_TASK, AGENTFORGE_WEIGHT_TOOL, AGENTFORGE_WEIGHT_ARGS, AGENTFORGE_WEIGHT_SCHEMA, AGENTFORGE_WEIGHT_INSTR, AGENTFORGE_WEIGHT_PATH) or via per-run CLI flags (--weight-task, --weight-tool, etc.). The judge LLM must use a different provider from the agent to prevent circular bias — enforcement is at the provider level (e.g., openai vs anthropic), not the individual model ID. The API returns HTTP 400 if provider and judge_provider are the same string.
Failure Clusters
Traces are automatically grouped into failure clusters:
| Cluster | Meaning |
|---|---|
wrong_tool |
Called an incorrect or unnecessary tool |
hallucinated_argument |
Passed a fabricated or invalid argument value |
looping |
Repeated the same tool call without progress |
premature_stop |
Ended the conversation before completing the task |
schema_violation |
Output did not match the declared schema |
constraint_breach |
Violated a behavioural constraint |
api_error |
Infrastructure failure (rate limit, 5xx, timeout) — not an agent quality issue |
no_failure |
Trace passed — no failure to classify |
Promotion Gatekeeper
A candidate variant must clear all three gates to be promoted:
-
Score Gate — Aggregate score must exceed the current champion by at least
+3%(configurable viaAGENTFORGE_SCORE_GATE_DELTA). -
Regression Gate — Must pass ≥ 99% of the scenarios the current champion passes (configurable via
AGENTFORGE_REGRESSION_GATE_THRESHOLD). Prevents "robbing Peter to pay Paul" improvements. -
Stability Gate — Must be evaluated on at least 3 independent random seeds before comparison, to account for LLM non-determinism (configurable via
AGENTFORGE_STABILITY_SEEDS).
If multiple candidates pass all gates, the one with the highest aggregate score is promoted. Promotion creates a new versioned agent file with an auto-generated changelog entry.
First run (no champion): When no champion exists yet, all three gates are automatically waived and the candidate is promoted unconditionally. This bootstraps the system on the first evaluation.
Configuration
All configuration is via environment variables. See .env.example for the full list.
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
— | PostgreSQL connection string (required) |
REDIS_URL |
redis://localhost:6379 |
Redis for caching |
OPENAI_API_KEY |
— | OpenAI API key |
ANTHROPIC_API_KEY |
— | Anthropic API key |
NVIDIA_API_KEY |
— | NVIDIA NIM API key (nvapi-…) |
AGENTFORGE_NVIDIA_MODEL |
mistralai/mistral-small-4-119b-2603 |
NVIDIA NIM model for agent runs (base URL is always https://integrate.api.nvidia.com/v1) |
AGENTFORGE_OLLAMA_BASE_URL |
http://localhost:11434/v1 |
Ollama base URL (OpenAI-compatible endpoint) |
AGENTFORGE_OLLAMA_MODEL |
llama3.2:3b |
Ollama model; overrides any model ID in the agent file at runtime |
AWS_ACCESS_KEY_ID |
— | AWS access key for Bedrock (can also be supplied via instance role or IRSA) |
AWS_SECRET_ACCESS_KEY |
— | AWS secret access key for Bedrock |
AWS_SESSION_TOKEN |
— | AWS session token (only required for temporary credentials) |
AWS_REGION |
us-east-1 |
AWS region for Bedrock API calls |
AGENTFORGE_BEDROCK_MODEL |
anthropic.claude-3-haiku-20240307-v1:0 |
Bedrock model ID for agent runs |
AGENTFORGE_JUDGE_PROVIDER |
openai |
LLM provider for the judge: openai | anthropic | nvidia | ollama | bedrock |
AGENTFORGE_JUDGE_MODEL |
gpt-4o |
Judge model ID |
AGENTFORGE_JUDGE_BASE_URL |
(provider default) | Override the judge base URL (e.g., Azure OpenAI or OpenAI-compatible proxy) |
AGENTFORGE_JUDGE_API_KEY |
(provider default) | Override the judge API key (useful when AGENTFORGE_JUDGE_BASE_URL points to a different endpoint) |
AGENTFORGE_HOST |
127.0.0.1 |
API server bind address |
AGENTFORGE_PORT |
8080 |
API server port |
AGENTFORGE_LOG_LEVEL |
info |
Log level (trace/debug/info/warn/error) |
AGENTFORGE_MAX_CONCURRENT_RUNS |
10 |
Max simultaneous background eval runs (HTTP 429 when exceeded) |
AGENTFORGE_API_KEY |
— | Bearer token for API authentication. When set, all /api/v1/* endpoints require Authorization: Bearer <key>. Unset = unauthenticated dev mode |
AGENTFORGE_DEFAULT_SCENARIOS |
100 |
Default scenario count per run |
AGENTFORGE_MAX_SCENARIOS |
2000 |
Maximum scenarios allowed per run (HTTP 400 when exceeded) |
AGENTFORGE_DEFAULT_CONCURRENCY |
10 |
Parallel worker count |
AGENTFORGE_DEFAULT_PASS_THRESHOLD |
0.85 |
Minimum score to pass a run |
AGENTFORGE_SCORE_GATE_DELTA |
0.03 |
Required score improvement to promote |
AGENTFORGE_REGRESSION_GATE_THRESHOLD |
0.99 |
Required pass-rate on champion scenarios |
AGENTFORGE_STABILITY_SEEDS |
3 |
Seeds required for stability gate |
AGENTFORGE_WEIGHT_TASK |
0.35 |
Task-completion scoring weight |
AGENTFORGE_WEIGHT_TOOL |
0.20 |
Tool-selection scoring weight |
AGENTFORGE_WEIGHT_ARGS |
0.20 |
Argument-correctness scoring weight |
AGENTFORGE_WEIGHT_SCHEMA |
0.15 |
Schema-compliance scoring weight |
AGENTFORGE_WEIGHT_INSTR |
0.07 |
Instruction-adherence scoring weight |
AGENTFORGE_WEIGHT_PATH |
0.03 |
Path-efficiency scoring weight |
OTEL_EXPORTER_OTLP_ENDPOINT |
— | When set, exports OpenTelemetry traces to this OTLP endpoint (e.g., http://otel-collector:4318). Requires the agentforge-observability crate to be compiled in. |
REDIS_URLvsAGENTFORGE_*: Redis uses the bareREDIS_URLkey (compatible with most hosting platforms). All other app-level settings use theAGENTFORGE_prefix.
AGENTFORGE_HOSTdefault: The server binds to127.0.0.1by default for security (localhost only). Set to0.0.0.0to accept external connections — docker-compose overrides this automatically for container networking.
Running Tests
# Start PostgreSQL first
docker-compose up -d postgres
# Run all tests
DATABASE_URL="postgres://agentforge:agentforge@localhost:5432/agentforge" \
cargo test --workspace
# Run tests for a specific crate
cargo test -p agentforge-scorer
cargo test -p agentforge-runner
cargo test -p agentforge-gatekeeper
# Run with output
cargo test --workspace -- --nocapture
The test suite covers:
- Agent file parsing (all 6 formats)
- Scenario generation (schema-derived, adversarial, domain-seeded)
- Runner execution with mocked LLM
- Scoring logic (all 6 dimensions)
- Optimizer variant generation
- Gatekeeper promotion logic
- REST API integration tests
- Database repository tests
GitHub Actions Marketplace
AgentForge is published as a reusable GitHub Action. No Rust toolchain, database, or build step required in your workflow — the action downloads a pre-built binary from the latest release automatically.
- uses: bhavinkotak/agentforge@v1
with:
agent_file: './agents/my-agent.yaml'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DATABASE_URL: ${{ secrets.AGENTFORGE_DATABASE_URL }}
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
agent_file |
yes | — | Path to the agent file (YAML, JSON, or .agent.md) |
scenarios |
no | 100 |
Number of test scenarios to generate |
concurrency |
no | 10 |
Parallel workers for running scenarios |
seed |
no | 42 |
Random seed for reproducible scenario generation |
threshold |
no | 0.85 |
Minimum aggregate score to gate promotion (0.0–1.0) |
provider |
no | openai |
LLM provider for the agent under test: openai | anthropic | nvidia | ollama | bedrock (ollama and bedrock require a self-hosted runner) |
judge_provider |
no | anthropic |
Judge LLM provider (must differ from provider at the provider level): openai | anthropic | nvidia | ollama | bedrock |
version |
no | (action ref) | Specific AgentForge release to use (e.g. v1.2.3). Defaults to the version of this action. |
Outputs
| Output | Description |
|---|---|
pass_rate |
Aggregate pass rate across all evaluated scenarios (0.0–1.0) |
aggregate_score |
Weighted aggregate score across all six dimensions (0.0–1.0) |
promoted |
Whether the agent was promoted to champion ("true" | "false") |
scorecard_path |
Path to the JSON scorecard artifact |
run_id |
AgentForge eval run UUID |
Use Cases
Block a merge when agent quality drops
Run a full eval cycle on every PR that touches agent files. Fail the check if the score falls below threshold — preventing regressions from being merged.
name: Agent Quality Gate
on:
pull_request:
paths: ['agents/**', '*.agent.md']
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bhavinkotak/agentforge@v1
id: eval
with:
agent_file: './agents/customer-support-agent.yaml'
scenarios: '200'
threshold: '0.85'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DATABASE_URL: ${{ secrets.AGENTFORGE_DATABASE_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # enables PR comment
- name: Report scorecard
if: always()
run: |
echo "Pass rate: ${{ steps.eval.outputs.pass_rate }}"
echo "Aggregate score: ${{ steps.eval.outputs.aggregate_score }}"
echo "Promoted: ${{ steps.eval.outputs.promoted }}"
Nightly improvement loop
Run AgentForge on a schedule to continuously generate, evaluate, and auto-promote improved agent variants.
name: Nightly Agent Improvement
on:
schedule:
- cron: '0 2 * * *' # 02:00 UTC every night
jobs:
improve:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bhavinkotak/agentforge@v1
with:
agent_file: './agents/customer-support-agent.yaml'
scenarios: '500'
concurrency: '20'
threshold: '0.88'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DATABASE_URL: ${{ secrets.AGENTFORGE_DATABASE_URL }}
Evaluate a GitHub Copilot .agent.md file
AgentForge natively parses Copilot agent files — just point agent_file at the .agent.md.
- uses: bhavinkotak/agentforge@v1
with:
agent_file: '.github/agents/code-review.agent.md'
scenarios: '100'
provider: 'openai'
judge_provider: 'anthropic'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DATABASE_URL: ${{ secrets.AGENTFORGE_DATABASE_URL }}
Tip: Pin to a specific version for reproducibility:
bhavinkotak/agentforge@v1.2.3
CI/CD Integration
Self-hosted / custom CI
If you prefer to build from source (e.g., air-gapped environments), use the CLI directly:
# .github/workflows/agent-eval.yml
name: Agent Evaluation
on:
push:
paths: ['agents/**']
pull_request:
paths: ['agents/**']
jobs:
evaluate:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: agentforge
POSTGRES_PASSWORD: agentforge
POSTGRES_DB: agentforge
ports:
- 5432:5432
redis:
image: redis:7-alpine
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Build AgentForge CLI
run: cargo build --release -p agentforge-cli
- name: Run Migrations
env:
DATABASE_URL: postgres://agentforge:agentforge@localhost:5432/agentforge
run: cargo sqlx migrate run
- name: Run AgentForge Evaluation
env:
DATABASE_URL: postgres://agentforge:agentforge@localhost:5432/agentforge
REDIS_URL: redis://localhost:6379
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
./target/release/agentforge run \
--agent ./agents/customer-support-agent.yaml \
--scenarios 200 \
--threshold 0.85
Exit codes: 0 = passed/promoted, 1 = gatekeeper blocked, 2 = error.
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for the full process. In short:
- Open an issue first for significant changes so we can discuss the approach.
- Fork and branch — create a feature branch from
main. - Follow code conventions —
cargo fmtandcargo clippy --all-targetsmust pass. - Add tests — all new behaviour must be covered.
- Open a PR — all PRs require approval from the project maintainer (@bhavinkotak) before merging.
See CONTRIBUTING.md for dev environment setup, commit message conventions, and the full review checklist.
Technical Stack
| Component | Technology | Rationale |
|---|---|---|
| Language | Rust | Memory safety, zero-cost abstractions, deterministic performance |
| API framework | Axum 0.8 | Async, ergonomic, tower-compatible middleware |
| Database | PostgreSQL 16 + SQLx 0.8 | Relational integrity + offline query checking |
| Caching | Redis (deadpool-redis) | Run state, rate limit tracking |
| LLM clients | reqwest 0.12 (rustls) | Async HTTP with TLS, no native deps |
| CLI | Clap 4 (derive) | Zero-boilerplate argument parsing |
| Async runtime | Tokio 1 (full) | Production async runtime |
| Serialization | serde + serde_json + serde_yaml | Full format support |
| Testing | tokio-test, mockall 0.14, wiremock 0.6 | Async mocks without external services |
| Observability | tracing 0.1 + tracing-subscriber | Structured logs, span context |
Roadmap
v2 (Post-MVP)
| Feature | Description |
|---|---|
| Online eval | ✅ Implemented — shadow-mode real traffic comparison via POST /api/v1/shadow-runs |
| Fine-tune exporter | ✅ Implemented — export trace pairs as JSONL via POST /api/v1/exports/finetune |
| Multi-agent testing | ✅ Implemented — agentforge-multiagent crate for composed agent teams |
| Red-teaming mode | ✅ Implemented — adversarial safety probes via --red-team CLI flag |
| Benchmark comparison | ✅ Implemented — compare against standard suites via POST /api/v1/benchmarks |
| Observability hooks | ✅ Implemented — OTLP trace export via OTEL_EXPORTER_OTLP_ENDPOINT |
| Cost optimizer | ✅ Implemented — model downgrade recommendations via --cost-optimize CLI flag |
Web dashboard (
web/) is already included in this repo and served via the Docker Compose stack on port 3000.
Multi-Agent Testing
The agentforge-multiagent crate lets you evaluate composed agent teams described as directed graphs. Each node in the graph is a full AgentFile; edges thread the output of one node into the input context of the next.
Graph spec (YAML)
# multi-agent-graph.yaml
id: "customer-triage-graph"
nodes:
- id: "classifier"
role: "Classify the incoming request"
agent:
name: "request-classifier"
model:
provider: openai
model_id: gpt-4o-mini
system_prompt: |
Classify the customer request into one of: billing, technical, general.
Output JSON: {"category": "<category>", "confidence": <0-1>}
output_schema:
type: object
properties:
category: { type: string, enum: [billing, technical, general] }
confidence: { type: number }
required: [category, confidence]
- id: "resolver"
role: "Resolve the classified request"
agent:
name: "request-resolver"
model:
provider: openai
model_id: gpt-4o
system_prompt: |
You are a resolution specialist. The classifier node output is available
in your context under the key "classifier". Use it to route appropriately.
edges:
- from: "classifier"
to: "resolver"
input_key: "classifier" # key injected into resolver's scenario context
API
Pass the graph spec to POST /api/v1/agents with Content-Type: application/yaml. The API will validate the graph topology (cycle detection included) and register each node agent. Then start a run as normal; the runner automatically executes nodes in topological order and threads outputs between them.
Scoring
Each node is scored independently across the six standard dimensions. A composite_score (unweighted mean of node scores) and a pass_rate across all node traces are reported in the MultiAgentScorecard returned by GET /api/v1/runs/:id/scorecard.
Benchmark Suites
The agentforge-benchmarks crate evaluates an agent against three public benchmark suites. Start a benchmark run via POST /api/v1/benchmarks and poll GET /api/v1/benchmarks/:id for results.
Supported suites
| Suite | suite value |
Task type | Metric |
|---|---|---|---|
| GAIA | "gaia" |
Real-world multi-step assistant tasks (Level 1–3) | Exact-match accuracy (%) |
| AgentBench | "agentbench" |
Code, DB, OS, web interaction | Weighted overall score |
| WebArena | "webarena" |
Browser-based web tasks | Task success rate (%) |
Accuracy and percentile rank
accuracy— fraction of tasks answered correctly (correct / total_tasks).percentile_rank— where your score falls relative to submissions tracked by AgentForge's built-in normalizer. A rank of0.75means the agent outperforms 75 % of tracked runs for that suite.
Example output
{
"id": "b1e3...",
"agent_id": "550e...",
"suite": "gaia",
"status": "complete",
"total_tasks": 165,
"correct": 132,
"accuracy": 0.8,
"percentile_rank": 0.82,
"started_at": "2026-05-25T02:00:00Z",
"completed_at": "2026-05-25T02:18:43Z"
}
Web Dashboard
The React/TypeScript dashboard is served on port 3000 (Docker) or npm run dev (port 5173) from the web/ directory.
Agents List
Agents are grouped by name so every agent family appears as a single row:
- Latest Version column shows the most recently created version.
- Versions count badge (stack icon) shows how many versions exist for that name.
- Click a row to open the Agent Detail page.
Agent Detail
- Summary panel — model, status, system prompt, tools, constraints.
- Score History — chart of aggregate scores over time.
- Version History — card listing all versions for the same agent name, sorted by semver ascending. The current version is highlighted with an indigo background and a
currentbadge. Click any row to navigate to that version. Click the diff icon to compare system prompts.
Diff Viewer
The diff viewer uses a real LCS-based unified diff algorithm:
- Removed lines are shown in red.
- Added lines are shown in green.
- Unchanged context lines are shown in gray.
Trigger a diff from the Runs page (scorecard diff between two versions) or from the Version History card (system-prompt diff between any two agent versions).
Privacy
AgentForge is a self-hosted platform. It sends no telemetry, analytics, or usage data to any external service. All evaluation data, agent files, and traces remain within your own infrastructure.
License
MIT
Dependencies
~12–21MB
~301K SLoC