Use when integrating LLMs into applications — API client design, streaming, token management, cost optimization, retry/fallback, model selection, prompt versioning, structured output, function calling, embeddings, RAG, fine-tuning, evaluation, safety guardrails, and rate limiting.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Use when integrating LLMs into applications — API client design, streaming, token management, cost optimization, retry/fallback, model selection, prompt versioning, structured output, function calling, embeddings, RAG, fine-tuning, evaluation, safety guardrails, and rate limiting.
user-invocable
true
allowed-tools
["Read","Write","Edit","Grep","Glob","Bash"]
LLM Integration Patterns for Applications
Patterns for integrating LLMs into production applications with reliability, cost control, and safety.
1. API Client Design
@dataclassclassLLMConfig:
provider: ModelProvider # ANTHROPIC, OPENAI
model: str
api_key: str
max_retries: int = 3
timeout_seconds: int = 60
max_tokens: int = 4096
temperature: float = 0.0
rate_limit_rpm: int = 60@dataclassclassLLMResponse:
content: str
model: str
input_tokens: int
output_tokens: int
latency_ms: float
cost_usd: float
cached: bool = False
Key principles: Wrap provider SDKs behind a unified interface. Store config externally. Track tokens/cost/latency on every call.
Rate limiting: Use token bucket per provider. Track RPM and TPM. Queue requests when approaching limits. Return Retry-After headers to callers.
3. Streaming
asyncdefstream_response(prompt: str) -> AsyncIterator[str]:
asyncwith client.messages.stream(model=model, messages=[{"role": "user", "content": prompt}]) as stream:
asyncfor text in stream.text_stream:
yield text
# SSE endpoint pattern (FastAPI)@app.post("/api/chat/stream")asyncdefchat_stream(request: ChatRequest):
asyncdefevent_generator():
asyncfor chunk in stream_response(request.message):
yieldf"data: {json.dumps({'text': chunk})}\n\n"yield"data: [DONE]\n\n"return StreamingResponse(event_generator(), media_type="text/event-stream")
4. Token Management and Cost Optimization
Strategy
Savings
Effort
Prompt caching (Anthropic)
50-90% on cache hits
Low
Semantic caching (Redis + embeddings)
30-60%
Medium
Shorter prompts (compress examples)
20-40%
Low
Smaller model for simple tasks
60-80%
Medium
Batch API (non-real-time)
50%
Low
Model routing: Classify request complexity, route simple queries to smaller/cheaper models, complex to larger. Use token counting (tiktoken for OpenAI, Anthropic API returns counts) for budget enforcement.
Validation strategy: Parse with Pydantic. On validation failure, retry with error message appended. After 2 failures, fall back to unstructured + regex extraction.
6. Function Calling / Tool Use
tools = [{
"name": "lookup_customer",
"description": "Look up customer by ID or name",
"input_schema": {
"type": "object",
"properties": {
"customer_id": {"type": "integer"},
"name": {"type": "string"},
},
},
}]
# Tool execution loop
response = client.messages.create(model=model, messages=messages, tools=tools)
while response.stop_reason == "tool_use":
tool_call = next(b for b in response.content if b.type == "tool_use")
result = execute_tool(tool_call.name, tool_call.input) # your dispatch
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": [{"type": "tool_result", "tool_use_id": tool_call.id, "content": str(result)}]})
response = client.messages.create(model=model, messages=messages, tools=tools)
Safety: Validate tool inputs. Whitelist allowed tools per user role. Log all tool executions. Set max iterations (default 10) to prevent infinite loops.
# Core RAG pipelinedefrag_query(question: str, top_k: int = 5) -> str:
query_embedding = embed(question)
chunks = vector_store.search(query_embedding, top_k=top_k * 3)
reranked = reranker.rank(question, chunks)[:top_k]
context = "\n---\n".join(f"[{c.source}]: {c.text}"for c in reranked)
return llm_call(f"Answer using ONLY the context below. Cite sources.\n\nContext:\n{context}\n\nQuestion: {question}")
Chunking strategies: Fixed-size (512 tokens, 50 overlap) for general docs. Semantic (by heading/section) for structured docs. Sentence-level for FAQ/support.
When to use RAG vs fine-tuning vs long context:
RAG: Dynamic data, need citations, large corpus (>100K tokens)
Fine-tuning: Consistent style/format, domain vocabulary, small fixed knowledge
Long context: Small corpus (<100K tokens), need full document reasoning
8. Embeddings
Model
Dimensions
Use Case
text-embedding-3-small
1536
Cost-effective general purpose
text-embedding-3-large
3072
Higher accuracy, larger index
Cohere embed-v3
1024
Multilingual
Local (e5-large)
1024
Privacy-sensitive, no API calls
Best practices: Normalize embeddings. Use cosine similarity. Store in pgvector, Pinecone, or Qdrant. Batch embed operations. Cache embeddings -- recompute only on content change.
9. Prompt Versioning
# prompts/invoice-extract/v3.yamlid:invoice-extractversion:3model:claude-sonnet-4-20250514temperature:0.0system:"You extract structured data from invoices."template:|
Extract the following from this invoice:
{schema}
Invoice text:
{document}
tests:-input: {document:"Invoice #123..."}
expected_keys: ["vendor", "total", "line_items"]
changelog:"v3: Added line_item extraction, switched to structured output"
Principles: Version-control prompts like code. Test on eval suite before deploying. Use feature flags for A/B testing prompt versions. Roll back instantly on regression.
Decision framework: Start with the smallest model that meets accuracy requirements. Upgrade only when eval scores demand it. Use larger models for complex/high-stakes tasks, smaller for classification/extraction/routing.
12. Production Checklist
Retry with exponential backoff on transient errors