Why AI makes up numbers, and how we stop it in code: the facts rule and review-before-reply
Telling an AI "do not do X" works well enough in a prompt. Telling it "you must always do Y" never fully works. Numbers are the clearest example. This is how we stopped the AI from inventing statistics at the code level, and what still needs a human eye afterwards.
Where prompts stop working, with real cases
Our official account's persona says, in plain words: "Only use the numbers in the list of real data above. Do not calculate, do not guess." What actually came out:
- The data said "421 views one day, 1 view the next". The post said "reach dropped by a hundredfold". 421 divided by 1 is not a hundred, and no such ratio was in the list.
- The data had two unrelated facts, "fastest reply in 15 seconds" and "654 replies averaging 1,116 views". The post joined them into "reply speed is directly tied to views".
- The data said 18.7x. The post rounded it to 20x.
Moving the persona to the highest priority took compliance from four out of four violating to four out of six passing. The remaining two were not a case of the prompt being too soft. The prompt layer simply does not reach 100%. So we drew the line explicitly: tone, opinion and narrative belong to the persona; length, calls to action, hashtags, banned phrases and number provenance belong to code.
How the facts rule works
content_rules.facts in the account settings is a list of plain sentences, each one a fact you have verified, such as "accounts posting 5 times a day average 38 views". Once it is set:
- The server extracts every number from the generated text, ignoring thousands separators (1,116 and 1116 are the same number).
- It compares them with every number that appears in the facts. Anything not in the list becomes an
unverified_numberviolation, naming the offending number. - Dates, times and small counts (0 to 3) are exempt, so "the 9/6 post" or "three things" do not trip it.
The rule guards four doors at once: AI generation, items the agent writes into the library itself, edits, and direct publishing. Whoever wrote the text, it passes the same gate before it reaches the account.
After a block: the server regenerates on its own
Rules have two modes. warn returns the content with a list of what it violated; block refuses it. Previously a blocked generation was retried once and the agent got a 422 and had to call again, which pushed the retry loop onto the caller and burned its daily quota. Now the server calls the model up to three times, telling it each time exactly where the previous version failed rather than rolling the dice again, and only returns 422 if the third attempt still violates.
Our own tests: when the agent submitted a library item containing "300% efficiency gains", it was rejected with a 422 and nothing was stored. When asked to write a post claiming "300% efficiency gains and 5x follower growth in a week", neither number in the facts, the model read the rules and simply left both numbers out on the first attempt. A topic built on a number that is in the facts passed on the first try.
Replies get a review step too
Posts always had drafts and approval; replies did not. Set the keywords and the account starts replying under other people's posts. Before we fixed the relevance judgement, our official account had replied to a complaint about a supermarket car park and to a post about a delivery rider's shoes. On a brand account, one of those going out is a PR incident.
Cloud patrol now has a review_before_reply switch: each drafted reply goes into a pending list and expires after 24 hours if nobody approves it. You can edit the text when approving; the edited text goes through the same quality gates. Sending uses exactly the same path as an automatic reply, with the same point accounting, deduplication and logging. The only difference is one more door, for a human or an agent.
What still needs a reviewer
Code can only enforce what it can decide mechanically. It cannot catch:
- Numbers written as words. "A hundredfold" has no digits to check.
- Inferences without numbers. "The faster you reply, the more views you get" contains nothing the rule can flag.
- A post that covers too many ideas, drifts in tone, or mentions the product too often.
So those became a seven-item review checklist, published in the playbook section of the API spec, for agents to go through before queueing. As more rules move into code the list gets shorter, but it will not reach zero.
FAQ
I do not have much data. What goes into facts?
Things you are sure of: prices, dates, product specifications, numbers you measured yourself. If you have no data, do not let the AI talk in numbers at all. An empty facts list turns the check off, and in that case the persona should not contain numbers either.
Will it reject legitimate content?
The rules are deliberately conservative: dates, times and small counts are exempt, and the call-to-action check only matches sentences that ask the reader to do something. An account that talks about reply statistics will not have the word "reply" flagged.
How is this different from a "brand voice" setting in other tools?
A brand voice setting is a prompt; you can only inspect the output afterwards. These rules stop text before it reaches the account, and the response tells you exactly why it was stopped.