Skip to content

Configuration

Text agents are created and managed from the Text Agents section of the dashboard. Each agent belongs to the organization and is shared across all org members.

  1. Go to Text Agents in the sidebar
  2. Click New Agent
  3. Fill in the fields (see below)
  4. Click Create
POST /api/text-agents
pub struct TextAgentData {
pub name: String,
pub purpose: String,
pub tier: TextAgentTier,
pub model: TextAgentModel,
pub knowledge_base_ids: Vec<Uuid>,
pub confidence_threshold: f64,
pub custom_instructions: Option<String>,
pub escalation_instructions: Option<String>,
pub restricted_topics: Option<String>,
pub temperature: Option<i32>, // 0–100 integer scale → divided by 100 for LLM
}

A human-readable label. Shown in the agent list, contact sidebar dropdowns, and channel default badges.

The core system prompt. Defines who the agent is, how it behaves, and what it’s for. This is the most important field.

Example:
You are an assistant for Acme Dental. Respond warmly and professionally.
Help patients reschedule appointments, answer questions about services,
and direct urgent issues to call the front desk at (555) 100-2000.
Keep replies under 160 characters when possible.

The purpose is injected at the top of the system prompt under # Role, followed by behavioral rules, org context, contact info, and output instructions.

Free-form instructions appended to the agent’s system prompt under ## Custom Instructions. Use this to override tone, enforce formatting rules, or add context that applies to all conversations.

Example:
- Always sign off with "— Acme Support"
- Never promise specific refund timelines
- If the contact mentions a competitor, redirect to our value proposition

Empty values are omitted from the prompt entirely.

Instructions for what the agent should do when it cannot answer confidently. Injected under ## Escalation in the system prompt.

Example:
If you are unsure, ask the contact to call us directly at (555) 100-2000
or say "Let me connect you with our team for more details."

Topics the agent must refuse to discuss. Injected under ## Restricted Topics. The agent is instructed to politely decline and redirect the conversation.

Example:
- Competitor pricing
- Internal company financials
- Legal advice or medical diagnoses
pub enum TextAgentTier {
GlobalDefault,
Premium,
Mid,
}

Tiers group models by quality and cost rather than provider. Changing the tier resets the model selection to that tier’s default.

TierBehavior
Global DefaultUses the model configured by an admin in Admin → AI Models → Text Agent Suggestions
PremiumHigh-quality, higher-cost models
MidBalanced quality and cost

When you select Global Default, the model selector disappears — the admin-configured model applies automatically.

Models are filtered by the selected tier. The model selector only appears when the tier is Premium or Mid.

Premium models:

Display NameOpenRouter IDDefault
GPT-5.4openai/gpt-5.4
Claude Opus 4.6anthropic/claude-opus-4.6
Gemini 3.1 Progoogle/gemini-3.1-pro-preview

Mid models:

Display NameOpenRouter IDDefault
GPT-5.4 Miniopenai/gpt-5.4-mini
Claude Haiku 4.5anthropic/claude-haiku-4.5
GPT-5.4 Nanoopenai/gpt-5.4-nano
Gemini 3.1 Flash Litegoogle/gemini-3.1-flash-lite-preview
DeepSeek V3.2deepseek/deepseek-v3.2

All models are routed through OpenRouter using a single openrouter_api_key from core_conf.

At runtime, the system resolves which model to use in this order:

  1. Per-agent model — if the agent has a specific model set (Premium or Mid tier), use it directly
  2. Admin global default — if the agent uses GlobalDefault, query the ai_model_config table for the text_agent_suggestions area
  3. Hardcoded fallback — if no admin config exists, fall back to google/gemini-3.1-pro-preview
let model_id = match agent_model.openrouter_id() {
Some(id) => id.to_string(),
None => resolve_model(AiArea::TextAgentSuggestions).await?,
};

Admins configure the global default model from Admin → AI Models. The Text Agent Suggestions row appears under the “Text Agent” tier group. This setting applies to all text agents using the GlobalDefault tier.

The admin can select any available model from the OpenRouter catalog. The selection is stored in the ai_model_config table:

