AI Agents

Add artificial intelligence capabilities to your Appivo applications

AI Agents

An AI agent is a named, reusable AI capability you invoke from your app. Give it inputs, it runs an LLM-driven pipeline — optionally calling tools, searching your data, or chaining several steps — and returns a result you can use in code or show in the UI.

Agents are good at the work that's awkward to write by hand: generating code, analysing text, translating, transcribing media, answering questions over your own content, and turning messy input into structured output.

There are two ways in:

  • Use a pre-built agent — Appivo ships a library of ready-made agents. Call one and you're done.
  • Build a custom agent — define your own agent as JSON: its inputs, model, prompts, tools, and steps.

Start with the pre-built agents; reach for a custom agent when none of them fit.

Pre-built agents

Appivo includes a library of pre-built agents you can call immediately. The most commonly used:

AgentWhat it does
generate-codeGenerates JavaScript for actions, following Appivo patterns
generate-data-modelCreates Appivo data models from a description
generate-queryBuilds optimized database queries
frontend-code-analyzerReviews code for security, performance, and best practices
clean-htmlCleans and sanitizes HTML
extract-keywordsPulls keywords from text
translateTranslates text between languages
transcribe-audioTranscribes audio to text
transcribe-imageExtracts text from images (OCR)
generate-responseGenerates a contextual response, often over retrieved content

Calling an agent

How you call an agent depends on where your code runs.

WhereHow
Server-side actioncontext.getAIFunctions().invokeAgent(name, options)
Client-side UIthe <aibutton> widget
Custom client UIa server action that proxies invokeAgent

From a server-side action

Call invokeAgent with the agent name and an arguments object. The result has a success flag, plus result or error:

const aiFunctions = context.getAIFunctions();

const aiResult = await aiFunctions.invokeAgent('generate-code', {
    arguments: {
        instructions: 'Create a function to calculate order totals with tax',
        context: { model: 'Order' }
    }
});

if (aiResult.success) {
    return { success: true, data: aiResult.result };
}
return { success: false, error: aiResult.error };

invokeAgent accepts a few optional fields alongside arguments:

await aiFunctions.invokeAgent('agent-name', {
    arguments: { query: 'user question', data: someData },

    // Prior turns, for conversational context
    history: [
        { role: 'user', message: 'previous question' },
        { role: 'ai',   message: 'previous response' }
    ],

    // Turn specific built-in tools off for this call
    disableTools: ['webSearch'],

    // Override the agent's prompts for this call
    prompts: {
        system: 'Custom system prompt',
        user: 'Custom user prompt template'
    }
});

From the UI

Drop an <aibutton> into a view to run an agent from the client. Wire onresult to handle the output and onask to show a loading state:

<aibutton
    :agent="'generate-data-model'"
    :onresult="handleAiResult"
    :onask="showLoadingState">
</aibutton>
methods: {
    handleAiResult(data) {
        this.processGeneratedModel(data);
    },
    showLoadingState() {
        this.isProcessing = true;
    }
}

From a custom client UI

For custom interfaces, wrap invokeAgent in a server action and call that action from the client:

// Server action: InvokeAIAgent
const result = await context.getAIFunctions().invokeAgent(params.agent, {
    arguments: params.arguments,
    history: params.history
});
return result;
// Client
const result = await context.executeAction('InvokeAIAgent', {
    agent: 'generate-code',
    arguments: { instructions: userPrompt, model: currentModel }
});

Utility AI functions

Beyond invokeAgent, the AI Functions API exposes a few direct helpers:

const aiFunctions = context.getAIFunctions();

// Detect the language of some text → { language: "English", code: "en" }
const langInfo = aiFunctions.detectLanguage(text);

// Summarize a document
const summary = aiFunctions.summarizeDocument(document, {
    chunkSize: 4000,
    maxOutputTokens: 1000,
    temperature: 0.2,
    model: 'gemini-2.5-flash'
});

How an agent is built

A custom agent is a small amount of JSON. Three pieces nest inside each other:

  • Agent — the named, reusable capability, with its inputs and output shape.
  • Chain — the pipeline the agent runs: its tools and its steps.
  • Step — one model call, with its own model, prompts, optional retriever, and tool access. Steps run in order and can branch.
AI Agenta named, reusable capabilityChainthe pipeline the agent runsInputToolsOutputSteps run in orderStep 1Model & parametersSystem & user promptRetriever (optional)Tool callsStep 2Model & parametersSystem & user promptRetriever (optional)Tool callsStep 3 …chain as many stepsas the task needs
An agent runs a chain; the chain runs one or more steps in order.

Building a custom agent

Define the agent as JSON, then register it (see Registering an agent). A complete example:

