Loquent · AI Agents Architecture · v2 · 2026-05-26
Architecture & migration · revised 2026-05-26

Loquent Agents.
One runtime, many personalities, learning over time.

Eight modules each run their own AI flow today. This document proposes a single AiAgent abstraction — powered by RIG, with curated Skills, platform-admin-managed system agents, and an evergreen Learning doc per agent + per org — that owns every AI operation in Loquent. Org admins create agents via natural language. Platform admins curate the templates. Agents refine their own learning every week and every month.

Audience · You + future coding agents Status · Direction confirmed; ready to scope Phase 0 First PR · Phase 0 only — ~1 week
What changed from v1: Plans are not in production → no retargeting, just deprecation. RIG replaces aisdk in the new runtime. Skills become curated records (Claude Code / Hermes style). System agents live in the DB, edited by platform admins via UI. Admin + org UI phases (1–4) come before any kind runs. Self-evolution is an evergreen ai_agent_learning doc (agent + org scope, weekly + monthly cadence) with versioned history — replaces the reflection-as-separate-table model.

TL;DR — the ten decisions that shape everything

01

New AiAgent runtime in src/mods/ai_agent/, powered by RIG. Owns every AI operation.

02

6 dimensions per agent: Persona, Memory, Goals, Rules, Tools, Access. Inspired by Hermes Agent SOUL.md.

03

Plans aren't in prod → deprecated, not retargeted. Build fresh; delete mods/plan later.

04

System agents live in the DB with is_system_agent. Admins manage via UI; signup clones into new orgs.

05

Skills are first-class curated records (title + when-to-use + body). Used by agents and the assistant.

06

Phases 1–4 are UI-first: admin → seed → org list → NLP create. Kinds come after.

07

Self-evolution: an evergreen Learning doc per agent + per org, weekly/monthly cadence, versioned for visualization. Lessons from failures feed each update.

08

OpenTelemetry GenAI semantic conventions on ai_agent_log. OTel-ready day one.

09

3-tier rules: Platform > Org > Agent. Platform layer immutable.

10

UI is the work. World-class UX is the deliverable, not a follow-up.

§1Context — why we're doing this

Loquent's AI capabilities live in eight disconnected places. Each has its own prompt-building, model resolution, tool list, logging shape, notification path, and permission story. There is no shared identity an end user can name, configure, or watch evolve.

8+
AI modules with their own flow
26
AiUsageFeature variants
54
mods::agent file refs (voice)
39
mods::text_agent file refs
71
mods::plan refs (unused in prod)
0
unified persona or memory

What an org owner experiences today

They see a dashboard briefing, a daily report email, three reply suggestions on every inbound message, and call analysis on the call detail page. None of those AI surfaces are aware of each other. The dashboard briefing doesn't know what the daily report told the owner this morning. The reply suggestions don't know what's already in the contact's memory.

What we want them to experience

One mental model: "These are my agents. They have personalities, they follow rules I taught them, they use skills I gave them, and they're learning from how I use them."

  • One identity per agent (org-scoped, kind-tagged, persona on file)
  • One creation flow: describe what you want, the agent extracts the config, you tweak and save
  • One timeline per run, with cost and latency per step
  • One rule system: platform rules layer under org rules layer under per-agent rules
  • One skill library: platform-curated + org-curated, attachable per agent
  • One learning loop: daily reflections, lessons from failures, lessons-into-prompts
i

Plans are not in production

The mods::plan/ code is sophisticated (typed tool-call log, JSONB state machine, cron executor) but no production org runs plans today. We borrow ideas from that code — but we do not retarget it. The new ai_agent system is built fresh; mods::plan/ is freely deletable.

§2Research summary

2.1 Hermes Agent — the source of inspiration

You mentioned Hermes Agent as the model. The reference is Nous Research's Hermes Agent — a self-improving multi-agent framework where every agent is a profile: an isolated unit with its own configuration, persona (SOUL.md), memory database, tool list, and cron jobs. Profiles connect independently to Telegram, Discord, Slack, Email gateways. Each is a complete CLI entity with its own personality.

i

What we steal from Hermes

The SOUL.md pattern — codifying persona as a first-class, shareable record. The isolated-profile concept — each agent is a complete identity. Loquent's adaptation: multi-tenant SaaS where org boundaries matter and agents share infrastructure.

