ObjectStackObjectStack

AI Capabilities Guide

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
  2. AI Agents
  3. Actions as Tools (auto-exposed)
  4. RAG Pipelines
  5. Natural Language Queries
  6. Predictive Analytics
  7. 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

TypeDescriptionUse Cases
AssistantInteractive helpersChatbots, Q&A, recommendations
WorkerBackground processorsData enrichment, automation
AnalystData analystsInsights, forecasting, reporting
CreatorContent generatorsEmails, documents, designs

Sales Assistant Agent

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

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)

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)

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 (auto-exposed)

Every declarative Action you attach to an object is automatically exposed as an AI tool, named action_<actionName>. When the LLM picks one, the AI runtime dispatches to the same handler Studio's row toolbar would invoke — your business logic stays in one place.

What gets exposed

The AI runtime walks every registered object's actions[] and exposes the ones that pass safety + wiring checks. Three action types are supported:

action.typeDispatch pathWiring needed
scriptIDataEngine.executeAction(object, target, ctx) — the same call Studio makesnone beyond the metadata service
apiHTTP call to action.target via ApiActionClient (default: fetch)apiBaseUrl (or a custom apiClient)
flowIAutomationService.execute(target, { triggerData })automation service registered

Studio-only types (url, modal, form) are always skipped. Dangerous actions — those with confirmText, mode: 'delete', or variant: 'danger' — are skipped by default, but can be opted into the HITL approval queue so an operator authorises every call. Owners can also opt out per action with aiExposed: false.

Wiring it up

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 }. Studio surfaces the skipped list with reasons (e.g. "no apiClient or apiBaseUrl configured", "mode='delete' — destructive") so action authors can see at a glance 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):

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.

    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.

    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:

// 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:

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 for the rationale.

Wiring

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:

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

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

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

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

// 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

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

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

// 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 →

On this page