{
  "name": "CustomerInsightsAgent",
  "description": "Analyzes customer feedback and generates actionable insights",
  "version": 1,

  "input": [
    { "name": "feedback", "type": "string", "description": "Feedback text to analyze" },
    { "name": "category", "type": "string", "description": "product | service | support" }
  ],

  "output": {
    "type": "json",
    "example": {
      "sentiment": "positive|neutral|negative",
      "score": 0.85,
      "insights": ["insight1", "insight2"],
      "actionItems": ["action1", "action2"]
    }
  },

  "steps": [
    {
      "name": "AnalyzeStep",
      "maxCycles": 3,
      "model": {
        "provider": "google",
        "name": "gemini-2.5-pro",
        "temperature": 0.7,
        "maxOutputTokens": 4096
      },
      "systemPrompt": {
        "text": "You are an expert customer feedback analyst."
      },
      "userPrompt": {
        "type": "template",
        "text": "Category: {{input.category}}\nFeedback: {{input.feedback}}\n\nAnalyze and provide insights."
      },
      "output": { "type": "json" }
    }
  ]
}

Inputs and output

Declare the inputs the agent accepts and the shape it returns.

  • Input types: string, number, boolean, object, array.
  • Output types: string (plain text), json (structured), html (formatted).

For json output, include an example — the model uses it as the target shape.

Model

Each step picks its own model and parameters:

"model": {
  "provider": "google|openai|anthropic",
  "name": "model-name",
  "temperature": 0.7,
  "maxOutputTokens": 4096,
  "topP": 0.95,
  "topK": 40
}
ProviderModels
Googlegemini-2.5-pro, gemini-2.5-flash, gemini-3-flash-preview
OpenAIgpt-4o, gpt-4o-mini, gpt-4-turbo
Anthropicclaude-3-haiku, claude-3-sonnet, claude-sonnet-4-20250514

Match the model to the job: a fast, cheap model (gemini-2.5-flash, gpt-4o-mini) for simple steps, a stronger one (gemini-2.5-pro) where reasoning matters.

Prompts

A step has a systemPrompt (who the model is) and a userPrompt (the task). Three forms:

// Plain text
"systemPrompt": { "text": "You are a helpful assistant." }

// Template — interpolate inputs with {{ }}
"userPrompt": {
  "type": "template",
  "text": "Process {{input.data}} for {{input.userName}}"
}

// Multi-part — mix text with document and image attachments
"userPrompt": {
  "parts": [
    { "type": "text", "text": "Analyze this document:" },
    { "name": "document", "type": "document" },
    { "name": "image", "type": "image" }
  ]
}

Multiple steps and branching

Chain steps for multi-stage work. A step's links route to the next step based on its output, so an agent can branch on what it finds:

Extract stepreads its own outputProcess invoiceProcess receipttype == invoicetype == receipt
Each link carries a condition on the step's output; the first match decides which step runs next.
{
  "name": "DocumentProcessor",
  "steps": [
    {
      "name": "ExtractInfo",
      "model": { "provider": "google", "name": "gemini-2.5-flash" },
      "output": { "type": "json" },
      "links": [
        { "condition": "output.documentType == 'invoice'", "nextStep": "ProcessInvoice" },
        { "condition": "output.documentType == 'receipt'", "nextStep": "ProcessReceipt" }
      ]
    },
    { "name": "ProcessInvoice", "model": { "provider": "google", "name": "gemini-2.5-pro" } },
    { "name": "ProcessReceipt", "model": { "provider": "google", "name": "gemini-2.5-flash" } }
  ]
}

Tools

Tools are capabilities the model can decide to call mid-turn — search your indexes, read a web page, summarize a document, generate a chart or image, run sandboxed code, remember and recall facts, or query a knowledge dataset. Declare them in the chain's tools array; the type field selects each one:

{
  "name": "DataAnalysisAgent",
  "tools": [
    { "type": "SEARCH", "index": "product-docs", "limit": 8 },
    { "type": "executeCode" }
  ],
  "steps": [
    {
      "name": "Analyze",
      "maxCycles": 5,
      "systemPrompt": { "text": "Use the tools to answer questions about the app's data." }
    }
  ]
}

A step that calls tools needs room to work: a tool call plus a final answer is at least two model turns, so set maxCycles to 2 or more (higher if you expect several calls in a row).

For every built-in tool — each type, its configuration, defaults, and a copy-paste cheat-sheet — see the AI Agent Tools reference.

Conversation memory

For conversational agents, control how much history a step carries:

{
  "name": "ConversationalStep",
  "chatWindowSize": 50,
  "chatWindowTokens": 32768,
  "includeFormat": true
}

For durable, per-user memory that persists across conversations — preferences, facts, decisions — use the memory tools and the AI Memory platform instead.

RAG: answering from your own content

Retrieval-Augmented Generation lets an agent answer from your documents and data instead of the model's general knowledge. You index your content in a search index, then a step retrieves the most relevant pieces and hands them to the model as context.

Your questionSearch indexyour contentModelAnswer
The question drives a search over your indexed content; the matches become context for the model's answer.

Create a search index

In the builder, go to Logic → Search Indexes, create an index, pick the data model and attributes to include, and choose a search type:

TypeHow it matches
TEXTKeyword search (BM25)
VECTORSemantic similarity, using embeddings
BOTHHybrid — combines both with Reciprocal Rank Fusion

Indexing options let you preprocess text before indexing, add transformers to generate extra searchable content, and control chunking (2000 characters max per chunk, 200-character overlap by default).

Retrieve inside a step

Attach a retriever to a step; the results land in {{retrievedDocuments}} for the prompt:

{
  "name": "AnswerWithContext",
  "retriever": {
    "type": "searchIndex",
    "indexName": "KnowledgeBase",
    "maxResults": 5,
    "minScore": 0.7
  },
  "model": { "provider": "google", "name": "gemini-2.5-pro" },
  "systemPrompt": { "text": "Answer using the provided context. If it isn't there, say so." },
  "userPrompt": {
    "type": "template",
    "text": "Context:\n{{retrievedDocuments}}\n\nQuestion: {{input.question}}"
  }
}

Search from an action

You can also search directly and pass the results to an agent yourself:

const searchResults = await context.searchIndex('KnowledgeBase', {
    query: userQuestion,
    type: 'BOTH',
    limit: 5
});

const answer = await context.getAIFunctions().invokeAgent('generate-response', {
    arguments: {
        question: userQuestion,
        context: searchResults.documents.map(d => d.content).join('\n'),
        instructions: 'Answer based on the provided context'
    }
});

For exact, relational questions ("how many retail clients", "who worked with Acme"), RAG isn't the right tool — similarity search returns similar text, never a count. Use a Knowledge dataset for that.

Examples

Code review agent

Reviews JavaScript against Appivo best practices and returns a structured verdict:

{
  "name": "CodeReviewer",
  "description": "Reviews JavaScript code for Appivo best practices",
  "input": [
    { "name": "code", "type": "string" },
    { "name": "context", "type": "object" }
  ],
  "output": {
    "type": "json",
    "example": { "score": 85, "issues": [], "suggestions": [] }
  },
  "steps": [
    {
      "name": "ReviewCode",
      "model": { "provider": "google", "name": "gemini-2.5-pro", "temperature": 0.3, "maxOutputTokens": 8000 },
      "maxCycles": 3,
      "systemPrompt": {
        "text": "You are an expert Appivo code reviewer. Analyze the code for security, performance, and best practices."
      },
      "userPrompt": {
        "type": "template",
        "text": "Review this code from {{input.context.action}}:\n\n```javascript\n{{input.code}}\n```"
      },
      "output": { "type": "json" }
    }
  ]
}

Translation agent with branching

Detects the language first, confirms if the model is unsure, then translates:

{
  "name": "TranslationAgent",
  "input": [
    { "name": "text", "type": "string" },
    { "name": "sourceLang", "type": "string" },
    { "name": "targetLang", "type": "string" }
  ],
  "steps": [
    {
      "name": "DetectLanguage",
      "model": { "provider": "google", "name": "gemini-2.5-flash" },
      "output": { "type": "json" },
      "userPrompt": { "text": "Detect the language of: {{input.text}}" },
      "links": [
        { "condition": "output.confidence < 0.8",  "nextStep": "ConfirmLanguage" },
        { "condition": "output.confidence >= 0.8", "nextStep": "Translate" }
      ]
    },
    { "name": "ConfirmLanguage", "model": { "provider": "google", "name": "gemini-2.5-pro" } },
    {
      "name": "Translate",
      "model": { "provider": "google", "name": "gemini-2.5-pro" },
      "systemPrompt": { "text": "You are a professional translator. Preserve formatting and tone." },
      "userPrompt": {
        "type": "template",
        "text": "Translate from {{input.sourceLang}} to {{input.targetLang}}:\n\n{{input.text}}"
      }
    }
  ]
}

Registering an agent

  1. Open the AI Agents section in the Application Builder.
  2. Click Create New Agent.
  3. Paste your JSON configuration.
  4. Save.

The agent is then callable by name from invokeAgent and <aibutton>.

Best practices

  • Be specific. "Create validation for the email field on User — must be unique and a valid format" beats "validate email." Pass the model, field, and requirements as arguments.
  • Right-size the model. Simple steps → a fast model; reasoning-heavy steps → a stronger one. Mixed within one agent is fine, step by step.
  • Always check success. Branch on the success flag, read result on success and error on failure, and wrap the call in try/catch for system errors.
  • Cache repeat work. For frequently requested, deterministic operations, cache on a key derived from the agent and arguments:
const cacheKey = `ai_${agentName}_${JSON.stringify(params)}`;
let result = await context.getCache(cacheKey);

if (!result) {
    result = await context.getAIFunctions().invokeAgent(agentName, { arguments: params });
    if (result.success) {
        await context.setCache(cacheKey, result, 3600); // 1 hour
    }
}

Troubleshooting

SymptomThings to check
Agent not foundName spelled correctly; agent registered in this app; you have permission to use it.
Empty or unexpected resultGive more specific instructions and context; for json output, include an example; review the step prompts.
Slow responsesUse a lighter model for simple steps; enable caching; split complex work into steps.
Token limit exceededReduce input size; summarize before processing; chunk large operations.

API reference

WhereMethodPurpose
Servercontext.getAIFunctions().invokeAgent(name, options)Run any agent
ServeraiFunctions.detectLanguage(text)Detect a text's language
ServeraiFunctions.summarizeDocument(doc, options)Summarize a document
Servercontext.searchIndex(name, options)Search a RAG index
Client<aibutton agent="name">Run an agent from the UI