2.2 Agent anatomy across modern frameworks

DimensionHermesCrewAILangGraphLettaLoquent
PersonaSOUL.mdrole / goal / backstorysystem prompt in nodeagent identityai_agent.persona JSONB
Memoryfile DBshort + opt-in longstate object3-tier core/recall/archivalai_agent_memory (short/long)
GoalsSOUL.md + crontasks in a Crewgraph nodesmemory-drivenai_agent.goals + per-run
RulesSOUL.mdsystem promptnode instructionsconstitutional (WIP)3-tier (platform / org / agent)
Toolsfn-call + skill libper-agent listgraph nodestool callsallowlist + skills
Accessgateway + envtask-scopednode-levelfn signaturesai_agent.access_scope
Skills~/.skills/ai_skill first-class table

No mainstream framework has built-in multi-tenant access control or organization-scoped rules. Loquent has to design this layer itself.

2.3 Self-evolution — two mechanisms, one evergreen doc

Voyager-style auto-extracted skills are dropped (Skills are curated — see §9). Generative-Agents reflection is folded into a single ai_agent_learning evergreen doc; each version is itself a period snapshot. Reflexion lessons stay as failure-triggered records that feed each learning update.

MethodTriggerHow Loquent uses it
Generative Agents
Park et al. 2023
Cadence (weekly per-agent, monthly per-org)Updates the evergreen ai_agent_learning doc; a new ai_agent_learning_version row is appended each cadence tick
Reflexion
Shinn et al. 2023
Failure / user feedbackai_agent_lesson rows: one-line verbal learnings from failed runs — feed into each learning update + injected directly into prompts

2.4 Observability — OpenTelemetry GenAI conventions

Since 2024, OpenTelemetry has defined semantic conventions for GenAI tracing. We adopt them in ai_agent_log on day one so a future export to Langfuse / Datadog / Grafana is a config change, not a refactor.

2.5 Policy-as-Prompt — layered rules

EFFECTIVE_SYSTEM_PROMPT = PLATFORM_RULES (immutable, code-seeded) + ORG_RULES (org admin editable) + AGENT_RULES (per-agent overrides) + AGENT_PERSONA (the SOUL.md equivalent) + AGENT_GOAL (what to achieve this run) + ATTACHED_SKILLS (curated skill bodies) + RECENT_LESSONS (Reflexion learnings) + KIND_INSTRUCTIONS

2.6 RIG — the chosen Rust LLM framework

0xPlaygrounds/rig is the LLM framework for the new runtime. Native Rust agents, pipelines, RAG, and typed tools. The agent runtime wraps RIG's Agent::builder() with Loquent-specific prompt construction, tool registration, and logging.

Existing aisdk callers in non-agent modules stay until they're retargeted onto ai_agent (and migrate to RIG transitively). No parallel aisdk-vs-RIG abstraction layer.

§3Current state in Loquent

Two modules already do most of what we need for the UI. They just don't know about each other.

3.1 Plans + Templates — not in production

mods::plan/ (71 file references) is a sophisticated autonomous-execution framework: typed tool-call log, 8-state JSONB state machine, cron-polled executor, contact assignments, autopilot gating, re-enrollment policy. Hard problems solved. But no production org uses it.

!

Implication

No retargeting, no feature-flag-gated coexistence, no parity tests on plan execution. The code stays untouched during agent development and is removed in a dedicated cleanup PR (suggested timing: between Phase 5 and Phase 6).

What we borrow as ideas — typed ToolCallVariant<I, O> pattern (per-kind tool enums), the 8-state machine shape, the cron poller pattern, the dual-write log shape.

3.2 Text Agent (mods::text_agent) — 39 file refs

Generates 3 suggested replies per inbound. Already has an NLP-driven setup flow at mods/text_agent/views/setup/ — this pattern is reused for the new agent NLP creation in Phase 4.

3.3 Voice Agent (mods::agent) — 54 file refs

Realtime/streaming voice agent. Owns the type Agent, the AgentResource permission, the phone_number.agent_id FK. Also has a setup wizard at mods/agent/views/setup/ — another reference for Phase 4 NLP creation.

!

Naming collision — voice agent stays

Voice agent owns the name Agent in code. We use AiAgent internally. UI shows "Agents" (for the new abstraction) vs "Voice Agents" (for the existing). Voice rename deferred to optional Phase 14.

