Внедрение генеративного ИИ в промышленную эксплуатацию неизбежно сталкивается с фактором неконтролируемого роста расходов (Cloud Cost Explosion). В отличие от классических микросервисов, где стоимость транзакции стремится к нулю, стоимость единичного LLM-запроса с большим контекстом (100k+ токенов) может достигать от $0.05 до $0.30. При трафике в миллионы запросов в месяц финансовая модель продукта становится убыточной без применения методологии FinOps.
AI FinOps базируется на трех инженерных практиках:
Архитектура каскадной обработки запроса снижает совокупную стоимость владения (TCO) на 60–80% без деградации качества финального ответа.
[ User Prompt / Context ]
|
v
[ Phase 1: Context Compressor ] -> Prunes redundant tokens / whitespace / low-entropy spans
|
v (Compressed Context)
[ Phase 2: Router / Triage LLM (e.g., Claude 3.5 Haiku / GPT-4o-mini) ]
|
+---> Complexity Score < 0.65 ---> [ Direct Fast Response ] (Low Cost: $0.0004)
|
`---> Complexity Score >= 0.65
|
v
[ Phase 3: Budget Ledger Check ]
| (Within Tenant Monthly Budget)
v
[ Flagship LLM (Claude 3.5 Sonnet / o3) ] -> [ High-Fidelity Response ]Ниже представлена реализация производственного каскадного роутера с Zod-валидацией сложности, расчетом финансовой стоимости по прейскуранту провайдера и динамическим менеджером бюджета.
import { z } from "zod";
export interface ModelPricing {
promptCostPerMillion: number;
completionCostPerMillion: number;
}
export const PRICING_TABLE: Record<string, ModelPricing> = {
"claude-3-5-haiku-20241022": {
promptCostPerMillion: 0.80, // $0.80 за 1M входных токенов
completionCostPerMillion: 4.00, // $4.00 за 1M выходных токенов
},
"claude-3-5-sonnet-20241022": {
promptCostPerMillion: 3.00, // $3.00 за 1M входных токенов
completionCostPerMillion: 15.00,// $15.00 за 1M выходных токенов
},
};
const TriageDecisionSchema = z.object({
complexityScore: z.number().min(0).max(1),
recommendedTier: z.enum(["FAST_TIER", "FLAGSHIP_TIER"]),
reasoning: z.string(),
directAnswer: z.string().optional(),
});
export type TriageDecision = z.infer<typeof TriageDecisionSchema>;
export interface ExecutionResult {
content: string;
modelUsed: string;
costUsd: number;
tokens: {
prompt: number;
completion: number;
};
}
/**
* Простой алгоритм сжатия контекста: удаление избыточных символов,
* стоп-строк и нормализация форматирования для снижения расхода токенов.
*/
export function compressContext(rawContext: string): string {
return rawContext
.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, "") // Удаление комментариев кода
.replace(/[ \t]+/g, " ") // Схлопывание множественных пробелов
.replace(/\n{3,}/g, "\n\n") // Ограничение переносов строк
.trim();
}
/**
* Калькулятор стоимости запроса на основе фактически израсходованных токенов
*/
export function calculateCallCost(
model: string,
promptTokens: number,
completionTokens: number
): number {
const pricing = PRICING_TABLE[model];
if (!pricing) {
throw new Error(`Модель ${model} отсутствует в таблице тарификации`);
}
const promptCost = (promptTokens / 1_000_000) * pricing.promptCostPerMillion;
const completionCost = (completionTokens / 1_000_000) * pricing.completionCostPerMillion;
return Number((promptCost + completionCost).toFixed(6));
}
export class BudgetManager {
private tenantSpending = new Map<string, number>();
constructor(private readonly hardLimitUsd: number) {}
public checkBudget(tenantId: string, estimatedCostUsd: number): boolean {
const currentSpent = this.tenantSpending.get(tenantId) ?? 0;
return currentSpent + estimatedCostUsd <= this.hardLimitUsd;
}
public trackSpending(tenantId: string, actualCostUsd: number): void {
const current = this.tenantSpending.get(tenantId) ?? 0;
this.tenantSpending.set(tenantId, current + actualCostUsd);
}
public getSpent(tenantId: string): number {
return this.tenantSpending.get(tenantId) ?? 0;
}
}Интеграция каскадного контура в обработчик API Next.js 15:
import { NextRequest, NextResponse } from "next/server";
import {
compressContext,
calculateCallCost,
BudgetManager
} from "@/lib/finops/model-router";
const globalBudgetManager = new BudgetManager(100.0); // Лимит $100 на сессию/арендатора
export async function POST(req: NextRequest) {
try {
const body = await req.json();
const { prompt, context = "", tenantId = "default-org" } = body;
if (!prompt || typeof prompt !== "string") {
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
}
// Этап 1: Компрессия входящего контекста
const optimizedContext = compressContext(context);
const fullPrompt = `${optimizedContext}\n\nTask: ${prompt}`.trim();
// Проверка жесткого бюджета арендатора
if (!globalBudgetManager.checkBudget(tenantId, 0.01)) {
return NextResponse.json(
{ error: "Monthly token expenditure limit reached (FinOps Hard Budget Limit)" },
{ status: 429 }
);
}
// Этап 2: Триаж запроса быстрой моделью Claude 3.5 Haiku
const triageSystemPrompt = `Analyze query complexity. Return JSON with fields:
complexityScore (0.0 to 1.0), recommendedTier ('FAST_TIER' | 'FLAGSHIP_TIER'), reasoning.
If complexity < 0.65 and request is simple, include directAnswer.`;
// Эмуляция вызова легковесной модели (Haiku)
const triagePromptTokens = 420;
const triageCompletionTokens = 85;
const triageCost = calculateCallCost(
"claude-3-5-haiku-20241022",
triagePromptTokens,
triageCompletionTokens
);
globalBudgetManager.trackSpending(tenantId, triageCost);
// Предположим, модель оценила запрос как простой
const isComplex = prompt.length > 500 || prompt.includes("архитектурный рефакторинг");
if (!isComplex) {
return NextResponse.json({
content: "Ответ сформирован быстрым каскадом (Fast Tier)",
tier: "FAST_TIER",
model: "claude-3-5-haiku-20241022",
costUsd: triageCost,
totalTenantSpentUsd: globalBudgetManager.getSpent(tenantId),
});
}
// Этап 3: Эскалация на флагманскую модель Claude 3.5 Sonnet
const flagshipPromptTokens = fullPrompt.length / 3;
const flagshipCompletionTokens = 400;
const flagshipCost = calculateCallCost(
"claude-3-5-sonnet-20241022",
flagshipPromptTokens,
flagshipCompletionTokens
);
globalBudgetManager.trackSpending(tenantId, flagshipCost);
return NextResponse.json({
content: "Глубокий аналитический ответ от флагманской модели (Flagship Tier)",
tier: "FLAGSHIP_TIER",
model: "claude-3-5-sonnet-20241022",
costUsd: triageCost + flagshipCost,
totalTenantSpentUsd: globalBudgetManager.getSpent(tenantId),
});
} catch (error) {
return NextResponse.json(
{ error: error instanceof Error ? error.message : "Internal Error" },
{ status: 500 }
);
}
}Удаление из промпта знаков препинания, скобок или ключевых ограничителей (Delimiters) алгоритмами сжатия. Модель теряет способность следовать структуре вывода (например, ломается генерация валидного JSON или теряются обязательные поля схемы Zod). Инструкции форматирования и схемы валидации должны исключаться из компрессии.
Использование слишком тяжелого триаж-промпта в модели каскада. Если классификация запроса потребляет 2000 токенов контекста в Haiku, а сам целевой ответ краток, накладные расходы на классификатор могут превысить экономию от отказа от флагманской модели. Промпт классификатора должен быть минималистичным (до 150-200 токенов).
Проверка баланса пользователя только в начале HTTP-запроса. Если агент входит в цикл генерации длинного вывода (например, 8000 токенов рассуждений), запрос завершится, баланс уйдет в глубокий минус, а провайдер выставит счет. Необходимо прерывать стрим (AbortController) по достижении лимита токенов в реальном времени.
Оптимизация только входного контекста при игнорировании многословия модели. Стоимость токенов генерации (Completion Tokens) у большинства провайдеров в 4-5 раз выше стоимости входных токенов. Для экономии критично задавать жесткие лимиты max_tokens и требовать лаконичности в системном промпте.
Удаление синтаксического шума, комментариев и дубликатов из RAG-контекста
Анализ сложности задачи малой моделью и расчет прогнозной стоимости
Атомарная проверка лимита расходов арендатора в кэше Redis
Инференс на выбранной модели (Fast Tier либо Flagship Tier)
Списание фактической стоимости и генерация аналитики в дашборд FinOps