Resumen con citas
RAG más allá de las preguntas y respuestas. Dale un tema y recupera las notas de
Showcase — Resumen con citas
RAG más allá de las preguntas y respuestas. Dale un tema y recupera las notas de
investigación relevantes y sintetiza un resumen corto que toma de varias de
ellas, citando cada afirmación con su [id]. Prueba "caffeine", "exercise" o
"morning light" y velo tejer las notas coincidentes — con fuentes.
Córrelo
bash bootstrap-secrets.sh # reads ../../../../.env, writes secrets/
docker compose up --build # default: PROVIDER=openai
Abre http://localhost:3000. Gemini: PROVIDER=gemini docker compose up --build.
Qué hay aquí
backend/store.py— elVectorStorecompartido.backend/ai_openai.py/backend/ai_gemini.py— las notas de investigación, el retrieval top-k y un prompt de resumir-con-citas.frontend/app/page.tsx— caja de tema + resumen con citas.
Detenlo
docker compose down
Ejecútalo en tu máquina
Descarga el proyecto como ZIP y córrelo con Docker. Levanta un backend FastAPI y un frontend Next.js en localhost:3000.
unzip cited-summary.zip
cd cited-summary
bash bootstrap-secrets.sh # one-time: pulls API keys into ./secrets
docker compose up --build # default provider: openai
# or: PROVIDER=gemini docker compose up --build
Escribe algo, elige un proveedor y ejecuta el mismo código de Código contra la API real. Requiere iniciar sesión.
Los mismos módulos que ejecuta el botón Run. El proyecto completo (frontend, Dockerfile, compose) está en el ZIP, pestaña README.
backend/ai_openai.py
"""Showcase 3 (OpenAI): cited multi-passage summaries.
RAG isn't only question answering. Give it a topic, retrieve the several
passages that touch it, and have the model synthesize a short summary that
pulls from all of them — with [id] citations so every claim is traceable. The
corpus is a set of short research notes; ask for "sleep" or "caffeine" and it
weaves the relevant notes together.
"""
from openai import OpenAI
from store import VectorStore
_client = OpenAI()
_EMBED_MODEL = "text-embedding-3-small"
_CHAT_MODEL = "gpt-5.4-nano"
_KB = [
{"id": "n1", "text": "Adults who sleep 7-9 hours report better mood and sharper attention than short sleepers."},
{"id": "n2", "text": "Caffeine has a half-life of about 5 hours, so an afternoon coffee can disrupt night sleep."},
{"id": "n3", "text": "A consistent sleep and wake time strengthens the circadian rhythm more than total hours alone."},
{"id": "n4", "text": "Bright morning light exposure advances the body clock and helps you fall asleep earlier."},
{"id": "n5", "text": "Moderate exercise improves sleep quality, but vigorous exercise close to bedtime can delay it."},
{"id": "n6", "text": "Alcohol shortens the time to fall asleep but fragments REM sleep later in the night."},
]
_SYSTEM = (
"You are a research assistant. Using ONLY the notes below, write a short "
"(2-4 sentence) summary answering the user's topic, and cite every claim "
"with its [id]. If the notes don't cover the topic, say so.\n\nNotes:\n{context}"
)
_store: VectorStore | None = None
def _embed(texts):
return [item.embedding for item in _client.embeddings.create(model=_EMBED_MODEL, input=texts).data]
def _get_store() -> VectorStore:
global _store
if _store is None:
_store = VectorStore(_embed)
_store.add(_KB)
return _store
def run(topic: str) -> str:
hits = _get_store().search(topic.strip(), k=4)
context = "\n".join(f"[{item['id']}] {item['text']}" for _, item in hits)
response = _client.responses.create(
model=_CHAT_MODEL,
instructions=_SYSTEM.format(context=context),
input=[{"role": "user", "content": f"Summarize what the notes say about: {topic.strip()}"}],
)
return response.output_text
backend/ai_gemini.py
"""Showcase 3 (Gemini): cited multi-passage summaries.
Same research notes and citation rule, synthesized by Gemini.
"""
import os
from google import genai
from google.genai import types
from store import VectorStore
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
_EMBED_MODEL = "gemini-embedding-001"
_CHAT_MODEL = "gemini-3.1-flash-lite"
_KB = [
{"id": "n1", "text": "Adults who sleep 7-9 hours report better mood and sharper attention than short sleepers."},
{"id": "n2", "text": "Caffeine has a half-life of about 5 hours, so an afternoon coffee can disrupt night sleep."},
{"id": "n3", "text": "A consistent sleep and wake time strengthens the circadian rhythm more than total hours alone."},
{"id": "n4", "text": "Bright morning light exposure advances the body clock and helps you fall asleep earlier."},
{"id": "n5", "text": "Moderate exercise improves sleep quality, but vigorous exercise close to bedtime can delay it."},
{"id": "n6", "text": "Alcohol shortens the time to fall asleep but fragments REM sleep later in the night."},
]
_SYSTEM = (
"You are a research assistant. Using ONLY the notes below, write a short "
"(2-4 sentence) summary answering the user's topic, and cite every claim "
"with its [id]. If the notes don't cover the topic, say so.\n\nNotes:\n{context}"
)
_store: VectorStore | None = None
def _embed(texts):
return [e.values for e in _client.models.embed_content(model=_EMBED_MODEL, contents=texts).embeddings]
def _get_store() -> VectorStore:
global _store
if _store is None:
_store = VectorStore(_embed)
_store.add(_KB)
return _store
def run(topic: str) -> str:
hits = _get_store().search(topic.strip(), k=4)
context = "\n".join(f"[{item['id']}] {item['text']}" for _, item in hits)
response = _client.models.generate_content(
model=_CHAT_MODEL,
contents=[types.Content(role="user", parts=[types.Part(text=f"Summarize what the notes say about: {topic.strip()}")])],
config=types.GenerateContentConfig(system_instruction=_SYSTEM.format(context=context)),
)
return response.text or ""
Archivos del proyecto
.gitignoreREADME.es.mdREADME.mdbackend/Dockerfilebackend/ai_gemini.pybackend/ai_openai.pybackend/main.pybackend/requirements.txtbackend/store.pybootstrap-secrets.shdocker-compose.ymlfrontend/Dockerfilefrontend/app/layout.tsxfrontend/app/page.tsxfrontend/next.config.tsfrontend/package.jsonfrontend/tsconfig.json