-- area = "text_agent_suggestions", model_id = "openai/gpt-5.4"
SELECT model_id FROM ai_model_config WHERE area = 'text_agent_suggestions';

A float between 0.0 and 1.0 (default: 0.7).

Used only when auto-reply is enabled on a contact assignment. The agent compares the highest-confidence suggestion against this threshold:

ConditionBehavior
confidence ≥ thresholdAuto-reply sent cleanly
confidence < thresholdReply still sent, but a warning alert note is added to the contact

Controls LLM output randomness. Stored as an integer from 0 to 100 and divided by 100 before sending to the provider (500.50).

  • Lower values (0–30) — more focused, deterministic replies
  • Higher values (70–100) — more varied, creative replies
  • Leave unset — uses the provider’s default temperature

Temperature is in the Advanced section of the form, collapsed by default. A “Reset to default” button clears the value back to unset.

pub const DEFAULT_TEMPERATURE_DISPLAY: i32 = 50; // shown when slider is first opened

A list of knowledge base IDs the agent can query during the agentic loop. The agent uses a query_knowledge tool call (backed by handle_query_knowledge_tool_call) to look up factual information before composing replies.

The tool accepts Vec<Uuid> and searches all linked knowledge bases simultaneously. Documents are fetched, formatted, and sent to GPT-4.1 Mini as a focused retrieval step — keeping the primary model’s context window clean.

Multiple knowledge bases can be linked to one agent. They’re stored as a JSON array in the knowledge_base_ids column.

The full system prompt assembled at runtime follows this structure:

# Role
You are {name}.
Purpose: {purpose}
Channel: SMS
## Custom Instructions
{custom_instructions}
## Escalation
When you cannot confidently answer…
{escalation_instructions}
## Restricted Topics
You must refuse to discuss…
{restricted_topics}
## Organization
{org_context}
## Contact
- Name: …
- Phone: …
- Tags: …
## Contact Notes
{notes}
## Task
Generate exactly 3 distinct reply suggestions…

Sections with no content are omitted entirely.

Navigate to any agent → its detail view. All fields are editable. Changes save via:

PUT /api/text-agents/{id}

The detail view also includes:

  • Channel Defaults — set this agent as the org-wide default for SMS and/or WhatsApp
  • Delete — removes the agent and cascades to all contact assignments and saved suggestions

The list shows all agents as cards with name, purpose excerpt, and tier/model badge (e.g., “Premium / GPT-5.4” or “Global Default / Global Default”).

CREATE TABLE text_agent (
id UUID PRIMARY KEY,
organization_id UUID NOT NULL REFERENCES organization(id) ON DELETE CASCADE,
name TEXT NOT NULL,
purpose TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL,
knowledge_base_ids JSONB NOT NULL DEFAULT '[]',
confidence_threshold FLOAT NOT NULL DEFAULT 0.7,
custom_instructions TEXT,
escalation_instructions TEXT,
restricted_topics TEXT,
temperature INTEGER, -- 0–100; NULL = provider default
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
ActionPermission Required
Create agentTextAgent:Collection:Create
View agentTextAgent:Instance:View
Update agentTextAgent:Instance:Update
Delete agentTextAgent:Instance:Delete

Org owners bypass all permission checks.

src/mods/text_agent/
├── types/
│ ├── text_agent_type.rs # TextAgent domain struct
│ ├── text_agent_data_type.rs # TextAgentData (create/update input)
│ ├── text_agent_tier_type.rs # TextAgentTier enum (Global Default, Premium, Mid)
│ └── text_agent_model_type.rs # TextAgentModel enum + tier grouping + backward compat
├── api/
│ ├── create_text_agent_api.rs # POST /api/text-agents
│ ├── get_text_agents_api.rs # GET /api/text-agents
│ ├── get_text_agent_api.rs # GET /api/text-agents/{id}
│ ├── update_text_agent_api.rs # PUT /api/text-agents/{id}
│ └── delete_text_agent_api.rs # DELETE /api/text-agents/{id}
├── components/
│ └── text_agent_form_component.rs # Shared create/edit form
└── services/
├── build_text_agent_context_service.rs # Assembles the system prompt
└── generate_text_agent_suggestions_service.rs # Agentic loop + KB tool