Building Convexa: a compile-time architecture for conversational agents

A product teardown of the system I own at Nagent AI. Written to show how I make architecture and go-to-market decisions, not to sell the product. Customer names and anything under NDA are left out; the numbers here are ones I am cleared to share.

Hariharan V (Hari)  |  Senior Product Manager, Nagent AI  |  linkedin.com/in/vhharan1

The problem

Convexa is a conversational execution agent - an AI chat agent, in the term most people use - that a company embeds on its website to talk to its visitors, whether it sells to consumers or to other businesses. It does not just answer questions: it qualifies a lead, writes the result back to the CRM, and hands off scheduling, inside one conversation.

When I took it over it was positioned as a "RAG knowledge chatbot": upload documents, get a bot that answers from them. That category is crowded, it is commoditising fast, and its buyers churn the moment a cheaper option ships. The two competitors we lost deals to both had a specific, repeated complaint in their public reviews: the bot confidently makes things up.

So I made two bets:

  1. Reposition from "answers questions" to "finishes a job." Qualify, sync, hand off - a measurable outcome, not a transcript.
  2. Make not-hallucinating the wedge. Turn the thing customers complain about into the thing we are unambiguously best at.

Everything below follows from those two bets.

The core design: one compiled prompt per customer, assembled at deploy time

A customer never writes a prompt. They answer functional questions in a one-screen onboarding flow ("what should the assistant do? capture leads? resolve issues? both?"). The backend turns those answers into a single system prompt by concatenating fixed blocks:

FINAL SYSTEM PROMPT =
    core guardrails            (identical for every customer, never edited)
  + brand and tone             (merge fields from onboarding: name, voice, languages)
  + admin's custom guardrails  (free text - validated against core before it is accepted)
  + 1-3 intent modules         (from a fixed set of 8: inform, support, qualify,
                                book, account, intake, feedback, promote)
  + 0-N custom intent modules  (compiled on demand when the fixed 8 do not cover a goal)
  + fallback policy            (per active job, a few admin toggles)
  + engagement policy          (idle-visitor re-engagement)
  + an intent router           (added only when 2+ intents are active)

The prompt is recompiled every time an admin changes a setting, and each compiled version is stored, so a deployment is a specific, inspectable artifact rather than a text box someone edited live.

Core guardrails identical for every customer, never edited Brand & tone merge fields from onboarding Admin custom guardrails * free text Intent modules + custom modules * 1-3 of a fixed set of 8 Fallback, engagement & router policies * validated against Core guardrails before it can go live Compile + version at deploy time One system prompt per customer stored · inspectable · new customer live in ~10 minutes
Every customer's system prompt is assembled from fixed blocks at deploy time and stored as a versioned artifact. Nothing is hand-written per customer, and admin free text is checked against Core before it can reach a live prompt.

Why modules instead of a per-customer prompt

The obvious alternative - a prompt-engineer tunes a bespoke prompt per customer - does not scale and does not stay safe. Every hand-written prompt is a place a guardrail can be accidentally dropped, and every change is a regression risk nobody can see.

The tradeoff I accepted: a module system is less expressive than a free hand. A customer with a genuinely unusual job has to either fit one of the eight intents or get a custom module compiled (which is validated the same way). In practice a real deployment uses one to three intents, so the ceiling has not bitten yet. What I bought for that constraint: a new customer goes live in about 10 minutes, and no customer can ship a prompt that violates a core rule.

Decision: a three-level guardrail precedence, with validation before anything goes live

Rules resolve in a fixed order:

Core Guardrails  >  Admin's Custom Guardrails  >  Intent Module defaults
precedence Core guardrails identity · prompt-injection resistance · grounding · scope safety · escalation · human handoff · never go silent Admin custom guardrails per customer · may restrict Core further, never loosen it Intent module defaults per-intent behaviour when nothing above overrides Release gate golden-dataset eval + adversarial set must pass clean Live per-customer prompt
Rules resolve top down. An admin can tighten Core for their own workspace but cannot loosen it, and no prompt change ships until it clears the eval gate.

Core guardrails are prepended verbatim to every deployment and cover identity and prompt-injection resistance, grounding, scope, safety and escalation, always-available human handoff, and a "never go silent" rule. An admin's own guardrails can restrict further - never loosen. That is not a convention we hope holds; every custom guardrail an admin writes is checked against the core set before it is accepted, and a custom intent module is checked the same way because it is also admin-authored free text heading into a live prompt.

