Your First Agent
Step-by-step guide to creating your first OSSA-compliant AI agent using the BuildKit CLI and OSSA specification.
Overview
In this tutorial, you'll create a complete OSSA agent that: - Validates code quality before commits - Integrates with GitLab for issue tracking - Runs autonomously in response to events - Follows the OSSA v0.2.4 specification
Time to complete: 20-30 minutes
Prerequisites
- Completed Development Setup
- OSSA CLI installed (
ossa --versionworks) - BuildKit CLI installed (
buildkit --versionworks) - GitLab account with API access
- Basic understanding of YAML and TypeScript/JavaScript
Step 1: Understanding OSSA Agents
What is an OSSA Agent?
OSSA (Open Standard for Scalable AI Agents) defines a vendor-neutral specification for AI agents, similar to how OpenAPI defines REST APIs.
Key components:
- Manifest (agent.ossa.yaml) - Declarative agent definition
- Capabilities - What the agent can do
- Runtime - Where the agent runs (local, Kubernetes, serverless)
- Communication - How agents interact with each other
Agent Types
OSSA defines four agent roles:
- Worker - Executes tasks (e.g., run tests, deploy code)
- Governor - Enforces policies (e.g., TDD compliance, security rules)
- Critic - Provides feedback (e.g., code review, performance analysis)
- Observer - Monitors and reports (e.g., system health, metrics collection)
We'll build a Governor agent that enforces code quality standards.
Step 2: Generate Agent Scaffold
Create Agent Directory
# Create workspace for agents
mkdir -p ~/Sites/LLM/my-agents
cd ~/Sites/LLM/my-agents
# Generate agent scaffold with OSSA CLI
ossa generate governor \
--name "Code Quality Enforcer" \
--id code-quality-enforcer
# This creates:
# code-quality-enforcer/
# ├── agent.ossa.yaml # OSSA manifest
# ├── src/
# │ ├── index.ts # Main entry point
# │ └── capabilities/ # Capability implementations
# ├── tests/
# │ └── index.test.ts # Unit tests
# ├── package.json
# ├── tsconfig.json
# └── README.md
Explore Generated Files
cd code-quality-enforcer
tree -L 2
# View the OSSA manifest
cat agent.ossa.yaml
Generated agent.ossa.yaml:
ossaVersion: "0.2.4"
agent:
id: code-quality-enforcer
name: Code Quality Enforcer
version: "1.0.0"
role: governor
description: Enforces code quality standards and best practices
runtime:
type: local
node:
version: "20.x"
entrypoint: "dist/index.js"
capabilities:
- name: validate_code
description: Validate code quality before commit
input_schema:
type: object
properties:
files:
type: array
items:
type: string
description: List of file paths to validate
standards:
type: string
enum: [drupal, javascript, python, php]
description: Coding standard to apply
required: [files]
output_schema:
type: object
properties:
valid:
type: boolean
violations:
type: array
items:
type: object
properties:
file:
type: string
line:
type: number
message:
type: string
severity:
type: string
enum: [error, warning, info]
summary:
type: string
triggers:
- type: webhook
event: "gitlab.push"
endpoint: "/webhooks/gitlab"
dependencies:
agents: []
services:
- name: gitlab
type: api
version: ">=15.0"
observability:
metrics:
enabled: true
port: 9090
logging:
level: info
format: json
tracing:
enabled: true
metadata:
author: "Your Name"
license: "Apache-2.0"
tags: [code-quality, governor, ci-cd]
Step 3: Implement Agent Logic
Install Dependencies
# Install base dependencies
npm install
# Add additional dependencies for code validation
npm install --save \
@typescript-eslint/eslint-plugin \
@typescript-eslint/parser \
eslint \
phpcs \
prettier
Implement Main Entry Point
Edit src/index.ts:
import express from 'express';
import { validateCode } from './capabilities/validate-code';
import { createLogger } from './utils/logger';
const app = express();
const logger = createLogger('code-quality-enforcer');
app.use(express.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', agent: 'code-quality-enforcer', version: '1.0.0' });
});
// Main capability endpoint
app.post('/capabilities/validate_code', async (req, res) => {
try {
const { files, standards = 'javascript' } = req.body;
if (!files || !Array.isArray(files)) {
return res.status(400).json({
error: 'Invalid input: files array required'
});
}
logger.info(`Validating ${files.length} files with ${standards} standards`);
const result = await validateCode(files, standards);
res.json(result);
} catch (error) {
logger.error('Validation failed', { error: error.message });
res.status(500).json({ error: 'Validation failed' });
}
});
// GitLab webhook endpoint
app.post('/webhooks/gitlab', async (req, res) => {
try {
const { event_name, commits } = req.body;
if (event_name !== 'push') {
return res.status(200).json({ message: 'Event ignored' });
}
// Extract changed files from commits
const changedFiles = commits.flatMap(commit =>
[...commit.added, ...commit.modified]
);
if (changedFiles.length === 0) {
return res.status(200).json({ message: 'No files to validate' });
}
logger.info(`Processing push event with ${changedFiles.length} changed files`);
// Validate changed files
const result = await validateCode(changedFiles, 'javascript');
if (!result.valid) {
logger.warn('Code quality violations detected', {
violations: result.violations.length
});
// Post comment to GitLab MR/commit
// (Implementation depends on GitLab API integration)
}
res.json({ processed: true, result });
} catch (error) {
logger.error('Webhook processing failed', { error: error.message });
res.status(500).json({ error: 'Processing failed' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Agent listening on port ${PORT}`);
});
Implement Validation Capability
Create src/capabilities/validate-code.ts:
import { ESLint } from 'eslint';
import * as fs from 'fs/promises';
import * as path from 'path';
interface ValidationResult {
valid: boolean;
violations: Array<{
file: string;
line: number;
message: string;
severity: 'error' | 'warning' | 'info';
}>;
summary: string;
}
export async function validateCode(
files: string[],
standards: string
): Promise<ValidationResult> {
const violations: ValidationResult['violations'] = [];
// Configure ESLint based on standards
const eslint = new ESLint({
overrideConfigFile: getConfigForStandard(standards),
useEslintrc: false,
});
for (const file of files) {
try {
// Skip non-code files
if (!isCodeFile(file)) continue;
// Read file content
const content = await fs.readFile(file, 'utf-8');
// Lint file
const results = await eslint.lintText(content, { filePath: file });
// Extract violations
for (const result of results) {
for (const message of result.messages) {
violations.push({
file: result.filePath,
line: message.line,
message: message.message,
severity: message.severity === 2 ? 'error' : 'warning',
});
}
}
} catch (error) {
violations.push({
file,
line: 0,
message: `Failed to validate: ${error.message}`,
severity: 'error',
});
}
}
const errorCount = violations.filter(v => v.severity === 'error').length;
const warningCount = violations.filter(v => v.severity === 'warning').length;
return {
valid: errorCount === 0,
violations,
summary: `Found ${errorCount} errors and ${warningCount} warnings in ${files.length} files`,
};
}
function isCodeFile(file: string): boolean {
const ext = path.extname(file);
return ['.ts', '.js', '.tsx', '.jsx', '.php', '.py'].includes(ext);
}
function getConfigForStandard(standards: string): string {
const configs = {
javascript: path.join(__dirname, '../../config/eslint.config.js'),
typescript: path.join(__dirname, '../../config/eslint.config.js'),
drupal: path.join(__dirname, '../../config/phpcs.xml'),
php: path.join(__dirname, '../../config/phpcs.xml'),
};
return configs[standards] || configs.javascript;
}
Create Logger Utility
Create src/utils/logger.ts:
import pino from 'pino';
export function createLogger(name: string) {
return pino({
name,
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => {
return { level: label };
},
},
});
}
Step 4: Write Tests
Edit tests/index.test.ts:
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
import request from 'supertest';
import express from 'express';
import { validateCode } from '../src/capabilities/validate-code';
describe('Code Quality Enforcer Agent', () => {
describe('Health Check', () => {
it('should return healthy status', async () => {
// Test implementation
});
});
describe('Validate Code Capability', () => {
it('should detect ESLint violations', async () => {
const files = ['./test-fixtures/bad-code.js'];
const result = await validateCode(files, 'javascript');
expect(result.valid).toBe(false);
expect(result.violations.length).toBeGreaterThan(0);
});
it('should pass clean code', async () => {
const files = ['./test-fixtures/good-code.js'];
const result = await validateCode(files, 'javascript');
expect(result.valid).toBe(true);
expect(result.violations.length).toBe(0);
});
});
describe('GitLab Webhook', () => {
it('should process push events', async () => {
// Test webhook processing
});
});
});
Run Tests
# Run tests
npm test
# Run with coverage
npm test -- --coverage
# Expected output:
# PASS tests/index.test.ts
# Code Quality Enforcer Agent
# ✓ should return healthy status (15 ms)
# ✓ should detect ESLint violations (82 ms)
# ✓ should pass clean code (45 ms)
#
# Test Suites: 1 passed, 1 total
# Tests: 3 passed, 3 total
Step 5: Validate OSSA Compliance
# Validate agent manifest
ossa validate agent.ossa.yaml
# Expected output:
# ✓ OSSA manifest is valid
# Agent ID: code-quality-enforcer
# Agent Type: governor
# OSSA Version: 0.2.4
# Capabilities: 1
# Runtime: local (Node.js 20.x)
Check for Common Issues
# Use BuildKit to validate
buildkit ossa validate agent.ossa.yaml --strict
# This checks for:
# - Required fields present
# - Schema validation
# - Capability input/output schemas
# - Runtime configuration
# - Best practices compliance
Step 6: Run Agent Locally
Build the Agent
# Compile TypeScript
npm run build
# Output in dist/ directory
ls -la dist/
Start the Agent
# Run locally
npm start
# Or with development auto-reload
npm run dev
# Expected output:
# {"level":"info","time":1699564800000,"name":"code-quality-enforcer","msg":"Agent listening on port 3000"}
Test Agent Endpoints
In another terminal:
# Health check
curl http://localhost:3000/health
# Response:
# {"status":"ok","agent":"code-quality-enforcer","version":"1.0.0"}
# Test validation capability
curl -X POST http://localhost:3000/capabilities/validate_code \
-H "Content-Type: application/json" \
-d '{
"files": ["src/index.ts"],
"standards": "typescript"
}'
# Response:
# {
# "valid": true,
# "violations": [],
# "summary": "Found 0 errors and 0 warnings in 1 files"
# }
Step 7: Register Agent with BuildKit
Create BuildKit Configuration
# Initialize BuildKit in project
buildkit init
# Register agent
buildkit agents register \
--manifest agent.ossa.yaml \
--local
# Verify registration
buildkit agents list
# Expected output:
# ID NAME TYPE STATUS
# code-quality-enforcer Code Quality Enforcer governor running
Configure GitLab Integration
# Set up GitLab webhook
buildkit gitlab webhooks create \
--project llm/demos/llm-platform-demo \
--url http://localhost:3000/webhooks/gitlab \
--events push,merge_request
# This creates a webhook in GitLab that triggers your agent
Step 8: Deploy Agent to Production
Option 1: Deploy to Kubernetes
# Build Docker image
docker build -t yourregistry.com/code-quality-enforcer:1.0.0 .
# Push to registry
docker push yourregistry.com/code-quality-enforcer:1.0.0
# Deploy with BuildKit
buildkit agents deploy \
--manifest agent.ossa.yaml \
--namespace agents \
--image yourregistry.com/code-quality-enforcer:1.0.0
Option 2: Deploy Locally with BuildKit
# Start agent as background service
buildkit agents start code-quality-enforcer --daemon
# Check status
buildkit agents status code-quality-enforcer
# View logs
buildkit agents logs code-quality-enforcer --follow
Step 9: Monitor Agent Activity
View Agent Metrics
# Get agent metrics
curl http://localhost:9090/metrics
# Or use BuildKit CLI
buildkit agents metrics code-quality-enforcer
Check Agent Logs
# View logs
buildkit agents logs code-quality-enforcer
# Filter by severity
buildkit agents logs code-quality-enforcer --level error
# Stream logs in real-time
buildkit agents logs code-quality-enforcer --follow
GitLab Integration
# View agent activity in GitLab
buildkit gitlab agent-activity \
--agent code-quality-enforcer \
--project llm/demos/llm-platform-demo
# Shows:
# - Webhook triggers
# - Validation results
# - Comments posted
# - Merge request blocks
Step 10: Create Agent Workflows
Combine your agent with other OSSA agents:
# Create workflow definition
buildkit workflows create code-review-workflow \
--agents code-quality-enforcer,security-scanner,test-runner \
--trigger gitlab.merge_request.open
Workflow definition (workflows/code-review.yaml):
name: code-review-workflow
version: "1.0.0"
trigger:
type: webhook
event: gitlab.merge_request.open
steps:
- agent: code-quality-enforcer
capability: validate_code
input:
files: "{{ event.changed_files }}"
standards: "javascript"
on_failure: block_merge
- agent: security-scanner
capability: scan_vulnerabilities
input:
files: "{{ event.changed_files }}"
on_failure: block_merge
- agent: test-runner
capability: run_tests
input:
test_suite: "unit"
on_failure: block_merge
- agent: code-quality-enforcer
capability: post_summary
input:
merge_request: "{{ event.merge_request.iid }}"
Advanced Features
Add AI-Powered Analysis
Enhance your agent with LLM capabilities:
// src/capabilities/ai-review.ts
import { LLMGateway } from '@bluefly/llm-gateway-client';
const llm = new LLMGateway({
baseUrl: process.env.LLM_GATEWAY_URL,
});
export async function aiCodeReview(code: string): Promise<string> {
const response = await llm.chat({
model: 'claude-3-sonnet',
messages: [
{
role: 'system',
content: 'You are a code reviewer. Identify potential issues, suggest improvements, and explain best practices.',
},
{
role: 'user',
content: `Review this code:\n\n${code}`,
},
],
});
return response.content;
}
Agent-to-Agent Communication
// src/capabilities/collaborate.ts
import { OSSAClient } from '@bluefly/ossa-client';
const ossa = new OSSAClient({
registryUrl: process.env.OSSA_REGISTRY_URL,
});
export async function requestSecurityScan(files: string[]) {
const securityAgent = await ossa.findAgent({
role: 'critic',
capabilities: ['scan_vulnerabilities']
});
const result = await securityAgent.invoke('scan_vulnerabilities', {
files,
});
return result;
}
Troubleshooting
Agent Won't Start
# Check logs
npm run dev 2>&1 | tee agent.log
# Verify dependencies
npm install
# Check port availability
lsof -i :3000
OSSA Validation Fails
# Get detailed validation errors
ossa validate agent.ossa.yaml --verbose
# Common issues:
# - Missing required fields
# - Invalid schema references
# - Incorrect OSSA version
# - Missing capabilities
GitLab Webhook Not Triggering
# Test webhook manually
curl -X POST http://localhost:3000/webhooks/gitlab \
-H "Content-Type: application/json" \
-d @test-fixtures/gitlab-push-event.json
# Check GitLab webhook delivery logs
# GitLab → Project → Settings → Webhooks → Edit → Recent Deliveries
Next Steps
Congratulations! You've created your first OSSA agent. Next:
- Build a workflow: Your First Workflow
- Deploy to Kubernetes: Deploy to K8s Tutorial
- Add monitoring: Monitor Agents
- Learn common pitfalls: Common Pitfalls