AI Business Maturity Model
Certifications
Find a CoachFind a SpeakerSign In
Deep Dive

/

Part 1

Part 1 of 6

The Worker Definition

The Employee's DNA

Every AI Employee is defined by a single TypeScript object — not a program. This config encodes identity, allowed tools, loop behavior, working memory structure, and domain expertise. The engine reads it; you write it.

The WorkerDefinition Interface

Every Employee implements this interface. It lives in a shared types file and is the contract between the config author and the engine. Coulee Tech defined this interface once and used it for every Employee they built — from a list builder to a document analyzer to a scheduling assistant.

lib/workers/types.ts

TypeScript
export interface WorkerDefinition {
  /** Unique identifier — used in URLs: /portal/staffing/[id] */
  id: string;

  /** Display name shown in the UI */
  name: string;

  /** Job title / role description */
  title: string;

  /** Short description of what this employee does */
  description: string;

  /** Longer description for the detail page */
  longDescription?: string;

  /** Emoji or icon identifier */
  icon: string;

  /** Whether this employee is available or coming soon */
  status: 'active' | 'coming_soon';

  /**
   * Tool names this employee is allowed to use.
   * These are looked up from the global tool registry.
   * If empty, the employee has access to all tools.
   */
  allowedTools: string[];

  /** Loop configuration overrides (merged with global defaults) */
  loopConfig: Partial<LoopConfig>;

  /**
   * Initial living document sections.
   * Keys become the section names tracked across passes.
   */
  sections: Record<string, string>;

  /** Pre-enrichment configuration */
  preEnrichment: WorkerPreEnrichmentConfig;

  /** Build the system prompt for this employee */
  buildSystemPrompt: (context: WorkerContext) => string;

  /** Build the synthesis prompt for final response generation */
  buildSynthesisPrompt?: (context: WorkerContext) => string;

  /** Starter prompts shown on the empty state */
  starterPrompts: string[];

  /** Approval button label (default: "Approve") */
  approvalLabel?: string;

  /** Empty state description shown before first message */
  emptyStateDescription?: string;
}

Field-by-Field Walkthrough

Here is what each field does and how it shapes the Employee's behavior at runtime. Examples use a generic "Record Analyst" Employee to illustrate the patterns.

id

string

The URL slug for this Employee. Used in API routes (/api/workers/[id]) and portal URLs (/portal/staffing/[id]). Must be unique across all Employees.

id: 'record-analyst'
// Accessible at: /api/workers/record-analyst
// Portal page:   /portal/staffing/record-analyst

allowedTools

string[]

A whitelist of tool names from the global registry. The engine will only allow the AI to call tools on this list. This is how you give each Employee a focused, safe capability set without duplicating tool code. An empty array grants access to all tools.

allowedTools: [
  // Read-only data access
  'data.searchRecords',
  'data.getRecordDetail',
  'data.getStats',
  // Analysis tools
  'analysis.crossReference',
  'analysis.scoreRecord',
  'analysis.batchScore',
  // Write tools (require approval)
  'data.updateRecord',
  'data.bulkUpdate',
  // External research
  'web.searchCompany',
]

loopConfig

Partial<LoopConfig>

Overrides for the global loop defaults. You only need to specify the values that differ from the defaults. The engine merges your overrides with the global config.

loopConfig: {
  maxPasses: 10,        // Default is 5 — complex tasks need more
  costBudget: 1.50,     // Default is $0.50 — generous for batch work
  autoApprove: false,   // Never auto-execute write tools
  thinkModel: 'google/gemini-3-flash-preview',    // Cheap + fast for decisions
  synthesizeModel: 'anthropic/claude-haiku-4.5',  // Fast for final formatting
  escalationModel: 'anthropic/claude-sonnet-4.6', // Heavy hitter when stuck
}
LoopConfig fields explained
FieldDefaultPurpose
maxPasses5Hard limit on loop iterations. Prevents runaway costs.
costBudget$0.50Hard stop on total LLM spend per session.
autoApprovefalseIf true, write tools execute without user confirmation.
thinkModelgemini-flashModel used for every decision pass. Should be cheap and fast.
synthesizeModelclaude-haikuModel used only for the final response synthesis step.
escalationModelclaude-sonnetSwitched to automatically when the AI gets stuck.
enablePreEnrichmenttrueWhether to run the pre-enrichment phase before the loop.

sections

Record<string, string>

The initial shape of the living document — the AI's working memory that persists across all passes. Keys become named sections. Values are the initial content (usually empty strings). The AI writes to these sections as it works; by the final pass they contain accumulated knowledge.

sections: {
  criteria: '',      // What the user is looking for
  plan: '',          // How the AI plans to approach the task
  work_queue: '',    // IDs or items being processed
  findings: '',      // Intermediate analysis results
  results: '',       // Final output data
  confidence: 'low', // Current confidence level (special field)
}

buildSystemPrompt

(context: WorkerContext) => string

The most important field. A function that returns the multi-thousand-word string encoding everything the AI needs to know to do its job. This is called once per session. It receives the organization and user context so it can personalize the prompt.

What a system prompt encodes

For Coulee Tech's List Builder Employee, the system prompt was several thousand words covering:

  • Data model knowledge — the three-tier structure (Prospects → Farm List → Customers)

  • Pass budget allocation — exactly what to do on each of the 10 passes

  • Domain expertise — quality signals, red flags, company size interpretation

  • Geographic targeting strategy — when to use city vs state vs radius searches

  • The complete workflow — 5 phases from criteria gathering to promotion

  • The tool menu — formatted list of all allowed tools with parameters

  • The output format contract — the exact Markdown table format required

  • The JSON response contract — how the AI must structure every response

  • Critical rules — hard rules the AI must never violate

