Repository navigation
Add DeepSeek cost-runtime policy for Runtime v2/AiSdkFlow #19
Description
Activity
这个 issue 的分析很扎实,尤其是 prefix stability、usage 归一化、计费拆分这几块,问题定位得很准。
不过看了一遍代码之后,我觉得这些改进可以直接在现有的 Runtime v2 路径上做,不需要单独起一套
AgentFlowResolver/CostAwareAiSdkFlow。现在
RuntimeRunner→AiSdkFlow→AiSdkBackend→ModelAdapter就是唯一的执行通道,issue 里提到的几件事其实都能在这条路径上落地:前缀稳定,把
buildSessionEnvironmentPromptFragment里的日期/cwd/git branch 从 system prompt 前缀移到每轮消息尾部。Anthropic 的 prompt caching 也是同样的机制,不只 DeepSeek 受益。Tool schema 规范化,
AiSdkBackend.send()组装 tools 的地方加个排序和 key 规范化,一个函数的事。Usage 归一化,
normalizeAiSdkUsage()已经在ModelAdapter里了,DeepSeek 的prompt_cache_hit_tokens/prompt_cache_miss_tokens映射加进去就行。计费这块,
computeCost()现在把inputTokens全量按 fresh input 算,又把cachedInputTokens按 cache read 再算一遍,如果inputTokens包含 cached 部分就是重复计费了。这个直接修就好。Provider 差异(reasoning token、finish reason、retry 语义)也是
ModelAdapter/ModelFactory现有的职责范围。如果走两条 flow,tool loop、permission gating、abort signal、usage 采集、telemetry 这些逻辑要么复制一份,要么两条路径共享实现但各自维护入口,后面改起来容易漏。
我觉得可以把 scope 收一下,在现有路径上逐步做这些改进,所有 provider 都能受益,代码也更好维护~
我同意这个收敛方向,尤其是“不为成本优化另起一条 flow”这个判断。
PR #20 实际已经按这个方向落了第一阶段:直接在现有
RuntimeRunner -> AiSdkFlow -> AiSdkBackend -> ModelAdapter路径里修 usage 归一化、cache-miss 计费、telemetry/settings/UI 透传,没有新增AgentFlowResolver或CostAwareAiSdkFlow。我会把 issue 后续 scope 调整成:
AgentFlow仍保留为长期架构 seam,但不是当前 cost work 的前置条件。- 下一阶段优先继续在现有路径上做 provider-visible request shape 稳定化:环境 prompt 的易变字段、tool schema canonicalization、PrefixShape diagnostics。
- 只有当 DeepSeek/某类 provider 真的需要不同 stepping engine、不同 tool loop 或不同 retry/abort 语义时,再考虑新的 flow/resolver;否则不要复制 tool loop、permission gating、abort、usage/telemetry 这套逻辑。
具体到评论里的点:
- usage 归一化和 double-charge 计费问题是对的,PR Add DeepSeek cache-aware usage accounting #20 已经修了。
- provider 差异放在
ModelAdapter/ModelFactory是对的。 - tool schema 排序/规范化应先作为
AiSdkBackendrequest assembly 的小模块落地,而不是新 flow。 - volatile env fragment 从 system prefix 移到 turn-tail 这个方向也对,但要小心 workspace instructions / memory / skills 这类仍然属于长期系统约束的片段,不能一刀切搬走。
2026-06-16 update: route after PR #20/#21/#22 and Reasonix review
After Phase 1-3 experiments, I think this issue should evolve away from “find the right DeepSeek history cap” and toward a cache-aware context policy on the existing Runtime v2 path.
Current state:
- PR Add DeepSeek cache-aware usage accounting #20 landed usage normalization / cache-aware pricing on the existing
RuntimeRunner -> AiSdkFlow -> AiSdkBackend -> ModelAdapterpath. - PR Add request-shape prefix diagnostics #21 landed request-shape / prefix diagnostics and moved volatile environment facts out of the durable system prefix.
- PR Add DeepSeek context budget diagnostics #22 adds prompt segment attribution and RuntimeEvent history-budget diagnostics, but after live DeepSeek tests and a Reasonix/Rive review, DeepSeek now has no default sliding-window history cap. It is opt-in via
MAKA_CONTEXT_HISTORY_BUDGET_TOKENS.
The key new evidence is that sliding-window turn dropping can be a bad DeepSeek cost optimization even when it reduces total input. In our paid live runs, the 256k history budget reduced total input once trimming began, but cache misses jumped sharply. In the high-payload A/B, budget-off had more total input but materially lower estimated cost because provider cache locality held.
So the route should be:
-
Keep the diagnostics from PR Add DeepSeek context budget diagnostics #22.
- prompt segment attribution
- context budget diagnostics
- DeepSeek/OpenAI-compatible cache hit/miss normalization
- request-shape diagnostics
-
Rename/split diagnostic semantics.
durablePrefixShape: model/provider + system prompt + provider options + tool schemarequestShape/historyProjectionShape: prior history projection and current request assembly
Reasonix’s
PrefixShapeonly explains stable local prefix surfaces. Provider cache hit/miss is a provider-reported fact. If history projection is included in a hash, calling it simplyprefixHashis misleading. -
Treat DeepSeek sliding-window trimming as an emergency request-size guard, not the default economy policy.
- DeepSeek default should remain off unless live baselines prove lower estimated cost by cache miss/cost, not total input.
- If an opt-in cap triggers, diagnostics should say it is request-size safety and show
history_projection_changednext to provider cache hit/miss.
-
Next implementation phase should be
diagnostic-rename + prune-before-compact.- Do not drop whole turns by default.
- First elide old large tool-result bodies into deterministic placeholders.
- Preserve RuntimeEvent/message identity, tool call id, tool name, assistant/tool pairing, and recent tail.
- Archive the original body before eliding it.
- Increment a rewrite version and surface a named reset reason.
-
After that, add the full Reasonix-style policy.
- rare named compaction reset near high-water context pressure
- archive-before-drop / archive-before-elide
- BM25 history
search+ boundedaroundretrieval - memory as synthesis cache: stable prefix index, on-demand read/search, approval-gated remember/forget
- provider cache diagnostics that correlate local rewrites with provider cache hit/miss without pretending we know DeepSeek’s internal cache key
Suggested next PR:
deepseek-cache-aware-context-phase4Scope:
- split durable-prefix vs request/history shape diagnostics
- add explicit-vs-derived cache miss metadata if missing from current telemetry
- implement stale tool-result pruning with archive refs and placeholders
- keep DeepSeek history cap opt-in
- add tests for tool-pair preservation, rewrite diagnostics, archive recovery, and cache/cost reporting
- extend the live baseline report fields to compare total input, cache hit, cache miss, output, and estimated cost across budget-off / opt-in budget / prune variants
Non-goal for the next PR: full summarizing compaction, history retrieval, and memory system migration. Those should follow after the prune path proves the economics and the diagnostics are unambiguous.
- PR Add DeepSeek cache-aware usage accounting #20 landed usage normalization / cache-aware pricing on the existing
Summary
Maka's Runtime v2 architecture already has the right place to add DeepSeek cost optimization: the
AgentFlow/AiSdkFlowsplit. We should integrate DeepSeek as a cost-awareAiSdkFlowpolicy selected by model/provider, not replace Maka's runtime or introduce a separate agent framework.This issue proposes a
CostRuntimelayer made of focused modules:The goal is to make DeepSeek cheap for ordinary follow-up turns by keeping provider-visible prefix bytes stable, pricing cached and fresh input correctly, and making cache misses explainable.
Product insight
Runtime v2's
AgentFlow/AiSdkFlowsplit is exactly where per-model or per-provider flows can live. DeepSeek should be a policy applied at that seam:RuntimeRunnercontinues to run a selectedAgentFlow.AiSdkFlowremains the AI SDK implementation.AiSdkFlowor cost-awareAiSdkFlowconfiguration for DeepSeek/cost-sensitive models.ModelAdapter/ provider factory code.This keeps the architecture incremental and avoids turning cost optimization into a sidecar, separate backend, or replacement framework.
Proposed architecture
Add a small resolver near
RuntimeKernel.startTurn():Default behavior returns the existing
AiSdkFlow. For DeepSeek or catalog-marked cost-sensitive models, returnAiSdkFlowwith aCostRuntimePolicy, or a smallCostAwareAiSdkFlowadapter while provider-visible request assembly still lives inAiSdkBackend.Add
CostRuntimeas modules, not one monolith:CostFlowPolicy: selects cost-aware behavior and records policy version.PrefixShape: hashes provider-visible system/tool/history/provider-option shape and reports local change reasons.PromptComposer: splits durable prefix from volatile turn-tail material.NormalizedProviderUsage: preserves cache-hit input, cache-miss input, output, reasoning, finish reason, and total tokens.CostPricing: prices fresh input, cached input, cache writes, and output separately.ProviderAdapterHardening: keeps DeepSeek reasoning/usage/retry semantics at the provider boundary.ToolSchemaRegistry: canonicalizes and sorts tool schemas beforestreamText.CapabilityEconomy: avoids paying every turn for optional or disabled tool families.HistoryMemoryEconomy: prunes/archives stale tool outputs and uses indexes/retrieval instead of prompt stuffing.CostObservability: exposes cached vs fresh input, reasoning/output, cost, and prefix-change reason in runtime events and telemetry.Why this matters
DeepSeek prompt caching is useful only when the start of the provider request is byte-stable across turns. Maka currently rebuilds the system prompt and tool map each call, includes volatile prompt fragments such as date/git/cwd, advertises a broad tool surface, and can replay old tool outputs into history. Some of this is correct product behavior, but today it is not tracked as cost-shaping request state.
We need to make the provider-visible shape explicit:
Required usage contract
Normalize provider usage at the adapter boundary:
Rules:
prompt_cache_hit_tokens=>cacheHitInputTokens.prompt_cache_miss_tokens=>cacheMissInputTokens.cacheHitInputTokens.max(0, inputTokens - cacheHitInputTokens - cacheWriteInputTokens).Pricing must use fresh input for full input price:
Prefix diagnostics
Add a
PrefixShapeobject computed before model invocation:Attach previous/current prefix hashes and a reason to usage events and telemetry:
first_turnsystem_prompt_changedtool_schema_changedmemory_index_changedworkspace_instructions_changedprovider_options_changedmodel_or_provider_changedhistory_projection_changedhistory_rewriteprovider_cache_expired_or_unknownunknownThis does not need to prove DeepSeek's internal cache key. It needs to explain local request-shape churn next to provider-reported cache hit/miss tokens.
Tool surface economy
Tool schemas are paid prompt surface. Add a
ToolSchemaRegistrythat:Then reduce default schema surface:
Context, memory, and history economy
Add a budgeter before provider message materialization:
historyRewriteVersion.Memory and workspace instructions should move toward indexes:
Phased implementation
Acceptance criteria
AiSdkFlowpolicy without changing non-DeepSeek behavior.Non-goals
validation_plan
Unit tests
model-adapterusage normalization:prompt_cache_hit_tokensandprompt_cache_miss_tokens.telemetry cost:
prefix shape:
prefixHash.tool schema registry:
tool_schema_changed.prompt composer:
history economy:
Integration tests
AiSdkBackendemits and persists token usage with cache hit/read, cache miss/fresh, reasoning, finish reason, and prefix reason.recordLlmCallreceives normalized usage, cost breakdown, finish reason, and prefix diagnostics.UI and storage tests
Live DeepSeek probes
Add env-gated probes that run only with credentials:
tool_schema_changed.system_prompt_changed.history_rewrite.open_questions
AiSdkBackendintoAiSdkFlowas part of this work, versus passing cost policy down first and refactoring later?PrefixShapebe persisted long-term: runtime events, LLM telemetry rows, run trace data, or a dedicated side table?PromptComposerrefactor?