Perplexity AI a popularisé le modèle de réponse aux questions avec des sources web citées en temps réel. L'architecture de base est simple : rechercher sur le web la question de l'utilisateur, fournir les résultats comme contexte à un LLM, et diffuser la réponse avec des citations de sources. Ce tutoriel construit un clone minimal de Perplexity en utilisant Next.js pour le frontend, Scavio pour la recherche en temps réel, et l'API de streaming OpenAI pour la réponse. Le résultat est une application web déployable qui répond aux questions avec des sources vivantes et citées.
Prérequis
- Node.js 18 ou supérieur
- npx create-next-app knowledge
- Une clé API Scavio
- Une clé API OpenAI
Parcours
Étape 1: Créer la route API pour la recherche et la réponse
Construisez une route API Next.js qui récupère les résultats Scavio, les formate en contexte, et diffuse une réponse GPT vers le client.
// app/api/answer/route.ts
import { NextRequest } from "next/server";
import OpenAI from "openai";
const openai = new OpenAI();
const SCAVIO_KEY = process.env.SCAVIO_API_KEY!;
async function fetchSources(query: string) {
const res = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": SCAVIO_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query, country_code: "us" })
});
const data = await res.json();
return (data.organic_results || []).slice(0, 5);
}
export async function POST(req: NextRequest) {
const { question } = await req.json();
const sources = await fetchSources(question);
const context = sources.map((s: any, i: number) => `[${i+1}] ${s.title}\n${s.snippet}\n${s.link}`).join("\n\n");
const stream = await openai.chat.completions.create({
model: "gpt-4o", stream: true,
messages: [
{ role: "system", content: "Answer concisely using the sources. Cite with [n]." },
{ role: "user", content: `Sources:\n${context}\n\nQuestion: ${question}` }
]
});
const encoder = new TextEncoder();
const readable = new ReadableStream({
async start(controller) {
controller.enqueue(encoder.encode(JSON.stringify({ sources }) + "\n"));
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content || "";
if (text) controller.enqueue(encoder.encode(text));
}
controller.close();
}
});
return new Response(readable, { headers: { "Content-Type": "text/plain" } });
}Étape 2: Construire le composant d'interface de recherche
Créez un composant React simple avec une entrée de recherche qui diffuse la réponse et affiche des cartes de sources.
// app/page.tsx
"use client";
import { useState } from "react";
export default function Home() {
const [question, setQuestion] = useState("");
const [answer, setAnswer] = useState("");
const [sources, setSources] = useState<any[]>([]);
async function handleSearch() {
setAnswer("");
setSources([]);
const res = await fetch("/api/answer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ question })
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let first = true;
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
if (first) {
const newline = text.indexOf("\n");
const meta = JSON.parse(text.slice(0, newline));
setSources(meta.sources);
buffer = text.slice(newline + 1);
first = false;
} else {
buffer += text;
}
setAnswer(buffer);
}
}
return (
<main>
<input value={question} onChange={e => setQuestion(e.target.value)} placeholder="Ask anything..." />
<button onClick={handleSearch}>Search</button>
<div>{answer}</div>
<div>{sources.map((s, i) => <a key={i} href={s.link}>[{i+1}] {s.title}</a>)}</div>
</main>
);
}Étape 3: Configurer les variables d'environnement
Configurez les clés API Scavio et OpenAI dans votre fichier .env.local.
SCAVIO_API_KEY=your_scavio_api_key
OPENAI_API_KEY=your_openai_api_keyExemple Python
import os
import requests
from openai import OpenAI
SCAVIO_KEY = os.environ.get("SCAVIO_API_KEY", "your_scavio_api_key")
client = OpenAI()
def search(question: str) -> list[dict]:
r = requests.post("https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_KEY},
json={"query": question, "country_code": "us"})
r.raise_for_status()
return r.json().get("organic_results", [])[:5]
def answer(question: str) -> None:
sources = search(question)
ctx = "\n\n".join(f"[{i+1}] {s['title']}\n{s.get('snippet', '')}\n{s['link']}" for i, s in enumerate(sources))
stream = client.chat.completions.create(
model="gpt-4o", stream=True,
messages=[
{"role": "system", "content": "Answer using sources. Cite with [n]."},
{"role": "user", "content": f"Sources:\n{ctx}\n\nQuestion: {question}"}
])
for chunk in stream:
text = chunk.choices[0].delta.content or ""
print(text, end="", flush=True)
print()
if __name__ == "__main__":
answer("What is the state of AI agents in 2026?")Exemple JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY || "your_scavio_api_key";
const { OpenAI } = require("openai");
const client = new OpenAI();
async function search(question) {
const res = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": SCAVIO_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query: question, country_code: "us" })
});
const data = await res.json();
return (data.organic_results || []).slice(0, 5);
}
async function answer(question) {
const sources = await search(question);
const ctx = sources.map((s, i) => `[${i+1}] ${s.title}\n${s.snippet || ""}\n${s.link}`).join("\n\n");
const stream = await client.chat.completions.create({
model: "gpt-4o", stream: true,
messages: [
{ role: "system", content: "Answer using sources. Cite with [n]." },
{ role: "user", content: `Sources:\n${ctx}\n\nQuestion: ${question}` }
]
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}
}
answer("State of AI agents 2026").catch(console.error);Sortie attendue
AI agents in 2026 have matured significantly. According to recent reports, the market
has shifted from experimental chatbots to production-grade autonomous systems [1].
Major frameworks like LangGraph and CrewAI now support stateful, multi-step workflows
out of the box [2]. Enterprise adoption has accelerated, with 40% of Fortune 500
companies deploying at least one agent-based system [3].
Sources:
[1] https://example.com/ai-agents-2026
[2] https://example.com/agent-frameworks
[3] https://example.com/enterprise-agents