Generic system prompt structure

TypeScript
function buildSystemPrompt(context: WorkerContext): string {
  return `
You are [Employee Name], an AI assistant specialized in [domain].

## Your Role
[2-3 sentences describing the job]

## Data Model
[Explain the data structures the AI will work with]

## Your Tools
${buildToolMenuText(allowedTools)}
// This dynamically formats all allowed tools with params

## Workflow
Pass 1: Confirm the user's criteria and load context
Pass 2-3: Search and gather data using parallel tool calls
Pass 4: Cross-reference and filter results
Pass 5+: Score, rank, and prepare final output

## Output Format
[Exact Markdown format the AI must produce]

## JSON Response Contract
Every response MUST be valid JSON:
{
  "thinking": "Your reasoning here",
  "tool_calls": [{ "tool": "...", "params": {} }],
  "should_respond": false,
  "confidence": "low|medium|high",
  "document_updates": { "section_name": "content" }
}

## Rules
1. Never call the same tool with the same params twice
2. Always confirm criteria before executing write tools
3. [domain-specific rules...]
`;
}

preEnrichment

WorkerPreEnrichmentConfig

Configuration for the pre-enrichment phase that runs before the main loop. A lightweight AI call selects which tools to run to pre-load context. Results are injected into Pass 1 only, so the AI starts with real data instead of asking basic questions.

preEnrichment: {
  enabled: true,
  maxTools: 3,          // Run at most 3 tools before the loop
  defaultTools: [       // Fallback if AI selection fails
    { tool: 'data.getStats', params: {} },
  ],
  buildPrompt: buildPreEnrichmentPrompt,
  // buildPrompt returns: "Given this user message, which of
  // these tools should I run first?" → AI returns JSON array
}

A Complete Generic Worker Definition

Here is a complete, runnable Worker Definition for a generic "Record Analyst" Employee. This is the pattern Coulee Tech used for every Employee they built — swap in your domain, tools, and expertise.

lib/workers/employees/record-analyst.ts

TypeScript
import type { WorkerDefinition } from '../types';
import { buildToolMenuText } from '../prompts';

const ALLOWED_TOOLS = [
  'data.searchRecords',
  'data.getRecordDetail',
  'data.getStats',
  'analysis.crossReference',
  'analysis.batchScore',
  'analysis.buildList',
  'data.updateRecord',  // write — requires approval
  'web.searchCompany',
];

export const recordAnalystWorker: WorkerDefinition = {
  id: 'record-analyst',
  name: 'Record Analyst',
  title: 'AI Data Research Specialist',
  description: 'Searches, cross-references, and scores records to build qualified lists.',
  icon: '🔍',
  status: 'active',

  allowedTools: ALLOWED_TOOLS,

  loopConfig: {
    maxPasses: 8,
    costBudget: 1.00,
    autoApprove: false,
    thinkModel: 'google/gemini-3-flash-preview',
    synthesizeModel: 'anthropic/claude-haiku-4.5',
    escalationModel: 'anthropic/claude-sonnet-4.6',
  },

  sections: {
    criteria: '',
    plan: '',
    work_queue: '',
    findings: '',
    results: '',
    confidence: 'low',
  },

  preEnrichment: {
    enabled: true,
    maxTools: 2,
    defaultTools: [
      { tool: 'data.getStats', params: {} },
    ],
    buildPrompt: (userMessage: string) => `
      The user said: "${userMessage}"
      Available pre-load tools:
      - data.getStats: Get overall database statistics
      - data.searchRecords: Search with filters
      Which tools (up to 2) should run before analysis?
      Return JSON: [{"tool": "...", "params": {}, "reason": "..."}]
    `,
  },

  buildSystemPrompt: ({ organizationId }) => `
You are Record Analyst, an AI data research specialist.

## Your Role
You search, cross-reference, and score records to build qualified
lists for the user. You work methodically across multiple passes,
building up a living document of your findings.

## Tools Available
${buildToolMenuText(ALLOWED_TOOLS)}

## Workflow
Pass 1: Confirm criteria with user, review pre-loaded stats
Pass 2-3: Search records using parallel tool calls
Pass 4: Cross-reference results against existing data
Pass 5-6: Score and rank surviving records
Pass 7+: Prepare final output table

## JSON Response Contract
{
  "thinking": "your reasoning",
  "tool_calls": [{ "tool": "...", "params": {} }],
  "should_respond": false,
  "confidence": "low|medium|high",
  "document_updates": { "section": "content" }
}

## Rules
- Never call the same tool with the same params twice
- Run multiple search tools in parallel when possible
- Confirm write actions before executing
- Organization context: ${organizationId}
  `,

  starterPrompts: [
    'Find 50 qualified records matching [criteria]',
    'Build a list of [category] records in [location]',
    'Cross-reference my existing list against the database',
  ],

  approvalLabel: 'Approve Updates',
  emptyStateDescription: 'Describe what records you need and I will search, score, and build a qualified list.',
};
Key Architectural Insight

Coulee Tech built their entire AI Employee platform by writing config objects, not programs. When they needed a new Employee, they wrote a new WorkerDefinition — not a new engine. The engine handles all execution logic. This means:

  • Adding a new Employee takes hours, not days

  • All Employees benefit from engine improvements automatically

  • Bugs in the loop are fixed in one place

  • The system prompt is the only thing that makes Employees different from each other


Back to Deep Dive Hub
Part 2: The Tool System