---
title: "AI Capabilities Guide"
description: "Complete guide to leveraging AI agents, RAG pipelines, and intelligent automation in ObjectStack"
---

# AI Capabilities Guide

Complete guide to leveraging AI agents, RAG pipelines, and intelligent automation in ObjectStack.

## Table of Contents

1. [AI Architecture](#ai-architecture)
2. [AI Agents](#ai-agents)
3. [Actions as Tools (explicit opt-in)](#actions-as-tools-explicit-opt-in)
   - [Human-In-The-Loop approval](#human-in-the-loop-approval)
4. [RAG Pipelines](#rag-pipelines)
5. [Natural Language Queries](#natural-language-queries)
6. [Predictive Analytics](#predictive-analytics)
7. [Best Practices](#best-practices)

---

## AI Architecture

ObjectStack provides a comprehensive AI platform:

```
┌─────────────────────────────────────┐
│        AI Orchestration             │ ← Coordinate AI workflows
├─────────────────────────────────────┤
│        AI Agents                    │ ← Autonomous actors
│  - Assistants                       │
│  - Workers                          │
│  - Analysts                         │
├─────────────────────────────────────┤
│        RAG Pipelines                │ ← Knowledge retrieval
│  - Vector Search                    │
│  - Semantic Chunking                │
│  - Reranking                        │
├─────────────────────────────────────┤
│        Model Registry               │ ← LLM management
│  - OpenAI, Anthropic, etc.         │
│  - Model routing                    │
│  - Cost tracking                    │
└─────────────────────────────────────┘
```

---

## AI Agents

AI agents are autonomous actors that perform tasks on behalf of users.

### Agent Types

| Type | Description | Use Cases |
|------|-------------|-----------|
| **Assistant** | Interactive helpers | Chatbots, Q&A, recommendations |
| **Worker** | Background processors | Data enrichment, automation |
| **Analyst** | Data analysts | Insights, forecasting, reporting |
| **Creator** | Content generators | Emails, documents, designs |

### Sales Assistant Agent

```typescript
import type { Agent } from '@objectstack/spec/ai';

export const SalesAssistantAgent: Agent = {
  name: 'sales_assistant',
  label: 'Sales Assistant',
  description: 'AI agent to help sales reps with lead qualification',
  
  role: 'assistant',
  
  instructions: `You are a sales assistant AI.
  
Your responsibilities:
1. Qualify incoming leads (BANT criteria)
2. Suggest next best actions
3. Draft personalized emails
4. Analyze win/loss patterns

Always be professional and data-driven.`,

  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0.7,
    maxTokens: 2000,
  },
  
  // Tools the agent can use
  tools: [
    {
      name: 'analyze_lead',
      description: 'Analyze a lead and provide qualification score',
      parameters: {
        lead_id: 'string',
      },
    },
    {
      name: 'suggest_next_action',
      description: 'Suggest next best action for an opportunity',
      parameters: {
        opportunity_id: 'string',
      },
    },
    {
      name: 'generate_email',
      description: 'Generate a personalized email template',
      parameters: {
        recipient_id: 'string',
        context: 'string',
        tone: 'string',
      },
    },
  ],
  
  // Knowledge sources
  knowledge: {
    sources: [
      {
        type: 'object',
        objectName: 'lead',
        fields: ['*'],
      },
      {
        type: 'object',
        objectName: 'opportunity',
        fields: ['*'],
      },
      {
        type: 'document',
        path: '/knowledge/sales-playbook.md',
      },
    ],
  },
  
  // When to trigger the agent
  triggers: [
    {
      type: 'object_create',
      objectName: 'lead',
      condition: 'rating = "hot"',
    },
    {
      type: 'object_update',
      objectName: 'opportunity',
      condition: 'ISCHANGED(stage)',
    },
  ],
};
```

### Customer Service Agent

```typescript
export const ServiceAgent: Agent = {
  name: 'service_agent',
  label: 'Customer Service Agent',
  description: 'AI agent to assist with support cases',
  
  role: 'assistant',
  
  instructions: `You are a customer service AI agent.
  
Your responsibilities:
1. Triage incoming cases
2. Suggest relevant knowledge articles
3. Draft response templates
4. Escalate critical issues

Always be empathetic and solution-focused.`,

  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0.5,
    maxTokens: 1500,
  },
  
  tools: [
    {
      name: 'triage_case',
      description: 'Analyze case and assign priority',
      parameters: {
        case_id: 'string',
      },
    },
    {
      name: 'search_knowledge',
      description: 'Search knowledge base for solutions',
      parameters: {
        query: 'string',
      },
    },
    {
      name: 'generate_response',
      description: 'Generate customer response',
      parameters: {
        case_id: 'string',
        tone: 'string',
      },
    },
  ],
  
  knowledge: {
    sources: [
      {
        type: 'object',
        objectName: 'case',
        fields: ['*'],
      },
      {
        type: 'document',
        path: '/knowledge/support-kb/**/*.md',
      },
    ],
  },
  
  triggers: [
    {
      type: 'object_create',
      objectName: 'case',
    },
  ],
};
```

### Lead Enrichment Agent (Worker)

```typescript
export const LeadEnrichmentAgent: Agent = {
  name: 'lead_enrichment',
  label: 'Lead Enrichment Agent',
  description: 'Automatically enrich lead data',
  
  role: 'worker',
  
  instructions: `You enrich lead records with additional data.
  
Tasks:
1. Look up company information
2. Enrich contact details
3. Add firmographic data
4. Research technology stack

Use reputable data sources.`,

  model: {
    provider: 'openai',
    model: 'gpt-3.5-turbo',
    temperature: 0.3,
    maxTokens: 1000,
  },
  
  tools: [
    {
      name: 'lookup_company',
      description: 'Look up company information',
      parameters: {
        company_name: 'string',
        domain: 'string',
      },
    },
    {
      name: 'enrich_contact',
      description: 'Enrich contact information',
      parameters: {
        email: 'string',
      },
    },
  ],
  
  triggers: [
    {
      type: 'object_create',
      objectName: 'lead',
    },
  ],
  
  schedule: {
    type: 'cron',
    expression: '0 */4 * * *', // Every 4 hours
    timezone: 'UTC',
  },
};
```

### Revenue Intelligence Agent (Analyst)

```typescript
export const RevenueIntelligenceAgent: Agent = {
  name: 'revenue_intelligence',
  label: 'Revenue Intelligence Agent',
  description: 'Analyze pipeline and provide insights',
  
  role: 'analyst',
  
  instructions: `You analyze sales data and provide insights.
  
Responsibilities:
1. Analyze pipeline health
2. Identify at-risk deals
3. Forecast revenue
4. Detect anomalies
5. Generate executive summaries

Use statistical analysis and ML.`,

  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0.2,
    maxTokens: 3000,
  },
  
  tools: [
    {
      name: 'analyze_pipeline',
      description: 'Analyze sales pipeline health',
      parameters: {
        user_id: 'string',
        time_period: 'string',
      },
    },
    {
      name: 'forecast_revenue',
      description: 'Generate revenue forecast',
      parameters: {
        time_period: 'string',
        method: 'string',
      },
    },
  ],
  
  knowledge: {
    sources: [
      {
        type: 'object',
        objectName: 'opportunity',
        fields: ['*'],
      },
      {
        type: 'analytics',
        dashboardName: 'sales_dashboard',
      },
    ],
  },
  
  schedule: {
    type: 'cron',
    expression: '0 8 * * 1', // Monday at 8am
    timezone: 'America/Los_Angeles',
  },
};
```

---

## Actions as Tools (explicit opt-in)

Declarative `Action` metadata can be exposed as AI-callable tools, named
`action_<actionName>`, but exposure is **explicit opt-in**. Add an `ai:` block
with `exposed: true` and an LLM-facing `description`; otherwise the action stays
human/UI-only.

```typescript
export const triageCaseAction = {
  name: 'triage_case',
  label: 'Triage Case',
  objectName: 'case',
  type: 'flow',
  target: 'case_triage',
  ai: {
    exposed: true,
    description: 'Triage a support case, suggest priority, and assign the next support queue.',
    category: 'action',
    requiresConfirmation: false,
  },
};
```

When the LLM picks an exposed action, the AI runtime dispatches to the same
handler the Console row toolbar would invoke, so your business logic stays in
one place.

### What gets exposed

The AI runtime walks every registered object's `actions[]` and registers only
actions with `ai.exposed === true` that also pass safety and wiring checks.
Three action types are supported:

| `action.type` | Dispatch path | Wiring needed |
|:---|:---|:---|
| `script` | `IDataEngine.executeAction(object, target, ctx)` — the same call Studio makes | none beyond the metadata service |
| `api` | HTTP call to `action.target` via `ApiActionClient` (default: `fetch`) | `apiBaseUrl` (or a custom `apiClient`) |
| `flow` | `IAutomationService.execute(target, { triggerData })` | `automation` service registered |

Console-only types (`url`, `modal`, `form`) are always skipped. Dangerous
actions — those with `confirmText`, `mode: 'delete'`, `variant: 'danger'`, or
`ai.requiresConfirmation: true` — are skipped unless the
[HITL approval queue](#human-in-the-loop-approval) is wired. To assert that a
destructive-looking action is safe for autonomous execution, set
`ai.requiresConfirmation: false`.

### Wiring it up

```typescript
import { AIServicePlugin } from '@objectstack/service-ai';

kernel.use(
  new AIServicePlugin({
    // Enables type:'api' action dispatch. Relative `target` paths
    // ('/api/v1/...') are resolved against this base URL.
    apiActionBaseUrl: process.env.OS_AI_ACTION_API_BASE_URL,
    // Forwarded on every api-action HTTP call (auth, environment id, ...).
    apiActionHeaders: { Authorization: `Bearer ${process.env.SERVICE_TOKEN}` },
  }),
);
```

If `automation` is already registered with the kernel, `type:'flow'` actions are picked up automatically — no extra wiring needed.

### `type:'api'` body assembly

For api actions, the request body is built from three sources (last wins):

1. **User-collected params** (the keys the LLM filled in).
2. **`recordIdParam`** — when row-context, the id is placed flat at `action.recordIdParam`, using `recordIdField` (default `'id'`) to pick the value off the record.
3. **`bodyExtra`** — constant fields that always override.

`bodyShape: { wrap: 'data' }` nests the user params under `data` while keeping `recordIdParam` flat — matching shapes like better-auth's `organization/update`.

### Diagnostics

`registerActionsAsTools()` returns `{ registered, skipped, warnings }`. The
diagnostics include reasons such as `"not AI-exposed"`,
`"no apiClient or apiBaseUrl configured"`, or
`"requires confirmation ... wire HITL approval"` so action authors can see
whether their action will be LLM-callable.

### Human-In-The-Loop approval

Destructive actions are too risky to let the LLM execute autonomously, but locking them away entirely defeats agentic UX. The HITL queue strikes the balance: the LLM gets to **propose** the call, a human gets to **approve** it.

Enable the queue via the plugin option (default: off):

```typescript
kernel.use(
  new AIServicePlugin({
    enableActionApproval: true,
    apiActionBaseUrl: process.env.OS_AI_ACTION_API_BASE_URL,
  }),
);
```

When the LLM picks an approval-gated tool (e.g. `action_delete_task`), the runtime:

1. Persists an `ai_pending_actions` row with `status:'pending'`, `tool_input`, `proposed_by`, etc.
2. Returns `{ status: 'pending_approval', pendingActionId }` to the model so it can summarise (e.g. "I've requested approval to delete task #42.").
3. The operator sees the proposal in the **AI Pending Actions** Studio inbox.
4. Clicking **Approve** calls `POST /api/v1/ai/pending-actions/:id/approve` (`ai:approve` permission). The service looks up the pre-registered dispatcher and executes the action with HITL routing disabled, so the same code path runs. The row transitions to `executed` (or `failed` if dispatch threw).
5. Clicking **Reject** calls `POST /api/v1/ai/pending-actions/:id/reject` with an optional reason.

The queue is exposed via four REST endpoints (`GET`, `GET/:id`, `POST/:id/approve`, `POST/:id/reject`) and via `IAIService.{proposePendingAction,approvePendingAction,rejectPendingAction,listPendingActions}` for programmatic use.

**Why a dedicated queue (not the multi-step `IApprovalService`)?** AI tool-call HITL is ephemeral: subject is the proposed *call*, not a stable record state; there's no predefined process; operators expect single-click yes/no. The pending-action queue is a thin write log + dispatcher map that delivers that UX without dragging in process-engine overhead.

#### End-to-end example

Two runnable demos live in `examples/app-todo/test/`:

- **`ai-hitl.test.ts`** — drives the tool registry directly (no LLM dependency). Asserts the full lifecycle: `variant:'danger'` action registered as a tool → invocation returns `pending_approval` without executing → row persisted → `approvePendingAction(id, actor)` re-runs the handler → row transitions to `executed`. Reject path is also covered.

  ```bash
  pnpm --filter @example/app-todo test:hitl
  ```

- **`ai-hitl-llm.test.ts`** — same scenario but with a real model behind Vercel AI Gateway. The LLM is given the auto-generated tool description and asked to "delete all completed tasks"; the framework returns `pending_approval` so the model summarises the wait instead of retrying. After we call approve from the test, the deletion completes.

  ```bash
  AI_GATEWAY_API_KEY=vck_... pnpm --filter @example/app-todo test:hitl:llm
  ```

  Gated on `AI_GATEWAY_API_KEY` (or `OPENAI_API_KEY`); exits 0 with a notice if no key is provided, so it can be wired into CI without leaking spend.

A trimmed view of the integration path:

```typescript
// 1. Plugin boots with approval gating enabled
new AIServicePlugin({
  adapter: new VercelLLMAdapter({ model }),
  enableActionApproval: true, // ← opt-in
})

// 2. LLM picks the gated tool — handler short-circuits to pending
const result = await aiService.toolRegistry.execute({
  type: 'tool-call',
  toolName: 'action_delete_completed',
  input: {},
} as never);
const envelope = JSON.parse((result.output as { value: string }).value);
// → { ok: true, status: 'pending_approval', pendingActionId: 'pa_...' }

// 3. Operator approves (REST or programmatic)
const outcome = await ai.approvePendingAction(envelope.pendingActionId, 'alice@example.com');
// → { status: 'executed', result: <handler return> }
```

### Permission-aware execution (RLS for agents)

Every AI tool call now runs with the **end-user's** `ExecutionContext`,
so the same row-level-security rules that protect the REST API
automatically scope what an agent can see and do. There is no separate
"agent permission" surface to maintain — if a user can't read account
`acc_42` through ObjectQL, neither can the LLM acting on their behalf.

**How it works:**

1. The REST routes for `/api/v1/ai/assistant/chat` and
   `/api/v1/ai/agents/:id/chat` pull the authenticated principal out of
   `req.user` and forward it to `aiService.chatWithTools(...)` as
   `toolExecutionContext: { actor, conversationId, environmentId }`.
   Both **cookie session** (`better-auth.session_token`) and **Bearer
   token** auth are resolved automatically by the dispatcher — your
   browser chats and your scripted API calls land in the same RLS
   context as a plain ObjectQL request from the same user.
2. The tool registry threads that context into every handler invocation.
3. Built-in data tools (`query_records`, `get_record`,
   `aggregate_data`) and auto-generated `action_*` tools convert the
   actor into a `{ userId, roles, permissions, isSystem: false }`
   engine context on each `IDataEngine` call. RLS engages exactly as
   it would for a hand-rolled API endpoint.
4. Action audit logs attribute the dispatch to the real user instead of
   a generic "AI Assistant" principal.

When you invoke `chatWithTools` from a custom server route, opt in by
passing the actor explicitly:

```typescript
await aiService.chatWithTools(messages, tools, {
  toolExecutionContext: {
    actor: {
      id: currentUser.id,
      name: currentUser.displayName,
      roles: currentUser.roles,
      permissions: currentUser.permissions,
    },
    conversationId,
    environmentId,
  },
});
```

Omit `toolExecutionContext` to keep the previous system-level behaviour
(used by cron jobs, internal callers, and the existing test suite).

### LLM-generated conversation titles

Whenever the assistant has produced its **second** message in a
conversation that still has no title, the AI service fires a short,
out-of-band LLM call (`≤ 20 characters`, single-line, no quotes) to
summarise what the user is asking about and writes the result back to
`ai_conversations.title`. The summarise call:

- Reuses the **active chat provider** (so it works with any
  `provider/model` you configured via Console → Settings → AI), and
  honours the same `${provider}_base_url` you wired for chat —
  including custom OpenAI-compatible endpoints (SiliconFlow, DeepSeek,
  a local OpenAI proxy, …).
- Runs as a fire-and-forget task with a short timeout — if the title
  call fails, the chat reply is unaffected and the conversation simply
  keeps its placeholder name.
- Skips re-titling once `title` is set, so manual edits via the
  conversation API are never overwritten.

The Console sidebar groups conversations by recency and displays the
title; if you ever need to force a regenerate, clear the field via the
`ai_conversations` REST API and send another message.

---

## Knowledge Protocol (RAG via adapter plugins)

ObjectStack ships a Knowledge Protocol that lets agents call `search_knowledge(query, sourceIds?, topK?)` against pluggable backends (RAGFlow, LlamaIndex, Dify, custom pgvector, …). The framework defines the contract and runs permission-aware filtering; the adapter plugin does the actual retrieval. See [the protocol design](../protocol/knowledge.mdx) for the rationale.

### Wiring

```ts
import { ObjectKernel } from '@objectstack/core';
import { KnowledgeServicePlugin } from '@objectstack/service-knowledge';
import { KnowledgeMemoryPlugin } from '@objectstack/knowledge-memory';
// or, for prod:
// import { KnowledgeRagflowPlugin } from '@objectstack/knowledge-ragflow';

const kernel = new ObjectKernel();
kernel.use(new KnowledgeServicePlugin({
  sources: [
    {
      id: 'task_notes',
      label: 'Task notes',
      adapter: 'memory',
      source: { kind: 'object', object: 'task', contentFields: ['title', 'notes'] },
    },
    {
      id: 'product_docs',
      label: 'Product docs',
      adapter: 'ragflow',
      source: { kind: 'http', urls: ['https://docs.example.com/sitemap.xml'] },
      options: { datasetId: 'rgf_doc_dataset' },
    },
  ],
}));
kernel.use(new KnowledgeMemoryPlugin());
// kernel.use(new KnowledgeRagflowPlugin({ endpoint, apiKey }));
```

Then register the AI tool so agents can call it:

```ts
import { registerKnowledgeTools } from '@objectstack/service-ai';

ctx.hook('ai:ready', async (ai) => {
  const knowledge = ctx.getService('knowledge');
  registerKnowledgeTools(ai.toolRegistry, { knowledgeService: knowledge });
});
```

### What you get for free

- **Permission-aware retrieval.** Every hit with a `sourceRecordId` is re-checked against the caller's `ExecutionContext` via `IDataEngine` — the same RLS that gates plain ObjectQL queries. A salesperson asking "find proposals about ACME" only sees the proposals they could already read directly. File / HTTP hits pass through (ACL is the adapter's problem).
- **Inline event sync.** When records on indexed objects change, the kernel's `IRealtimeService` events drive `KnowledgeService.handleRecordUpsert/Delete` automatically. No cron, no queue (yet — Phase 2).
- **Adapter swap, zero LLM changes.** Move from `memory` to `ragflow` to a custom adapter without touching the `search_knowledge` tool or any agent prompt.

### Why we did not build a vector DB

Mature OSS (RAGFlow, LlamaIndex, Dify) already nail chunking, embedding, hybrid retrieval, rerank, and OCR. ObjectStack's value is **the metadata-native source declaration + the permission-aware filter on top of those engines** — not yet-another-RAG-stack. Customers pick the retrieval engine that matches their data; we make sure it talks to ObjectStack the same way every time.

---

## RAG Pipelines

Retrieval-Augmented Generation (RAG) provides AI with access to your knowledge base.

### Sales Knowledge RAG

```typescript
import type { RagPipeline } from '@objectstack/spec/ai';

export const SalesKnowledgeRAG: RagPipeline = {
  name: 'sales_knowledge',
  label: 'Sales Knowledge Pipeline',
  description: 'Sales playbook and best practices',
  
  // Define indexes
  indexes: [
    {
      name: 'sales_playbook_index',
      type: 'vector',
      
      // Data sources
      sources: [
        {
          type: 'document',
          path: '/knowledge/sales/**/*.md',
          watch: true, // Auto-update on changes
        },
        {
          type: 'document',
          path: '/knowledge/products/**/*.pdf',
          watch: true,
        },
        {
          type: 'object',
          objectName: 'opportunity',
          fields: ['name', 'description', 'stage', 'amount'],
          filter: {
            stage: 'closed_won',
            close_date: { $gte: '{last_12_months}' },
          },
        },
      ],
      
      // Embedding model
      embedding: {
        provider: 'openai',
        model: 'text-embedding-3-large',
        dimensions: 1536,
      },
      
      // Chunking strategy
      chunking: {
        strategy: 'semantic',
        chunkSize: 1000,
        chunkOverlap: 200,
      },
      
      // Metadata extraction
      metadata: {
        extractors: [
          {
            type: 'title',
            source: 'filename',
          },
          {
            type: 'date',
            source: 'modified_date',
          },
        ],
      },
    },
  ],
  
  // Retrieval strategy
  retrieval: {
    strategy: 'hybrid',
    
    vectorSearch: {
      topK: 10,
      scoreThreshold: 0.7,
      algorithm: 'cosine',
    },
    
    keywordSearch: {
      enabled: true,
      weight: 0.3,
    },
    
    reranking: {
      enabled: true,
      model: 'cohere-rerank',
      topK: 5,
    },
  },
  
  // Generation
  generation: {
    model: {
      provider: 'openai',
      model: 'gpt-4',
      temperature: 0.7,
      maxTokens: 2000,
    },
    
    promptTemplate: `You are a sales expert. Use the context to answer.

Context:
{context}

Question: {question}

Answer based on the context. If uncertain, say so.`,
  },
  
  // Caching
  caching: {
    enabled: true,
    ttl: 3600, // 1 hour
  },
};
```

### Support Knowledge RAG

```typescript
export const SupportKnowledgeRAG: RagPipeline = {
  name: 'support_knowledge',
  label: 'Support Knowledge Pipeline',
  description: 'Customer support knowledge base',
  
  indexes: [
    {
      name: 'support_kb_index',
      type: 'vector',
      
      sources: [
        {
          type: 'document',
          path: '/knowledge/support/**/*.md',
          watch: true,
        },
        {
          type: 'object',
          objectName: 'case',
          fields: ['subject', 'description', 'resolution'],
          filter: {
            is_closed: true,
            resolution: { $ne: null },
          },
        },
      ],
      
      embedding: {
        provider: 'openai',
        model: 'text-embedding-3-small',
        dimensions: 768,
      },
      
      chunking: {
        strategy: 'fixed',
        chunkSize: 512,
        chunkOverlap: 100,
      },
    },
  ],
  
  retrieval: {
    strategy: 'vector_only',
    
    vectorSearch: {
      topK: 5,
      scoreThreshold: 0.75,
      algorithm: 'cosine',
    },
  },
  
  generation: {
    model: {
      provider: 'openai',
      model: 'gpt-4',
      temperature: 0.3,
      maxTokens: 1500,
    },
    
    promptTemplate: `You are a support specialist. Help resolve customer issues.

Knowledge Base:
{context}

Issue: {question}

Provide a clear, step-by-step solution.`,
  },
};
```

---

## Natural Language Queries

Allow users to query data using natural language.

### NLQ Configuration

```typescript
import type { NLQConfig } from '@objectstack/spec/ai';

export const CrmNLQConfig: NLQConfig = {
  name: 'crm_nlq',
  label: 'CRM Natural Language Queries',
  
  // Objects available for querying
  objects: [
    'account',
    'contact',
    'lead',
    'opportunity',
    'case',
  ],
  
  // LLM for query translation
  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0,
  },
  
  // Example queries for training
  examples: [
    {
      query: 'Show me all high-value opportunities closing this quarter',
      soql: 'SELECT Name, Amount, CloseDate FROM Opportunity WHERE Amount > 100000 AND CloseDate >= THIS_QUARTER',
    },
    {
      query: 'Which accounts have the most open cases?',
      soql: 'SELECT Account.Name, COUNT(Id) FROM Case WHERE IsClosed = false GROUP BY Account.Name ORDER BY COUNT(Id) DESC',
    },
  ],
};
```

### Using NLQ

```typescript
// User asks in natural language
const query = "Show me my top 10 opportunities by amount";

// AI translates to SOQL
const soql = await nlq.translate(query);
// Result: SELECT Name, Amount FROM Opportunity WHERE OwnerId = '{userId}' ORDER BY Amount DESC LIMIT 10

// Execute query
const results = await execute(soql);
```

---

## Predictive Analytics

Use machine learning for predictions and recommendations.

### Lead Scoring

```typescript
import type { PredictiveModel } from '@objectstack/spec/ai';

export const LeadScoringModel: PredictiveModel = {
  name: 'lead_scoring',
  label: 'Lead Scoring Model',
  description: 'Predict lead conversion probability',
  
  type: 'classification',
  
  trainingData: {
    objectName: 'lead',
    features: [
      'annual_revenue',
      'number_of_employees',
      'industry',
      'lead_source',
      'rating',
    ],
    label: 'is_converted',
    filter: {
      created_date: { $gte: '{last_12_months}' },
    },
  },
  
  model: {
    algorithm: 'gradient_boosting',
    hyperparameters: {
      n_estimators: 100,
      learning_rate: 0.1,
      max_depth: 5,
    },
  },
  
  deployment: {
    mode: 'realtime',
    trigger: 'on_create',
    outputField: 'conversion_score',
  },
};
```

### Revenue Forecasting

```typescript
export const RevenueForecastModel: PredictiveModel = {
  name: 'revenue_forecast',
  label: 'Revenue Forecasting Model',
  description: 'Forecast monthly revenue',
  
  type: 'regression',
  
  trainingData: {
    objectName: 'opportunity',
    features: [
      'amount',
      'probability',
      'stage',
      'age_days',
      'account.annual_revenue',
    ],
    label: 'close_date',
    filter: {
      stage: { $in: ['closed_won', 'closed_lost'] },
    },
  },
  
  model: {
    algorithm: 'time_series',
    method: 'prophet',
  },
  
  deployment: {
    mode: 'batch',
    schedule: '0 0 1 * *', // Monthly
  },
};
```

---

## Best Practices

### 1. AI Agent Design

✅ **DO:**
- Define clear agent roles and responsibilities
- Provide detailed instructions
- Use appropriate temperature settings
- Test with real-world scenarios
- Monitor agent performance

❌ **DON'T:**
- Give agents conflicting instructions
- Use high temperatures for factual tasks
- Deploy without testing
- Ignore cost implications

### 2. RAG Pipeline Design

✅ **DO:**
- Use semantic chunking for better context
- Implement hybrid search (vector + keyword)
- Enable reranking for accuracy
- Cache frequently accessed content
- Monitor retrieval quality

❌ **DON'T:**
- Chunk too large or too small
- Rely solely on vector search
- Skip metadata extraction
- Forget to update indexes

### 3. Prompt Engineering

✅ **DO:**
- Be specific and clear
- Provide examples
- Use role-playing ("You are a...")
- Include constraints
- Test variations

❌ **DON'T:**
- Be vague or ambiguous
- Assume context
- Use complex jargon
- Write overly long prompts

### 4. Cost Management

✅ **DO:**
- Choose appropriate models (GPT-3.5 vs GPT-4)
- Implement caching
- Set token limits
- Monitor usage
- Use cheaper models for simple tasks

❌ **DON'T:**
- Always use the most expensive model
- Skip caching
- Allow unlimited tokens
- Ignore cost metrics

### 5. Security & Privacy

✅ **DO:**
- Implement access controls
- Mask sensitive data
- Log AI interactions
- Review outputs
- Follow data privacy regulations

❌ **DON'T:**
- Expose PII to external APIs
- Skip output validation
- Ignore audit trails
- Trust AI blindly

---

## Real-World Integration

### Complete Sales AI Workflow

```typescript
// 1. Lead comes in
LeadEnrichmentAgent.enrich(lead);

// 2. AI scores the lead
score = LeadScoringModel.predict(lead);
lead.conversion_score = score;

// 3. If hot, sales assistant qualifies
if (score > 80) {
  analysis = SalesAssistantAgent.analyze(lead);
  task = createTask({
    subject: `Follow up on hot lead: ${lead.name}`,
    priority: 'high',
    assignedTo: lead.owner,
  });
}

// 4. Sales rep asks for help
response = SalesKnowledgeRAG.query({
  question: "What's our pitch for fintech companies?",
  context: { industry: 'finance' },
});

// 5. Generate personalized email
email = SalesAssistantAgent.generateEmail({
  recipient: lead,
  context: analysis,
  tone: 'professional',
});

// 6. Track in pipeline
RevenueIntelligenceAgent.analyzePipeline();
```

---

**Next:** [Coding Standards →](/docs/guides/standards)
