---
title: Build a Research Agent
description: "A 30-minute guided build. Learn the model by writing one. End with a working agent that knows what it does not know."
---

This tutorial teaches the model by building one thing end-to-end: a research agent that investigates a question, accumulates evidence, detects when sources disagree, and stops when it has enough to act.

You will not call an LLM during this tutorial. Every "agent output" is a literal string so the tutorial runs deterministically with no API key beyond the Beliefs key. At the end you'll see how to swap in Claude, GPT, or any other model.

**You'll learn one concept per section.** Each builds on the previous. Copy the code as you go. The final section assembles everything into one runnable file.

<Callout type="info" title="What you need">
- Node 18+
- A Beliefs API key ([request access](/dev/beta) if you don't have one yet)
- ~30 minutes
</Callout>

## What you're building

By the end, you'll have a research agent that:

1. Takes a research question
2. Reads what it currently believes about the question
3. Investigates the highest-priority gap
4. Detects when new evidence contradicts what it already knew
5. Stops when its **clarity score** crosses a threshold
6. Reports what it found, and what it deliberately doesn't know

```
┌────────────────────────────────────────────────────────────┐
│  Turn 1   clarity 0.18  ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░  │
│  Turn 2   clarity 0.34  ██████████░░░░░░░░░░░░░░░░░░░░░░  │
│  Turn 3   clarity 0.51  ███████████████░░░░░░░░░░░░░░░░░  │
│  Turn 4   clarity 0.68  ████████████████████░░░░░░░░░░░░  │
│  Turn 5   clarity 0.74  ██████████████████████░░░░░░░░░░  │  → STOP
└────────────────────────────────────────────────────────────┘
```

The clarity score is what makes this different from a turn-counter or a token budget. The agent stops when it has *learned* enough, not when it has *talked* enough.

---

## 1. Setup

### Install

```bash
mkdir research-agent && cd research-agent
npm init -y
npm pkg set type=module
npm install beliefs tsx typescript
npm install -D @types/node
```

The `npm pkg set type=module` line marks the package as ESM so top-level `await` works in your script. Without it, you'd need to wrap each section in `async function main() { ... }`, a small annoyance, but worth setting up once.

### Configure TypeScript

Create `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "Bundler",
    "esModuleInterop": true,
    "strict": true
  }
}
```

### Set your key

```bash
export BELIEFS_KEY=bel_live_xxx
```

### First call

Create `agent.ts`:

```ts
import Beliefs from 'beliefs'

const beliefs = new Beliefs({
  apiKey: process.env.BELIEFS_KEY!,
  namespace: `research-${Date.now()}`,
  writeScope: 'space',
})

const world = await beliefs.read()
console.log('beliefs:', world.beliefs.length)
console.log('clarity:', world.clarity)
```

Run it:

```bash
npx tsx agent.ts
```

You should see something like:

```
beliefs: 0
clarity: 0.25
```

**What just happened.** You created a fresh belief state in a unique namespace. It has zero beliefs. Clarity defaults to a low baseline because there's nothing to be clear about yet. Every call from here will operate against this same namespace.

<Callout type="info" title="Why a unique namespace">
`namespace: \`research-${Date.now()}\`` makes every tutorial run a fresh slate. In production, pick stable namespaces (one per project, customer, or session).
</Callout>

<Callout type="warning" title="Your numbers will differ">
The engine uses LLM extraction, so exact claims, counts, and clarity values vary across runs. Your output will show the same *patterns* (clarity rising turn-over-turn, contradictions detected when sources disagree, gaps closing as evidence comes in) but different *exact* values. That's expected and correct. Wherever a tutorial output shows a range or `~`, treat it as illustrative.
</Callout>

→ Concept: **[World](/dev/core/world)**. The read-only view of everything your agent currently believes.

---

## 2. Tell the agent what to research

A research agent needs a goal. Goals are first-class in beliefs, not just a string in your prompt.

Add this to `agent.ts`:

```ts
await beliefs.add('Determine the size of the AI developer tools market', {
  type: 'goal',
})

const world = await beliefs.read()
console.log('goals:', world.goals)
```

Output:

```
goals: [ 'Determine the size of the AI developer tools market' ]
```

**What just happened.** `add()` with `type: 'goal'` registers what the agent is *trying to do*, distinct from claims (what the agent thinks is *true*). The clarity score now considers whether the goal has been resolved.

→ Concept: **[Intent](/dev/core/intent)**. Goals, decisions, and constraints. What the agent wants, distinct from what it knows.

---

## 3. Seed what you already know

Often you start an investigation with priors: things you've heard, suspect, or have weak evidence for. Beliefs lets you assert these explicitly with a confidence score.

```ts
await beliefs.add('AI dev tools market is around $4B', {
  confidence: 0.6,
  type: 'assumption',
})

await beliefs.add('GitHub Copilot has the largest market share', {
  confidence: 0.7,
  type: 'assumption',
})

await beliefs.add('Missing breakdown by enterprise vs individual developers', {
  type: 'gap',
})

const state = await beliefs.read()
console.log('beliefs:', state.beliefs.length)
console.log('gaps:   ', state.gaps.length)
console.log('clarity:', state.clarity.toFixed(2))
```

Output (illustrative; exact values vary):

```
beliefs: 2
gaps:    1
clarity: 0.30–0.40
```

**What just happened.** Three calls, three different shapes:

- `type: 'assumption'`: a stated belief without supporting evidence yet. Confidence = 0.6 means "I lean this way, but haven't done the work."
- `type: 'gap'`: something the agent has flagged as unknown. Gaps are first-class. They reduce clarity until filled.

Notice clarity went up, but only slightly. That's because **stated confidence is not the same as evidence**. The system tracks both:

- **Decision resolution:** how confident you are in the answer (0.6 here)
- **Knowledge certainty:** how much *evidence* backs the answer (zero here, you just stated it)

A claim stated at 0.95 with zero evidence is in a different epistemic category than a claim at 0.65 with 40 supporting observations. The clarity score reflects this.

→ Concept: **[Beliefs](/dev/core/beliefs)**. What types exist, how confidence works, why the two-channel model matters.

---

## 4. The core loop

Now the loop that defines a beliefs-aware agent: **read context, act, feed observation**.

For this tutorial, "act" returns a literal string: what your agent might output if you'd run a real LLM. In Section 9 you'll swap it for a real model.

```ts
async function fakeAgent(_systemPrompt: string, _userMessage: string): Promise<string> {
  // Pretend an LLM ran. Return a realistic agent output.
  return `Based on a Gartner 2024 report, the AI developer tools market
is valued at $4.2B. The top three players (GitHub Copilot, Cursor, and
Tabnine) account for approximately 65% of the market. Enterprise adoption
is currently around 40% of total spend, with individual developers making
up the remainder. The market is growing at roughly 25% year over year.`
}

const userMessage = 'Research the AI developer tools market'

// 1. Read what the agent currently believes
const context = await beliefs.before(userMessage)

// 2. Run the agent
const output = await fakeAgent(context.prompt, userMessage)

// 3. Feed the observation
const delta = await beliefs.after(output)

console.log('changes:  ', delta.changes.length)
console.log('clarity:  ', delta.clarity.toFixed(2))
console.log('readiness:', delta.readiness)
```

Output (illustrative):

```
changes:   4–7
clarity:   0.45–0.55
readiness: medium
```

**What just happened.** Three calls did real work:

- `before(message)`: returned a `BeliefContext` with `prompt` (a serialized summary of the current belief state, ready to inject into a system prompt), plus the agent's beliefs, goals, gaps, clarity, and recommended next moves.
- `fakeAgent(...)`: produced output. In production this is your LLM call; the system prompt is `context.prompt`.
- `after(output)`: extracted beliefs from the agent's text, detected if any conflicted with what was already there, and updated the world state. `delta.changes` is the list of what changed; `delta.readiness` is a coarse `'low' | 'medium' | 'high'` label derived from `clarity`.

Clarity jumped because the agent now has evidenced claims (Gartner cited as a source) instead of bare assumptions.

→ Concept: **[The loop](/dev/sdk/patterns)**. Patterns for single-turn, multi-turn, streaming, and tool-aware agent loops.

---

## 5. Watching clarity rise

A research agent should run more than one turn. Let's loop until clarity is high enough.

```ts
async function fakeAgentTurn2(_prompt: string, _focus: string): Promise<string> {
  return `Looking deeper into enterprise adoption: among Fortune 500 companies,
72% have at least piloted an AI coding assistant, but only 31% have rolled it
out company-wide. The biggest blockers cited are security review (mentioned by
58% of CIOs surveyed), licensing complexity (44%), and uncertainty about ROI
(37%). Adoption is highest in technology and financial services, lowest in
healthcare and government.`
}

async function fakeAgentTurn3(_prompt: string, _focus: string): Promise<string> {
  return `On individual developer adoption: of approximately 28 million
professional developers worldwide, 9.2 million have used an AI coding
assistant at least monthly in 2024, about 33% penetration. Among
those, 4.1 million pay personally for a tool (the rest use free tiers
or employer-provided licenses). Average individual spend is ~$15/month
across paid users.`
}

const turns = [fakeAgentTurn2, fakeAgentTurn3]

for (let i = 0; i < turns.length; i++) {
  const ctx = await beliefs.before(userMessage)

  // If clarity is already high, stop early
  if (ctx.clarity > 0.7) {
    console.log(`\nclarity ${ctx.clarity.toFixed(2)}, stopping`)
    break
  }

  // Use the highest-value move as the focus for this turn
  const focus = ctx.moves[0]?.target ?? userMessage
  const output = await turns[i](ctx.prompt, focus)
  const d = await beliefs.after(output)

  console.log(
    `turn ${i + 2}: clarity ${d.clarity.toFixed(2)}, ` +
    `+${d.changes.length} changes, readiness ${d.readiness}`,
  )
}
```

Output (illustrative; typically reaches `'high'` readiness within a few turns):

```
turn 2: clarity 0.55–0.65, +N changes, readiness medium
turn 3: clarity 0.70–0.80, +M changes, readiness high
```

**What just happened.** The loop reads `clarity` and stops when it's high enough. Each turn:

- Uses `ctx.moves[0].target` as the focus. The engine suggests the highest-value gap to investigate next.
- Calls the "agent" with `ctx.prompt`: a serialized summary of current state, so the agent acts with awareness of what's already known.
- Feeds the output back via `after()`. Extraction, conflict detection, and clarity recompute happen automatically.

After a few turns, clarity typically crosses your `'high'` threshold. The agent has enough to act.

→ Concept: **[Clarity](/dev/core/clarity)**. What the score actually measures, the two-channel model, and how to use it for routing decisions.

→ Concept: **[Moves](/dev/core/moves)**. How the engine ranks next-best actions by how much they'd reduce uncertainty.

---

## 6. Contradictions

What happens if a tool returns evidence that disagrees with what the agent already believes?

```ts
const conflictingTool = `Tool result from market_research_db:
{
  "source": "IDC Q4 2024 AI DevTools Tracker",
  "finding": "Global AI developer tools market is $6.8B, not $4.2B as
    earlier estimates suggested. The discrepancy is because earlier
    figures excluded embedded AI features in mainstream IDEs (VS Code
    Copilot, JetBrains AI Assistant). When those are included, the
    market is 60% larger than Gartner's narrower scope.",
  "methodology": "Bottom-up survey of 2,400 enterprises across 18 countries"
}`

const delta = await beliefs.after(conflictingTool, {
  tool: 'market_research_db',
  source: 'IDC Q4 2024 Tracker',
})

const world = await beliefs.read()
console.log('contradictions:', world.contradictions.length)
for (const c of world.contradictions) {
  console.log(' -', c)
}
```

Output (the contradiction summary string is engine-formatted and will vary):

```
contradictions: 1
 - <engine-formatted summary of the conflicting beliefs>
```

**What just happened.** The engine extracted a new belief from the tool result ("market is $6.8B") and recognized it directly conflicts with an existing belief ("market is around $4B"). Both are kept. Nothing is silently overwritten. The contradiction surfaces in `world.contradictions` (a `string[]`) and reduces the clarity score until you resolve it.

The two beliefs aren't equally weighted, though. The new claim has:
- A tool source (`market_research_db`) tagged via `{ tool, source }`
- A concrete methodology cited in the text

The original was `type: 'assumption'` with no evidence. When the system fuses them, the evidenced claim dominates, but the original is preserved in the trace so you can see how the agent's view shifted.

→ Concept: **[World](/dev/core/world)**. How `world.contradictions` and `world.edges` surface conflicts and supersedence.

---

## 7. Resolving, and following moves

Now use the engine's recommended next move to direct what to investigate.

```ts
const ctx = await beliefs.before(userMessage)

console.log('top 3 moves the engine suggests:')
for (const m of ctx.moves.slice(0, 3)) {
  console.log(` - [${m.action}] ${m.target}`)
  console.log(`   reason: ${m.reason}`)
}

// Investigate the top move
const topMove = ctx.moves[0]
if (topMove) {
  const investigation = `Resolving the market-size question: I cross-checked
the IDC figure against Forrester and McKinsey reports. Forrester pegs the
"AI-augmented dev tools" market at $7.1B for 2024, closer to IDC than
Gartner. The discrepancy is methodology: Gartner's $4.2B excludes embedded
AI features in IDEs, while IDC and Forrester include them. The $6.8-7.1B
range is the broader market; $4.2B is the narrow "AI-native" tools market.`

  await beliefs.after(investigation, { source: 'Forrester + McKinsey cross-check' })
}

const final = await beliefs.read()
console.log('\nfinal clarity:', final.clarity.toFixed(2))
console.log('contradictions:', final.contradictions.length)
```

Output (illustrative; move actions, targets, and reasons are engine-generated):

```
top 3 moves the engine suggests:
 - [<action>] <target>
   reason: <engine's reasoning>
 - ...

final clarity: 0.75–0.85
contradictions: 0
```

Common `action` values: `clarify`, `gather_evidence`, `resolve_uncertainty`, `compare_paths`, `validate`. `target` is the specific claim or gap to act on (for example, `"market size by region"` or `"enterprise vs individual split"`); `reason` is the engine's plain-English explanation of why the move is high-value right now.

**What just happened.** `ctx.moves` is a ranked list of recommended next actions. The engine derived these from the current state. It knows which gaps are open, which beliefs are weakly evidenced, and which contradictions need clarifying. You don't have to plan the next step yourself; you can just route on `moves[0]`.

After feeding the cross-check, the engine sees the methodology distinction, supersedes the old "around $4B" assumption, and the contradiction typically resolves. Clarity climbs.

→ Concept: **[Moves](/dev/core/moves)**. Q-value ranking, executor types, and how to use moves for autonomous routing.

---

## 8. Trace: what changed and why

Every transition is recorded. Look at the audit trail.

```ts
const entries = await beliefs.trace()

console.log(`total transitions: ${entries.length}\n`)
console.log('most recent 5:')
for (const e of entries.slice(0, 5)) {
  const conf = e.confidence
    ? ` (${e.confidence.before?.toFixed(2) ?? '?'} → ${e.confidence.after?.toFixed(2) ?? '?'})`
    : ''
  console.log(` - ${e.action}${conf} | ${e.reason ?? '—'}`)
}
```

Output (abbreviated; specific reasons and confidence shifts will vary):

```
total transitions: <N>

most recent 5:
 - updated (0.X → 0.Y) | <engine reason>
 - resolved | <engine reason>
 - created | <engine reason>
 - ...
```

Each `TraceEntry` carries `action` (`'created' | 'updated' | 'removed' | 'resolved'`), optional `beliefId`, optional `confidence` shift `{ before, after }`, optional `agent`, optional `source`, `timestamp`, and optional `reason`.

**What just happened.** Every belief mutation (created, updated, removed, resolved) landed in the ledger with the reason and the confidence shift. You can replay the agent's reasoning at any point. In production this is what you show on a "why did the agent decide X?" debug page.

→ Concept: **[Ledger](/dev/internals/how-it-works)**. What's recorded, replay semantics, and how to query the trail.

---

## 9. The complete agent

Here's everything assembled into one file. Save as `agent.ts` and run.

```ts
import Beliefs from 'beliefs'

// ─── Setup ─────────────────────────────────────────────────────────

const beliefs = new Beliefs({
  apiKey: process.env.BELIEFS_KEY!,
  namespace: `research-${Date.now()}`,
  writeScope: 'space',
})

const userMessage = 'Research the AI developer tools market'

// ─── Stub agent ────────────────────────────────────────────────────
// Each turn returns a realistic agent output. In production, replace
// the body of `runAgent` with a call to your LLM (see Section 10).

const turnOutputs = [
  `Based on a Gartner 2024 report, the AI developer tools market is valued
at $4.2B. The top three players (GitHub Copilot, Cursor, and Tabnine)
account for approximately 65% of the market. Enterprise adoption is
currently around 40% of total spend, with individual developers making up
the remainder. Growing at roughly 25% YoY.`,

  `Looking deeper into enterprise adoption: among Fortune 500 companies,
72% have at least piloted an AI coding assistant, but only 31% have rolled
it out company-wide. Biggest blockers: security review (58%), licensing
complexity (44%), and uncertainty about ROI (37%). Highest in technology
and financial services, lowest in healthcare and government.`,

  `On individual developer adoption: of approximately 28 million professional
developers worldwide, 9.2 million have used an AI coding assistant at least
monthly in 2024, about 33% penetration. Of those, 4.1 million pay personally
for a tool. Average individual spend is ~$15/month across paid users.`,
]

const conflictingTool = `Tool result from market_research_db:
{ "source": "IDC Q4 2024 AI DevTools Tracker",
  "finding": "Global AI developer tools market is $6.8B, not $4.2B. Earlier
    estimates excluded embedded AI features in mainstream IDEs.",
  "methodology": "Bottom-up survey of 2,400 enterprises across 18 countries" }`

const reconciliation = `Cross-checked IDC against Forrester and McKinsey.
Forrester: $7.1B for 2024, closer to IDC. The discrepancy is methodology:
Gartner's $4.2B excludes embedded IDE features; IDC and Forrester include
them. The $6.8-7.1B range is the broader market; $4.2B is the narrow
"AI-native" tools market.`

async function runAgent(_systemPrompt: string, turn: number): Promise<string> {
  return turnOutputs[turn] ?? ''
}

// ─── Goal + priors ─────────────────────────────────────────────────

await beliefs.add(userMessage, { type: 'goal' })
await beliefs.add('AI dev tools market is around $4B', {
  confidence: 0.6,
  type: 'assumption',
})
await beliefs.add('GitHub Copilot has the largest market share', {
  confidence: 0.7,
  type: 'assumption',
})
await beliefs.add('Missing breakdown by enterprise vs individual developers', {
  type: 'gap',
})

// ─── Research loop ─────────────────────────────────────────────────

const TARGET_CLARITY = 0.7
const MAX_TURNS = 5

for (let turn = 0; turn < MAX_TURNS; turn++) {
  const ctx = await beliefs.before(userMessage)
  console.log(
    `turn ${turn + 1}: clarity ${ctx.clarity.toFixed(2)}, ` +
    `${ctx.beliefs.length} beliefs, ${ctx.gaps.length} gaps`,
  )

  if (ctx.clarity >= TARGET_CLARITY) {
    console.log(`  → clarity hit ${TARGET_CLARITY}, stopping`)
    break
  }

  const output = await runAgent(ctx.prompt, turn)
  if (!output) break
  await beliefs.after(output)
}

// ─── Conflicting evidence + reconciliation ─────────────────────────

console.log('\nfeeding conflicting tool result...')
await beliefs.after(conflictingTool, {
  tool: 'market_research_db',
  source: 'IDC Q4 2024 Tracker',
})

const afterConflict = await beliefs.read()
console.log(`  contradictions: ${afterConflict.contradictions.length}`)

console.log('\ncross-checking and reconciling...')
await beliefs.after(reconciliation, { source: 'Forrester + McKinsey' })

// ─── Report ────────────────────────────────────────────────────────

const final = await beliefs.read()

console.log('\n── Final state ──')
console.log(`clarity:        ${final.clarity.toFixed(2)}`)
console.log(`beliefs:        ${final.beliefs.length}`)
console.log(`gaps remaining: ${final.gaps.length}`)
console.log(`contradictions: ${final.contradictions.length}`)

console.log('\n── Top beliefs ──')
const top = [...final.beliefs]
  .sort((a, b) => b.confidence - a.confidence)
  .slice(0, 5)
for (const b of top) {
  console.log(` [${b.confidence.toFixed(2)}] ${b.text}`)
}

console.log('\n── What we still don\'t know ──')
for (const gap of final.gaps) console.log(` - ${gap}`)
```

Run it:

```bash
npx tsx agent.ts
```

Expected output (illustrative; specific numbers and extracted texts vary across runs):

```
turn 1: clarity 0.25–0.35, 2 beliefs, 1 gaps
turn 2: clarity 0.45–0.55, 5–7 beliefs, 1 gaps
turn 3: clarity 0.60–0.70, 8–10 beliefs, 1 gaps
turn 4: clarity 0.70–0.80, 11–13 beliefs, 0 gaps
  → clarity hit 0.7, stopping
  (or: loop exits when stub outputs are exhausted)

feeding conflicting tool result...
  contradictions: 1

cross-checking and reconciling...

── Final state ──
clarity:        0.75–0.85
beliefs:        13–15
gaps remaining: 0
contradictions: 0

── Top beliefs ──
 [0.85–0.95] <engine-extracted claim about broader market>
 [0.80–0.90] <engine-extracted claim about adoption>
 [0.75–0.85] <engine-extracted claim about market share>
 ...

── What we still don't know ──
(empty when the agent has filled its declared gaps)
```

That's a complete agent. It investigated, recognized when its assumptions were wrong, reconciled competing sources, and stopped when it had enough to act, all without you writing any tracking code.

---

## 10. Swap in a real LLM

Replace `runAgent` with a call to your model of choice. The rest of the file stays identical.

### Anthropic

```ts
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()

async function runAgent(systemPrompt: string, turn: number): Promise<string> {
  const focus = turn === 0
    ? userMessage
    : `Investigate further: ${(await beliefs.before(userMessage)).moves[0]?.target ?? userMessage}`

  const msg = await client.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    system: systemPrompt,
    messages: [{ role: 'user', content: focus }],
  })
  return msg.content
    .filter((b): b is { type: 'text'; text: string } => b.type === 'text')
    .map((b) => b.text)
    .join('')
}
```

### Vercel AI SDK

```ts
import { generateText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'

async function runAgent(systemPrompt: string, _turn: number): Promise<string> {
  const { text } = await generateText({
    model: anthropic('claude-sonnet-4-20250514'),
    system: systemPrompt,
    prompt: userMessage,
  })
  return text
}
```

### OpenAI

```ts
import OpenAI from 'openai'
const openai = new OpenAI()

async function runAgent(systemPrompt: string, _turn: number): Promise<string> {
  const completion = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userMessage },
    ],
  })
  return completion.choices[0]?.message?.content ?? ''
}
```

The belief layer is provider-agnostic. Anything that takes a system prompt and returns text plugs in here.

→ Reference: **[Hack Guide](/dev/tutorial/hack-guide)**. Full integration patterns for every major framework.

---

## What you learned

You touched eight concepts in one build:

| Concept | Where it appeared | Reference |
|---|---|---|
| World state | `beliefs.read()` returning the full picture | [World](/dev/core/world) |
| Goals | `add(text, { type: 'goal' })` | [Intent](/dev/core/intent) |
| Beliefs + types | `add` with `type: 'assumption'`, `'gap'` | [Beliefs](/dev/core/beliefs) |
| The loop | `before → act → after` | [Loop Patterns](/dev/sdk/patterns) |
| Clarity | Stopping condition | [Clarity](/dev/core/clarity) |
| Moves | Ranked next actions | [Moves](/dev/core/moves) |
| Contradictions | Auto-detected via `after()` | [World](/dev/core/world) |
| Trace | Audit trail of every transition | [Ledger](/dev/internals/how-it-works) |

## Where to go next

<CardGroup cols={3}>
  <DocsCard title="Multi-agent" description="Multiple agents contributing to one shared belief state. Evidence weighted by source quality, conflicts resolved automatically." href="/dev/sdk/patterns" />
  <DocsCard title="Streaming" description="SSE streams for live belief updates and per-extraction events." href="/dev/sdk/streaming" />
  <DocsCard title="Patterns" description="Common integration patterns: tool-aware loops, clarity-driven routing, partial-extraction." href="/dev/sdk/patterns" />
</CardGroup>

You now have the model. The rest of the docs are reference for the parts you haven't needed yet.
