From 83f07fa29ebbf68f0453dd4a1fb5ecd2f96f803a Mon Sep 17 00:00:00 2001 From: Cosmo Date: Wed, 22 Jul 2026 21:02:15 +0000 Subject: [PATCH] =?UTF-8?q?perf(voice):=20prompt-caching=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20Claude=20+=20=D0=BE=D1=82=D1=81=D0=B5=D0=B2=20=D0=BC?= =?UTF-8?q?=D1=91=D1=80=D1=82=D0=B2=D1=8B=D1=85=20=D1=81=D1=83=D1=89=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - claudeRequest: system разбит на кэшируемый базовый промпт (cache_control ephemeral) + волатильный живой контекст отдельным блоком. Схемы инструментов попадают в кэшируемый префикс (render order tools->system) и читаются за ~10%. - buildLiveContext: пропускаем entity в состоянии unavailable/unknown — меньше токенов и шума в снимке. - Цель: ~20k input/запрос на Haiku давал ~$0.02+/запрос; кэш стабильной части (инструменты+база) режет стоимость в разы, ответы не меняются. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01UYsVrPyJ5phJeVsAGciS1v --- app/api/voice/chat/route.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/api/voice/chat/route.ts b/app/api/voice/chat/route.ts index cacc585..7959bc3 100644 --- a/app/api/voice/chat/route.ts +++ b/app/api/voice/chat/route.ts @@ -98,7 +98,9 @@ function historyToAnthropicMessages(history: HistoryMessage[]): any[] { return result } -async function claudeRequest(system: string, messages: any[], tools: any[]): Promise { +async function claudeRequest(baseSys: string, liveCtx: string, messages: any[], tools: any[]): Promise { + const system: any[] = [{ type: 'text', text: baseSys, cache_control: { type: 'ephemeral' } }] + if (liveCtx) system.push({ type: 'text', text: liveCtx }) const body: any = { model: 'claude-haiku-4-5-20251001', max_tokens: MAX_TOKENS, system, messages } if (tools.length > 0) body.tools = tools const opts: any = { @@ -137,6 +139,7 @@ async function buildLiveContext(): Promise { const s = d.sensors || {} const lines: string[] = [] for (const [id, st] of Object.entries(states)) { + if (st.state === 'unavailable' || st.state === 'unknown') continue const name = st.attributes?.friendly_name || id let extra = '' if (st.attributes?.preset_mode) extra += `, режим ${st.attributes.preset_mode}` @@ -206,7 +209,8 @@ export async function POST(req: Request) { // Живой контекст (время + снимок устройств) — только для LLM-пути (fast-path // выше уже вышел). Даёт модели понимание, что включено и что вообще есть в // доме, чтобы рассуждать про умный дом и не переспрашивать очевидное. - const fullSys = sysPrompt + (await buildLiveContext()) + const liveCtx = await buildLiveContext() + const fullSys = sysPrompt + liveCtx // ======== GROQ ======== if (provider === 'groq') { @@ -281,7 +285,7 @@ export async function POST(req: Request) { try { for (let round = 0; round < MAX_TOOL_ROUNDS; round++) { const t0 = Date.now() - const resp = await claudeRequest(fullSys, messages, anthropicTools) + const resp = await claudeRequest(sysPrompt, liveCtx, messages, anthropicTools) console.log(`[voice/chat] claude ${agent} r${round+1} ${Date.now()-t0}ms stop=${resp.stop_reason} in=${resp.usage?.input_tokens} out=${resp.usage?.output_tokens}`) const content: any[] = resp.content || [] const stopReason = resp.stop_reason || 'end_turn'