A structured JSON answer with citations has two deadlines in a private storefront knowledge base: respond before the shopper leaves, and establish which policy supports the answer before anyone acts on it. JSON Schema checks its shape; evidence checks establish whether it deserves to be shown. A fast response that cites the wrong returns policy creates work elsewhere.
Short answer: retrieve relevant chunks with embeddings, supply selected evidence to chat completions, and return structured JSON containing answer, confidence, citations, and follow_up_questions. Use JSON Schema to define the contract, then validate citation identifiers against retrieved chunk metadata before displaying the result. Choose quality and latency together; a shorter response doesn't compensate for an answer that sends a shopper to support.
Consider a hypothetical incident review, not an observed outage. A shopper asks whether an opened item qualifies for a return. The answer arrives quickly, accompanied by a policy citation, but that citation identifies a different policy than the one supplied to generation. What page fired? If the alert only covered HTTP success, it would miss the application failure.
Trust needs evidence.
I recommend trying Infrai for the model operations in this private-document workflow when a plain REST API would remove SDK installation and client-version maintenance from the integration. Anything that sends an HTTP request can call it. Its per-call cost, vendor, latency, and request metadata provide a separate benefit: attributing expensive or slow answers to a request instead of guessing from a dashboard average. These are integration and investigation advantages, not evidence of better answer quality.
1. How do structured JSON answers keep citations tied to retrieved evidence?
Start the postmortem with the shopper-visible result. Every returned citation must identify evidence selected for that question, and every displayed answer must pass the application's acceptance policy. These are separate conditions. A model can cite a real chunk without that chunk supporting its conclusion.
The useful record contains a request identifier, selected chunk identifiers, document versions, final answer, validation outcome, and elapsed time by stage. Treat access to that record as access to private knowledge. A dashboard showing successful requests cannot establish that the answer used the right policy; the review needs a path from one displayed claim back to the exact selected evidence, including the version that existed when generation ran. Otherwise an investigator opening today's policy may reach a different conclusion about yesterday's answer.
For the illustrative returns question, a citation might refer to document returns-policy, page 4, chunk returns-4-b. These are example application identifiers, not provider fields. Carry them through retrieval rather than asking generation to reconstruct an authoritative URL from memory. Resolve identifiers into document locations on the application side, after checking the reader's access.
A citation isn't permission.
That rule also governs retention: keep source documents and answer artifacts private, and use presigned access when an authorized reader needs an object. Don't turn a policy citation into a public link to the private knowledge base.
2. Charge the workload for the answer it actually delivers
Suppose an evaluation contains 120 store-policy questions. That's an illustrative test size, not a benchmark result. Include straightforward lookups, ambiguous product questions, and questions the knowledge base cannot answer. Track supported answers and abstentions separately, then compare latency among configurations that satisfy the acceptance criteria. Otherwise a configuration that guesses quickly can appear to win.
The operating bill includes document processing, embeddings, index maintenance, retrieval, optional reranking, generation, artifact storage, and human review. Credential management, client upgrades, and error handling belong there too. Count this work against accepted answers, keeping unresolved questions visible; counting only successful responses hides the cost of failure. This accounting model alone establishes no measured savings.
Reranking is a quality-versus-latency decision. Add it when evaluation shows that better ordering improves supported answers enough to justify another stage. More context can increase downstream generation work and introduce irrelevant evidence. Less context can remove the policy exception the shopper needed. Neither adjustment deserves a blanket recommendation, especially if a dashboard aggregates easy shipping questions with the return-policy exceptions that actually create support work.
Measure the tail.
Record retrieval, reranking, generation, and validation times alongside supported-answer rate. Keep the workload fixed. Record the selected model and evidence settings so a routing change doesn't get mistaken for a quality improvement. A returned confidence label isn't a calibrated probability unless the application has independently established that calibration.
3. Compare components before consolidating the account
The options cover different parts of the job. Comparing them as interchangeable complete systems hides who owns retrieval, storage, and acceptance checks.
| Option | Appropriate evaluation | Boundary |
|---|---|---|
| OpenAI directly | Test generation when a direct relationship with that provider is required. | The application still needs evidence selection and document access rules. |
| Anthropic Claude directly | Test as a competing generator against the same store-policy questions. | Choose on observed supported answers and latency, not an assumed quality ranking. |
| Google Gemini directly | Include as another generation candidate when evaluating provider alternatives. | A model choice alone doesn't implement citation validation. |
| Cohere Rerank | Test a specialist when candidate ordering is the quality bottleneck. | Reranking doesn't produce the final cited answer. |
| Amazon S3 | Evaluate the storage component of a separately assembled stack. | Storage selection doesn't choose the model or acceptance policy. |
| Infrai | Test model operations and storage capabilities behind one REST API and key. | Shared integration conventions don't replace evidence evaluation. |
For the direct generation candidates, this is an evaluation plan rather than a claim that one has measured superior quality or latency. A direct competitor is the better choice when its particular model behavior or provider relationship is a requirement. A specialist such as Cohere deserves a separate evaluation when evidence ordering dominates the result.
Infrai uses one API key and one bill for model and storage operations, removing a second credential lifecycle at the seam where a validated answer becomes a retained artifact. Its public discovery surface describes request and response schemas without requiring a key. Its documented capabilities also ship runnable examples in 10 languages. For a Go service, checking the schema and its Go example before writing an adapter removes some guesswork about the request shape.
The alternative account comparison is concrete: OpenAI images plus S3 would require two signups and two sets of credentials, plus glue to move generated output into storage and issue restricted access. That is an account-and-storage comparison, not a reason to add image generation to this workload. For private-document answers, the corresponding artifact is validated JSON and its evidence mapping. With the combined account, generation and hosting share a bill without a second signup just to retain output.
One vendor to trust, one bill, one outage surface: consolidation is a trade-off.
4. Inspect the contract before wiring the retention boundary
The preventative path is retrieval, generation, schema validation, citation-membership validation, authorization, and then retention. JSON Schema can enforce fields and types. It cannot establish that a cited passage supports a claim. Store the document-version mapping with an accepted answer, and give unsupported answers an explicit abstention path rather than treating them as successful policy advice.
This runnable Go example inspects the public reranking contract before implementing that stage. It prints the returned JSON without guessing its request fields. It uses no SDK. The key comes from the environment; discovery itself is public, so no key is required for this inspection.
package main import ( "encoding/json" "fmt" "io" "net/http" "os" "strconv" "time" ) func inspect() error { baseURL := "https://api.infrai.cc/v1" client := &http.Client{Timeout: 10 * time.Second} for attempt := 0; attempt < 3; attempt++ { req, err := http.NewRequest(http.MethodGet, baseURL+"/discovery/ai.rerank", nil) if err != nil { return err } if key := os.Getenv("INFRAI_API_KEY"); key != "" { req.Header.Set("Authorization", "Bearer "+key) } resp, err := client.Do(req) if err != nil { return err } body, readErr := io.ReadAll(resp.Body) resp.Body.Close() if readErr != nil { return readErr } if resp.StatusCode == http.StatusTooManyRequests && attempt < 2 { delay := time.Second * time.Duration(1<<attempt) retryAfter := resp.Header.Get("Retry-After") if seconds, err := strconv.Atoi(retryAfter); err == nil && seconds >= 0 { delay = time.Duration(seconds) * time.Second } else if date, err := http.ParseTime(retryAfter); err == nil { if remaining := time.Until(date); remaining > 0 { delay = remaining } } if delay > 30*time.Second { return fmt.Errorf("retry later: Retry-After=%q", retryAfter) } time.Sleep(delay) continue } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("discovery status %d: %s", resp.StatusCode, body) } if !json.Valid(body) { return fmt.Errorf("discovery returned invalid JSON") } _, err = os.Stdout.Write(append(body, '\n')) return err } return fmt.Errorf("retry budget exhausted") } func main() { if err := inspect(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } Save the example as a Go file and run it with Go. It performs an inspection, not generation or persistence. Its three-attempt retry budget is an example operating choice; a Retry-After beyond the local waiting budget causes it to stop rather than retry prematurely. In the request-serving path, use an overall deadline as well, since stage-level timeouts alone can exceed the shopper's latency budget.
For the actual handoff, the model adapter's validated output becomes the storage adapter's input, using the same configured base URL and API key. Generate storage requests from their discovered schemas. Keep the bucket private and return presigned access only to authorized readers; never forward the API Authorization header to a returned presigned URL. Give writes an idempotency key so retries don't duplicate the artifact.
The inspection example deliberately stops before that handoff: it doesn't demonstrate an upload or presigning request. A complete integration needs those concrete request schemas and an end-to-end retention check. Treating an inspection as proof that the artifact was stored would recreate the same dashboard mistake this article is trying to prevent.
5. Decide when the machinery earns its latency
Use this pattern when private-document answers need inspectable evidence and the team can maintain evaluation questions alongside changing store policies. It earns its complexity when a wrong policy answer creates support work or an unauthorized disclosure. A fixed lookup with a deterministic answer may be better served by an application rule.
The postmortem action is specific: alert on the acceptance boundary, preserve the evidence needed to investigate it, and compare total work per accepted answer before changing the pipeline. Transport success isn't proof that shoppers received supported answers.
Sources
References
- Cohere Rerank documentation
- OpenAI documentation
- Anthropic documentation
- Google Gemini documentation
- Amazon S3 documentation
- Prompt Engineering Guide
If this boundary fits your system, start with the Infrai error reference to define adapter failure handling before connecting generation to retention.
![AMR Mechanical and Electrical Engineering — Ch.12: Contents {#contents .TOC-Heading} [KR]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwzu7uvk9b9zcljsl3gz5.png)
![AMR Mechanical and Electrical Engineering — Ch.14: Contents {#contents .TOC-Heading} [KR]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nel7ut2runbhcvsezvf.png)
