Buddy
A virtual pet companion for your AI β Designed to provide in-context code review feedback with personality. Grow with your buddy and level up together. Works with Claude Code, Codex, CursorCLI, Github CopilotCLI, OpenClaw, and any MCP compatible clients
Ask AI about Buddy
Powered by Claude Β· Grounded in docs
I know everything about Buddy. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Buddy
An evaluator-optimizer sidecar agent for engineering workflows. Buddy helps primary coding agents when their work becomes slow, ambiguous, or repeatedly unsuccessful.
What is Buddy?
Buddy is not a generic chatbot. It is a specialized engineering evaluator that receives structured problem packets from primary coding agents, searches its episodic memory for similar past problems, generates and evaluates solution hypotheses, and returns either a concrete recommendation or a rejection with actionable feedback.
Primary Agent Buddy
| |
| 1. Submit problem packet -----> |
| | 2. Search episodic memory (Qdrant)
| | 3. Build solution hypotheses
| | 4. Evaluate against constraints
| | 5. Score confidence + risks
| <---- 6. Return recommendation |
| |
| 7. Close task + store learnings |
Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β External Agents β
β (Claude, Cursor, Copilot, etc.) β
ββββββββββββ¬βββββββββββββββββββββββ¬βββββββββββββββββββββ
β REST API β MCP (stdio)
βΌ βΌ
ββββββββββββββββββββββ βββββββββββββββββββββββ
β API Controllers β β MCP Tool Server β
β (5 endpoints) β β (7 tools) β
ββββββββββ¬ββββββββββββ ββββββββββ¬βββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββ
β Application Services β
β EvaluatorOptimizerService β
β EscalationService β
β QdrantMemoryService β
ββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββ βββββββββββββββββββββ
β Database β β External APIs β
β (SQLite) β β OpenAI (GPT-5.4) β
β β β Qdrant (vectors) β
ββββββββββββββββ βββββββββββββββββββββ
Tech Stack
| Component | Technology |
|---|---|
| Framework | Laravel 13.x |
| PHP | 8.5+ |
| AI SDK | laravel/ai v0.3.2 |
| Evaluator Model | GPT-5.4 (configurable) |
| Vector DB | Qdrant (episodic memory + semantic search) |
| Database | SQLite (dev) / PostgreSQL (production) |
| Queue | Laravel database queue (dev) / Redis (production) |
| MCP Transport | stdio (JSON-RPC 2.0) |
| Testing | PHPUnit 12.x with Laravel AI SDK fakes |
| Formatting | Laravel Pint |
Quick Start
Prerequisites
- PHP 8.5+
- Composer
- An OpenAI API key
- Qdrant running locally (or via Docker)
Local Setup
# Clone and install
git clone <repo-url> buddy
cd buddy
composer install
# Configure environment
cp .env.example .env
php artisan key:generate
# Set your API keys in .env
# OPENAI_API_KEY=sk-...
# Create database and run migrations
touch database/database.sqlite
php artisan migrate
# Start development server
composer dev
Docker Setup (recommended)
No PHP 8.5 required on the host. The Docker entrypoint automatically bootstraps a fresh clone:
# Clone and start β that's it
git clone https://github.com/ikarolaborda/buddy.git
cd buddy
docker compose build
docker compose up -d
The entrypoint detects a fresh clone and automatically:
- Copies
.env.exampleto.env(if.envis missing) - Installs Composer dependencies inside the container (if
vendor/is missing) - Generates
APP_KEY(if not set) - Creates the SQLite database and runs migrations (if database is missing)
If everything is already present, the entrypoint skips all bootstrap steps.
After startup, set your OpenAI API key in .env:
# Edit .env and set OPENAI_API_KEY=sk-...
This starts two services:
- app β Laravel HTTP server on port 8000
- queue β Background queue worker for async evaluations
Both connect to your existing Qdrant container (qdrant-memory-db) via the qdrant-memory_default Docker network.
Configuration
All Buddy-specific configuration lives in config/buddy.php and .env:
| Variable | Default | Description |
|---|---|---|
BUDDY_MODEL | gpt-5.4 | AI model for evaluation |
BUDDY_EMBEDDING_MODEL | text-embedding-3-small | Model for vector embeddings |
BUDDY_MAX_EVALUATION_STEPS | 10 | Max tool-use steps per evaluation |
BUDDY_EVALUATION_TIMEOUT | 120 | Timeout in seconds |
QDRANT_HOST | http://localhost | Qdrant server host |
QDRANT_PORT | 6333 | Qdrant server port |
QDRANT_API_KEY | (empty) | Qdrant API key (optional) |
QDRANT_COLLECTION | buddy_episodes | Qdrant collection for episodes |
QDRANT_KNOWLEDGE_COLLECTION | buddy_knowledge | Qdrant collection for distilled knowledge |
QDRANT_VECTOR_SIZE | 1536 | Embedding vector dimensions |
AI provider keys are configured via Laravel AI SDK in config/ai.php:
| Variable | Description |
|---|---|
OPENAI_API_KEY | OpenAI API key |
REST API
All endpoints are prefixed with /api/buddy.
Create Task
POST /api/buddy/tasks
Create a new evaluation task from a structured problem packet.
Request:
{
"source_agent": "claude",
"task_summary": "Login page returns 500 after OAuth callback",
"problem_type": "bug",
"repo": "acme/webapp",
"branch": "feature/oauth-fix",
"constraints": ["preserve backward compatibility"],
"evidence": [
{"type": "error_log", "content": "NullPointerException at AuthController:42"}
],
"artifacts": [
{"type": "stacktrace", "content": "..."}
],
"requested_outcome": "Fix the 500 error on OAuth callback"
}
Response (201):
{
"data": {
"task_id": "01JDZK...",
"source_agent": "claude",
"task_summary": "Login page returns 500 after OAuth callback",
"problem_type": "bug",
"status": "pending",
"attempt_count": 0,
"created_at": "2026-03-21T20:00:00.000Z"
}
}
Get Task
GET /api/buddy/tasks/{ulid}
Returns the task with its latest recommendation (if evaluated).
Attach Artifact
POST /api/buddy/tasks/{ulid}/artifacts
Attach evidence to an existing task before evaluation.
Request:
{
"type": "log",
"content": "ERROR 2026-03-21 Auth failed at line 42",
"metadata": {"file": "auth.log"}
}
Artifact types: log, test_output, stacktrace, code_snippet, diff, config, screenshot, other.
Evaluate Task
POST /api/buddy/tasks/{ulid}/evaluate
POST /api/buddy/tasks/{ulid}/evaluate?async=1
Run the evaluator-optimizer agent on the task. Without ?async=1, this runs synchronously and returns the recommendation directly. With ?async=1, the evaluation is dispatched to a queue worker and returns immediately with status 202.
Synchronous Response (200):
{
"task": { "task_id": "01JDZK...", "status": "completed", "..." : "..." },
"evaluation": {
"accepted": true,
"confidence": "high",
"summary": "The OAuth callback needs a null check on the user object.",
"recommended_plan": [
"Add null check at AuthController:42",
"Add test coverage for null user case"
],
"rejected_reasons": [],
"required_followups": [],
"risks": ["Minimal - isolated change"],
"next_actions": ["Apply the fix", "Run test suite"],
"memory_hits": ["Similar OAuth issue resolved 2 weeks ago"]
}
}
Close Task
POST /api/buddy/tasks/{ulid}/close
Close a completed task and optionally store durable learnings in Qdrant.
Request:
{
"learnings_summary": "OAuth callback must validate user object before redirect."
}
MCP Server
Buddy exposes itself as an MCP server so external coding agents can use it as a tool. The MCP server runs inside Docker β no PHP 8.5 installation required on the host.
Connecting from Claude Code
Add to your Claude Code settings (~/.claude/settings.json or project-level .claude/settings.json):
{
"mcpServers": {
"buddy": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--network", "qdrant-memory_default",
"-e", "QDRANT_HOST=http://qdrant-memory-db",
"-v", "/home/iclaborda/Aerolambda/buddy/.env:/var/www/html/.env",
"-v", "/home/iclaborda/Aerolambda/buddy/database:/var/www/html/database",
"buddy-app",
"php", "artisan", "buddy:mcp-server"
]
}
}
}
Secrets (OPENAI_API_KEY, APP_KEY) are read from the mounted .env file β they never appear in the Claude config. Only the Docker network override (QDRANT_HOST) is passed as -e because the hostname differs inside the container network.
Prerequisites: Build the image once with docker compose build from the Buddy directory. Set your API keys in the Buddy .env file.
Starting the MCP Server Directly
# Via Docker (recommended β no PHP 8.5 required)
docker run --rm -i \
--network qdrant-memory_default \
-e QDRANT_HOST=http://qdrant-memory-db \
-v /path/to/buddy/.env:/var/www/html/.env \
-v /path/to/buddy/database:/var/www/html/database \
buddy-app \
php artisan buddy:mcp-server
# Or natively (requires PHP 8.5)
php artisan buddy:mcp-server
Available MCP Tools
| Tool | Description |
|---|---|
buddy.submit_problem | Submit a structured problem packet for evaluation |
buddy.get_task_status | Get the current status of a task |
buddy.get_recommendation | Trigger evaluation and get the recommendation |
buddy.search_memory | Search episodic memory for past problems/patterns |
buddy.store_memory | Store distilled engineering knowledge |
buddy.attach_artifact | Attach evidence to an existing task |
buddy.close_task | Close a task and optionally store learnings |
Escalation Trigger Policy
Buddy is designed to be called when a primary agent's work degrades. The recommended trigger policy escalates to Buddy when at least two of these conditions are true:
| Condition | Threshold |
|---|---|
| Time elapsed | > 5 minutes |
| Failed attempts | > 2 |
| Repeated test failure | persists |
| Confidence | low |
| Root cause | ambiguous |
| Evidence | conflicts |
The EscalationTrigger DTO and EscalationService implement this policy. See the agent-facing protocol document (AGENT.md) for integration details.
Memory System
Buddy uses Qdrant for long-term episodic memory:
- Storage: Every closed task can store distilled learnings as vectors
- Retrieval: On every evaluation, Buddy searches memory for similar past episodes
- Metadata Filtering: Memories are tagged by
task_intent,stack,subsystem,symptom,root_cause,solution_pattern, andoutcome - Embeddings: Generated via OpenAI's
text-embedding-3-smallmodel throughlaravel/ai
Only distilled, reusable knowledge is stored. Raw transcripts are not persisted unless distilled first.
Initializing Qdrant Collections
# Ensure Qdrant is running, then use tinker to create collections:
php artisan tinker
>>> app(\App\Services\QdrantMemoryService::class)->ensureCollectionExists()
Testing
# Run all tests
php artisan test
# Run specific test suites
php artisan test --filter=BuddyTaskApiTest
php artisan test --filter=EscalationTriggerTest
Tests use laravel/ai's built-in Agent::fake() to mock AI interactions. The test suite runs entirely against SQLite with no external service dependencies.
Project Structure
app/
βββ Ai/
β βββ Agents/
β β βββ EvaluatorOptimizerAgent.php # Core AI agent
β βββ Tools/
β βββ SearchMemoryTool.php # Agent tool for memory search
βββ Console/Commands/
β βββ McpServerCommand.php # MCP stdio server
βββ DTOs/
β βββ ProblemPacket.php # Input contract
β βββ EvaluationResult.php # Output contract
β βββ EscalationTrigger.php # Escalation policy
β βββ MemorySearchResult.php # Qdrant result wrapper
βββ Enums/
β βββ TaskStatus.php # pending/evaluating/completed/failed/closed
β βββ ProblemType.php # bug/test_failure/performance/...
β βββ RunStatus.php # started/completed/failed
β βββ Confidence.php # high/medium/low/none
β βββ ArtifactType.php # log/test_output/stacktrace/...
βββ Http/
β βββ Controllers/Api/Buddy/
β β βββ BuddyTaskController.php # REST API (5 actions)
β βββ Requests/Buddy/ # Form request validation
β βββ Resources/Buddy/ # API response formatting
βββ Jobs/
β βββ EvaluateTaskJob.php # Async evaluation queue job
βββ Mcp/
β βββ McpTool.php # Tool interface
β βββ BaseMcpTool.php # Base class
β βββ McpToolRegistry.php # Tool registration
β βββ Tools/ # 7 MCP tool implementations
βββ Models/
β βββ BuddyTask.php # Primary entity
β βββ BuddyRun.php # Evaluation run
β βββ BuddyArtifact.php # Evidence attachment
β βββ BuddyRecommendation.php # AI recommendation
β βββ BuddyQuestion.php # Clarifying question
β βββ BuddyMemoryReference.php # Qdrant memory link
β βββ BuddyDecisionLog.php # Audit trail
βββ Services/
βββ EvaluatorOptimizerService.php # Orchestration
βββ QdrantMemoryService.php # Qdrant HTTP client
βββ EscalationService.php # Trigger evaluation
Design Decisions
- Single escalation hop: Buddy never spawns other Buddy instances. This prevents unbounded agent loops.
- Qdrant via HTTP facade: No extra PHP client package. The Qdrant REST API is simple enough that
Http::baseUrl()is sufficient. - Laravel 13 PHP attributes: Controller middleware (
#[Middleware]) and queue job configuration (#[Tries],#[Timeout],#[FailOnTimeout]) are declared via attributes. - laravel/ai structured output: The
HasStructuredOutputinterface withJsonSchemabuilder guarantees the AI returns data matching theEvaluationResultshape. - Auditable AI: Every evaluation creates a
BuddyRun(with model and token usage), aBuddyRecommendation, and aBuddyDecisionLog.
License
MIT
