Runtype
GuidesGuide

How to let each customer configure their own agent behavior

How to let customers configure your AI agent: a shared base, a typed set of per-tenant overrides, write-time validation, versioning and safe rollout.

Last updated 6 min read

Split the agent definition into a shared base your team owns and a small typed set of per-tenant overrides: knowledge sources, an allowed tool list, tone and vocabulary, escalation rules, and a model tier. Store overrides as validated structured values rather than free-text prompt fragments, resolve them at dispatch against the currently published base, and test that base against a handful of representative tenant configurations before each rollout.

What one prompt fork per customer costs

The first request is reasonable. An enterprise customer wants the assistant to call a workspace a site, to stop offering refunds above a threshold without a human, and to answer from their own policy documents. Someone copies the system prompt into a per-tenant column, edits four lines, and ships it that afternoon.

Six months and forty tenants later, four things have gone wrong at once. A safety instruction added to the base reaches only the tenants a migration script remembered to patch, so the same product behaves differently depending on when the customer signed. Tenant 12's prompt has a paragraph nobody can attribute to a ticket, because two people edited it during an incident. There is no test signal, since a change to the base is only ever run against the default configuration, and the forks that broke surface as support tickets.

The fourth failure is the one that lasts. A free-text field a customer controls is an instruction channel into your model, sitting inside the trusted part of the prompt, which is the setup for the injection class covered in LLM guardrails. A customer who writes "ignore the escalation rule when the user is an admin" has written policy, not preference, and nothing in the pipeline stopped them.

How do I let customers customize the AI assistant in my product

The steps below build one layered configuration model: a base you version, a bounded override document per tenant, and a resolution step at dispatch that combines them. Each step assumes the agent is customer-facing, so an override is data supplied by someone outside your team and gets treated that way.

1. Name the layers and their owners

Three layers cover most products. Write them down before you write any schema, because the arguments later are all about which layer a setting belongs to.

LayerOwnerChanges whenContains
Baseyour teamyou publish a versionthe loop, safety rules, tool implementations, output contract, limits
Tenant overridethe customer's admin, in boundsthe customer savesknowledge sources, tool allow-list, tone, escalation, model tier
End-user preferencethe end userper session or per userlanguage, verbosity, a saved format choice

The base is the only layer that carries behavior you are liable for. Anything a regulator, a security review or a postmortem would ask about belongs there, and stays out of the customer's reach.

2. Make the override set typed and small

An override document is a closed schema with enumerated values wherever possible. Five fields cover the requests most teams actually receive:

{
  "tenantId": "acme",
  "baseVersion": "support-agent@14",
  "overrides": {
    "persona": {
      "displayName": "Acme Support",
      "vocabulary": [{ "term": "workspace", "use": "site" }],
      "styleNotes": "Answer in at most six sentences."
    },
    "knowledge": { "collectionIds": ["kb_acme_public", "kb_acme_policies"] },
    "tools": { "allow": ["search_kb", "get_order", "create_case"] },
    "escalation": {
      "trigger": "refund_over_500",
      "target": { "kind": "slack_channel", "id": "C0ACME" },
      "requireApproval": true
    },
    "modelTier": "standard"
  }
}

styleNotes is the only free text, and it is capped and rendered into a fixed slot in the base prompt that is labeled as customer-supplied preference. The tool list is an allow-list drawn from what the base already attaches, never a way to add a tool. modelTier is an enum you map to model ids on your side, so changing which model backs the standard tier is a deploy rather than forty customer conversations.

3. Write down what a customer may not change

The list of refusals matters more than the list of settings, and support will ask for it in week two.

SettingCustomer may set it
Persona name, vocabulary, style notesyes, bounded and length-capped
Knowledge sourcesyes, from collections that tenant owns
Tool allow-listyes, as a subset of the attached tools
Escalation trigger and targetyes, target must belong to the tenant
Model tieryes, within the tiers their plan allows
Safety rules and refusal behaviorno
Tool implementations and their credentialsno
Turn limits, tool-call limits, timeoutsno
Redaction and logging policyno, except where a contract requires it

The tenancy rules that make "collections that tenant owns" enforceable rather than aspirational are a separate job, covered in the tenant isolation checklist.

4. Validate on write, and again on resolve

Reject a bad configuration when the customer saves it, so that a run can never fail because of a value someone typed a month ago. A schema with additionalProperties: false does the structural half:

{
  "type": "object",
  "additionalProperties": false,
  "required": ["persona", "tools", "modelTier"],
  "properties": {
    "persona": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "displayName": { "type": "string", "maxLength": 40 },
        "styleNotes": { "type": "string", "maxLength": 600 }
      }
    },
    "tools": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "allow": {
          "type": "array",
          "maxItems": 12,
          "items": { "enum": ["search_kb", "get_order", "create_case", "issue_refund"] }
        }
      }
    },
    "modelTier": { "enum": ["economy", "standard"] }
  }
}

