跳到正文
DEV Community· Ken·· 4 小時前AI 評分58

上下文優化不等於串流恢復:整合 context-mode 時避免掩蓋故障

Context Optimization Is Not Stream Recovery: Integrating context-mode Without Hiding Failures

AI 導讀

Ken 以外部整合者身份探討如何整合 context-mode,提出把工具輸出的上下文管理與串流傳輸分開,避免截斷證據或傳輸故障被介面掩蓋。mksglu/context-mode 專案稱在 17 個平台可減少 98% 輸出量,作者表示未重現相關測量。文中示範以 Next.js App Router 配合 AI SDK 5 建立串流路由,並建議拒絕超大輸入、保留中斷回應的未完成狀態,避免自動重試。

正文

Your production agent fails at 3:47 AM. The on-call engineer opens the transcript: thirty-two thousand lines of test output, followed by a cheerful "I've analyzed the failures and here's my recommendation." The model never saw the actual error. It hit the context ceiling three tool calls ago and has been operating on stale summaries since.

This is not a conversation-history problem. A five-turn chat consumes maybe 8,000 tokens. A single pytest run with verbose output can return 50,000. One file search across a monorepo can yield 200 results. The agent keeps working, the streaming UI keeps typing, and nothing alerts you that the decisions being made are now based on truncated evidence.

mksglu/context-mode attacks that specific failure mode by sandboxing tool output, enforcing routing through MCP hooks, and persisting session memory outside the immediate inference context. The project reports 98% output reduction across 17 platforms. I have not reproduced those measurements.

As an external integrator, I evaluated one narrower question: Can a tool-output boundary reduce model input without making streamed responses misleading when the transport fails?

That requires keeping context management and stream delivery architecturally separate.

Put the reduction boundary before inference

The architecture splits into two paths.

The agent execution path routes tools through context-mode's supported mechanisms. Large outputs stay outside the model's immediate context; the model receives bounded, semantically selected information. Session memory maintains continuity across turns.

The presentation path takes the resulting conversation, invokes inference, and streams assistant output into React using the Vercel AI SDK.

Do not insert a generic truncator between those paths. Cutting a pytest log at 8,000 characters preserves setup noise while removing the actual failure line. Cutting serialized messages can corrupt tool-call relationships, leaving the model with a reference to a result that no longer exists in context.

Instead, enforce an application-level contract: the conversation submitted for inference must already contain bounded, relevant tool results. Preserve a reference to complete output wherever the execution environment supports later retrieval. Context-mode's configuration and retrieval semantics are deployment-specific; they are not interchangeable with AI SDK transport settings.

This matters because smaller input does not make delivery reliable. A proxy can still buffer output, a browser can disappear, and a user can cancel mid-sentence.

A bounded streaming route

The following Next.js App Router handler is the inference boundary, not a replacement for context-mode's MCP integration.

Install the compatible AI SDK 5 family: ai@5, @ai-sdk/openai@2, and @ai-sdk/react@2 for the client. Commit the lockfile. Set B_LOST_API_KEY and MODEL_ID in the server environment. The model identifier must be supported by your configured endpoint.

B-Lost's OpenAI-compatible relay at https://b-lost.com/v1 is the gateway in this topology. Whether its complete deployment path delivers unbuffered SSE is something to measure, not assume.

// app/api/chat/route.ts
import {
  convertToModelMessages,
  streamText,
  validateUIMessages,
} from "ai";
import { createOpenAI } from "@ai-sdk/openai";

export const runtime = "nodejs";
export const maxDuration = 60;

const MAX_INPUT_BYTES = 120_000;

export async function POST(request: Request) {
  const apiKey = process.env.B_LOST_API_KEY;
  const modelId = process.env.MODEL_ID;

  if (!apiKey || !modelId) {
    return Response.json(
      { error: "Inference configuration is missing." },
      { status: 503 },
    );
  }

  // Also enforce a body-size limit at the ingress proxy.
  const body = await request.text();

  if (new TextEncoder().encode(body).byteLength > MAX_INPUT_BYTES) {
    return Response.json(
      { error: "Reduce tool output before requesting inference." },
      { status: 413 },
    );
  }

  let messages;
  try {
    const payload = JSON.parse(body);
    messages = await validateUIMessages({
      messages: payload.messages,
    });
  } catch {
    return Response.json(
      { error: "Invalid UI message payload." },
      { status: 400 },
    );
  }

  const provider = createOpenAI({
    apiKey,
    baseURL: "https://b-lost.com/v1",
  });

  try {
    const result = streamText({
      model: provider.chat(modelId),
      messages: await convertToModelMessages(messages),
      abortSignal: AbortSignal.any([
        request.signal,
        AbortSignal.timeout(55_000),
      ]),
      maxRetries: 0,
      maxOutputTokens: 2048,
    });

    return result.toUIMessageStreamResponse({
      headers: {
        "Cache-Control": "no-cache, no-transform",
        "X-Accel-Buffering": "no",
      },
      onError: () => "Generation interrupted. Retry explicitly.",
    });
  } catch {
    return Response.json(
      { error: "Unable to start generation." },
      { status: 502 },
    );
  }
}

The byte limit is an admission guard, not a token budget. It runs after reading the body, so ingress must enforce its own limit. Neither this guard nor message validation constitutes authentication or tenant authorization.

The 55-second deadline leaves headroom below the route's 60-second deployment allowance. Platforms impose different limits. The abort signal requests cancellation; propagation of browser disconnects through hosting infrastructure still needs testing.

Diagnose the boundary that actually failed

Use useChat from @ai-sdk/react with its default transport pointed at /api/chat. This route emits the AI SDK UI-message stream protocol. A raw OpenAI SSE parser is the wrong consumer.

That distinction explains a common integration failure: the network tab shows incoming bytes, but the UI never updates because the client expects a different event format. Do not manually split arbitrary network chunks on newlines; chunk boundaries need not match SSE frames or UTF-8 character boundaries.

For buffering diagnostics, compare upstream first-byte time, route first-byte time, and the browser's first rendered text. Run curl -N against the deployed route with a valid message payload. If curl receives periodic events but React updates in bursts, investigate rendering pressure. With the AI SDK 5 React hook, experimental_throttle can reduce update frequency; rendering expensive Markdown on every delta can otherwise dominate the main thread.

If both curl and the browser receive one final burst, investigate the relay and hosting path. X-Accel-Buffering: no is advisory, not a universal buffering override.

Keep interruption visible

The deliberate trade-off here is rejecting oversized conversations rather than silently degrading them. The agent must return to its context-management layer and select better evidence.

Disabling automatic retries avoids treating a second generation as continuation of the first. It sacrifices some transient-error recovery for predictable UI semantics.

After cancellation or transport failure, keep partial assistant text visibly incomplete. Do not promote it into persistent session memory as a completed conclusion. Context-mode can reduce what enters inference; the application still owns whether an interrupted answer becomes durable agent history.


The hardest operational dilemma: you can reduce what enters the model, or you can retry failed streams automatically, but doing both without losing signal fidelity requires explicit session-state reconciliation that most frameworks do not provide. When your agent's context budget runs out mid-task, do you surface the failure to the user, retry with reduced context and risk nondeterministic behavior, or persist partial work and rely on the next turn to recover? How are you handling context exhaustion and stream interruption in production agent systems?

來源:DEV Community · dev.to