cache

package
v0.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: None detected not legal advice Imports: 0 Imported by: 0

Documentation

Overview

PromptHashTracker - Prompt Cache 内容哈希刷新机制.

背景:Anthropic Prompt Cache 采用内容哈希匹配--相同内容的请求命中同一缓存 slot. 当系统提示词发生变化时(如注入了新工具描述,新记忆片段,新 FLYTO.md 节), 旧缓存 slot 自动失效,下一次请求会产生新的 cache slot(引发 cache write).

本模块的职责:在引擎侧感知"提示词是否变化",变化时主动重置 SectionCache, 让 buildSystemPromptWithContext 触发完整重渲--确保 Anthropic 收到 与上次不同的内容,令缓存 slot 正确更新.

升华改进(ELEVATED): 早期实现 无此机制,每次请求都重新渲染,浪费计算; 我们用 sha256 哈希做内容感知,仅当内容真正变化时才重置-- 静态内容不变时 SectionCache 命中,CPU + latency 双赢. 替代方案:<轮询时间戳判断> - 否决:时间戳无法感知内容等价(同一内容在不同时间重新生成,哈希相同可省去重置).

替代方案2:<每次强制重置 SectionCache> - 否决:SectionCache 里有 FLYTO.md 文件读取等 I/O,无谓重置浪费 latency.

tool_schema_hash.go - 工具 Schema 变化追踪与缓存稳定性分类(模块 22.2).

解决的问题:

Anthropic Prompt Cache 会缓存工具列表(tools 数组).
只要工具的描述/Schema 发生任何变化,整个 tools 缓存前缀失效,
触发 cache write,该轮所有工具 token 按全价计费.

假设系统有 50 个工具,系统提示词 + 工具列表共 50k tokens:
  命中缓存:50k × $3/M × 10% = $0.015/轮
  未命中:  50k × $3/M       = $0.15/轮(贵 10 倍)

如果一个工具描述含有动态内容(session ID,时间戳,每轮变化的列表),
每轮都会破坏所有工具的缓存.

本模块的职责:

  1. 精确识别哪个工具的 Schema 变了(不是"工具集变了"这种粗粒度)
  2. 统计每个工具的稳定性(过去 N 轮是否有变化)
  3. 提供 StableFirst 排序:稳定工具放前面,不稳定工具放后面 调用方据此把 cache_control 标记放在最后一个稳定工具后, 保护前缀缓存不被不稳定工具破坏

升华改进(ELEVATED): 早期实现用 djb2 32-bit 哈希, 碰撞率在 50 个工具场景下不可忽视(用于诊断日志,偶尔误判可接受). 我们改用 SHA-256:[32]byte 直接 == 比较,零分配,碰撞概率工程上可忽略. 早期方案仅做诊断日志;我们额外实现稳定性窗口 + StableFirst 排序,真正影响 API 费用. 替代方案:<沿用 djb2,只做诊断> - 否决:工具描述稳定性直接影响 Prompt Cache 命中率, 值得更可靠的哈希 + 可操作的排序输出.

Package cache 提供通用的 TTL 缓存实现.

TTLCache 是一个泛型的带过期时间的缓存,支持:

  • 过期后返回旧值 + 后台异步刷新(stale-while-revalidate 模式)
  • Thundering herd 防护(同一 key 只有一个 goroutine 在刷新)
  • 缓存 miss 时同步加载(GetOrLoad)
  • 手动设置和失效

设计要点:

  • 读操作用 RWMutex,高并发读性能好
  • 后台刷新用 goroutine,不阻塞读请求
  • 刷新中标记防止多个 goroutine 同时刷新同一 key

Index

Constants

This section is empty.

Variables

View Source
var ErrNoLoader error = errNoLoader{}

ErrNoLoader 是 loader 为 nil 时 GetOrLoad 返回的哨兵错误.

Functions

This section is empty.

Types

type PromptHashTracker

type PromptHashTracker struct {
	// contains filtered or unexported fields
}

PromptHashTracker 跟踪系统提示词内容的 SHA-256 哈希.

线程安全:Check 可从任意 goroutine 调用(runLoop 主循环 + 潜在并发 Dream 请求).

精妙之处(CLEVER): 存储 [32]byte 而非 string/hex-- [32]byte 可直接用 == 比较(编译器展开为定长内存比较),无分配,无哈希碰撞. 相比 hex.EncodeToString 少一次 malloc,高频调用(每轮 API 请求前)收益明显.

func NewPromptHashTracker

func NewPromptHashTracker() *PromptHashTracker

NewPromptHashTracker 创建一个新的 PromptHashTracker.

初始状态:未设置任何哈希.首次 Check 调用将返回 changed=true, 确保首次请求前必然触发 SectionCache 重置(初始化语义).

func (*PromptHashTracker) Check

func (t *PromptHashTracker) Check(content []byte) (changed bool)

Check 计算 content 的 sha256 哈希并与上次结果对比.

