Skip to main content
This assistant searches the live web with Opper’s built-in web tool, then uses structured output to write a grounded answer that cites its sources. Two steps: search, then one structured call through the gateway.
python app.py

You › What is Mistral’s largest open-weight model?

Tool › web_search(“Mistral largest open-weight model”)
→ 5 results from the web

Bot › Mistral’s largest open-weight model is Mistral Medium 3.5, a 128B-parameter model for reasoning, coding, and instruction-following.

sources: [“mindstudio.ai/blog/what-is-mistral-medium-3-5…”]

Search results go in, a typed answer with sources comes out.

The assistant

import os, json, requests
from openai import OpenAI

KEY = os.environ["OPPER_API_KEY"]
client = OpenAI(base_url="https://api.opper.ai/v3/compat", api_key=KEY)

def web_search(query: str) -> list[dict]:
    r = requests.post(
        "https://api.opper.ai/v3/tools/web/search",
        headers={"Authorization": f"Bearer {KEY}"},
        json={"query": query},
    )
    r.raise_for_status()
    return r.json()["results"]

ANSWER_SCHEMA = {
    "type": "object",
    "properties": {
        "answer": {"type": "string", "description": "A concise answer grounded in the search results."},
        "sources": {"type": "array", "items": {"type": "string"}, "description": "URLs of the results you actually used."},
    },
    "required": ["answer", "sources"],
}

def research(question: str):
    results = web_search(question)
    r = client.chat.completions.create(
        model="openai/gpt-5-mini",
        messages=[
            {"role": "system", "content": "Answer the question using only the provided search results. Cite the URLs you used in sources."},
            {"role": "user", "content": f"Question: {question}\n\nResults:\n{json.dumps(results)}"},
        ],
        response_format={"type": "json_schema", "json_schema": {"name": "answer", "schema": ANSWER_SCHEMA}},
    )
    return json.loads(r.choices[0].message.content)

if __name__ == "__main__":
    data = research("What is Mistral's largest open-weight model?")
    print(data["answer"])
    print("Sources:", data["sources"])
Run it:
pip install openai requests
export OPPER_API_KEY="your-api-key"
python app.py

How it works

  • Search. web_search calls Opper’s hosted web tool (POST /v3/tools/web/search) and returns a list of {title, url, snippet} results. No separate search API key to manage.
  • Answer. The structured output call gets the question plus those results and returns a typed answer. Because the output is schema-constrained, answer and sources come back clean after a single parse.
  • Grounding. The model only sees the results you pass in, so it answers from the search rather than from memory. If a fact isn’t in the results, it can say so.
Pass more or fewer results to trade cost for coverage, and add an Observe rule that checks every answer actually cites a source.

What’s next

Structured output

The schema-constrained call behind the answer step.

Web search

The portable web tool, across every model.

Tools

Let the model call your code directly.

Observe

Score answers for grounding and citation quality.