Why this mattered enough to build: the demo that wins B2B deals is "type your own rule and watch it take effect." The demo that loses the renewal is the one where a customer's well-meaning rule quietly disabled the safety layer. Validation-before-live lets us keep the first demo without the second outcome.

Decision: strict grounding as the wedge, with a hard rule against dead ends

The agent answers only from the connected knowledge base, the current conversation, and explicitly connected tools. If the answer is not in one of those, it says so - it does not guess prices, dates, stock levels, or policy. The dashboard can show a "Grounded" tag on answers that are backed by real content, and the phrasing of "I don't have that" is varied so it does not read like a canned line.

The part I care most about: a bare "I don't know" is treated as a failure, not a safe default. The proactivity rule in the core guardrails forces every decline to name a next step in the same breath - an alternative source, a handoff, or a clarifying question. There is one deliberate exception (a "log my issue and leave me alone" intake mode), which is a named, scoped choice rather than a hole in the rule.

This is the difference between "safe but useless" and "safe and still moving the conversation forward," and it is why I think of grounding as a growth feature, not a compliance feature.

Decision: an eval suite that gates every prompt release

Because the prompt is assembled from shared modules, a change to one module can regress every customer that uses it. So no prompt change ships without passing a golden-dataset evaluation: a fixed set of representative conversations, each with an expected behaviour (answer from a document, say "I don't know," call the CRM tool, refuse, escalate), scored by a mix of deterministic checks and an LLM-as-judge rubric. Regression against the previous version blocks the release. A separate adversarial set - prompt-injection attempts, jailbreaks, out-of-scope asks - has to pass clean.

This is the least glamorous part of the system and the one that lets me change a shared module without holding my breath.

Decision: onboarding is functional questions, not prompt writing

The version I inherited had an eight-step configuration wizard. Most people did not finish it. I cut it to a single intent-capture screen - the admin picks what the assistant should do, and reaches a clear picture of the assistant's scope in under four clicks. Everything the old wizard asked is still asked, later, in context, with a working assistant already in front of the user to react to. I benchmarked 16 competitor onboarding flows before settling on this; the ones that converted best all deferred configuration until after first value.

Decision: price on the outcome, and only gate the outcome

Pricing is three self-serve tiers plus a custom Enterprise tier, billed per conversation, not per seat - unlimited teammates share one workspace. A $1 trial gives the full entry plan for 14 days.

Two calls I had to defend:

Integration model

CRM sync is bi-directional and runs through Composio rather than a hand-rolled integration: routing rules, a deal-versus-ticket split, and sentiment and lead-quality scoring written back to the CRM, plus a per-conversation quota that feeds metered billing. The agent also spans modalities - text, image, voice, and video - across model providers, so the same conversation can, for example, take a spoken question and return a generated image.

Deliberately not live yet, and named as roadmap rather than implied: voice conversations, calendar booking, live human takeover as a staffed queue, a governed-autonomy (L0-L4) selector, CRMs beyond the first, and channels beyond the website widget. Saying so plainly is part of the grounding pitch - the product should not over-claim any more than the agent should.

Decision: treat latency as a product feature, not an infra afterthought

For a chat-shaped product, the gap between a message and a reply is felt the same way a person going quiet mid-conversation is felt. The metric that matters here is time-to-first-token (TTFT) - how long before the visitor sees anything happen, not how long the full answer takes to finish generating. Early on that gap was 4.7 seconds median - technically correct answers, slow enough to feel broken. Four changes got it to 1.2 seconds, and none of them traded quality for speed:

Results so far

Early production, first paying enterprise customer, a deal I ran end to end from proof-of-concept through security review:

The sample is early and the unanswered rate will move as query diversity grows; I would rather report a real number on a small base than a projected one.

What I would do next

  1. Close the loop on unanswered conversations - route every "I don't know" into a weekly knowledge-base gap report, so the number that matters most has an owner and a workflow, not just a dashboard tile.
  2. Ship the autonomy selector. Right now the agent's boldness is fixed per job; customers in regulated spaces want a dial, and an approvals queue behind it.
  3. A second CRM. The Composio layer makes this mostly a config and eval problem rather than an integration rewrite, which was part of why I chose it.

What I owned

Repositioning and the platform's system-prompt architecture; the guardrail precedence and validation model; the eval suite; onboarding; the latency and cost work (knowledge-base migration, context bounding, prompt caching, streaming); pricing, packaging, and the product-led growth motion; the go-to-market source of truth (competitor matrix, ICP, positioning, launch plan); and the CRM integration spec. Team: cross-functional, engineering and design, at a pre-Series-A company.