3.4 AI infrastructure (mods::ai)

  • ai_usage_log table + 26-variant AiUsageFeature enum at src/mods/ai/types/ai_usage_type.rs:8-37. Stays stable. Each AiAgentKind maps to an existing variant.
  • spawn_log_ai_usage(AiUsageEntry) at src/mods/ai/services/log_ai_usage_service.rs:13
  • OpenRouter as default provider

3.5 Scheduling, signup, permissions

  • cron_tab crate; jobs registered at src/app/jobs/app_jobs.rs:17
  • Signup hook at src/mods/signup/services/finalize_signup_service.rs:174 — where Phase 2 inserts seed_default_agents
  • define_resources! at src/bases/auth/types/resources.rs:84-88 — where new permission blocks (AiAgent, SystemAiAgent, AiSkill) go

§4Target architecture — the AiAgent concept

Internal type / table / permission is AiAgent. UI label is "Agents". The voice agent stays as is.

4.1 The 6 dimensions

┌────────────────────────────────────────────────────────────────┐ │ AiAgent │ ├────────────────────────────────────────────────────────────────┤ │ Persona (tone, voice, signature, traits — SOUL.md-like) │ │ Memory (short-term scratchpad + long-term facts) │ │ Goals (run-level goal + persistent objectives) │ │ Rules (per-agent; platform + org layers added at run) │ │ Tools (allowlist of tool names — resolved at runtime) │ │ Access (read/write scopes; defaults to triggering user) │ └────────────────────────────────────────────────────────────────┘ + Skills (M2M — curated) + Schedule (1:N — optional)

4.2 System (base) agents — DB-resident, admin-managed

System agents are stored as regular ai_agent rows with is_system_agent = true. Loquent admins create / edit them via an admin UI (Phase 1). Sign-up clones them into each new org (Phase 2) with is_system_default = true and system_source_id pointing back.

When an org admin edits any field on a clone → is_system_default flips to false in the same transaction. Orphans the row from future platform-template updates (so a customer's edits aren't overwritten).

i

Why DB rather than Rust constants?

Platform staff can iterate on system-agent personas, goals, default rules, and tool allowlists without shipping a release. The admin UI is the editing surface — same shape as the org-facing edit form, just gated by a different permission.

4.3 Agent kinds (Rust enum)

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum AiAgentKind {
    // Phase 5-7 — initial runnable kinds
    DashboardBriefingAgent,
    TextReplyAgent,
    CustomReportAgent,

    // Phase 10 — onboarded existing AI ops
    AnalyzerAgent,
    TaskExtractionAgent,
    AutoTagAgent,
    ContactEnrichmentAgent,
    ContactMemoryAgent,
    AssistantAgent,

    // Phase 11 — meta-agent
    ReflectionAgent,
}

4.4 The runtime — RIG inside run_ai_agent

pub async fn run_ai_agent(
    ctx: &AppContext,
    agent_id: Uuid,
    trigger: AiAgentTriggerSource,
    input_payload: serde_json::Value,
) -> AppResult<AiAgentRun> {
    // 1. Load AiAgent + assigned skills + recent lessons
    // 2. Resolve layered rules (platform + org + agent)
    // 3. Build RIG Agent with:
    //      system_prompt = persona + goals + rules + skills + lessons + kind_instructions
    //      tools = resolve(tools_allowlist)
    //      model = agent.model_override.or(org_default_model)
    // 4. Create AiAgentRun (state=Executing) + emit Run.Started log
    // 5. Drive RIG event loop, logging every event to ai_agent_log
    // 6. On completion: state=Completed + aggregate cost/tokens
    // 7. Flush all logs in batches
}

§5Data model

5.1 New tables

TablePurposePhase
ai_agentThe agent identity (all 6 dimensions + system-agent flags)0
ai_agent_runOne execution; state machine, parent/child, trigger source0
ai_agent_logUnified, OTel-compatible timeline; typed per kind via JSONB0
ai_agent_memoryShort-term scratchpad + long-term facts (pgvector deferred)0
ai_skillCurated skills — title, when-to-use, body. Platform or org scope.0 schema, used Phase 8
ai_agent_skill_linkM2M between agents and skills0
ai_agent_scheduleCron-driven runs; FOR UPDATE SKIP LOCKED dispatch7
platform_ai_ruleImmutable platform-tier rules; seeded by migration9
ai_agent_learningEvergreen AI-curated doc — one per agent + one per org; current_version_id FK11
ai_agent_learning_versionFull version history of the learning doc; each version is a period snapshot11
ai_agent_lessonReflexion verbal lessons from failures — feed updates + injected to prompts11
ai_agent_versionOptional version history (auto-title + change description)12 (deferred)

