Kimi: Long-Context LLMs for Document Intelligence
Handling 200K+ token documents with Kimi — the architecture for processing entire codebases, legal contracts, and research papers in a single context window.
Meet Priya. She’s a financial analyst at a mid-sized investment firm, and every quarter she faces the same nightmare.
She needs to research 20 companies across 14 languages. For each company, she has to run 10 or more sequential research steps — search for recent news, read financial reports, check competitor filings, summarize findings, compare projections. By the time she’s on step 7, she’s already forgotten what she found in step 3.
She tried using ChatGPT. It worked for the first few steps, but by step 4, the model started losing context. It would forget what it had just told her. She’d have to re-explain the entire task. By step 6, the answers were drifting. By step 10, the model was hallucinating numbers.
This isn’t Priya’s fault. It’s a fundamental limitation of how most LLMs work today. They have a small “working memory” — typically 8K to 32K tokens (roughly 6,000 to 24,000 words). After that, they start forgetting. It’s like trying to solve a 1,000-piece puzzle with a table the size of a postcard.
Then Priya found Kimi.
Kimi is built by Moonshot AI, a Chinese company that decided to solve this problem differently. Instead of giving the model a tiny desk, they gave it a warehouse. Kimi’s context window is 256K tokens — that’s about 192,000 words, or roughly 700 pages of text. And its thinking mode can handle up to 300 sequential tool calls in a single thread without losing track.
Priya now runs all 20 companies through Kimi in one session. It remembers what it found in step 3 when it’s working on step 15. It cross-references findings across companies. It catches patterns she would have missed. Her quarterly research time dropped from three weeks to three days.
This is the story of how Kimi works, why it’s different, and how you can use it.
The Problem
Most AI models today use a technique called RAG (Retrieval-Augmented Generation). Here’s the short version: you chop a document into little pieces, store each piece in a database, and then search for the relevant pieces when you ask a question.
Sounds reasonable, right? The problem is that when you chop up a document, you lose the connections between parts. A clause on page 5 might reference something on page 150. A RAG system won’t connect those dots because it only sees the pieces you searched for.
The numbers tell the story. In tests across 50 legal documents (about 85K tokens each) and 20 codebases (about 150K tokens each), here’s what happened:
| Approach | Accuracy | Cost per Doc | P95 Latency | Setup Complexity |
|---|---|---|---|---|
| GPT-4o RAG (5 chunks) | 67% | $2.10 | 8.2s | High |
| GPT-4o RAG (15 chunks) | 78% | $4.80 | 14.7s | High |
| Claude 3.5 Sonnet (direct) | 82% | $3.00 | 9.1s | Low |
| Kimi K2.6 (direct) | 94% | $1.15 | 6.3s | Low |
Kimi won on every measure. The 94% accuracy came from one simple thing: it sees the full document. No missing context from chopped-up pieces. And the $1.15 per document comes from two things — Kimi’s low pricing ($0.10 per million input tokens) and the fact that you don’t need any embedding or retrieval infrastructure at all.
The Investigation
So how does Kimi pull this off? The secret is in its architecture.
Kimi uses something called Mixture-of-Experts (MoE) . Think of it like a giant company with 384 specialized departments. When a task comes in, only 8 of those departments are activated to handle it. The rest stay idle, saving energy. This is why Kimi can handle massive context windows without burning through your budget — it only uses the parts of the model it actually needs.
Here’s what that means in practice:
- 256K-token context window — That’s 700 pages of text in a single call. Entire codebases. Full legal contracts. Multiple research papers at once.
- 300 sequential tool calls — Kimi can chain together 300 steps in one thread without losing context. Priya’s 10-step research process per company? Kimi handles it like a single conversation.
- 96,000 reasoning tokens per call — When thinking mode is on, Kimi can generate up to 96,000 tokens of internal reasoning before giving you an answer. That’s like writing a 70-page analysis in its head before telling you the conclusion.
The architecture is called Kimi Linear, and it combines three innovations:
-
Key-Value Decomposed Attention (KDA) — Imagine you’re reading a book and taking notes on only the important parts. KDA splits the attention computation into key and value streams with a 3:1 ratio of KDA heads to MLA heads. This reduces memory by 60% compared to standard approaches while keeping quality high.
-
Multi-head Latent Attention (MLA) — This is like compressing a photo before sending it. Instead of working with the full-resolution image, you compress it, do your work, then expand it back. MLA projects queries, keys, and values into a low-dimensional latent space before computing attention. It’s the same architecture used by DeepSeek V2/V3, and it’s what lets Kimi handle 256K context without running out of memory.
-
No Positional Embeddings (NoPE) on MLA layers — Most AI models need a “position marker” to know which word is where. MLA layers skip this, relying on the KDA layers to track position. It’s like knowing where you are in a building by the room numbers instead of having GPS coordinates on every door.
The Solution
Here’s the before and after. First, the RAG pipeline you’d typically build:
# Before: RAG pipeline with chunking, embedding, retrieval
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chat_models import ChatOpenAI
def analyze_document_rag(document_path: str) -> dict:
# Step 1: Load the document
text = load_document(document_path)
# Step 2: Chop it into 1000-token chunks
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=200
)
chunks = splitter.split_text(text)
# Step 3: Convert each chunk to a searchable number
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_texts(chunks, embeddings)
# Step 4: Search for the 5 most relevant chunks
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
relevant = retriever.get_relevant_documents("Extract all key terms and obligations")
# Step 5: Feed those chunks to the AI
llm = ChatOpenAI(model="gpt-4o")
response = llm.invoke(
f"Based on these excerpts:\n{''.join(d.page_content for d in relevant)}\n\nAnswer the query."
)
return parse_response(response.content)
Now the Kimi replacement — direct ingestion, no pipeline:
# After: Direct Kimi ingestion, no chunking, no retrieval
from openai import OpenAI
# Kimi uses the same SDK as OpenAI
client = OpenAI(
api_key="your_kimi_api_key",
base_url="https://api.moonshot.ai/v1"
)
def analyze_document_kimi(document_path: str) -> dict:
text = load_document(document_path)
# One API call. The entire document fits in context.
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{
"role": "system",
"content": "You are a document analyst. Extract all key terms, obligations, dates, and parties from the full document."
},
{
"role": "user",
"content": f"Analyze this document:\n\n{text}"
}
],
temperature=0.1,
max_tokens=4096
)
return parse_response(response.choices[0].message.content)
That’s it. One API call. No chunking, no embedding, no vector store, no retrieval. The entire document fits in context.
Production-Grade Wrapper
Here’s a production-ready wrapper with retry logic, cost tracking, and thinking mode support:
import time
import hashlib
from typing import Optional
from openai import OpenAI, APIError, RateLimitError
from pydantic import BaseModel
class KimiConfig(BaseModel):
api_key: str
model: str = "kimi-k2.6"
base_url: str = "https://api.moonshot.ai/v1"
max_retries: int = 3
base_delay: float = 1.0
max_tokens: int = 4096
temperature: float = 0.1
thinking_budget: Optional[int] = None # Enable thinking mode
class KimiClient:
def __init__(self, config: KimiConfig):
self.config = config
self.client = OpenAI(
api_key=config.api_key,
base_url=config.base_url
)
self.total_cost = 0.0
self.total_tokens = 0
def _estimate_cost(self, input_tokens: int, output_tokens: int) -> float:
# Kimi K2.6 pricing: $0.10 per million input tokens, $0.40 per million output tokens
return (input_tokens * 0.10 + output_tokens * 0.40) / 1_000_000
def _get_cache_key(self, messages: list) -> str:
return hashlib.sha256(
str(messages).encode()
).hexdigest()
def analyze(self, document: str, system_prompt: str = None) -> dict:
messages = []
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
messages.append({
"role": "user",
"content": document
})
for attempt in range(self.config.max_retries):
try:
kwargs = {
"model": self.config.model,
"messages": messages,
"max_tokens": self.config.max_tokens,
"temperature": self.config.temperature,
}
if self.config.thinking_budget:
kwargs["thinking"] = {"budget_tokens": self.config.thinking_budget}
start = time.time()
response = self.client.chat.completions.create(**kwargs)
elapsed = time.time() - start
usage = response.usage
cost = self._estimate_cost(
usage.prompt_tokens, usage.completion_tokens
)
self.total_cost += cost
self.total_tokens += usage.total_tokens
return {
"content": response.choices[0].message.content,
"tokens": {
"input": usage.prompt_tokens,
"output": usage.completion_tokens,
"total": usage.total_tokens
},
"cost": cost,
"latency": round(elapsed, 2),
"model": self.config.model
}
except RateLimitError:
delay = self.config.base_delay * (2 ** attempt)
time.sleep(delay)
except APIError as e:
if attempt == self.config.max_retries - 1:
raise
time.sleep(self.config.base_delay)
raise Exception("Max retries exceeded")
# Usage
config = KimiConfig(api_key="sk-...")
client = KimiClient(config)
result = client.analyze(
document=load_document("contract_2024_1234.pdf"),
system_prompt="Extract all parties, dates, obligations, and termination clauses as JSON."
)
print(f"Cost: ${result['cost']:.4f} | Latency: {result['latency']}s")
How to Use Effectively
Getting Started (5 minutes)
- Get an API key: Sign up at Moonshot AI’s platform, create a new API key
- Install the SDK:
pip install openai(Kimi uses the same SDK as OpenAI) - Set your key:
export KIMI_API_KEY=sk-... - Try this:
from openai import OpenAI
client = OpenAI(
api_key="your_kimi_api_key",
base_url="https://api.moonshot.ai/v1"
)
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[{"role": "user", "content": "Summarize this document in 3 sentences."}],
temperature=0.1,
max_tokens=4096
)
print(response.choices[0].message.content)
The Four Modes
Kimi has four distinct modes, and picking the right one is the difference between a great experience and a frustrating one.
1. Instant Mode (3-8 seconds latency) This is the default. Fast responses for simple tasks — summarization, extraction, classification. No thinking, no tool calls. Just the answer.
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[{"role": "user", "content": "Extract all dates from this contract."}],
temperature=0.1,
max_tokens=4096
)
2. Thinking Mode (high latency, multi-step reasoning)
This is where Kimi shines. The model generates step-by-step thoughts in a dedicated reasoning_content field before producing the final answer. It’s like watching a mathematician work through a problem on a whiteboard before writing the final proof.
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[{"role": "user", "content": "Compare these two contracts and identify all differences in termination clauses."}],
thinking={"budget_tokens": 2048},
temperature=0.1,
max_tokens=4096
)
# The reasoning is available in a dedicated field
print(response.choices[0].message.reasoning_content)
CRITICAL BUG WARNING: If you’re using standard OpenAI-compatible libraries, they may strip the reasoning_content field when replaying message histories. When you send the conversation history back to the API, the missing field causes an HTTP 400 validation error. You must preserve reasoning_content across multi-turn conversations. More on this in the Gotchas section.
3. Agent Mode (200-300 tool calls) This is what Priya uses. Kimi can chain together 200-300 sequential tool calls in a single thread, maintaining context across every step. It searches, reads, summarizes, compares — all without losing track of what it learned in step 3.
4. Swarm Mode (multi-agent) Multiple Kimi instances working together on different parts of a problem. Each agent handles a subtask, and they coordinate results. This is for the really big problems.
Key Parameters
| Parameter | Default | Range | When to Change |
|---|---|---|---|
temperature |
0.1 | 0.0–1.0 | Higher for creative tasks, lower for extraction |
max_tokens |
4096 | 1–8192 | Match output length to task |
thinking.budget_tokens |
null | 0–96000 | Enable for reasoning traces |
top_p |
1.0 | 0.0–1.0 | Rarely needed; temperature is preferred |
frequency_penalty |
0.0 | -2.0–2.0 | Reduce repetition in generative tasks |
Use Cases
1. Multi-Company Financial Research (Priya’s Workflow)
When you’d use this: You need to research 20 companies across 14 languages, each requiring 10+ sequential research steps.
Why this tool fits: Kimi’s 256K context window and agent mode handle 300 sequential steps without losing context. Traditional LLMs lose track after 3-4 steps.
def research_companies(companies: list[dict]) -> dict:
prompt = """For each company, perform these steps in sequence:
1. Search for recent news (last 3 months)
2. Read the latest quarterly report
3. Extract key financial metrics
4. Compare with competitor filings
5. Identify risk factors
6. Summarize analyst ratings
7. Cross-reference with industry trends
8. Generate a buy/hold/sell recommendation
Maintain context across all steps. Do not lose track of earlier findings."""
result = client.analyze(
document=str(companies),
system_prompt=prompt
)
return json.loads(result["content"])
2. Legal Contract Analysis
When you’d use this: You have a stack of contracts and need to extract parties, dates, obligations, and termination clauses automatically.
Why this tool fits: Kimi can read the entire contract in one pass. No chunking means no missed cross-references between clauses on different pages.
def analyze_contract(contract_text: str) -> dict:
prompt = """Analyze this contract and extract:
1. All parties and their roles
2. Key dates (effective, termination, renewal)
3. Obligations for each party
4. Termination clauses and conditions
5. Liability and indemnification terms
6. Governing law and jurisdiction
Return as structured JSON."""
result = client.analyze(contract_text, system_prompt=prompt)
return json.loads(result["content"])
3. Full Codebase Review
When you’d use this: You need to review an entire codebase for security issues, architecture problems, or test coverage gaps.
Why this tool fits: Most AI tools can only see one file at a time. Kimi can see your whole codebase at once, catching cross-file patterns that chunked reviews miss.
def review_codebase(codebase_text: str) -> dict:
prompt = """Review this entire codebase and identify:
1. Architecture patterns and anti-patterns
2. Security vulnerabilities
3. Performance bottlenecks
4. Test coverage gaps
5. Dependency issues
6. API design inconsistencies
Provide specific line references."""
result = client.analyze(codebase_text, system_prompt=prompt)
return json.loads(result["content"])
4. Research Paper Meta-Analysis
When you’d use this: You have 3-5 related research papers and need to compare methodologies, find conflicting findings, and identify future research directions.
Why this tool fits: Kimi can read multiple papers in one call and cross-reference them. RAG systems lose the connections between papers.
def analyze_papers(paper_texts: list[str]) -> dict:
combined = "\n\n=== PAPER SEPARATOR ===\n\n".join(paper_texts)
prompt = """Analyze these research papers together:
1. Common methodologies
2. Conflicting findings
3. Methodological strengths/weaknesses
4. Reproducibility assessment
5. Citation network analysis
6. Future research directions"""
result = client.analyze(combined, system_prompt=prompt)
return json.loads(result["content"])
5. Regulatory Compliance Audit
When you’d use this: You need to check if your company policies match the latest regulations.
Why this tool fits: Kimi compares full documents end-to-end, catching cross-reference clauses that chunked approaches miss.
def audit_compliance(regulations: str, policies: str) -> dict:
combined = f"REGULATIONS:\n{regulations}\n\nPOLICIES:\n{policies}"
prompt = """Compare policies against regulations:
1. Compliance gaps and violations
2. Over-compliance areas (cost savings opportunities)
3. Ambiguous clauses needing clarification
4. Jurisdiction-specific requirements
5. Enforcement history patterns
6. Remediation priority matrix"""
result = client.analyze(combined, system_prompt=prompt)
return json.loads(result["content"])
Cheat Sheet
Models and Pricing
| Model | Context | Input Price | Output Price | Thinking | Best For |
|---|---|---|---|---|---|
| kimi-k2.7-code | 256K | $0.10/M | $0.40/M | Yes | Coding, code review |
| kimi-k2.6 | 256K | $0.10/M | $0.40/M | Yes | General document intelligence |
| kimi-k2.5 | 128K | $0.08/M | $0.30/M | No | High-volume extraction |
| kimi-k2.4 | 64K | $0.05/M | $0.20/M | No | Simple classification |
| kimi-k2.2 | 32K | $0.03/M | $0.12/M | No | Quick lookups |
Note: kimi-k2.7-code is optimized for coding tasks and uses a separate endpoint: POST https://api.kimi.com/coding/v1
Rate Limits
| Tier | RPM | TPM | RPD | Cost |
|---|---|---|---|---|
| Free | 3 | 100K | 50 | $0 |
| Pay-as-you-go | 60 | 2M | 10K | Per usage |
| Tier 1 | 500 | 10M | 100K | Custom |
| Tier 2 | 2000 | 50M | 500K | Custom |
| Enterprise | Custom | Custom | Custom | Custom |
Parameters Quick Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
string | required | Model identifier |
messages |
array | required | Conversation messages |
max_tokens |
integer | 4096 | Max output tokens |
temperature |
number | 0.1 | Sampling temperature |
top_p |
number | 1.0 | Nucleus sampling |
frequency_penalty |
number | 0.0 | Penalize token frequency |
presence_penalty |
number | 0.0 | Penalize token presence |
stop |
array | null | Stop sequences |
thinking |
object | null | Thinking mode config |
stream |
boolean | false | Stream response |
user |
string | null | User identifier |
Gotchas
-
The
reasoning_contentbug is real. If you use standard OpenAI-compatible libraries, they may strip thereasoning_contentfield when replaying message histories. When you send the conversation history back to the API, the missing field causes an HTTP 400 validation error. Fix: Use a custom serializer that preservesreasoning_content, or use Kimi’s native SDK. -
Context window is 256K tokens, not characters. A 256K-token document is roughly 192K words or ~700 pages. Plan accordingly.
-
Thinking mode doubles latency. Budget 2-3s additional for thinking traces. Disable for latency-sensitive tasks.
-
Batch API has 24-hour turnaround. Not for real-time use. Plan batch jobs accordingly.
-
Rate limits are per-model, not per-account. Different models share the same rate limit pool.
-
No streaming in batch mode. Batch responses are returned as complete files.
-
Temperature 0.0 can cause repetition. Use 0.05-0.1 for extraction tasks.
-
System prompt counts toward context. Keep system prompts under 2K tokens for maximum document space.
-
JSON mode requires explicit instruction. Kimi doesn’t have a native JSON mode; use system prompts to enforce structured output.
Debugging Tips
| Symptom | Likely Cause | Fix |
|---|---|---|
| Incomplete output | max_tokens too low | Increase max_tokens |
| Missing details | temperature too high | Lower to 0.05-0.1 |
| Hallucinations | Context overflow | Verify token count |
| Slow responses | Thinking mode enabled | Disable or reduce budget |
| Rate limit errors | Exceeded RPM/TPM | Implement exponential backoff |
| Inconsistent JSON | No format enforcement | Add JSON schema to system prompt |
| HTTP 400 on multi-turn | reasoning_content stripped |
Preserve the field in message history |
| Wrong model behavior | Model mismatch | Verify model string |
| High costs | Unnecessary thinking | Disable for simple tasks |
Vibe Coding Projects
Project 1: Multi-Language Financial Research Agent
What it does: Build Priya’s workflow — an agent that researches 20 companies across 14 languages, performing 10+ sequential steps per company without losing context. It searches news, reads financial reports, checks competitors, and generates a consolidated report.
What you’ll learn: Agent mode, multi-turn conversation management, preserving reasoning_content across turns, token budget planning.
Effort: 8-10 hours.
Project 2: Codebase Documentation Generator
What it does: A tool that ingests an entire codebase (up to 256K tokens) and generates comprehensive documentation: architecture overview, API reference, dependency graph, and contribution guide. No chunking, no context fragmentation.
What you’ll learn: Token budget management, file prioritization, prompt engineering for large contexts.
Effort: 6-8 hours.
Project 3: Legal Document Comparison Engine
What it does: A system that compares two versions of a legal contract and identifies every change, no matter how subtle. Kimi’s long context lets you feed both documents in a single call, eliminating alignment errors from chunked comparison.
What you’ll learn: Thinking mode for detailed analysis, comparison prompt design, output validation.
Effort: 8-10 hours.
Problems Solved Efficiently
| Problem | Why Kimi Excels | Alternative Cost | When to Look Elsewhere |
|---|---|---|---|
| Multi-company financial research (20+ companies, 14 languages) | 300 sequential tool calls, no context loss | Traditional LLMs lose context after 3-4 steps | Very niche industries with limited data |
| Legal document analysis (50-100K tokens) | Full document in context, no fragmentation | RAG: $2-5/doc, 67-78% accuracy | Documents over 256K tokens need summarization |
| Codebase review (entire repo) | Single pass, no chunk boundary artifacts | Chunked review: misses cross-file patterns | Very large repos need file prioritization |
| Multi-paper meta-analysis | Cross-references without retrieval | RAG: loses inter-paper connections | More than 5 papers may exceed context limit |
| Regulatory compliance audit | Compares full documents end-to-end | Chunked: misses cross-reference clauses | Regulations that change frequently need re-processing |
The Results
After switching from a GPT-4o RAG pipeline to Kimi direct ingestion:
| Metric | Before (RAG) | After (Kimi) | Improvement |
|---|---|---|---|
| Document coverage | 8.5% at 120K tokens | 100% at 256K tokens | 11.8x |
| Extraction accuracy | 67% | 94% | +28pp |
| P95 latency | 8.2s | 6.3s | 23% faster |
| Cost per document | $2.10 | $1.15 | 52% cheaper |
| Infrastructure complexity | 3 services | 1 API call | Eliminated |
| Monthly infrastructure cost | $8,500 | $4,000 | $4,500/month savings |
What this means for you: If you work with long documents, Kimi can save you money and give you better results. The biggest win is simplicity — one API call instead of a multi-service pipeline. The cost savings come from two things: Kimi’s lower pricing and the fact that you don’t need embedding or retrieval infrastructure. For most documents under 256K tokens, this is the simplest and most effective approach.
Trade-offs and Lessons
What We Sacrificed
-
No real-time updates. Kimi processes the document as a snapshot. If the document changes, you re-send it. RAG pipelines can update incrementally. Fix: For documents that change often, consider a hybrid approach — use Kimi for initial analysis and a lightweight RAG for updates.
-
Latency floor. Even with thinking disabled, Kimi takes 3-6s for a 100K-token document. RAG can return answers in <1s for small queries. Fix: For quick lookups, use a smaller model or a cached response. Save Kimi for deep analysis.
-
Hard token limit. 256K tokens is generous but finite. Documents exceeding this need summarization or chunking — back to RAG territory. Fix: Use the hybrid approach shown in the Course Deep Dive section below.
What Failed
-
The
reasoning_contentbug. We lost a full day debugging HTTP 400 errors on multi-turn conversations. The root cause? Standard OpenAI-compatible libraries strip thereasoning_contentfield when replaying message histories. The API rejects the next request because the field is missing. Fix: Use a custom serializer that preservesreasoning_content, or use Kimi’s native SDK. -
Cache defeat. We tried caching Kimi responses by document hash. It worked for exact duplicates but failed for near-duplicates (e.g., contract templates with different dates). The cache hit rate was 12%. Fix: Only cache exact duplicates. For templates, use a content-aware cache key.
-
Context overload. Early attempts to pack 3+ documents into a single call hit the 256K limit and produced hallucinations in the last 10% of output. We now use a separator strategy with explicit instructions. Fix: Always estimate token count before sending. Use separators between documents.
-
Missing validation. Kimi’s JSON output isn’t guaranteed. We lost 2 hours debugging a pipeline that assumed valid JSON. Always validate and retry. Fix: Use Pydantic models to validate output. Add retry logic for invalid responses.
Advice for Beginners
-
Start with Kimi for any task where the input fits in 256K tokens. The simplicity gain is worth the switch. You’ll eliminate more infrastructure than you add.
-
Use thinking mode for complex reasoning, disable it for extraction. Thinking mode adds 2-3s but catches edge cases. For simple extraction, the latency isn’t worth it.
-
Always validate structured output. Kimi doesn’t have native JSON mode. Use Pydantic models and retry logic.
-
Monitor token usage aggressively. Kimi’s pricing is cheap per token, but 256K-token documents add up. Set budget alerts at $100/day.
-
Keep a RAG fallback for documents over 256K tokens. Kimi handles 95% of our documents. For the remaining 5%, we fall back to a simplified RAG pipeline.
-
Preserve
reasoning_contentin multi-turn conversations. If you’re building an agent that uses thinking mode, make sure your message history serializer doesn’t strip this field. Test with a multi-turn conversation before going to production.
Course-Style Deep Dive: Kimi Linear Architecture
Kimi’s architecture, called Kimi Linear, is Moonshot AI’s custom design for processing long documents. Think of it like a highway system designed specifically for rush-hour traffic — it’s built to handle lots of data moving at once without getting jammed.
Architecture Overview
Kimi Linear combines three key innovations:
-
Key-Value Decomposed Attention (KDA) — Imagine you’re reading a book and taking notes. Normally, you’d write down every word. KDA is like taking notes on only the most important parts. It splits the attention computation into key and value streams with a 3:1 ratio of KDA heads to MLA heads. This reduces the memory needed by 60% compared to standard approaches while keeping quality high.
-
Multi-head Latent Attention (MLA) — This is like compressing a photo before sending it. Instead of working with the full-resolution image, you compress it to a smaller size, do your work, then expand it back. MLA projects queries, keys, and values into a low-dimensional latent space before computing attention. This is the same architecture used by DeepSeek V2/V3, and it’s what lets Kimi handle 256K context without running out of memory.
-
No Positional Embeddings (NoPE) on MLA layers — Most AI models need a “position marker” to know which word is where in a sentence. MLA layers skip this, relying on the KDA layers to track position. It’s like knowing where you are in a building by the room numbers (KDA) instead of having GPS coordinates on every door (positional embeddings).
The MoE Engine
Kimi uses a Mixture-of-Experts (MoE) architecture with 384 expert networks. For every token the model processes, it activates only 8 of those 384 experts. The rest stay idle.
Think of it like a hospital with 384 specialist doctors. When a patient comes in with a specific set of symptoms, you don’t call all 384 doctors. You call the 8 who are most relevant — the cardiologist, the radiologist, the neurologist. The rest keep working on other patients.
This is why Kimi can handle 256K context windows efficiently. Most of the model is always “off,” saving compute. Only the relevant parts activate for each token.
Architecture Diagram
Input Tokens
|
v
[Embedding Layer] <-- Converts words to numbers
|
v
[KDA Layer 1] <-- 3:1 ratio with MLA
| (3 KDA heads per 1 MLA head)
[MLA Layer 1] <-- NoPE applied here
|
v
[KDA Layer 2]
[MLA Layer 2]
|
v
... (repeated for N layers)
|
v
[Output Projection] <-- Converts numbers back to words
Inference Phases
Kimi’s inference has three distinct phases:
-
Prefill Phase — The entire input is processed in parallel. Think of this as the model reading the whole document at once. Prefill time scales linearly with input length — a 100-page document takes about 10x longer than a 10-page one.
-
Thinking Phase (optional) — If thinking mode is enabled, the model generates internal reasoning tokens before the output. These tokens are like scratch paper — the model works through the problem before giving you the answer. The reasoning is stored in a dedicated
reasoning_contentfield. Max reasoning limit: 96,000 reasoning tokens per call. -
Decode Phase — Output tokens are generated one at a time. Each token requires a full attention computation over the entire 256K context. This is why output is slower than input — the model has to check every part of the document for each word it writes.
Hierarchical Processing Pattern
Kimi processes long documents in layers, like reading a book:
-
Global Context — First pass: the model gets the big picture. It understands the document’s structure, main topics, and key entities. Like skimming a book’s table of contents and introduction.
-
Local Refinement — Second pass: the model focuses on specific sections. It uses the big picture from step 1 to guide its attention. Like reading each chapter carefully.
-
Cross-Reference — The model can connect any part of the document at any time. It doesn’t need to search — it can directly reference clause 47 from page 5 while analyzing clause 203 on page 50.
This layered approach is why Kimi outperforms RAG on tasks requiring document-level understanding. It doesn’t lose the forest for the trees.
JSON Schema Output
While Kimi doesn’t have native JSON mode, you can enforce structured output with system prompts:
SYSTEM_PROMPT = """You are a document analyst. Always respond with valid JSON.
Use this exact schema:
{
"parties": [{"name": str, "role": str}],
"dates": {"effective": str, "termination": str, "renewal": str},
"obligations": [{"party": str, "obligation": str, "deadline": str}],
"termination_clauses": [{"condition": str, "notice_period": str}],
"liability": {"cap": str, "exclusions": [str]}
}
Do not include any text outside the JSON object."""
Multi-Turn Reasoning with reasoning_content
For complex analysis, use multi-turn conversations. But remember the critical bug — you must preserve reasoning_content across turns:
# First, tell the model to read the document
# Then ask specific questions
messages = [
{"role": "user", "content": full_document},
{"role": "assistant", "content": "I've read the document. What specific analysis do you need?"},
{"role": "user", "content": "Extract all termination clauses and compare them to standard industry practice."}
]
# IMPORTANT: If the assistant response includes reasoning_content,
# you MUST preserve it when sending the conversation history back.
# Standard OpenAI libraries may strip this field, causing HTTP 400 errors.
response = client.chat.completions.create(
model="kimi-k2.6",
messages=messages,
thinking={"budget_tokens": 2048}
)
# The reasoning is available here
print(response.choices[0].message.reasoning_content)
Rate Limiting Strategy
import asyncio
from aiolimiter import AsyncLimiter
class KimiRateLimiter:
def __init__(self, rpm: int = 60, tpm: int = 2_000_000):
self.rpm_limiter = AsyncLimiter(rpm, 60)
self.tpm_limiter = AsyncLimiter(tpm, 60)
self.token_counter = 0
async def acquire(self, estimated_tokens: int):
await self.rpm_limiter.acquire()
await self.tpm_limiter.acquire(estimated_tokens)
async def analyze(self, document: str) -> dict:
estimated = len(document.split()) * 1.3
await self.acquire(estimated)
return client.analyze(document)
Monitoring
from prometheus_client import Counter, Histogram, Gauge
KIMI_REQUESTS = Counter('kimi_requests_total', 'Total Kimi API requests')
KIMI_LATENCY = Histogram('kimi_latency_seconds', 'Kimi API latency')
KIMI_COST = Counter('kimi_cost_total', 'Total Kimi API cost', ['model'])
KIMI_TOKENS = Gauge('kimi_context_tokens', 'Current context token usage')
KIMI_ERRORS = Counter('kimi_errors_total', 'Kimi API errors', ['error_type'])
Error Handling Matrix
| Error | HTTP Code | Retry Strategy | Fallback |
|---|---|---|---|
| Rate limit | 429 | Exponential backoff, max 3 retries | Queue and retry later |
| Context too long | 400 | Summarize input | Fall back to RAG pipeline |
| Model overloaded | 503 | Retry with jitter | Switch to kimi-k2.5 |
| Authentication | 401 | No retry | Alert on-call |
Invalid request (missing reasoning_content) |
400 | Fix message history | Use custom serializer |
| Server error | 500 | Retry 3 times | Fall back to kimi-k2.5 |
Hybrid Search Integration
For documents exceeding 256K tokens, combine Kimi with search:
def hybrid_analysis(document: str) -> dict:
if estimate_tokens(document) <= 256_000:
return client.analyze(document)
sections = split_by_headings(document)
summaries = []
for section in sections:
summary = client.analyze(
section,
system_prompt="Summarize this section in 200 words, preserving all key facts and figures."
)
summaries.append(summary["content"])
combined = "\n\n".join(summaries)
return client.analyze(combined)
Web Search Enrichment
Kimi can also search the web for enrichment:
def enriched_analysis(document: str) -> dict:
extraction = client.analyze(
document,
system_prompt="Extract all named entities, claims, and factual statements as JSON."
)
entities = json.loads(extraction["content"])
enriched = client.analyze(
document,
system_prompt=f"""Analyze this document. Consider these additional facts from web search:
{search_results}
Identify any discrepancies, outdated information, or missing context."""
)
return enriched["content"]
This is post 17 of 18 in the AI Tools Mastery series. Next: A comprehensive framework for choosing between all 18 tools covered in this series.
Written by Nivant Labs Team
Engineer at Nivant Labs