Files
zeekay bda16252a7 auth, auto, base, blog, modules: the words go through @hanzo/i18n
The sign-in callback, initiate and logout pages, authz, auto, the
connectors page, base, the blog index and its three posts, and the Go
module page wrap their copy: server pages with <T>, client pages with
t(), and copy held in data with phrase(). Figures in the posts' prose
are values; product names, code and IAM's own codes stay as written.
Models says its two sentences whole ("{n}+ models", "every model on
the gateway"); the noun prop no caller passed is gone.
2026-10-22 18:13:04 -07:00

226 lines
10 KiB
TypeScript

import type { Metadata } from 'next'
import Link from 'next/link'
import { ArrowLeft, Calendar, Clock } from 'lucide-react'
import { phrase } from '@hanzo/i18n'
import { T } from '@hanzo/i18n/react'
import { Text, XStack, YStack } from '@/components/ui'
import { Band, Card } from '@/components/marketing/band'
import { Action, Chip, Display, Lede, Title } from '@hanzo/ui/marketing'
import { Grid } from '@hanzo/ui/grid'
import { Code } from '@/components/code'
import { DOCS } from '@/components/home/nav-data'
/**
* Kai 1: what a decision model is and how to call it. Every figure is the
* public cookbook's (hanzoai/kai) or production's read of 2026-10-10; the code
* is the cookbook's SDK calls. Results live on /kai, derived from the runs.
*/
const TYPESCRIPT = `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: { tool_call: 'bash.exec', command: 'pytest -v tests/test_agent.py' },
questions: {
next: {
type: 'choice',
instructions: 'What should the agent do with this command?',
criteria: { approve: 'safe to run', reject: 'unsafe', clarify: 'needs the user first' },
},
},
},
})
console.log(decision.answers.next.choice, decision.answers.next.probabilities)`
const PYTHON = `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": {"tool_call": "bash.exec", "command": "pytest -v tests/test_agent.py"},
"questions": {
"next": {
"type": "choice",
"instructions": "What should the agent do with this command?",
"criteria": {"approve": "safe to run", "reject": "unsafe", "clarify": "needs the user first"},
},
},
}))
print(decision.answers["next"].choice, decision.answers["next"].probabilities)`
/** Four facts, each measured: production's read and the cookbook's. The note's figures are its values. */
const FACTS: readonly [string, string, string, Record<string, string>?][] = [
[phrase('Decision Latency'), '<1 ms', phrase('p50 (one question, GPU bf16)')],
[phrase('Output'), '0 tokens', phrase('Kai writes no text; output is not billed')],
[phrase('Price'), '$0.021', phrase('Per million input tokens; Jev costs {price}'), { price: '$0.042' }],
[phrase('Context Window'), '32,768', phrase('Native reach across text, code, and multimodal state')],
]
const TITLE = 'Kai 1: Decisions, Not Completions — Hanzo AI'
const DESCRIPTION = 'Kai answers typed questions with a probability for each answer and writes no text. What it is, how to call it, and its limits.'
export const metadata: Metadata = {
title: TITLE,
description: DESCRIPTION,
alternates: { canonical: 'https://hanzo.ai/blog/kai-decision-models' },
openGraph: { title: TITLE, description: DESCRIPTION, url: 'https://hanzo.ai/blog/kai-decision-models', siteName: 'Hanzo AI', type: 'article' },
twitter: { card: 'summary_large_image', title: TITLE, description: DESCRIPTION },
}
const BODY = { fontSize: '$3', color: 'var(--text-secondary)', '$platform-web': { lineHeight: '1.8em' } } as const
export default function KaiDecisionModelsPage() {
return (
<YStack minH="100vh" bg="var(--background)">
<Band rule={false} pad="$14" measure={860}>
<YStack gap={24}>
<XStack render={<Link href="/blog" />} items="center" gap={6}>
<ArrowLeft size={14} color="var(--muted-foreground)" data-i18n-mirror />
<Text fontSize="$1" color="var(--muted-foreground)">
<T>Back to Blog</T>
</Text>
</XStack>
<XStack items="center" justify="space-between" flexWrap="wrap" gap={12}>
<Chip px={10} py={3} fontSize="$1" fontFamily="$mono" color="var(--foreground)" borderWidth={0} bg="var(--glass-strong)">
KAI 1
</Chip>
<XStack items="center" gap={16}>
<XStack items="center" gap={6}>
<Calendar size={13} color="var(--muted-foreground)" />
<Text fontSize="$1" color="var(--muted-foreground)">
September 28, 2026
</Text>
</XStack>
<XStack items="center" gap={6}>
<Clock size={13} color="var(--muted-foreground)" />
<Text fontSize="$1" color="var(--muted-foreground)">
<T values={{ n: 3 }}>{'{n} min read'}</T>
</Text>
</XStack>
</XStack>
</XStack>
<Display mb={0} text="left">
<T>Kai 1: Decisions, Not Completions</T>
</Display>
<Lede mb={0} text="left">
<T>{"Kai is Hanzo's decision model. Give it a state and a question whose answers you list, and it returns a probability for each answer. It writes no text."}</T>
</Lede>
</YStack>
</Band>
<Band rule={true} pad="$10" measure={860}>
<Grid columns={{ min: 200, max: 4 }} gap={16}>
{FACTS.map(([label, figure, note, values]) => (
<Card key={label} p={20} bg="$panel" borderWidth={0} rounded="var(--radius-xl)">
<YStack gap={6}>
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
<T>{label}</T>
</Text>
<Text fontSize="$6" fontWeight="600" color="var(--foreground)">
{figure}
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
<T values={values}>{note}</T>
</Text>
</YStack>
</Card>
))}
</Grid>
</Band>
<Band rule={true} pad="$12" measure={860}>
<YStack gap={40}>
<YStack gap={16}>
<Title quiet={false}><T>Why not generate the decision</T></Title>
<Text {...BODY}>
<T>{'An agent spends many of its steps on choices: which tool, whether a test passed, whether to stop. Asking a language model to write that choice as JSON means parsing what it wrote, retrying when it does not parse, and guessing how sure it was.'}</T>
</Text>
<Text {...BODY}>
<T
values={{
choice: <Text fontFamily="$mono">choice</Text>,
score: <Text fontFamily="$mono">score</Text>,
noul: <Text fontFamily="$mono">noul</Text>,
}}
>
{'Kai answers in the shape of the question. A {choice} gets a probability for every option, a {score} a distribution over its levels, a {noul} the probability that it holds. Nothing is generated, so nothing is parsed.'}
</T>
</Text>
</YStack>
<YStack gap={16}>
<Title quiet={false}><T>One call</T></Title>
<Text {...BODY}>
<T
values={{
endpoint: <Text fontFamily="$mono">POST https://api.hanzo.ai/v1/decisions</Text>,
sdk: <Text fontFamily="$mono">hanzoai</Text>,
kai: <Text fontFamily="$mono">kai</Text>,
auto: <Text fontFamily="$mono">&quot;auto&quot;</Text>,
fast: <Text fontFamily="$mono">&quot;fast&quot;</Text>,
deep: <Text fontFamily="$mono">&quot;deep&quot;</Text>,
pin15: <Text fontFamily="$mono">kai-1.5</Text>,
pin14: <Text fontFamily="$mono">kai-1.4</Text>,
pin13: <Text fontFamily="$mono">kai-1.3</Text>,
}}
>
{'{endpoint}, from the {sdk} SDKs or over HTTP. The model id {kai} answers as the unified decision model with adaptive reasoning ({auto}, {fast}, {deep}; Kai-1.7 target). Version pins {pin15}, {pin14}, and {pin13} provide deterministic reproducibility across text, code, and long-context evidence.'}
</T>
</Text>
<Card p="$4" bg="$panel">
<Code lang="typescript" code={TYPESCRIPT} />
</Card>
<Card p="$4" bg="$panel">
<Code lang="python" code={PYTHON} />
</Card>
</YStack>
<YStack gap={16}>
<Title quiet={false}><T>Limits</T></Title>
<Text {...BODY}>
<T
values={{
trained: '1,024',
reach: '32,768',
flag: <Text fontFamily="$mono">routing.extrapolated</Text>,
pin: <Text fontFamily="$mono">kai-1.4</Text>,
}}
>
{'kai-1.3, the default, was trained on {trained} tokens of state beside each question. It reads up to {reach} and marks a longer read {flag}; past {trained} its answers are unreliable. Keep the state to what decides the question, or ask for {pin}, which was trained on long states and marks no read up to {reach} extrapolated.'}
</T>
</Text>
</YStack>
<Card p={36} bg="$panel" borderWidth={1} borderColor="var(--border)" rounded="var(--radius-2xl)" mt={24}>
<YStack items="center" gap={16} $platform-web={{ textAlign: 'center' }}>
<Text fontSize="$5" fontWeight="600" color="var(--foreground)">
<T>Results, request shapes and limits</T>
</Text>
<XStack items="center" justify="center" gap={12} flexWrap="wrap" mt={8}>
<Action href="/kai/guide" fill>
<T>How to use Kai</T>
</Action>
<Action href="/kai#benchmarks"><T>Kai against Jev</T></Action>
<Action href={`${DOCS}/docs/decisions`} target="_blank" rel="noopener noreferrer">
<T>API reference</T>
</Action>
</XStack>
</YStack>
</Card>
</YStack>
</Band>
</YStack>
)
}