SPB Git

spb/ai-atlas

Public
11.2 KB · 147 lines tsx
1import type { Metadata } from 'next';2import Link from 'next/link';3import { TierBadge } from '@/components/ui/badges';4import { DataTable, Td, Th } from '@/components/ui/data-table';5import { Container, PageHeader, Section } from '@/components/ui/section';6import { Unavailable } from '@/components/ui/unavailable';7import { api, safe } from '@/lib/api';8import { fmtAgo, fmtInt, humanize } from '@/lib/format';9import { eventLabel, routes, TIER_LABELS } from '@/lib/site';10import type { Methodology } from '@/lib/types';1112export const metadata: Metadata = { title: 'Methodology — provenance, tiers, confidence, quality score, events', description: 'How AI Atlas records facts: source tiers, confidence levels, temporal claims, conflict handling, the data-quality score, change events and estimates.', alternates: { canonical: '/methodology' } };13export const revalidate = 3600;1415/** The API may return dicts or lists for these vocabularies; normalise to rows. */16function rows(v: unknown, keyName: string): { key: string; label?: string; description?: string; extra?: Record<string, unknown> }[] {17  if (!v) return [];18  if (Array.isArray(v)) return v.map((x) => (typeof x === 'string' ? { key: x } : { key: String((x as Record<string, unknown>)[keyName] ?? (x as Record<string, unknown>).key ?? (x as Record<string, unknown>).name ?? ''), label: (x as Record<string, unknown>).label as string | undefined, description: ((x as Record<string, unknown>).description ?? (x as Record<string, unknown>).text) as string | undefined, extra: x as Record<string, unknown> }));19  if (typeof v === 'object') return Object.entries(v as Record<string, unknown>).map(([k, d]) => (typeof d === 'string' ? { key: k, description: d } : { key: k, ...(d as Record<string, unknown>), extra: d as Record<string, unknown> }));20  return [];21}2223export default async function MethodologyPage() {24  const m: Methodology | null = await safe(api.methodology());25  const tiers = rows(m?.tiers, 'tier');26  const conf = rows(m?.confidence_levels, 'key');27  const events = rows(m?.event_types, 'event_type');28  const extractors = rows(m?.extractors, 'key');29  return (30    <Container>31      <PageHeader eyebrow="Methodology" title="How AI Atlas records facts" lede="The dataset is the product. These are the rules every connector, extractor and page follows — and the vocabularies the API exposes." />32      <div className="prose-atlas max-w-3xl text-[15px] leading-relaxed text-ink-2">33        <p>AI Atlas is built from first-party connectors that read public documents directly: official documentation, pricing pages, model cards, release notes, papers, feeds, sitemaps and repositories. Nothing depends on a third-party data API. Every document is snapshotted and archived; every fact points back to a snapshot.</p>34        <p>Facts are <strong className="text-ink">temporal claims</strong>: a property, a value, a source, a tier, a confidence, an extractor and a validity interval. When a better-or-equal source states a new value, the old claim is superseded (its interval closes) and the new one becomes current. When a worse source disagrees, the value is stored as <em>conflicting</em> and flagged for review — it is never averaged or silently overwritten. Missing means missing: the site shows “Unavailable” rather than a guess.</p>35      </div>3637      <Section id="tiers" eyebrow="Source tiers" title="Primary sources first">38        {m && tiers.length ? (39          <DataTable caption="Source tiers">40            <thead><tr><Th>Tier</Th><Th>Meaning</Th><Th>Description</Th></tr></thead>41            <tbody>42              {tiers.map((t, i) => (43                <tr key={`${t.key}-${i}`}>44                  <Td primary><TierBadge tier={Number(t.key)} /></Td>45                  <Td label="Meaning" className="text-ink">{t.label ?? TIER_LABELS[Number(t.key)] ?? humanize(t.key)}</Td>46                  <Td label="Description" wide className="text-ink-2">{t.description ?? '—'}</Td>47                </tr>48              ))}49            </tbody>50          </DataTable>51        ) : m ? (52          <ul className="space-y-1 text-sm text-ink-2">{[1, 2, 3, 4].map((t) => <li key={t} className="flex items-center gap-2"><TierBadge tier={t} /> {TIER_LABELS[t]}</li>)}</ul>53        ) : (54          <Unavailable what="Tier vocabulary" compact />55        )}56        <p className="mt-3 max-w-3xl text-sm text-ink-2">A tier 1 source is the entity's own publisher (a lab's documentation, a provider's pricing page, an arXiv listing for a paper). Tier 2 are quality secondary sources, tier 3 community sources, tier 4 unverified. A higher tier can supersede a lower one; the reverse produces a flagged conflict.</p>57      </Section>5859      <Section id="confidence" eyebrow="Confidence" title="Confidence levels">60        {m && conf.length ? (61          <dl className="kv max-w-3xl">62            {conf.map((c, i) => (63              <div key={`${c.key}-${i}`}>64                <dt className="mono">{c.key}</dt>65                <dd className="text-ink-2">{c.description ?? c.label ?? '—'}</dd>66              </div>67            ))}68          </dl>69        ) : (70          <Unavailable what="Confidence vocabulary" compact />71        )}72        <p className="mt-3 max-w-3xl text-sm text-ink-2">Default confidence follows the tier (tier 1 → high, tier 2 → medium, tiers 3–4 → low). A claim becomes <em>conflicted</em> when a current value is contradicted by another source. Extractors can raise confidence to <em>verified</em> when a value is confirmed by several independent tier 1–2 sources.</p>73      </Section>7475      <Section id="quality" eyebrow="Data quality" title="The quality score measures our knowledge, not the entity">76        <p className="max-w-3xl text-sm leading-relaxed text-ink-2">77          <span className="mono text-ink">quality.score = 100 × (0.25 completeness + 0.25 primary-source ratio + 0.20 freshness + 0.15 agreement + 0.15 source diversity)</span>. It says how well AI Atlas knows an entity — how many expected attributes are filled, how much comes from tier 1, how recently it was re-observed, how few conflicts remain and how many independent sources agree. It says nothing about whether a model is good.78        </p>79        {m && m.metrics?.length > 0 && (80          <DataTable caption="Metric definitions" className="mt-5">81            <thead><tr><Th>Metric</Th><Th>Definition</Th><Th>Version</Th></tr></thead>82            <tbody>83              {m.metrics.map((x, i) => (84                <tr key={String(x.key ?? x.name ?? i)}>85                  <Td primary className="mono text-xs">{String(x.key ?? x.name ?? '—')}</Td>86                  <Td label="Definition" wide className="text-ink-2">{String(x.description ?? x.formula ?? x.label ?? '—')}{x.unit ? <span className="text-ink-3"> · {String(x.unit)}</span> : null}</Td>87                  <Td label="Version" className="mono text-xs text-ink-3">{x.version !== undefined ? String(x.version) : '—'}</Td>88                </tr>89              ))}90            </tbody>91          </DataTable>92        )}93      </Section>9495      <Section id="events" eyebrow="History" title="Change events">96        <p className="max-w-3xl text-sm leading-relaxed text-ink-2">Material properties — context length, status, license, openness, parameters, release date, deprecation and retirement dates, versions, prices — emit events when they change. Noisy metrics (downloads, likes, stars) are stored as time series and never generate events. Descriptions and other soft text follow their own source without events. Each event has a category and an importance from 0 (minor) to 3 (major), and links to the source document that triggered it.</p>97        {m && events.length ? (98          <DataTable caption="Event types" className="mt-5" compact>99            <thead><tr><Th>Event type</Th><Th>Label</Th><Th>Category</Th><Th num>Recorded</Th><Th>Last seen</Th></tr></thead>100            <tbody>101              {events.map((e, i) => (102                <tr key={`${e.key}-${String(e.extra?.category ?? '')}-${i}`}>103                  <Td primary className="mono text-xs">{e.key}</Td>104                  <Td label="Label" className="text-ink-2">{e.label ?? e.description ?? eventLabel(e.key)}</Td>105                  <Td label="Category" className="text-ink-2">{String(e.extra?.category ?? '—')}</Td>106                  <Td num label="Recorded" className="tnum text-ink-2">{e.extra?.count !== undefined ? fmtInt(e.extra.count) : e.extra?.importance !== undefined ? `importance ${String(e.extra.importance)}` : '—'}</Td>107                  <Td label="Last seen" className="text-ink-2">{typeof e.extra?.last_seen_at === 'string' ? fmtAgo(e.extra.last_seen_at) : '—'}</Td>108                </tr>109              ))}110            </tbody>111          </DataTable>112        ) : (113          <Unavailable what="Event vocabulary" compact className="mt-4" />114        )}115      </Section>116117      <Section id="extractors" eyebrow="Extraction" title="Deterministic before LLM">118        <p className="max-w-3xl text-sm leading-relaxed text-ink-2">Stage 1 runs on every document: DOM selectors, JSON-LD, tables, Markdown, feeds. Only documents flagged as needing it go to the local LLM factory (MacLustr, OpenAI-compatible), through versioned schemas with full token accounting. LLM-extracted values are marked as such in provenance. Improving a parser bumps its version and reprocesses archived snapshots — never a re-crawl.</p>119        {m && extractors.length > 0 && (120          <dl className="kv mt-5 max-w-3xl">121            {extractors.map((x, i) => (122              <div key={`${x.key}-${i}`}>123                <dt className="mono">{x.key}</dt>124                <dd className="text-ink-2">{x.description ?? x.label ?? '—'}</dd>125              </div>126            ))}127          </dl>128        )}129      </Section>130131      <Section id="benchmarks" eyebrow="Benchmarks" title="Results are never compared blindly">132        <p className="max-w-3xl text-sm leading-relaxed text-ink-2">Benchmark results are append-only and carry their configuration (harness, prompting, number of shots, judge). Leaderboards rank current rows under the benchmark's own direction; results with different configurations are shown with their config so the reader can judge comparability. We do not compute composite indices.</p>133      </Section>134135      <Section id="estimates" eyebrow="Estimates" title="Hardware fit is an estimate">136        <p className="max-w-3xl text-sm leading-relaxed text-ink-2">The only derived figures on AI Atlas are hardware-fit estimates: memory need ≈ parameters × bytes per parameter (4-bit 0.5 × 1.15 overhead, 8-bit 1.0, fp16 2.0) plus a KV-cache allowance for the chosen context. They are labelled <span className="border border-dashed border-warning/60 px-1 text-[11px] uppercase text-warning">Estimated</span> everywhere and never mixed with observed facts.</p>137      </Section>138139      <Section eyebrow="Crawling" title="Respectful by design">140        <p className="max-w-3xl text-sm leading-relaxed text-ink-2">141          Connectors honour robots.txt, use per-domain rate limits and conditional requests, identify as <Link href={routes.bot()} className="link mono">AIAtlasBot</Link>, never bypass access controls and never collect private data. The list of sources and connector health is public on <Link href={routes.sources()} className="link">/sources</Link>.142        </p>143      </Section>144    </Container>145  );146}147
Files · c509d7e03d5aed8bc61fb24bc1c8bb3de1f4b9e2
Menu

Theme