Files
hanzo.ai/app/kai/guide/page.tsx

339 lines
14 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from 'next'
import { Hero } from '@/components/models/kit'
import { Box, Text, XStack } from '@/components/ui'
import { Action, Line, More } from '@hanzo/ui/marketing'
import { Grid } from '@hanzo/ui/grid'
import { Band, Card, Head, INK } from '@/components/marketing/band'
import { Code } from '@/components/code'
import { SiteHeader, SiteFooter } from '@/components/home/shell'
import { DOCS, KEYS } from '@/components/home/nav-data'
import { KAI } from '@/lib/bench-data'
import { named } from '@/lib/kai'
import { rate, record } from '@/lib/tally'
/**
* /kai/guide: how to call Kai, from the public cookbook (hanzoai/kai) and the
* Decisions reference. The request and the SDK calls are the cookbook's; the
* limits are its measured facts and the production read of 2026-10-10. The
* accuracy row is derived from the research runs (KAI), named by the revision
* they were measured on.
*/
const TITLE = 'How to use Kai, Hanzo’s decision model'
const DESCRIPTION =
'Developer guide for Kai decision models: API reference, structured outputs, low-latency reasoning, and token pricing on Hanzo Cloud.'
export const metadata: Metadata = {
title: TITLE,
description: DESCRIPTION,
alternates: { canonical: 'https://hanzo.ai/kai/guide' },
openGraph: {
title: TITLE,
description: DESCRIPTION,
url: 'https://hanzo.ai/kai/guide',
siteName: 'Hanzo AI',
type: 'article',
},
twitter: { card: 'summary_large_image', title: TITLE, description: DESCRIPTION },
}
const CURL = `curl -sS --fail-with-body https://api.hanzo.ai/v1/decisions \\
-H "Authorization: Bearer $HANZO_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"model": "kai",
"state": "Customer email: Cancel my plan immediately. Your service was down during our product launch.",
"questions": {
"department": {
"type": "choice",
"instructions": "Route ticket to appropriate team",
"criteria": {
"executive_escalations": "high-value customer cancellation threats",
"support": "general technical inquiries",
"billing": "standard invoice updates"
}
},
"urgency": {
"type": "score",
"instructions": "Rate ticket urgency",
"criteria": ["low", "normal", "high", "critical"]
},
"churn_risk": {
"type": "noul",
"instructions": "Is this account at risk of churning?"
}
}
}'`
const PYTHON = `# pip install hanzoai
import os
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import AiApi
from hanzoai.cloud.models.ai_decisions_request import AiDecisionsRequest
ai = AiApi(ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"])))
decision = ai.post_decisions(AiDecisionsRequest.from_dict({
"model": "kai",
"state": {"ticket_id": 4821, "text": "Can I get an extension on my payment due date?"},
"questions": {
"team": {
"type": "choice",
"instructions": "Which team handles this?",
"criteria": {"billing": "invoices and payments", "support": "technical issues"},
},
"is_churn_risk": {"type": "noul", "instructions": "Is the user churning?"},
},
}))
print(decision.answers["team"].choice, decision.answers["team"].probabilities)
print(decision.answers["is_churn_risk"].noul)`
const TYPESCRIPT = `// npm install hanzoai
import { AiApi, Configuration } from 'hanzoai'
const ai = new AiApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }))
const { data: decision } = await ai.postDecisions({
aiDecisionsRequest: {
model: 'kai',
state: { pull_request: 104, diff_lines: 480, files: ['auth.ts', 'token.go'] },
questions: {
risk: {
type: 'score',
instructions: 'Rate security risk of code change',
criteria: ['low: documentation or cosmetic', 'medium: internal logic', 'high: security or auth paths'],
},
},
},
})
console.log(decision.answers.risk.score, decision.answers.risk.probabilities)`
const BEFORE = `# Jev on OpenRouter
curl https://openrouter.ai/api/alpha/decisions \\
-H "Authorization: Bearer $OPENROUTER_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{"model": "typesafe/jev-1.13", "state": "…", "questions": {…}}'`
const AFTER = `# Kai on Hanzo: the same body
curl https://api.hanzo.ai/v1/decisions \\
-H "Authorization: Bearer $HANZO_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{"model": "kai", "state": "…", "questions": {…}}'`
/** The three question types: what a question sends and what its answer holds, as the cookbook states them. */
const TYPES: readonly [string, string, string][] = [
['choice', '`criteria`: option → description, or a list of options.', '`choice`, `probabilities` (one per option, summing to 1), `confidence`.'],
['score', '`criteria`: the levels, lowest first.', '`score` (the expected level index), `legend`, `probabilities` by level index.'],
['noul', '`instructions`: a yes/no question.', '`noul`: P(true). kai-1.3, the default, does not read a noul’s `criteria`.'],
]
/** What a request may hold and what it costs, from the cookbook's measured facts and production's read. */
const LIMITS: readonly [string, string][] = [
['Model', 'The model id `kai` answers from kai-1.3, the default. Ask for `kai-1.4` by id for the unified version: one encoder and one head, trained on long states. Ask for `kai-1.5` by id for the agentic preview: whether a tool fits a request, and whether to call one at all.'],
['Questions', 'Up to 100 a request; 101 is refused with 422.'],
[
'State',
'kai-1.3, the default, was trained on 1,024 tokens of state beside each question. Up to 32,768 is answered with `routing.extrapolated: true` and is unreliable: a fact at the end of a 3,982-token state read 0.507. `kai-1.4` was trained on long states and marks no read up to 32,768 extrapolated. Past 32,768 the answer is 422 `state_too_long`.',
],
['Time', 'One yes/no question takes <1 ms of server time at p50 in production on GPU (0.27 ms cached in process). Every response reports its own `latency_ms`.'],
['Price', '$0.021 per million input tokens; output is free. Jev costs $0.042.'],
]
/** A sentence whose `code` spans are set in the mono face. */
function Prose({ text }: { text: string }) {
return (
<>
{text.split('`').map((part, i) =>
i % 2 ? (
<Text key={i} fontFamily="$mono">
{part}
</Text>
) : (
part
),
)}
</>
)
}
export default function KaiGuidePage() {
const tally = KAI ? record(KAI.index.wins) : null
const revision = KAI ? named(KAI.run) : null
return (
<>
<SiteHeader />
<Box render="main" minH="100vh" bg="$background" pt="$10" $platform-web={{ color: 'var(--foreground)' }}>
<Hero
crumb="Kai Guide"
badge="Hanzo decision model · 0 output tokens"
title="How to Use Kai"
lede={
<>
Send a state and typed questions to <Text fontFamily="$mono">POST /v1/decisions</Text> with{' '}
<Text fontFamily="$mono">model: &quot;kai&quot;</Text>. Kai answers each question with a probability for every answer it
allows, and writes no text.
</>
}
>
<Action href={KEYS} fill>
Get an API key
</Action>
<Action href={`${DOCS}/docs/decisions`}>Read the reference</Action>
</Hero>
<Band pad="$11" measure={960}>
<Head eyebrow="Questions" title="Three question types" lede="Name each question; its answer comes back under the same name." />
<Grid columns={{ min: 260, max: 3 }} gap={16}>
{TYPES.map(([type, send, get]) => (
<Card key={type} p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">{type}</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$2">
Send <Prose text={send} />
</Line>
<Line color="var(--muted-foreground)" size="sm">
Get <Prose text={get} />
</Line>
</Card>
))}
</Grid>
</Band>
<Band pad="$11" measure={960} ground={INK}>
<Head
eyebrow="Call it"
title="One request, three questions"
lede="Every response also carries id, model, usage (output_tokens is 0), routing (the checkpoint that answered: kai-1.3 for the model id kai), state_hash and latency_ms."
/>
<Grid columns={1} gap={24}>
<Card p="$4" bg="$panel">
<Code lang="bash" code={CURL} />
</Card>
<Card p="$4" bg="$panel">
<Code lang="python" code={PYTHON} />
</Card>
<Card p="$4" bg="$panel">
<Code lang="typescript" code={TYPESCRIPT} />
</Card>
</Grid>
</Band>
<Band pad="$11" measure={960}>
<Head eyebrow="Limits" title="What a request may hold" />
<Box>
{LIMITS.map(([name, text]) => (
<XStack key={name} gap="$4" py="$3" borderTopWidth={1} borderColor="var(--border)" items="baseline" flexWrap="wrap">
<Text width={120} $max-sm={{ width: '100%' }} fontSize="$3" lineHeight="$3" fontWeight="600" color="$ink">
{name}
</Text>
<Text flex={1} minW={240} fontSize="$2" lineHeight="$2" color="var(--muted-foreground)">
<Prose text={text} />
</Text>
</XStack>
))}
</Box>
</Band>
<Band pad="$11" measure={960}>
<Head
eyebrow="Recipes & Blueprints"
title="Composable Decision Recipes"
lede="Modular Rust structs and TypeScript workflows that sit inside your pipelines: retail triage, industrial diagnostics, confidence routing, and agent loops."
/>
<Grid columns={{ min: 280, max: 3 }} gap={16}>
<Card p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">recipes::retail</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$3">
Shelf inventory triage, return fraud checks, and corporate pricing safety guardrails.
</Line>
</Card>
<Card p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">recipes::industrial</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$3">
Multi-criteria powertrain trade studies and telemetry equipment readiness gating.
</Line>
</Card>
<Card p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">recipes::routing</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$3">
Confidence margin gates that stand on high-certainty decisions and escalate low-confidence queries to Zen.
</Line>
</Card>
<Card p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">recipes::zen</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$3">
OpenAI-compatible request builders and SSE stream accumulators for reasoning thought tokens.
</Line>
</Card>
<Card p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">recipes::kata</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$3">
Autonomous test-and-repair agent state machine gating code patch generation vs human review.
</Line>
</Card>
<Card p="$5">
<Line weight="700" mb="$2">
<Text fontFamily="$mono">recipes::gen</Text>
</Line>
<Line color="var(--muted-foreground)" size="sm" mb="$3">
Conversational assistant harness managing multi-turn history and Model Context Protocol (MCP) dispatch.
</Line>
</Card>
</Grid>
<XStack mt="$6" flexWrap="wrap" gap="$4">
<Action href={`${DOCS}/docs/guides/cookbook/recipes`}>Read the Recipes Guide</Action>
<Action href={`${DOCS}/docs/guides/cookbook`}>View All Cookbook Pipelines</Action>
<Action href="https://github.com/hanzoai/kai/tree/main/recipes" target="_blank" rel="noopener noreferrer">
Kai recipes on GitHub ↗
</Action>
</XStack>
</Band>
<Band pad="$11" measure={960} ground={INK}>
<Head
eyebrow="Coming from Jev"
title="Change the URL, the key and the model"
lede="Kai takes the request body Jev takes. The same endpoint also serves typesafe/jev-1.13 with a Hanzo key, at Jev’s price."
/>
<Grid columns={{ min: 320, max: 2 }} gap={20}>
<Card p="$5">
<Code lang="bash" code={BEFORE} />
</Card>
<Card p="$5">
<Code lang="bash" code={AFTER} />
</Card>
</Grid>
<Line color="var(--muted-foreground)" size="sm" mt="$6">
What differs: Kai answers at most 100 questions a request, refuses a state past 32,768 tokens with 422, and kai-1.3, the default, ignores a
noul’s criteria. OpenAI’s Decisions body (<Text fontFamily="$mono">input</Text> and a list of questions) is refused.
</Line>
{tally && revision && tally.kai != null && tally.jev != null ? (
<Line color="var(--muted-foreground)" size="sm" mt="$4">
On the frozen harness, {revision} averaged {rate(tally.kai)} accuracy against Jev’s {rate(tally.jev)} across the {tally.total} suites it
leads, both models on the same questions.
</Line>
) : null}
{tally ? (
<XStack mt="$4">
<More href="/kai#benchmarks">The runs →</More>
</XStack>
) : null}
</Band>
</Box>
<SiteFooter />
</>
)
}