5.2 Key columns on ai_agent

ColumnTypeNotes
organization_idUuid?NULL for system agents (per Q12)
kindVARCHARAiAgentKind serde tag
is_system_agentBOOLPlatform-owned base template
is_system_defaultBOOLUnedited clone of a system agent
system_source_idUuid? FK ai_agentPoints clones back to source
personaJSONB{ tone, voice, signature, traits, avoid, soul_md? }
goalsJSONBVec<AiAgentGoal>
rules_overrideJSONBVec<String> — per-agent rules
tools_allowlistJSONBVec<ToolName>
access_scopeJSONB{ read[], write[], owner_user_id? }
budgetJSONBDaily / monthly cents, per-run tokens, spent today/month
enable_reflectionBOOLOpt-in to self-evolution
enable_autopilotBOOLTool calls execute without approval

5.3 The ai_agent_log.entry typed payload

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum AiAgentLogEntry {
    Thought(AiAgentLogThought),
    LlmGeneration(AiAgentLogLlmGeneration),
    ToolCall(AiAgentLogToolCall),    // typed per kind via ToolCallVariant<I,O>
    SystemEvent(AiAgentLogSystemEvent),
    Reflection(AiAgentLogReflection),
    Failover(AiAgentLogFailover),
}
?

Memory model — flagged for research

The ai_agent_memory table above is the v1 placeholder, modeled on Loquent's existing contact-memory pattern (a single string updated in place by AI). The deeper architecture — tiered memory (core / recall / archival per Letta), semantic recall via embeddings, structured-vs-freeform shape, attribution to specific tools or facts — is deferred as a dedicated research deliverable post-v1. Build the v1 schema; revisit before Phase 10 onboards heavy memory writers like ContactMemoryAgent.

5.4 Existing tables — additive changes

  • text_agent → add ai_agent_id Uuid? FK ai_agent (Phase 6)
  • contact_note → add written_by_ai_agent_run_id Uuid? FK ai_agent_run (Phase 10)

No ai_agent_id columns on plan or plan_template. Those modules are deprecated.

§6Migration phases — UI first, kinds after