返回 changed=true 的情况:

  • 首次调用(从未有过基准哈希)
  • content 内容与上次调用时不同

返回 changed=false:content 与上次完全相同(字节级等价).

精妙之处(CLEVER): sha256.Sum256 是一次性计算,无需 New()+Write()+Sum()-- 对于系统提示词(通常 2-20KB),Sum256 直接计算比流式 Hash 快(无 Write 开销). 替代方案:<用 CRC32/FNV-1a 哈希> - 否决:CRC32 碰撞率高(提示词相近时假阴性风险),SHA256 碰撞概率可工程上忽略.

func (*PromptHashTracker) LastHash

func (t *PromptHashTracker) LastHash() ([32]byte, bool)

LastHash 返回当前记录的哈希(调试/监控用). 若从未调用 Check,返回全零哈希和 false.

func (*PromptHashTracker) Reset

func (t *PromptHashTracker) Reset()

Reset 清除已记录的哈希,使下次 Check 一定返回 changed=true.

适用场景:

  • 用户调用 /clear(完全重置会话,系统提示需重新缓存)
  • 用户调用 /compact(上下文压缩后重建,缓存失效)
  • 动态注入了新的 PromptBundle 或 Section(内容已知变化,跳过哈希计算)

升华改进(ELEVATED): 提供显式 Reset 而非只依靠内容哈希-- 某些场景(/clear,Bundle 切换)调用方明确知道缓存已失效, 显式 Reset 比等内容哈希检测更及时(避免哈希相同但语义已变的边界情况).

type TTLCache

type TTLCache[K comparable, V any] struct {
	// contains filtered or unexported fields
}

TTLCache 是一个泛型的带 TTL 的缓存. K 是键类型(必须 comparable),V 是值类型.

func NewTTLCache

func NewTTLCache[K comparable, V any](ttl time.Duration, loader func(K) (V, error)) *TTLCache[K, V]

NewTTLCache 创建一个新的 TTL 缓存. ttl 是条目的生存时间. loader 是缓存 miss 时的加载函数(可以为 nil,但 GetOrLoad 会返回错误).

func (*TTLCache[K, V]) Clear

func (c *TTLCache[K, V]) Clear()

Clear 清空缓存.

func (*TTLCache[K, V]) Close

func (c *TTLCache[K, V]) Close()

Close 停止后台清理 goroutine 并释放资源.

精妙之处(CLEVER): sync.Once 保护 close(stopCh)-- 重复调用 Close()(如 defer + 显式调用)会 panic "close of closed channel". Once 使 Close() 幂等,调用方无需追踪是否已关闭.

func (*TTLCache[K, V]) Get

func (c *TTLCache[K, V]) Get(key K) (V, bool)

Get 获取缓存值. 如果 key 存在且未过期,返回 (value, true). 如果 key 存在但已过期,返回旧值 (value, true) 并触发后台刷新. 如果 key 不存在,返回 (zero, false).

func (*TTLCache[K, V]) GetOrLoad

func (c *TTLCache[K, V]) GetOrLoad(key K) (V, error)

GetOrLoad 获取缓存值,如果 miss 则同步加载. 与 Get 不同,这个方法在缓存 miss 时会阻塞等待加载完成. 如果 loader 为 nil,缓存 miss 时返回错误.

func (*TTLCache[K, V]) Invalidate

func (c *TTLCache[K, V]) Invalidate(key K)

Invalidate 使指定 key 的条目失效. 下次 Get 会触发后台刷新(如果有 loader),GetOrLoad 会同步加载.

func (*TTLCache[K, V]) Len

func (c *TTLCache[K, V]) Len() int

Len 返回缓存中的条目数量(包括已过期但未清理的).

func (*TTLCache[K, V]) Peek

func (c *TTLCache[K, V]) Peek(key K) (V, bool)

Peek 读取缓存值,不触发后台刷新. 纯观察性操作,适合监控和调试.

func (*TTLCache[K, V]) Set

func (c *TTLCache[K, V]) Set(key K, value V)

Set 手动设置缓存条目.

type ToolEntry

type ToolEntry struct {
	Name    string // 工具唯一标识符
	Content []byte // 规范化内容字节(用于 SHA-256 哈希)
}

ToolEntry 是提交给 ToolSchemaTracker 的最小单元.

Content 是工具的规范化字节表示--调用方负责生成,通常是 json.Marshal 后的 {name, description, input_schema} 结构体. cache_control 字段不应包含在 Content 中(它是元数据,不是工具"内容").

type ToolSchemaChanges

type ToolSchemaChanges struct {
	Added   []string // 新增工具名(上轮没有,本轮出现)
	Removed []string // 删除工具名(上轮有,本轮消失)
	Changed []string // 变化工具名(同名但 Content 哈希不同)
	Stable  bool     // true 当 Added=Removed=Changed=[]
}

ToolSchemaChanges 描述两次 Track 调用之间工具集的变化.

