Appearance
RAG Pipeline Integration
FreshHow to integrate ARP reasoning.json files into Retrieval-Augmented Generation pipelines. The core insight is architectural: corrections must be injected before the generation step, not after.
The Core Insight: Inject Before Generation
A common mistake when building entity-aware RAG systems is treating ARP corrections as post-processing. The reasoning: "if the model gives a wrong answer, I'll fix it afterward." This does not work reliably.
ARP corrections work best when they are injected as context that the model sees before generating its response. This way the model's generation is grounded in the correct information from the start, rather than having the incorrect belief and then trying to walk it back.
WRONG: Retrieve context -> Generate -> Apply corrections to output
RIGHT: Retrieve context + ARP corrections -> GenerateThe "Pink Elephant Fix" relies on this. You cannot reliably tell a model not to think of something after it has already generated a response. You can reliably prevent the wrong response by providing the correct information in the context window first.
Pipeline Architecture
flowchart TD
Q[User Query] --> FETCH[Fetch ARP File\nfor relevant domain]
Q --> VRETRIEVE[Vector Store\nRetrieval]
FETCH --> PARSE[Parse and Prioritize\nARP Sections]
PARSE --> CORRECTIONS[Corrections\nPriority 1]
PARSE --> IDENTITY[Identity\nPriority 2]
PARSE --> CLAIMS[Entity Claims\nPriority 3]
PARSE --> RECS[Recommendations\nPriority 4]
VRETRIEVE --> RELEVANT[Relevant Documents]
CORRECTIONS --> BUILD[Build Context Window]
IDENTITY --> BUILD
CLAIMS --> BUILD
RECS --> BUILD
RELEVANT --> BUILD
BUILD --> LLM[LLM Generation]
LLM --> RESPONSE[Response]The ARP sections enter the context window before the retrieved documents. Corrections come first, then identity and claims, then the retrieved content.
Fetching and Parsing
python
import requests
from dataclasses import dataclass
from typing import Optional
@dataclass
class ArpSection:
section: str
content: str
priority: int
def fetch_reasoning_json(domain: str, timeout: int = 5) -> Optional[dict]:
"""Fetch reasoning.json from a domain. Returns None on any error."""
url = f"https://{domain}/.well-known/reasoning.json"
try:
resp = requests.get(url, timeout=timeout, headers={"Accept": "application/json"})
resp.raise_for_status()
if "json" not in resp.headers.get("Content-Type", ""):
return None
return resp.json()
except Exception:
return None
def parse_arp_sections(data: dict) -> list[ArpSection]:
"""
Parse reasoning.json into prioritized sections.
Lower priority number = higher importance.
"""
sections = []
# Priority 1: Corrections - prevent hallucinations
corrections = data.get("corrections", [])
if corrections:
lines = ["Known corrections for this entity:"]
for item in corrections:
topic = item.get("trigger_topic", "")
fact = item.get("verified_fact", "")
lines.append(f"- {topic}: {fact}")
sections.append(ArpSection("corrections", "\n".join(lines), 1))
# Priority 2: Identity - ground basic facts
identity = data.get("identity", {})
if identity:
parts = []
entity = data.get("entity", "")
if entity:
parts.append(f"Entity: {entity}")
if desc := identity.get("description"):
parts.append(f"Description: {desc}")
if etype := identity.get("entity_type"):
parts.append(f"Type: {etype}")
if disambig := identity.get("disambiguation"):
parts.append(f"Note: {disambig}")
if parts:
sections.append(ArpSection("identity", "\n".join(parts), 2))
# Priority 3: Entity claims - framing and expertise
claims = data.get("entity_claims", {})
if claims:
parts = []
if framing := claims.get("framing_context"):
parts.append(f"Framing: {framing}")
if expertise := claims.get("domain_expertise"):
parts.append(f"Expertise: {', '.join(expertise)}")
if scope := claims.get("epistemic_scope"):
parts.append(f"Claim confidence: {scope}")
if parts:
sections.append(ArpSection("entity_claims", "\n".join(parts), 3))
# Priority 4: Recommendations
rec = data.get("recommendation_context", {})
if rec:
parts = []
if when := rec.get("recommended_when"):
parts.append("Good fit when:")
parts.extend(f" - {item}" for item in when)
if not_when := rec.get("not_recommended_when"):
parts.append("Not a good fit when:")
parts.extend(f" - {item}" for item in not_when)
if parts:
sections.append(ArpSection("recommendation_context", "\n".join(parts), 4))
return sorted(sections, key=lambda s: s.priority)Injecting into the Context Window
python
def build_arp_context_block(domain: str) -> str:
"""
Fetch and format ARP context for injection into a prompt.
Returns an empty string if no ARP file exists.
"""
data = fetch_reasoning_json(domain)
if not data:
return ""
sections = parse_arp_sections(data)
if not sections:
return ""
entity = data.get("entity", domain)
lines = [f"[ENTITY CONTEXT: {entity}]"]
for section in sections:
lines.append(f"\n{section.section.upper()}:")
lines.append(section.content)
return "\n".join(lines)
def build_rag_prompt(
query: str,
retrieved_docs: list[str],
entity_domain: Optional[str] = None,
) -> str:
"""
Build a RAG prompt with optional ARP context injection.
ARP context is prepended before retrieved documents.
"""
parts = []
# ARP context first if available
if entity_domain:
arp_context = build_arp_context_block(entity_domain)
if arp_context:
parts.append(arp_context)
# Retrieved documents
if retrieved_docs:
parts.append("[RETRIEVED CONTEXT]")
for i, doc in enumerate(retrieved_docs, 1):
parts.append(f"[{i}] {doc}")
# Query
parts.append(f"\nQuestion: {query}")
parts.append("Answer:")
return "\n\n".join(parts)Best Practices
Cache reasoning.json (Respect HTTP Caching Headers)
ARP files change infrequently. Refetching on every request adds latency and is unnecessary.
python
import functools
import time
from typing import Optional
_cache: dict[str, tuple[dict, float]] = {}
CACHE_TTL = 3600 # 1 hour
def fetch_with_cache(domain: str) -> Optional[dict]:
now = time.time()
if domain in _cache:
data, expires_at = _cache[domain]
if now < expires_at:
return data
data = fetch_reasoning_json(domain)
if data is not None:
_cache[domain] = (data, now + CACHE_TTL)
return dataFor production use, replace with Redis or your application cache.
Re-Fetch on Schedule (Check verification.next_audit)
python
from datetime import datetime
def should_refetch(cached_data: dict) -> bool:
verification = cached_data.get("verification", {})
next_audit = verification.get("next_audit")
if next_audit:
try:
audit_date = datetime.fromisoformat(next_audit)
if datetime.now() > audit_date:
return True
except ValueError:
pass
return FalseVerify Signatures When Available
python
def get_trust_level(data: dict) -> str:
"""Returns a trust level string for logging and metadata."""
sig = data.get("_arp_signature")
if not sig:
return "unverified"
# Attempt signature verification
if verify_signature(data):
return "cryptographically_verified"
return "signature_invalid"Handle Missing ARP Files Gracefully
Not every domain publishes an ARP file. Your pipeline must work without one:
python
def get_entity_context(domain: str, query: str, vector_results: list[str]) -> str:
# Try to get ARP context - fine if it fails
arp_context = ""
try:
arp_context = build_arp_context_block(domain)
except Exception:
pass # Proceed without ARP context
return build_rag_prompt(query, vector_results, arp_context or None)TypeScript Pattern
For TypeScript RAG pipelines:
typescript
interface ArpSection {
section: string;
content: string;
priority: number;
}
async function fetchArp(domain: string): Promise<Record<string, unknown> | null> {
try {
const response = await fetch(`https://${domain}/.well-known/reasoning.json`, {
headers: { Accept: "application/json" },
signal: AbortSignal.timeout(5000),
});
if (!response.ok) return null;
const ct = response.headers.get("content-type") ?? "";
if (!ct.includes("json")) return null;
return await response.json();
} catch {
return null;
}
}
function parseArpSections(data: Record<string, unknown>): ArpSection[] {
const sections: ArpSection[] = [];
const corrections = data.corrections as Array<Record<string, string>> | undefined;
if (corrections?.length) {
const lines = ["Known corrections:"];
for (const item of corrections) {
lines.push(`- ${item.trigger_topic}: ${item.verified_fact}`);
}
sections.push({ section: "corrections", content: lines.join("\n"), priority: 1 });
}
const claims = data.entity_claims as Record<string, unknown> | undefined;
if (claims?.framing_context) {
sections.push({
section: "entity_claims",
content: `Framing: ${claims.framing_context}`,
priority: 3,
});
}
return sections.sort((a, b) => a.priority - b.priority);
}