Front-loading UI is the key shift from v1. By the time the first kind goes live in Phase 5, the org-facing surfaces are already polished.

  1. PHASE 0 Foundations — first PR
    M · ~1 week

    Core tables (ai_agent, ai_agent_run, ai_agent_log, ai_skill, ai_agent_skill_link, ai_agent_memory) + Rust types + RIG-powered generic executor + admin-only debug endpoint. Add AiAgent, SystemAiAgent, AiSkill permission resources. No user-visible change. Build foundation; ship behind admin gate.

  2. PHASE 1 Platform admin area
    L · ~2 weeks

    Loquent-admin-only UI to list, create, edit, archive system agents. Gated by SystemAiAgent permission. All 6 dimensions editable. Reusable components (agent_persona_editor, agent_goals_editor, etc.) drive both this admin UI and the org UI in Phase 3.

  3. PHASE 2 Sign-up seeding
    S · 3–4 days

    New orgs receive a deep-cloned copy of each active system agent on signup. Clones get is_system_default = true + system_source_id set. Non-blocking (matches existing notification-preference seeder).

  4. PHASE 3 Org UI — list + detail + edit
    XL · ~3 weeks

    Org-facing agent management. Card grid (kind icon, status, primary metric). Detail view with sections (Identity, Persona, Goals, Rules, Tools, Access, Schedule, Recent Runs, Metrics). Edit form with progressive disclosure. Edit handler flips is_system_default to false on any field change. "Customized" / "Using Platform Default" badge.

  5. PHASE 4 Org UI — NLP-driven creation
    L · ~2 weeks

    "Describe the agent you want" chat-driven creation. Reuses the existing setup-wizard pattern from mods/agent/views/setup/ (voice) and mods/text_agent/views/setup/ (text). RIG-based extraction agent populates: name, kind, persona, goals, suggested tools_allowlist, schedule. Confirmation step before save. Blank-form fallback always available.

  6. PHASE 5 Dashboard Briefing Agent — first running kind
    M · ~1 week

    Retargets generate_dashboard_briefing_service to run_ai_agent(DashboardBriefingAgent, ...). Lowest blast radius (failures are cosmetic). Per-org feature flag; 7-day A/B parity test against legacy.

  7. PHASE 6 Text Reply Agent
    L · ~2 weeks

    Refactor mods/text_agent to back persona/rules on ai_agent. Structured output (RIG schema::<T>()) for 3-suggestion JSON. Auto-fallback to legacy on parse failure. Per-org flag, 10% → 100% rollout.

  8. PHASE 7 Custom Report Agent + scheduling
    L · ~2 weeks

    Replace send_daily_report_job with run_due_ai_agents_job driven by ai_agent_schedule. FOR UPDATE SKIP LOCKED dispatch. Schedule editor UI: cron-spec builder + recipient picker + section toggles.

  9. PHASE 8 Skills — first-class curated
    L · ~2 weeks

    Platform + org skill curation UIs. Skill picker on agent edit. Prompt builder injects enabled+linked skills into the system prompt. Migration seeds initial platform skills ("Email Tone Guide", "SMS Length & Compliance", "Daily Briefing Structure", "Contact Note Etiquette"). Default body length cap 4000 chars.

  10. PHASE 9 Layered rules
    M · ~1 week

    3-tier rule injection. platform_ai_rule seeded via migration; org rules editable; agent rules editable. Each run snapshots its effective rules in ai_agent_log. UI shows platform rules read-only.

  11. PHASE 10 Onboard remaining AI operations
    XL · parallelizable

    One PR per kind: AnalyzerAgent, TaskExtractionAgent, AutoTagAgent, ContactEnrichmentAgent, ContactMemoryAgent, AssistantAgent. Each replaces inline aisdk calls with run_ai_agent. Orchestration stays where it is.

  12. PHASE 11 Self-evolution — reflections + lessons
    XL · ~3 weeks

    Evergreen ai_agent_learning doc (agent + org scope), AI-refined weekly per-agent + monthly per-org. Full versioned history for evolution visualization. Reflexion lessons from failures feed each update. LearningAgent kind. Views: /agents/{id}/learning + /org/learning. Opt-in per org; manual trigger available; admin can roll back versions.

  13. PHASE 12 Versioning (nice-to-have)
    M · ~1 week

    Optional version history. On save: snapshot full agent state, auto-generate version title, accept optional change description. "History" tab on detail view. "Restore version". Retention policy (last 20 per agent by default).

  14. PHASE 13 Budgets + observability UI
    L · ~2 weeks

    Enforce per-agent + per-org budgets before each LLM call. Ship the unified timeline view + cost dashboards. /agents/{id}/runs/{run_id} timeline, /agents/{id}/metrics.

  15. PHASE 14 Voice agent rename — optional, deferred
    L · mechanical

    Rename mods/agent/mods/voice_agent/. Only after ai_agent runtime is stable for ≥6 months. Skip if no friction emerges.

  16. PHASE X Plans / Templates cleanup
    S · 1 PR

    Delete mods/plan/. Drop the related tables. Remove Plan and PlanTemplate from define_resources!. Suggested timing: between Phase 5 and Phase 6, when confidence in the new runtime is high.

§7Observability — every operation, indexed

Every operation an agent performs is a row in ai_agent_log. Indexed for fast timeline rendering, fast cost dashboards, and as the input corpus for the reflection pipeline.

kindWhenWhat's in entry
system_eventRun started, state transition, schedule advanced, budget enforced, rules applied{ event, prev_state?, new_state?, reason? }
thoughtLLM produced reasoning text{ text, confidence? }
llm_generationEvery RIG completion (prompt → response){ provider, model, finish_reason, prompt_summary, completion_summary } + ai_usage_log_id FK
tool_callEvery typed tool invocation{ tool_name, input, output, status, duration_ms } — typed per kind via ToolCallVariant<I,O>
reflectionA reflection cycle ran{ scope, period_start, period_end, source_log_ids[] }
failoverNew path failed, legacy path took over{ from_path, to_path, reason }

OpenTelemetry attribute mapping

