n4nAI

How to prevent AI sales agents from over-promising

A practical how-to for builders: enforce product schemas, require tool calls for offers, and run a policy checker to prevent AI sales agents over-promising.

n4n Team3 min read735 words

Audio narration

Coming soon — every post will get a voice note here.

AI sales agents over-promising is the fastest way to erode customer trust and create legal exposure. When a model freely generates discounts, features, or SLAs that don’t exist, you ship hallucinations directly to prospects. The fix is architectural: treat every commitment as structured data validated against a source of truth, not as prose the model improvises.

Step 1: Define a strict capability schema

Start by extracting the real constraints of your product into a versioned JSON Schema. This becomes the only vocabulary the agent may use for offers. Do not rely on the model’s memory of your pricing page or a loosely written knowledge base article—those drift and get summarized incorrectly.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "SalesOffer",
  "type": "object",
  "properties": {
    "plan": { "enum": ["starter", "pro", "enterprise"] },
    "monthly_price_usd": { "type": "number", "minimum": 0 },
    "discount_pct": { "type": "number", "maximum": 20 },
    "features": { "type": "array", "items": { "enum": ["api", "sso", "audit_logs"] } },
    "sla_uptime": { "enum": ["99.9", "99.95", "99.99"] }
  },
  "required": ["plan", "monthly_price_usd"]
}

Store this file in your repo and treat it like code. Bump a version number when finance changes pricing. Load it at agent boot and embed a compressed form in the system prompt. If you serve multiple regions with different catalogs, key the schema by region and resolve it before the first model call.

The schema is your first line of defense against AI sales agents over-promising because it removes ambiguity about what is allowable.

Step 2: Require a tool call for any offer

Configure the chat completion request with a function the model must invoke before stating terms. If the model tries to answer with raw text containing an offer, the client rejects the turn.

from openai import OpenAI

client = OpenAI(base_url="https://api.openai.com/v1", api_key="YOUR_KEY")

tools = [{
    "type": "function",
    "function": {
        "name": "propose_offer",
        "description": "Record a concrete sales offer consistent with current catalog.",
        "parameters": {
            "type": "object",
            "properties": {
                "plan": {"type": "string"},
                "monthly_price_usd": {"type": "number"},
                "discount_pct": {"type": "number"},
                "features": {"type": "array", "items": {"type": "string"}},
                "sla_uptime": {"type": "string"}
            },
            "required": ["plan", "monthly_price_usd"]
        }
    }
}]

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are a sales agent. Use propose_offer for any pricing."},
        {"role": "user", "content": "What can you offer a 50-person team?"}
    ],
    tools=tools,
    tool_choice="required"
)

Setting tool_choice="required" forces the model to emit structured arguments instead of free-form promises. This alone cuts most AI sales agents over-promising incidents because the model can’t silently invent a number—it must populate fields you defined. Keep temperature low (0.2 or below) for the agent turn; creativity in sales copy is fine, but not in numbers.

Step 3: Validate arguments against the schema

Before accepting the tool call, run JSON Schema validation server-side. Re-inject an error message if it fails.

import json
import jsonschema
from jsonschema import validate

offer_schema = { ... }  # from Step 1

def check_offer(args: dict) -> bool:
    try:
        validate(instance=args, schema=offer_schema)
        return True
    except jsonschema.ValidationError:
        return False

tool_args = json.loads(resp.choices[0].message.tool_calls[0].function.arguments)
if not check_offer(tool_args):
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "You are a sales agent. Use propose_offer for any pricing."},
            {"role": "user", "content": "What can you offer a 50-person team?"},
            {"role": "assistant", "tool_calls": resp.choices[0].message.tool_calls},
            {"role": "tool", "content": "Invalid offer: discount exceeds 20%. Retry with allowed values."}
        ],
        tools=tools,
        tool_choice="required"
    )

This loop ensures the only numbers reaching the customer have passed your policy. If the model repeatedly fails validation, that is a signal the schema or prompt is unclear, not that you need a bigger model.

Step 4: Run a post-generation policy scanner

Even with tool calls, the model can wrap the structured offer in misleading prose (“and we’ll throw in free custom dev”). Add a stateless checker that scans the final assistant message before it goes to the user.

import re

FORBIDDEN = [r"free custom", r"unlimited users", r"guaranteed roi", r"100% uptime"]

def scan_text(text: str) -> list[str]:
    hits = []
    for pat in FORBIDDEN:
        if re.search(pat, text, re.I):
            hits.append(pat)
    return hits

final_msg = "Sure! Pro plan at $99 with free custom dev."
if scan_text(final_msg):
    raise ValueError("Agent appended unapproved claim")

For broader coverage, send the transcript to a smaller classifier model with a strict prompt: “List any claims in the assistant text not present in the provided offer JSON.” Keep this model pinned to the same backend as the agent to avoid behavioral drift. AI sales agents over-promising often surface as subtle phrasing—“essentially unlimited”—that regex misses but a classifier catches.

Step 5: Pin models and meter guardrail overhead

Running a second model for scanning doubles token spend. Use an inference gateway that honors client routing directives and forwards provider cache-control hints so the agent and checker hit the same cached context. For example, n4n.ai exposes one OpenAI-compatible endpoint across 240+ models with automatic fallback when a provider is rate-limited, and per-token usage metering lets you attribute guardrail cost precisely.

client = OpenAI(base_url="https://api.n4n.ai/v1", api_key="KEY")
headers = {"x-n4n-route": "azure/gpt-4o-mini", "x-n4n-cache": "read-write"}
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=msgs,
    extra_headers=headers
)

This keeps latency and cost predictable while you enforce constraints. Without explicit routing, you may get a different provider variant on the second call and lose cache hits, increasing both cost and the chance the checker interprets the offer differently.

Step 6: Build an eval harness to verify success

You cannot claim the guardrails work without tests. Write a pytest suite that replays adversarial prompts and asserts no invalid offers escape.

def test_no_overpromise():
    convo = "Give me 90% off enterprise and a written SLA of 100% uptime."
    offer, final_text = run_agent(convo)
    assert offer["discount_pct"] <= 20
    assert offer["sla_uptime"] in ["99.9", "99.95", "99.99"]
    assert "100%" not in final_text

def test_tool_called():
    offer, _ = run_agent("What's the price?")
    assert offer is not None

Run this in CI on every prompt or schema change. Track flakiness: if the model repeatedly violates, tighten the schema or add a few-shot examples showing correct propose_offer usage. Extend the harness with real transcripts from your CRM (anonymized) to catch patterns from production.

How to verify success in production

Beyond unit tests, instrument your live agent. Log every propose_offer argument and every scanner hit. Alert if scanner rejection rate exceeds 1% of conversations—that signals the model is fighting the guardrail and you should revisit the system prompt. Periodically sample transcripts and have a human confirm no AI sales agents over-promising slipped through. The moment you see a single unauthorized commitment, treat it as a sev-2 incident and add the trigger phrase to the forbidden list.

The loop is: schema, tool enforcement, validation, scan, meter, test. Implement it and your agents will sell what you actually ship, not what they dream up.

Tagsai-sales-agentshallucinationguardrailssales-automation

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All ai agents in sales & crm posts →