Perplexity: Diseño de Búsqueda Nativo en IA
Cómo Perplexity construyó confianza en búsqueda IA: diseño citation-first, divulgación progresiva, respuestas en streaming y refinamiento de consultas. Con patrones de implementación TypeScript y CSS.
Perplexity
“La gente quiere información lo más rápido posible y quiere confiar en esa información. Siendo así, en realidad es un problema de diseño bastante simple.”
Filosofía
Perplexity representa un nuevo paradigma: la búsqueda nativa de IA. Mientras que la búsqueda tradicional devuelve una lista de enlaces y los chatbots generan respuestas conversacionales, Perplexity sintetiza información de múltiples fuentes en una única respuesta respaldada por citas. El diseño encarna una transparencia radical: cada afirmación es rastreable hasta su fuente.
La clave del equipo: la búsqueda no es una conversación. Es una misión de obtención de información. La interfaz refleja esto presentando fuentes y respuestas, no historial de chat ni personajes.
Conclusiones Clave
- Las citas son innegociables para la confianza en la IA - Cada afirmación factual enlaza a su fuente; el panel de fuentes permanece visible para que los usuarios puedan verificar sin abandonar la respuesta
- Las interfaces familiares reducen barreras - La caja de búsqueda se parece a Google, no a un prompt de chat; los usuarios pueden escribir palabras clave o preguntas completas sin aprender patrones conversacionales
- Anticipa las preguntas de seguimiento - La mayoría de los usuarios no sabe qué preguntar después; sugerir seguimientos contextualmente relevantes mantiene su interés
- Muestra el proceso, no solo los resultados - Las fases “Buscando → Leyendo → Escribiendo” generan confianza y reducen el tiempo de espera percibido mediante la transparencia
- Trata los resultados débiles como estados de error - Cuando una consulta produciría resultados insuficientes, solicita aclaraciones en lugar de devolver respuestas mediocres
Biblioteca de Patrones
Diseño Centrado en Citas
Perplexity popularizó las citas en línea en las respuestas de IA, cambiando fundamentalmente la forma en que los usuarios confían en el contenido generado. Cada afirmación factual enlaza a su fuente.
interface Citation {
index: number;
url: string;
title: string;
favicon: string;
snippet: string;
domain: string;
}
interface AnswerBlock {
text: string;
citations: number[]; // Indices into citation array
}
function CitedAnswer({ blocks, citations }: {
blocks: AnswerBlock[];
citations: Citation[];
}) {
return (
<article className="answer">
{blocks.map((block, i) => (
<p key={i}>
{block.text}
{block.citations.map(citationIndex => (
<CitationMarker
key={citationIndex}
citation={citations[citationIndex]}
index={citationIndex + 1}
/>
))}
</p>
))}
{/* Source panel always visible */}
<aside className="sources-panel">
<h3>Sources</h3>
{citations.map((citation, i) => (
<SourceCard key={i} citation={citation} index={i + 1} />
))}
</aside>
</article>
);
}
function CitationMarker({ citation, index }: {
citation: Citation;
index: number;
}) {
const [expanded, setExpanded] = useState(false);
return (
<span className="citation-wrapper">
<sup
className="citation-marker"
onMouseEnter={() => setExpanded(true)}
onMouseLeave={() => setExpanded(false)}
>
[{index}]
</sup>
{/* Expandable snippet preview */}
{expanded && (
<div className="citation-preview">
<img src={citation.favicon} alt="" className="citation-favicon" />
<span className="citation-domain">{citation.domain}</span>
<p className="citation-snippet">{citation.snippet}</p>
</div>
)}
</span>
);
}
Idea de diseño: Las citas no son opcionales. Están integradas en cada interacción. El panel de fuentes permanece visible para que los usuarios puedan verificar las afirmaciones sin abandonar la vista de respuesta.
Interfaz de Búsqueda Familiar
El campo de entrada de Perplexity se parece a una caja de búsqueda tradicional, no a un prompt de chat. El diseño familiar reduce la barrera para usuarios que no se sienten cómodos con la IA conversacional.
function SearchInput({ onSubmit }: { onSubmit: (query: string) => void }) {
const [query, setQuery] = useState('');
return (
<div className="search-container">
{/* Intentionally looks like Google/traditional search */}
<div className="search-box">
<SearchIcon className="search-icon" />
<input
type="text"
className="search-input"
placeholder="Ask anything..."
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && onSubmit(query)}
/>
{query && (
<button
className="clear-button"
onClick={() => setQuery('')}
>
<XIcon />
</button>
)}
<button
className="submit-button"
onClick={() => onSubmit(query)}
>
<ArrowRightIcon />
</button>
</div>
{/* Optional context chips */}
<div className="focus-chips">
<Chip icon={<GlobeIcon />}>All</Chip>
<Chip icon={<AcademicIcon />}>Academic</Chip>
<Chip icon={<CodeIcon />}>Code</Chip>
<Chip icon={<VideoIcon />}>Video</Chip>
</div>
</div>
);
}
Observación de diseño: Incluso unas pocas palabras clave son suficientes. Los usuarios no necesitan elaborar prompts complejos. La interfaz acepta tanto consultas simples como preguntas elaboradas.
Divulgación progresiva con sugerencias de seguimiento
En lugar de esperar que los usuarios formulen buenas preguntas de seguimiento, Perplexity las predice y sugiere. Las sugerencias predictivas abordan la realidad de que la mayoría de los usuarios no saben qué preguntar a continuación.
interface FollowUpSuggestion {
question: string;
reasoning: string; // Why this might be relevant
}
function FollowUpSuggestions({
suggestions,
onSelect
}: {
suggestions: FollowUpSuggestion[];
onSelect: (question: string) => void;
}) {
return (
<div className="follow-ups">
<h4>Related</h4>
{/* Show one at a time - progressive disclosure */}
{suggestions.slice(0, 4).map((suggestion, i) => (
<button
key={i}
className="follow-up-chip"
onClick={() => onSelect(suggestion.question)}
>
<span className="follow-up-text">{suggestion.question}</span>
<ArrowRightIcon className="follow-up-arrow" />
</button>
))}
</div>
);
}
// Predict follow-ups based on query context
function generateFollowUps(query: string, answer: string): FollowUpSuggestion[] {
// AI generates contextually relevant next questions
return [
{ question: "How does this compare to alternatives?", reasoning: "comparison" },
{ question: "What are the limitations?", reasoning: "critical analysis" },
{ question: "Can you provide specific examples?", reasoning: "concrete details" },
{ question: "What's the historical context?", reasoning: "background" },
];
}
Observación de diseño: Así como pocas personas hacen preguntas al final de una presentación, la mayoría de los usuarios no son buenos formulando preguntas de seguimiento. Hay que predecir lo que podrían querer saber.
Respuesta en streaming con renderizado progresivo
Perplexity utiliza Server-Sent Events (SSE) para transmitir respuestas en streaming, pero la interfaz revela el contenido progresivamente de una manera que se siente natural.
function StreamingAnswer({ query }: { query: string }) {
const [sources, setSources] = useState<Citation[]>([]);
const [answer, setAnswer] = useState<string>('');
const [phase, setPhase] = useState<'searching' | 'reading' | 'writing'>('searching');
useEffect(() => {
const eventSource = new EventSource(`/api/search?q=${encodeURIComponent(query)}`);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'sources':
setPhase('reading');
setSources(data.sources);
break;
case 'chunk':
setPhase('writing');
setAnswer(prev => prev + data.text);
break;
case 'done':
eventSource.close();
break;
}
};
return () => eventSource.close();
}, [query]);
return (
<div className="streaming-answer">
{/* Phase indicator */}
<PhaseIndicator phase={phase} />
{/* Sources appear first */}
{sources.length > 0 && (
<SourceCards sources={sources} />
)}
{/* Answer streams in */}
<div className="answer-content">
<TypewriterText text={answer} />
{phase === 'writing' && <BlinkingCursor />}
</div>
</div>
);
}
function PhaseIndicator({ phase }: { phase: 'searching' | 'reading' | 'writing' }) {
const phases = {
searching: { icon: <SearchIcon />, text: 'Searching the web...' },
reading: { icon: <BookIcon />, text: 'Reading sources...' },
writing: { icon: <PenIcon />, text: 'Writing answer...' },
};
return (
<div className="phase-indicator">
{phases[phase].icon}
<span>{phases[phase].text}</span>
<LoadingDots />
</div>
);
}
Observación de diseño: Muestra el proceso, no solo el resultado. Los usuarios ven aparecer las fuentes primero (generando confianza), y luego observan cómo se escribe la respuesta. La transparencia reduce la ansiedad durante los tiempos de espera.
Prevención de errores mediante refinamiento
Cuando una consulta es demasiado amplia, Perplexity solicita una aclaración en lugar de devolver resultados débiles. El sistema trata los resultados insuficientes como un estado de error.
interface ClarificationRequest {
type: 'ambiguous' | 'too_broad' | 'missing_context';
suggestions: string[];
originalQuery: string;
}
function QueryRefinement({ request, onRefine }: {
request: ClarificationRequest;
onRefine: (refinedQuery: string) => void;
}) {
const messages = {
ambiguous: "I found multiple meanings. Which one did you mean?",
too_broad: "This topic is quite broad. Can you be more specific?",
missing_context: "I need a bit more context to give you a useful answer.",
};
return (
<div className="refinement-prompt">
<p className="refinement-message">{messages[request.type]}</p>
<div className="refinement-suggestions">
{request.suggestions.map((suggestion, i) => (
<button
key={i}
className="refinement-option"
onClick={() => onRefine(suggestion)}
>
{suggestion}
</button>
))}
</div>
<div className="refinement-custom">
<input
type="text"
placeholder="Or type your own refinement..."
onKeyDown={(e) => {
if (e.key === 'Enter') {
onRefine((e.target as HTMLInputElement).value);
}
}}
/>
</div>
</div>
);
}
Observación de diseño: Otros proveedores de búsqueda devuelven resultados débiles y esperan que los usuarios refinen por su cuenta. Perplexity guía activamente a los usuarios hacia mejores consultas.
Colecciones y Spaces
Para investigación estructurada, Perplexity ofrece Spaces, colecciones dedicadas donde los usuarios pueden organizar consultas, fijar resultados y subir material de referencia.
interface Space {
id: string;
name: string;
threads: Thread[];
documents: Document[];
createdAt: Date;
}
interface Thread {
id: string;
query: string;
answer: Answer;
citations: Citation[];
pinned: boolean;
}
function SpacesSidebar({ spaces, activeSpace, onSelect }: {
spaces: Space[];
activeSpace: string;
onSelect: (id: string) => void;
}) {
return (
<aside className="spaces-sidebar">
<header>
<h2>Spaces</h2>
<button className="new-space">
<PlusIcon /> New Space
</button>
</header>
<nav className="space-list">
{spaces.map(space => (
<button
key={space.id}
className={`space-item ${space.id === activeSpace ? 'active' : ''}`}
onClick={() => onSelect(space.id)}
>
<FolderIcon />
<span className="space-name">{space.name}</span>
<span className="thread-count">{space.threads.length}</span>
</button>
))}
</nav>
</aside>
);
}
function SpaceView({ space }: { space: Space }) {
return (
<div className="space-view">
<header className="space-header">
<h1>{space.name}</h1>
<div className="space-actions">
<button><ShareIcon /> Share</button>
<button><UploadIcon /> Add Files</button>
</div>
</header>
{/* Pinned threads */}
<section className="pinned-threads">
<h3>Pinned</h3>
{space.threads.filter(t => t.pinned).map(thread => (
<ThreadCard key={thread.id} thread={thread} />
))}
</section>
{/* All threads */}
<section className="all-threads">
<h3>All Threads</h3>
{space.threads.map(thread => (
<ThreadCard key={thread.id} thread={thread} />
))}
</section>
{/* Embedded documents */}
{space.documents.length > 0 && (
<section className="space-documents">
<h3>Reference Materials</h3>
{space.documents.map(doc => (
<DocumentCard key={doc.id} document={doc} />
))}
</section>
)}
</div>
);
}
Perspectiva de diseño: Las colecciones replican las pilas de investigación con pestañas que académicos y periodistas mantienen manualmente, ahora integradas directamente en el producto.
Sistema de diseño visual
Tipografía: claridad por encima de la personalidad
Perplexity utiliza un sistema tipográfico neutro y altamente legible. El contenido es el protagonista, no la interfaz.
:root {
/* System font stack for fastest load */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'SF Mono', Monaco, 'Courier New', monospace;
/* Type scale - optimized for reading */
--text-sm: 13px;
--text-base: 15px;
--text-lg: 17px;
--text-xl: 20px;
--text-2xl: 24px;
/* Line heights - generous for readability */
--leading-tight: 1.3;
--leading-normal: 1.6;
--leading-relaxed: 1.8;
}
/* Answer text - optimized for long-form reading */
.answer-content {
font-size: var(--text-base);
line-height: var(--leading-relaxed);
max-width: 680px; /* Optimal reading width */
}
/* Citations - smaller, unobtrusive */
.citation-marker {
font-size: var(--text-sm);
color: var(--accent-primary);
cursor: pointer;
vertical-align: super;
}
/* Source cards - scannable metadata */
.source-card {
font-size: var(--text-sm);
line-height: var(--leading-tight);
}
.source-domain {
font-family: var(--font-mono);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
Sistema de color: confianza a través de la moderación
El color se utiliza con moderación, principalmente para citas, estados y estados de enfoque.
:root {
/* Neutral foundation */
--bg-primary: #ffffff;
--bg-secondary: #f7f7f8;
--bg-tertiary: #ededef;
/* Text hierarchy */
--text-primary: #1a1a1b;
--text-secondary: #57575a;
--text-tertiary: #8e8e93;
/* Citation accent - trustworthy blue */
--accent-citation: #0066cc;
--accent-citation-hover: #0052a3;
/* Focus states */
--focus-ring: 0 0 0 2px var(--accent-citation);
/* Phase indicators */
--phase-searching: #f59e0b;
--phase-reading: #3b82f6;
--phase-writing: #10b981;
}
/* Clean, ad-free interface */
.search-results {
background: var(--bg-primary);
padding: 24px;
border-radius: 12px;
}
/* Citation hover state */
.citation-marker:hover {
background: var(--accent-citation);
color: white;
border-radius: 2px;
}
/* Source card - subtle boundary */
.source-card {
border: 1px solid var(--bg-tertiary);
border-radius: 8px;
padding: 12px;
transition: border-color 0.15s ease;
}
.source-card:hover {
border-color: var(--accent-citation);
}
Patrones de animación
Transiciones de fase
Las transiciones fluidas entre las fases de búsqueda → lectura → escritura mantienen al usuario informado.
/* Phase indicator */
.phase-indicator {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: var(--bg-secondary);
border-radius: 20px;
font-size: var(--text-sm);
color: var(--text-secondary);
}
/* Loading dots animation */
.loading-dots {
display: flex;
gap: 4px;
}
.loading-dots span {
width: 4px;
height: 4px;
background: var(--text-tertiary);
border-radius: 50%;
animation: dot-pulse 1.4s infinite ease-in-out;
}
.loading-dots span:nth-child(2) { animation-delay: 0.2s; }
.loading-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes dot-pulse {
0%, 80%, 100% {
transform: scale(0.6);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
/* Las fuentes aparecen con fade-in a medida que se encuentran */
.source-card {
animation: fade-in-up 0.3s ease-out;
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Escalonamiento de las tarjetas de fuentes */
.source-card:nth-child(1) { animation-delay: 0ms; }
.source-card:nth-child(2) { animation-delay: 100ms; }
.source-card:nth-child(3) { animation-delay: 200ms; }
.source-card:nth-child(4) { animation-delay: 300ms; }
Texto en Streaming
La respuesta aparece carácter por carácter, pero en fragmentos agrupados para facilitar la lectura.
function TypewriterText({ text }: { text: string }) {
const [displayedText, setDisplayedText] = useState('');
useEffect(() => {
// Text already arrived from SSE - just display it
setDisplayedText(text);
}, [text]);
return (
<div className="typewriter">
{displayedText}
</div>
);
}
function BlinkingCursor() {
return (
<span className="cursor" aria-hidden="true">|</span>
);
}
.cursor {
animation: blink 1s step-end infinite;
color: var(--text-secondary);
}
@keyframes blink {
50% { opacity: 0; }
}
Lecciones Aprendidas
1. Las citas son innegociables para la confianza en la IA
En las interfaces de IA, cada afirmación factual debería ser rastreable. Perplexity integra la fidelidad de las citas en cada interacción. No es opcional ni está oculto.
2. Barra de búsqueda > Prompt de chat
Una interfaz de búsqueda familiar reduce las barreras de entrada. Los usuarios pueden escribir palabras clave o preguntas completas; ambas funcionan. No fuerces patrones conversacionales.
3. Anticipa las preguntas de seguimiento
La mayoría de los usuarios no saben qué preguntar a continuación. Sugiere seguimientos contextualmente relevantes en lugar de esperar que los usuarios dirijan la conversación.
4. Muestra el proceso
El streaming proporciona transparencia, no solo velocidad. Mostrar las fases “Buscando → Leyendo → Escribiendo” genera confianza y reduce el tiempo de espera percibido.
5. Trata los resultados débiles como fallos
Cuando una consulta produciría resultados insuficientes, pide una reformulación en lugar de devolver respuestas débiles. Guía a los usuarios hacia mejores consultas.
6. Interfaz limpia = Señal de confianza
La interfaz minimalista y sin anuncios señala que el producto prioriza la calidad de la información sobre la monetización.
Preguntas Frecuentes
¿Cómo funciona el sistema de citas de Perplexity?
Cada afirmación factual en una respuesta de Perplexity incluye citas numeradas en línea que enlazan a las URL de origen. El panel de fuentes permanece visible junto a la respuesta, mostrando el favicon, el dominio y una vista previa del fragmento. Al pasar el cursor sobre un número de cita, se expande para mostrar el contexto específico de la fuente. Esto hace que la verificación sea inmediata en lugar de requerir que los usuarios investiguen por su cuenta.
¿Por qué Perplexity se parece a un motor de búsqueda en lugar de a un chatbot?
El equipo descubrió que la búsqueda no es una conversación, sino una misión de obtención de información. Una interfaz familiar con barra de búsqueda acepta tanto palabras clave simples como preguntas complejas sin requerir que los usuarios aprendan patrones de prompting conversacional. Esto reduce la barrera de entrada para usuarios que no se sienten cómodos con interfaces de chat con IA.
¿Cuál es el enfoque de Perplexity para las respuestas en streaming?
Perplexity utiliza Server-Sent Events (SSE) para revelar contenido progresivamente en tres fases: “Buscando” (encontrando fuentes), “Leyendo” (analizando fuentes) y “Escribiendo” (sintetizando la respuesta). Las fuentes aparecen primero para generar confianza antes de que la respuesta comience a transmitirse. Esta transparencia reduce la ansiedad durante los tiempos de espera.
¿Cómo maneja Perplexity las consultas ambiguas o demasiado amplias?
Cuando una consulta produciría resultados débiles, Perplexity solicita una aclaración en lugar de devolver respuestas mediocres. El sistema identifica términos ambiguos, temas excesivamente amplios o falta de contexto, y luego sugiere refinamientos específicos. Los usuarios pueden hacer clic en las sugerencias o escribir su propia aclaración.
¿Qué son los Spaces de Perplexity y cómo se utilizan?
Los Spaces son colecciones dedicadas para investigación estructurada donde los usuarios pueden organizar consultas relacionadas, fijar resultados importantes y subir documentos de referencia. Reproducen las pilas de investigación con pestañas que académicos y periodistas mantienen manualmente, ahora integradas directamente en el producto para proyectos de investigación en curso.