Loquent fieldOTel attribute
ai_agent_run.trace_idtrace.id
ai_agent_log.span_idgen_ai.* span id
ai_agent.kindagent.kind (custom)
ai_usage_log.modelgen_ai.request.model
ai_usage_log.input_tokensgen_ai.usage.input_tokens
ai_usage_log.cached_tokensgen_ai.usage.cache_read_tokens
(computed)cost_cents (custom)
ai_agent_log.duration_msspan duration

Indexes for fast analysis

  • (ai_agent_run_id, created_at) — timeline render
  • (ai_agent_id, created_at DESC) — "show me this agent's recent activity"
  • (organization_id, created_at DESC) partial WHERE kind IN ('llm_generation','tool_call') — cost dashboards
  • GIN on entry JSONB — attribute search (e.g. "find all tool calls where input.to_number = '+1...'")

§8Self-evolution — Learning + Lessons

A single evergreen Learning doc per agent + per org, AI-refined on cadence with full version history. Reflexion lessons from failures feed each update. No auto-extracted skills — those are curated.

Weekly (Sun 23:00 org-tz) │ ai_agent_log (agent's week) ──┐ │ ai_agent_lesson (recent) ──┤ ▼ prior learning version ──┼──► RunWeeklyAgentLearningJob │ │ │ ▼ │ ai_agent_learning (agent-scope) │ ai_agent_learning_version ← new version appended │ Monthly (1st, 03:00 UTC) │ all agent learnings (org's month) ──► RunMonthlyOrgLearningJob │ ▼ ai_agent_learning (org-scope) ai_agent_learning_version ← new version Manual: admin clicks "Refresh learning now" → version with cadence='manual'

Two scopes

  • Agent learning — what this specific agent has learned about its own runs (e.g. "DashboardBriefingAgent has learned to lead with new leads, then handled calls, because the owner clicks those sections first").
  • Org learning — cross-cutting patterns across all the org's agents (e.g. "This org's customers respond more to morning messages; weekend messages get lower response rates").

The learning document shape

Markdown. The LearningAgent rewrites it in-place each cadence tick — adding new patterns, integrating recent runs, retiring outdated observations. Each rewrite produces a new ai_agent_learning_version row with the full new content and a what_changed summary. The version timeline IS the evolution visualization.

