The page should say that private knowledge answers are failing the release contract: which validation gate rejected them, how much of the release error budget they consumed, and whether review capacity is keeping up. An HTTP success count cannot answer those questions. For a developer tool answering questions over a private knowledge base, the useful comparison between a text moderation API and chat completions with a JSON Schema constraint starts at the decision you can safely act on.
Short answer: separate moderation from answer generation, then require three gates before release: a parseable decision, a decision that satisfies the application contract, and an answer whose citations belong to the authorized retrieval set. Compare a dedicated moderation interface with schema-constrained generation against that same contract and an independently labeled evaluation set. Include execution and review costs, but keep correctness as the acceptance criterion.
The alert below is a proposed design, not a production incident or a measured benchmark. Its purpose is to expose a failure that a green transport dashboard could conceal.
What should the page have caught earlier?
Imagine the on-call opening an alert for rejected answer releases. Requests are completing, but some generated decisions contain an unfamiliar action value; other responses contain valid action values alongside citations outside the request's authorized retrieval set. Those are different failures, and combining them into a single model-error counter would make the next deployment harder to judge.
Reject that release.
Consider a developer asking, “Which internal document explains rotating the staging deployment token?” The proposed answer contract contains an action and document identifiers, with the answer text held separately until validation succeeds. A decision to allow an answer does not authorize access to a cited document. Conversely, a permission check says nothing about whether the answer is acceptable under the content policy. Both checks belong in the release path, with the original user's permissions carried through retrieval and citation validation.
Work backward from the page to an earlier signal: the fraction of attempted releases rejected by each contract gate, grouped by deployment version and policy version. This should be available during a canary, before a broader rollout consumes the release SLO's error budget. A separate evaluation signal must track cases where a structurally valid allow decision disagrees with the labeled policy outcome; syntax checks cannot discover those cases.
That's the trap. A valid object can still encode the wrong decision.
Three gates turn a model response into a release decision
Treat these as application requirements for the example system, rather than promises about a particular provider. Gate one accepts exactly one JSON object, rejects unknown fields, and rejects trailing content. Gate two admits only the action values that the application implements and checks their relationships with the other fields. Gate three verifies citation membership against the authorized document set produced for this request.
The proposed actions are allow, block, and review. An allowed answer must cite at least one authorized document. Block and review decisions carry no citations in this deliberately small contract, because neither releases an answer. That rule is a design choice; applications that expose evidence to reviewers would need a separate, permission-checked review contract.
JSON Schema is useful for expressing the structural portion of this boundary, but permission membership depends on request context. Keep that check in application code. In particular, do not accept a document identifier merely because it matches the expected string pattern, and do not ask the generator to decide whether the user has access to it.
The following Go example focuses on the receiving boundary. It assumes the caller has already imposed a response-size limit and passes the authorized retrieval set from trusted application state. The enclosing handler must withhold the answer whenever this function returns an error; choosing whether to retry, queue review, or return an explicit unavailable state belongs to that handler.
package moderation import ( "encoding/json" "errors" "io" ) type Decision struct { Action string `json:"action"` DocIDs []string `json:"doc_ids"` } func Validate(r io.Reader, authorized map[string]bool) (Decision, error) { var d Decision dec := json.NewDecoder(r) dec.DisallowUnknownFields() if err := dec.Decode(&d); err != nil { return Decision{}, errors.New("decision_parse") } var extra any if err := dec.Decode(&extra); err != io.EOF { return Decision{}, errors.New("decision_trailing_content") } if d.DocIDs == nil { return Decision{}, errors.New("decision_missing_doc_ids") } switch d.Action { case "allow": if len(d.DocIDs) == 0 { return Decision{}, errors.New("decision_missing_evidence") } for _, id := range d.DocIDs { if !authorized[id] { return Decision{}, errors.New("decision_unauthorized_citation") } } case "block", "review": if len(d.DocIDs) != 0 { return Decision{}, errors.New("decision_unexpected_evidence") } default: return Decision{}, errors.New("decision_unknown_action") } return d, nil } This is a boundary check, not a complete JSON Schema implementation or a policy classifier. The Go decoder documentation describes additional parsing behavior, including the handling of duplicate keys; if the contract requires rejecting duplicate keys, enforce that requirement with an additional parser check. Passing this function also does not establish that a cited document supports the answer. Evidence support needs its own evaluation and, where required, a runtime check.
How should a text moderation API compare with JSON Schema chat completions?
For the moderation-API versus schema-constrained-chat question, ask what each candidate interface actually returns and how its documented output maps to your policy. If an interface supplies category signals, the application still needs an explicit mapping from those signals to allow, block, or review. If an interface supplies a generated decision object, evaluate the policy judgment independently of whether the object passes validation. Neither comparison requires treating a schema as proof of moderation accuracy.
For a caller implemented in Node.js, preserve exactly the same boundary: validate the received object and its request-specific permissions before acting on it. The Go example illustrates the contract rather than a provider integration. Language choice should not change the evaluation labels, the review policy, or the release criteria.
The buy-versus-build discussion belongs here, after defining correctness. This table is a planning checklist, not a claim that every managed or self-hosted implementation has these properties.
The trade-off is ownership.
| Decision area | Buy: acceptance questions | Build: obligations to budget |
|---|---|---|
| Policy coverage | Does documented behavior cover the labeled cases and escalation needs? | Maintain labels, policy mappings, and regression evaluations. |
| Output contract | Which response states require normalization or rejection? | Maintain parsing, validation, and compatibility rules. |
| Capacity | What documented limits and measured latency fit the SLO? | Provision serving capacity and test saturation behavior. |
| Operations | What diagnostics and change controls are available? | Own deployment, rollback, observability, and on-call response. |
A cheaper successful invocation is a weak decision criterion if its rejected outputs or unnecessary review decisions exhaust the team's operational capacity. Compare total work per accepted, policy-correct answer on the same workload. Record invocation charges when applicable, serving resources, retries, and review time separately so that a change in the review policy cannot masquerade as a model efficiency improvement. No unit-price ranking is needed.
A limitation of the proposed validator is its narrow scope: it cannot detect a policy misclassification. Schema validation is not suitable as the only content safety control, and building more validation code will not resolve missing policy evaluation. However, moving that code into a managed interface also leaves the application responsible for permission checks and its own release SLO.
Instrument the release path before expanding traffic
Add a counter at the release boundary with bounded labels for gate and outcome. Record attempts even when parsing fails, and attach deployment and policy versions through the telemetry system's supported conventions. Keep raw prompts, private document identifiers, and answer bodies out of metric labels. For investigation, use access-controlled traces with an explicit retention policy and only the fields needed to explain a decision.
Count user-visible outcomes too. A review response may be a correct policy outcome, while an expired review request may still violate the answer availability objective. Define that distinction in the SLO before setting alerts; otherwise a system can improve its apparent safety merely by leaving more users waiting.
Capacity planning starts with admitted request rate and measured service time. As an illustrative planning assumption, 20 requests per second with a mean two-second moderation stage implies about 40 requests in that stage on average under steady conditions. That arithmetic is not a concurrency limit. Tail latency, burst arrivals, retries, and any additional answer checks still need load testing, while review arrivals must be compared with reviewer throughput rather than model throughput. Put retry limits inside the overall request deadline so that overload cannot silently multiply the offered work.
Before rollout, exercise malformed objects, unsupported action values, missing evidence, unauthorized citations, and policy disagreements. Include quoted code and internal security documentation in the labeled corpus: the application's intended treatment of those texts must be explicit. Change one decision component at a time during a canary, preserve the previous policy mapping for rollback, and compare both release failures and labeled policy errors. A lower schema rejection rate alone is insufficient evidence to expand traffic.
Set the page threshold against review capacity
Page on actionable release-budget consumption and review backlog risk, with a sustained window appropriate to the SLO. Route isolated contract failures to investigation when they don't justify interrupting an engineer. The precise threshold needs measured traffic, baseline rejection rates, and a declared error budget; there is no defensible universal percentage.
A threshold set too low consumes attention and can encourage unnecessary holds; a threshold set too high allows rejection spikes or review queues to grow before intervention. False-positive cost therefore includes interrupted on-call time and delayed developer answers, alongside avoidable review work. Close the loop by measuring those outcomes after threshold changes. The goal is an alert that leads to a specific release or capacity decision, with enough evidence to make it.
References
The specifications below support the structural-validation and decoding boundaries discussed here. They do not establish a classifier's policy accuracy or a deployment's SLO performance.
- JSON Schema validation specification: https://json-schema.org/draft/2020-12/json-schema-validation
- Go standard library JSON decoder documentation: https://pkg.go.dev/encoding/json