> ## Documentation Index
> Fetch the complete documentation index at: https://novita.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Web-Grounded Answers

A model alone can't answer questions about events after its training cutoff, or cite a source. The fix is *grounding*: retrieve relevant web content at request time and pass it to the model as context. This guide shows the pattern using Novita [AI Search](/docs/guides/ai-search-quickstart) for retrieval and the [LLM API](/docs/guides/llm-api) for synthesis — all behind one API key.

## What is RAG?

Retrieval-augmented generation (RAG) is the pattern behind every grounded answer here: instead of relying on what the model memorized during training, you *retrieve* relevant documents at request time and *augment* the prompt with them, so the model *generates* its answer from that fresh, verifiable context.

That retrieval step is exactly what AI Search provides. The web becomes your knowledge base, and you don't have to build or maintain a vector database to get started — a search call returns the passages, and the LLM does the rest. When you later want retrieval over your *own* private documents, the same three-step shape applies; you just swap web search for a vector store.

RAG buys you three things a bare model can't offer:

* **Freshness** — answers reflect content published after the model's training cutoff.
* **Attribution** — every claim can point back to a source URL the user can check.
* **Reduced hallucination** — grounding the model in retrieved text keeps it from inventing facts.

## How grounding works

A single RAG turn follows three steps:

1. **Search** (retrieve) the web for pages relevant to the question.
2. **Extract** (augment) the content you need — snippets or full page text — into the prompt.
3. **Synthesize** (generate) an answer by passing that content to an LLM, asking it to cite sources.

You can do this with two calls (search + LLM) or even one, since both Exa and Tavily can synthesize an answer for you. Choose based on how much control you need over the final wording and citations.

## Let the provider write the answer

The fastest path. Tavily's Search returns an LLM-generated answer when you set `include_answer`, and Exa offers a dedicated [Answer](/docs/api-reference/model-apis-exa-answer) endpoint. One call, no orchestration.

<CodeGroup>
  ```bash Tavily theme={"system"}
  curl -X POST 'https://api.novita.ai/v3/tavily/search' \
    -H "Authorization: Bearer ${NOVITA_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "query": "What changed in the latest Python release?",
      "include_answer": "advanced",
      "max_results": 5
    }'
  ```

  ```bash Exa theme={"system"}
  curl -X POST 'https://api.novita.ai/v3/exa/answer' \
    -H "Authorization: Bearer ${NOVITA_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "query": "What changed in the latest Python release?",
      "text": true
    }'
  ```
</CodeGroup>

Use this when you want a good answer with minimal code. Reach for the retrieve-then-synthesize approach below when you need control over the model, tone, output format, or how citations are presented.

## Retrieve, then synthesize with an LLM

This gives you full control. Search for sources, then hand the results to the [LLM API](/docs/guides/llm-api) with instructions to answer using only that context and to cite each claim.

```python theme={"system"}
import os
import requests
from openai import OpenAI

NOVITA_KEY = os.environ["NOVITA_API_KEY"]
QUESTION = "What changed in the latest Python release?"

# 1. Retrieve relevant web content.
search = requests.post(
    "https://api.novita.ai/v3/tavily/search",
    headers={"Authorization": f"Bearer {NOVITA_KEY}"},
    json={
        "query": QUESTION,
        "max_results": 5,
        "include_raw_content": "text",
    },
).json()

# 2. Build a compact, numbered context block the model can cite.
sources = []
for i, r in enumerate(search["results"], start=1):
    body = (r.get("raw_content") or r.get("content") or "")[:1500]
    sources.append(f"[{i}] {r['title']} ({r['url']})\n{body}")
context = "\n\n".join(sources)

# 3. Synthesize a grounded, cited answer.
client = OpenAI(base_url="https://api.novita.ai/openai", api_key=NOVITA_KEY)
completion = client.chat.completions.create(
    model="deepseek/deepseek-v4-flash",
    messages=[
        {
            "role": "system",
            "content": (
                "Answer using ONLY the provided sources. "
                "Cite claims with [n] referring to the source number. "
                "If the sources don't contain the answer, say so."
            ),
        },
        {"role": "user", "content": f"Question: {QUESTION}\n\nSources:\n{context}"},
    ],
)
print(completion.choices[0].message.content)
```

The same flow works with Exa Search — swap the route to `/v3/exa/search` and read `text` from each result instead of `raw_content`.

## Tuning retrieval quality

The quality of a grounded answer depends far more on *what you retrieve* than on the model. A few high-impact knobs:

* **Freshness.** For time-sensitive questions, constrain by date. Tavily exposes `time_range` and `start_date`/`end_date`; Exa exposes `startPublishedDate`/`endPublishedDate`. This keeps stale pages out of the context.
* **Source trust.** Use `include_domains`/`excludeDomains` to restrict retrieval to sources you trust (official docs, reputable publishers) and to keep out low-quality sites.
* **Topic hints.** Tavily's `topic` (`news`, `finance`, `general`) and Exa's `category` steer the engine toward the right kind of result.
* **Right-sizing context.** Cap extracted text (Tavily `chunks_per_source`, Exa `maxCharacters`) so you pass enough signal without blowing the model's context window or your token budget.

## Search vs. extract vs. crawl

Pick the retrieval primitive that matches your need:

* **Search** — you have a question and need to *discover* relevant pages. The default starting point.
* **Extract / Contents** — you already know the URLs and want clean text from them. Good for grounding on a fixed set of documents. See [Tavily Extract](/docs/api-reference/model-apis-tavily-extract) and [Exa Contents](/docs/api-reference/model-apis-exa-contents).
* **Crawl / Map** — you want to ingest a whole site or section, following links. Good for building a knowledge base offline. See [Tavily Crawl](/docs/api-reference/model-apis-tavily-crawl) and [Tavily Map](/docs/api-reference/model-apis-tavily-map).

<Tip>
  For a fuller agent that searches iteratively and reasons over many sources, see the [DeepSearcher integration](/docs/guides/deepsearcher).
</Tip>

## Taking it to production

* **Handle empty results.** If search returns nothing relevant, have the model say it can't answer rather than inventing one. The system prompt above does this.
* **Cache when you can.** Identical queries return similar results; caching retrieval cuts cost and latency.
* **Mind rate limits.** Retrieval and LLM calls are limited separately — a search request doesn't draw down your LLM quota. Back off on `429` for either. See [LLM rate limits](/docs/guides/llm-rate-limits) for the LLM side.
* **Keep keys server-side.** Your Novita key authenticates every call here — never ship it in client-side code.