What feeds each update

  • The window of ai_agent_log entries since the last update
  • Recent ai_agent_lesson rows (Reflexion failure verbals)
  • The current learning version (so the AI knows what's already captured)
  • For org-level: all per-agent learning versions from the past month

Closing the loop

build_ai_agent_prompt_service injects the agent's current learning version + the org's current learning + recent ai_agent_lesson rows into every run's system prompt. Skills (curated) layer alongside.

$

Cost discipline

Opt-in per org via ai_agent.enable_reflection. 14-day free trial. Cheapest learning model (e.g. deepseek-v3.2). Per-agent monthly token budget — if exceeded, the next update is skipped and a system_event log is written. Manual trigger doesn't bypass the budget.

Recoverable drift

If a learning update goes off the rails, admin can roll back to a prior version, or edit the markdown directly. The what_changed summary on each version is the audit trail.

§9Skills — first-class curated

Skills are reusable knowledge capsules — like Claude Code skills or Hermes skills. Title, short description (agent-focused "when to use this"), and body (the actual instructions). Curated by humans, attached to agents, also usable by the Loquent assistant.

FieldPurpose
titleShort label, e.g. "Draft Daily Summary Email"
short_descriptionAgent-focused: "Use this when composing a daily summary email for the org owner. Includes formatting guidelines, recipient tone, sign-off."
body_markdownThe instructions / template / guidance the agent reads. Cap default 4000 chars.
organization_idNULL = platform-curated; non-null = org-curated
tagsFiltering / search
is_platform_curatedTrue if part of Loquent's curated set

Scopes

  • Platform skills (organization_id = NULL) — admin-curated, available to all orgs
  • Org skills — org-admin curated, scoped to that org

Assignment

Agents reference skills via the ai_agent_skill_link M2M table. At prompt-build time, all enabled+linked skills are loaded into the system prompt (Q11: load-all approach for v1; LLM-driven activation is a future evolution).

Initial platform-curated skill seed

  • Email Tone Guide — formatting / sign-off conventions
  • SMS Length & Compliance — 160-char awareness, opt-out language
  • Daily Briefing Structure — what sections, what order
  • Contact Note Etiquette — what's appropriate to record

More added by Loquent staff over time via the admin UI.

Skills vs Rules — the distinction

i

Rules are constraints ("never quote a price you can't honor"). Layered — platform wins.

Skills are capabilities ("here's how to draft a daily summary"). Attached per agent; both platform-curated and org-curated coexist; no override semantics.

Skills + the Loquent assistant

The existing mods/assistant/ consumes skills the same way agents do (Phase 8 lights this up for mods/ai_agent; an explicit assistant_skill_link table can be added later if the assistant adopts the same curated-skill pattern).

§10The three initial running kinds

These three become real LLM-calling agents in Phases 5–7. Everything before that is foundation + UI.

10.1 Dashboard Briefing Agent Phase 5

  • Kind: DashboardBriefingAgent
  • Trigger: Manual (dashboard load) + scheduled (daily 5 AM org-tz)
  • Persona default: "Concise, data-driven analyst. Plain English. No jargon."
  • Tools: get_workspace_summary, get_workspace_needs_attention, get_engagement_stats
  • Output: Markdown briefing on the dashboard
  • Wraps: generate_dashboard_briefing_service.rs

10.2 Text Reply Agent Phase 6

  • Kind: TextReplyAgent
  • Trigger: Webhook (inbound SMS)
  • Persona default: From existing text_agent row (migrated)
  • Tools: query_knowledge
  • Output: Structured 3-suggestion JSON (high / med / low confidence)
  • Wraps: generate_text_agent_suggestions_service.rs
  • Fallback: Auto-fallback to legacy on parse failure

10.3 Custom Report Agent Phase 7

  • Kind: CustomReportAgent
  • Trigger: Scheduled (ai_agent_schedule row)
  • Persona default: "Helpful daily-digest assistant."
  • Config (in ai_agent_schedule.config_payload): recipients, sections, delivery_channels, optional 500-char custom prompt
  • Tools: get_recent_leads, get_handled_calls, get_messages_window, get_priority_tasks
  • Output: Notifications via existing notify_service
  • Wraps: generate_daily_report_service.rs

§11Org onboarding — clone system agents

Sign-up clones every active system agent into the new org. Org admins inherit a working configuration; their first edit "adopts" the clone.

// In finalize_signup_service.rs, after line 174:
seed_default_agents(&txn, organization_id, owner_user_id).await
    .or_log_and_continue();

The seeder reads WHERE is_system_agent = true AND is_active = true and, for each row, performs a deep clone into the new org:

  • is_system_agent = false
  • is_system_default = true
  • system_source_id = <source_row_id>
  • organization_id = <new_org_id>
  • All other fields copied verbatim
  • Schedule rows + skill-link rows cloned too

The "Customized" flip

When the org admin saves any edit on a clone, the save handler flips is_system_default = false in the same transaction. The UI shows a badge: "Customized" (was system default; now org-owned) versus "Using Platform Default" (untouched clone). Platform-template updates do not propagate to existing clones — orgs own their copies.

§12Layered rules — platform > org > agent

Platform rules are immutable from any UI. Org rules are org-admin editable. Agent rules are per-agent.

Platform rules (immutable; seeded by migration)

  • "Never make legal, medical, or financial advice claims."
  • "Never share PII outside the org boundary."
  • "Always announce when a message is AI-generated if asked."
  • "Never autopilot a destructive action without explicit org-owner approval."
  • "Always log every tool call with full input/output."

Editing platform rules requires a code change + migration — auditable, reviewable, deployable. Orgs cannot mutate this layer.

Org rules (editable)

  • "Sign messages as 'Alex from Acme Co.'"
  • "Use first-name only when addressing contacts."
  • "Never discuss competitor X."

Agent rules (editable per agent)

  • "This text agent only handles inbound questions about pricing."
  • "This report agent emails the founder, not the team."

Audit

Each ai_agent_run records its effective rules in ai_agent_log (kind=system_event, event=RulesApplied). If an org rule attempts to override a platform rule, the snapshot shows both and the platform rule wins.

§13Verification plan

How we know each phase is safe to ship.

PhaseVerification
0cargo check + cargo clippy green; migrations apply + roll back; admin endpoint hit; AiAgentRunState transitions unit-tested; RIG builds for server target.
1Admin creates + edits + archives a system agent through the UI; all 6 dimensions roundtrip; /test-ui validates.
2New signup smoke test in dev creates an org → asserts N clones (N = active system agents).
3Org admin: list view, detail view, edit field → "Customized" badge appears. Mobile responsive. /test-ui required.
4NLP input "I need a daily 8am email of new leads" → extracts CustomReportAgent + schedule + persona. Editable before save.
57-day side-by-side parity test: old + new dashboard briefing; cost parity within ±10%; no double-billing.
6Nightly synthetic: 100 inbounds → 3 suggestions each via new path, 0 fallbacks. 10% → 100% rollout.
77-day shadow run of dispatcher against is_enabled=false schedules; verify timing. Then flip a test org.
8Skill attached to agent: prompt-build injects body; effective prompt visible in log; ai_usage shows expected token increase.
9Run with org rule attempting platform override: snapshot shows both; platform wins.
10Per-kind parity test against legacy.
114-week-old agent: ≥1 learning version (weekly) at /agents/{id}/learning; org has ≥1 monthly version at /org/learning; timeline browsable. Any failed run within the window produces an ai_agent_lesson. Admin can roll back to a prior version.
12Edit + save → new version row with auto-title; restore old version → fields match.
13Budget exceeded → next run rejected with user-visible message + system_event log.

End-to-end (after Phase 7)

  1. Sign up a new org
  2. Provision a phone number
  3. Observe DashboardBriefingAgent, CustomReportAgent, TextReplyAgent clones created
  4. Open dashboard → briefing renders via new path
  5. Send SMS → 3 suggestions generated
  6. Wait for daily report cron → email delivered
  7. Inspect /agents/{id}/runs/{run_id} timeline for each
  8. Confirm ai_usage_log rows have matching ai_agent_log rows

§14Open questions for you

Q1–Q10 resolved on 2026-05-26 at recommended defaults. Q11–Q15 are new.

Resolved 2026-05-26

Q1 Naming → AiAgent internally + UI "Agents". Voice rename deferred to optional Phase 14.
Q2 Plans framing → Dropped entirely. Plans aren't in prod; deprecate mods/plan instead of retargeting.
Q3 Cutover → Coexist with flags for retargeted modules; 30-day cleanup tail. No flag for net-new surfaces.
Q4 Self-evolution → Opt-in per org with 14-day free trial.
Q5 Vector embeddings → Defer pgvector; JSONB + GIN + recency for v1.
Q6 Tool-call typing → Typed enum per kind, JSONB column on ai_agent_log.
Q7 Text suggestion output → Structured + auto-fallback on parse failure.
Q8 Voice agent future → Phase 14+ optional; share only persona/rules/budget if integrated.
Q9 Custom-report freedom → Canned section toggles + a single optional 500-char custom prompt.
Q10 First PR scope → Phase 0 only — schema + types + RIG executor + admin endpoint.

New — please confirm or redirect

Q11
Skill activation — load-all vs LLM-picks?
  • (b) Load skill descriptions only; LLM picks via tool call which to load body for. More like Claude Code's runtime model. Higher complexity, deferred.
Q12
Where do system agents live (organization_id)?
  • (b) A designated platform-org row (e.g. loquent_internal_org). Simpler FK constraints but requires creating + maintaining that org.
Q13
When to delete mods::plan/?
  • (a) Cleanup PR immediately after Phase 0 lands. Bigger first batch.
  • (c) Defer indefinitely. Bit-rot risk; dead-code cost.
Q14
Versioning storage shape
  • (b) Event-sourced changes (one row per field edit). Lower storage, harder restore.
Q15
Skill body length cap default?
  • (a) 2000 chars — tight
  • (c) No cap; rely on a prompt-size warning in the picker.

§15References

  1. Hermes Agent — Nous Research
  2. Generative Agents — Park et al. (UIST 2023)
  3. Reflexion — Shinn et al. (NeurIPS 2023)
  4. Voyager — Wang et al. (2023)
  5. Anthropic: Building Effective Agents (Dec 2024)
  6. CrewAI Documentation — Agents & Memory
  7. Letta (MemGPT) — Agent Memory
  8. OpenTelemetry GenAI Semantic Conventions
  9. Langfuse — Open-Source LLM Observability
  10. Policy-as-Prompt — arXiv:2509.23994
  11. RIG — Rust LLM Framework
  12. Claude Code Skills