Stable = true 当且仅当 Added/Removed/Changed 均为空, 即本轮工具集与上一轮完全相同(字节级等价).

type ToolSchemaTracker

type ToolSchemaTracker struct {
	// contains filtered or unexported fields
}

ToolSchemaTracker 逐轮追踪工具 Schema 哈希,统计稳定性, 支持按稳定性排序以优化 Anthropic Prompt Cache 命中率.

线程安全:所有方法均持 mu 锁,可从任意 goroutine 调用. 生命周期:与 Engine 实例绑定(Engine 级单例),Session 不拥有 Tracker.

关键不变量:

  • hashes[name] 始终是最近一次 Track 时该工具的 SHA-256
  • lastChangeTurn[name] 是该工具最近一次内容变化的轮次号
  • firstSeenTurn[name] 是该工具首次出现的轮次号
  • turnCount 单调递增,每次 Track 调用 +1

func NewToolSchemaTracker

func NewToolSchemaTracker() *ToolSchemaTracker

NewToolSchemaTracker 创建 ToolSchemaTracker,使用默认稳定性窗口(5 轮).

func NewToolSchemaTrackerWithWindow

func NewToolSchemaTrackerWithWindow(window int) *ToolSchemaTracker

NewToolSchemaTrackerWithWindow 创建指定稳定性窗口的 ToolSchemaTracker. window < 1 时自动修正为 1.

func (*ToolSchemaTracker) Reset

func (t *ToolSchemaTracker) Reset()

Reset 清除所有追踪状态,使下次 Track 从零开始.

适用场景:

  • Plugin 系统重载(工具集可能大幅变化,旧稳定性数据无参考价值)
  • Engine Close 后重用同一实例(理论上不会发生,但作为防御性接口提供)

func (*ToolSchemaTracker) StabilityReport

func (t *ToolSchemaTracker) StabilityReport() ToolStabilityReport

StabilityReport 返回当前所有追踪工具的稳定性快照. 主要用于调试和 observer 事件(不影响请求路径).

func (*ToolSchemaTracker) StableFirstWithBoundary

func (t *ToolSchemaTracker) StableFirstWithBoundary(names []string) (sorted []string, stableCount int)

StableFirstWithBoundary 返回按稳定性排序的工具名,以及稳定工具的数量.

返回值:

  • sorted:工具名切片,前 stableCount 个是稳定工具,后面是不稳定工具
  • stableCount:稳定工具的数量

调用方可据此把 cache_control 放在 sorted[stableCount-1] 对应的工具定义上, 保护前缀缓存不被后面的不稳定工具影响.

升华改进(ELEVATED): 早期实现 只计算 changedToolSchemas(诊断日志), 不影响工具列表顺序,也不放 cache_control--cache break 只能事后归因,不能预防. 我们的 StableFirstWithBoundary 让调用方能主动调整工具顺序 + cache_control 放置, 从"事后诊断"变为"主动预防",对 SDK/SaaS 场景每轮节省最多 90% 工具 token 费用. 替代方案:<仅做诊断日志,不排序> - 否决:诊断可以有,但费用优化更有价值.

func (*ToolSchemaTracker) Track

func (t *ToolSchemaTracker) Track(tools []ToolEntry) ToolSchemaChanges

Track 记录本轮工具集,返回与上一轮相比的变化情况. 每次 API 请求前调用一次,入参是当轮将要发送的完整工具列表.

算法:

  1. turnCount++
  2. 逐工具计算 SHA-256(Content),与缓存哈希比较
  3. 新增 = 上轮没有,本轮有;删除 = 上轮有,本轮没有;变化 = 同名但哈希不同
  4. 更新内部状态(哈希,lastChangeTurn,firstSeenTurn)
  5. 返回 ToolSchemaChanges(切片已排序,便于确定性比较)

精妙之处(CLEVER): Added/Removed/Changed 切片按名称排序-- 相同变化集合的两次调用返回完全相同的切片,方便测试断言和日志去重. 替代方案:按遍历顺序(非确定性,依赖 map 迭代顺序,测试会 flaky).

func (*ToolSchemaTracker) TurnCount

func (t *ToolSchemaTracker) TurnCount() int

TurnCount 返回累计 Track 轮次(调试用).

type ToolStabilityReport

type ToolStabilityReport struct {
	// StableTools 在过去 Window 轮内未变化.
	// 把这些工具排在工具列表前面 + 在最后一个稳定工具处加 cache_control,
	// 可以最大化 Anthropic Prompt Cache 命中率.
	StableTools []string
	// UnstableTools 在过去 Window 轮内至少变化一次.
	// 排在工具列表后面,不影响前缀缓存.
	UnstableTools []string
	// TurnCount 当前累计轮次数(调试用).
	TurnCount int
	// Window 稳定性判定窗口(轮次).
	Window int
}

ToolStabilityReport 是工具稳定性快照. 用于调试和外部决策(如决定 cache_control 边界放哪里).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL