perplexity-reference-architecture
Apply a reference architecture for search-augmented apps using Perplexity. Includes model routing, citation pipelines, and research orchestration.
Install
mkdir -p .claude/skills/perplexity-reference-architecture && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5344" && unzip -o skill.zip -d .claude/skills/perplexity-reference-architecture && rm skill.zipInstalls to .claude/skills/perplexity-reference-architecture
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Implement Perplexity reference architecture with model routing, citationKey capabilities
- →Route models by search depth
- →Implement citation extraction
- →Orchestrate multi-query research
- →Perform automated fact-checking
- →Cache search results
How it works
The architecture uses a three-tier design: a search service for routing and caching, a citation pipeline for parsing, and an orchestrator for multi-step research.
Inputs & outputs
When to use perplexity-reference-architecture
- →Designing new Perplexity integrations
- →Reviewing project architecture
- →Establishing standards for AI search apps
- →Structuring citation pipelines
About this skill
Perplexity Reference Architecture
Overview
Production architecture for AI-powered search with Perplexity Sonar API. Three tiers: search service (model routing + caching), citation pipeline (extract, validate, store), and research orchestrator (multi-query synthesis).
Architecture
┌─────────────────────────────────────────────┐
│ Application Layer │
│ (Search Widget, Research Agent, Fact Check) │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ Search Service Layer │
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ Model │ │ Query │ │ Response │ │
│ │ Router │ │ Cache │ │ Parser │ │
│ └──────────┘ └──────────┘ └─────────────┘ │
└──────────────────────┬──────────────────────┘
│
┌──────────────────────▼──────────────────────┐
│ api.perplexity.ai/chat/completions │
│ sonar | sonar-pro | sonar-reasoning-pro │
└─────────────────────────────────────────────┘
Prerequisites
- Perplexity API key with Sonar access
- OpenAI-compatible client library (
openaipackage) - Redis for production caching (LRU for development)
Instructions
Step 1: Search Service with Model Routing
// src/perplexity/search-service.ts
import OpenAI from "openai";
import { createHash } from "crypto";
type SearchDepth = "quick" | "standard" | "deep" | "reasoning";
const MODEL_MAP: Record<SearchDepth, { model: string; maxTokens: number; timeout: number }> = {
quick: { model: "sonar", maxTokens: 256, timeout: 10000 },
standard: { model: "sonar", maxTokens: 1024, timeout: 15000 },
deep: { model: "sonar-pro", maxTokens: 4096, timeout: 30000 },
reasoning: { model: "sonar-reasoning-pro", maxTokens: 4096, timeout: 45000 },
};
export class SearchService {
constructor(
private client: OpenAI,
private cache: Map<string, { result: any; expiry: number }> = new Map()
) {}
async search(query: string, depth: SearchDepth = "standard", opts: {
recencyFilter?: "hour" | "day" | "week" | "month";
domainFilter?: string[];
systemPrompt?: string;
} = {}) {
const config = MODEL_MAP[depth];
const cacheKey = this.hashQuery(query, config.model, opts);
// Check cache
const cached = this.cache.get(cacheKey);
if (cached && cached.expiry > Date.now()) {
return { ...cached.result, cached: true };
}
const response = await this.client.chat.completions.create({
model: config.model,
messages: [
...(opts.systemPrompt ? [{ role: "system" as const, content: opts.systemPrompt }] : []),
{ role: "user" as const, content: query },
],
max_tokens: config.maxTokens,
...(opts.recencyFilter && { search_recency_filter: opts.recencyFilter }),
...(opts.domainFilter && { search_domain_filter: opts.domainFilter }),
} as any);
const result = {
answer: response.choices[0].message.content || "",
citations: (response as any).citations || [],
searchResults: (response as any).search_results || [],
model: response.model,
usage: response.usage,
};
// Cache with TTL based on query type
const ttl = opts.recencyFilter === "hour" ? 900_000 : 3600_000;
this.cache.set(cacheKey, { result, expiry: Date.now() + ttl });
return { ...result, cached: false };
}
private hashQuery(query: string, model: string, opts: any): string {
return createHash("sha256")
.update(JSON.stringify({ query: query.toLowerCase().trim(), model, ...opts }))
.digest("hex");
}
}
Step 2: Citation Pipeline
// src/perplexity/citation-pipeline.ts
export interface Citation {
url: string;
domain: string;
index: number;
}
export function extractCitations(answer: string, citationUrls: string[]): Citation[] {
return citationUrls.map((url, i) => ({
url,
domain: new URL(url).hostname,
index: i + 1,
}));
}
export function renderCitationsAsMarkdown(answer: string, citations: Citation[]): string {
let rendered = answer;
for (const c of citations) {
rendered = rendered.replaceAll(`[${c.index}]`, `${c.index}`);
}
return rendered;
}
export function deduplicateCitations(citations: Citation[]): Citation[] {
const seen = new Set<string>();
return citations.filter((c) => {
const normalized = c.url.split("?")[0].replace(/\/$/, "");
if (seen.has(normalized)) return false;
seen.add(normalized);
return true;
});
}
Step 3: Research Orchestrator
// src/perplexity/research-orchestrator.ts
export class ResearchOrchestrator {
constructor(private searchService: SearchService) {}
async research(topic: string): Promise<{
sections: Array<{ question: string; answer: string; citations: string[] }>;
bibliography: string[];
}> {
// Phase 1: Decompose topic (fast model)
const overview = await this.searchService.search(
`Break "${topic}" into 4-5 key research questions. List one per line.`,
"quick"
);
const questions = overview.answer.split("\n").filter((q) => q.trim().length > 10);
// Phase 2: Deep dive each question
const sections = [];
const allCitations = new Set<string>();
for (const question of questions.slice(0, 5)) {
const result = await this.searchService.search(question, "deep", {
systemPrompt: `Research context: ${topic}. Provide detailed, well-cited answer.`,
});
sections.push({
question: question.trim(),
answer: result.answer,
citations: result.citations,
});
result.citations.forEach((url: string) => allCitations.add(url));
// Rate limit protection
await new Promise((r) => setTimeout(r, 2000));
}
return { sections, bibliography: [...allCitations] };
}
}
Step 4: Fact-Check Service
export async function factCheck(
claim: string,
searchService: SearchService
): Promise<{ verdict: string; confidence: string; sources: string[] }> {
const result = await searchService.search(
`Verify this claim with sources. State whether it is accurate, partially accurate, or inaccurate: "${claim}"`,
"deep",
{ systemPrompt: "You are a fact-checker. Be precise and cite sources." }
);
return {
verdict: result.answer,
confidence: result.citations.length > 3 ? "high" : result.citations.length > 1 ? "medium" : "low",
sources: result.citations,
};
}
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| No citations returned | Using sonar for complex query | Upgrade to sonar-pro |
| Stale information | No recency filter | Add search_recency_filter |
| High cost | sonar-pro for simple queries | Route by depth |
| Rate limit on research | Too many sequential queries | Add 2s delay between calls |
Output
- Search service with model routing by query depth
- Citation extraction and rendering pipeline
- Multi-query research orchestrator
- Fact-checking service
Resources
Prerequisites
Limitations
- →Rate limit protection requires manual delays
How it compares
This architecture standardizes the integration of search-augmented apps compared to ad-hoc API implementations.
Compared to similar skills
perplexity-reference-architecture side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| perplexity-reference-architecture (this skill) | 1 | 27d | No flags | Advanced |
| mcp-builder | 136 | 3mo | Review | Advanced |
| nodejs-backend-patterns | 12 | 2mo | No flags | Intermediate |
| juicebox-reference-architecture | 0 | 27d | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
mcp-builder
anthropics
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
nodejs-backend-patterns
wshobson
Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.
juicebox-reference-architecture
jeremylongshore
Implement Juicebox reference architecture. Use when designing system architecture, planning integrations, or implementing enterprise-grade Juicebox solutions. Trigger with phrases like "juicebox architecture", "juicebox design", "juicebox system design", "juicebox enterprise".
vendor-implementation
No-Trade-No-Life
基于Hyperliquid成功实现经验,为新交易所供应商提供Yuan框架集成指南。使用此技能当需要为新的交易所创建供应商实现,包括项目结构设计、API集成、核心服务实现和最佳实践。适用于交易所API集成、金融系统开发、微服务架构设计。
perplexity-architecture-variants
jeremylongshore
Choose and implement Perplexity validated architecture blueprints for different scales. Use when designing new Perplexity integrations, choosing between monolith/service/microservice architectures, or planning migration paths for Perplexity applications. Trigger with phrases like "perplexity architecture", "perplexity blueprint", "how to structure perplexity", "perplexity project layout", "perplexity microservice".
mcp-builder
Harmitx7
Model Context Protocol (MCP) server integration mastery. Building custom MCP servers, standardizing tool exposes, managing standardized communication between large language models and localized datasets, securing boundary contexts, and architecting resource schemas. Use when modifying, extending, or