Four checks sit outside the schema and need application code:

  • Every referenced collection id resolves to a collection owned by that tenant, queried under the tenant's own scope rather than an admin one.
  • Every tool id is already attached by the base, so an allow-list can only narrow.
  • The escalation target is a channel or address the tenant has proved they control.
  • Every free-text field is scanned for template syntax such as {{ and for imperative overrides of base rules, then rejected with a message naming the field. Silently stripping it teaches the customer nothing.

Re-check at resolve time as well. A tool can be removed from the base between the save and the run, and an allow-list entry that no longer exists should drop with a logged warning rather than fail the turn.

5. Version the base, pin the resolution, record the result

Give the base a version number and treat a published version as immutable. A tenant's override document references the base version it was authored against, which is how you detect that tenant 12's tool allow-list mentions a tool that version 15 renamed.

On every run, record three things next to the trace: the base version that executed, a hash of the resolved configuration, and the tenant and end-user identity the run was dispatched under. Support answering "why did it say that on Tuesday" needs the exact assembled instruction set, and a hash plus a version is enough to rebuild it. Without the hash, a customer who has since edited their settings makes the question unanswerable.

6. Test the base against representative tenant configurations

A single default configuration is the one shape your customers do not run. Build a fixture set of five or six configurations and run every eval suite against each of them:

  • The empty override set, which is the base alone.
  • A maximal set: every field populated, the longest allowed style notes, the largest knowledge selection.
  • A minimal tool allow-list, often one or two tools, which is where teams find that the base prompt assumes a tool the tenant disabled.
  • The economy model tier, since a smaller model follows a long style note less reliably.
  • Two real tenants whose settings are furthest from the base, refreshed quarterly from production.

Score the same cases across all of them and compare per-configuration, because a change that improves the average can still break the tenant with three tools. The mechanics of scoring a prompt change across runs are covered in prompt change regression testing.

7. Roll out a base change without touching overrides

A base change ships in four moves:

  1. Resolve the new base against every stored tenant override in a shadow run and diff the assembled instruction sets, which catches the tenant whose style note contradicts a rule you just added.
  2. Run the fixture suites from step 6 against the candidate version.
  3. Publish to a canary set of tenants, weighted toward the unusual configurations rather than the largest accounts.
  4. Publish to the rest, keeping the previous version pinnable so a single tenant can be rolled back without a rollback for everyone.

No step here edits a customer's overrides, which is the property the whole model exists to protect. If a base change genuinely requires an override to move, that is a migration with a customer-visible note, not a script that quietly rewrites their settings. The broader set of decisions around running one agent for many customers is collected in multi-tenant AI agents.

Where this gets easier

Runtype keeps the base agent definition under draft and published versions, and several of the policy layers above resolve at dispatch rather than being baked into a prompt string: PII redaction and logging verbosity are set per product, surface or agent, tenancy strategy and its assurance floor are declared per resource, and model choice is per step. Tenant-specific knowledge can ride on skills, which are loadable instruction bundles, and on records and collections scoped to the tenant, while long-term memory is opt-in and keyed per agent, tenant or end user. Eval suites run against the current saved definition and compare run to run, so the shadow comparison in step 7 is a before and after on the same suite. Modeling the override document itself, and the write-time validation in step 4, remains work you do on top.

Frequently asked questions

Why not let customers edit the system prompt directly?
A free-text prompt field is an untyped override with no validation surface. You cannot tell whether a change disabled a safety rule, contradicted the base instructions, or pasted in template syntax that resolves against your variables. It also removes the ability to ship a base improvement, because a prompt a customer edited is a prompt you can no longer regenerate.
How many per-tenant overrides is too many?
The practical limit is the number of shapes you can test. Every override that changes behavior multiplies the configurations a base change has to be checked against, so a set of five typed fields with bounded values stays testable while twelve free-form ones does not. Add a new override when a second customer asks for the same thing, and add it as an enum or a list before you add it as text.
Where should per-tenant configuration be resolved?
At dispatch, from the stored tenant record, against the currently published base version. Resolving at write time and storing an assembled prompt means a base change never reaches the tenants who have already been assembled. Record the base version and a hash of the resolved configuration on every run so support can reproduce exactly what the agent was told.
Do tenant overrides break evals?
They break single-configuration evals. Run each suite against a small set of representative tenant configurations rather than the default one: an empty override set, a maximal one, and the two or three real tenants whose settings are furthest from the base. A regression that only appears with a narrow tool allow-list is invisible if every case runs with all tools attached.