最后更新:2026-04-16
TokenRouter 的缓存策略采用可插拔设计,每个 LLM 厂商实现自己的 cacheinject.Injector 接口。详细设计参见 缓存智能管理。
type Injector interface {
Name() string
Supports(vendor string) bool
Inject(blocks []block.Block) ([]block.Block, error)
}mkdir internal/cacheinject/myvendor
touch internal/cacheinject/myvendor/myvendor.gopackage myvendor
import "github.com/tokenrouter/internal/block"
type MyVendorInjector struct{}
func (i *MyVendorInjector) Name() string { return "myvendor" }
func (i *MyVendorInjector) Supports(vendor string) bool {
return vendor == "myvendor"
}
// Inject 在 Block 中注入缓存控制标记
func (i *MyVendorInjector) Inject(blocks []block.Block) ([]block.Block, error) {
for idx, b := range blocks {
if b.Type == block.BlockSystem {
// 示例:在 system 消息上添加缓存标记
for mIdx := range b.Messages {
blocks[idx].Messages[mIdx].CacheControl = &envelope.CacheControl{
Type: "ephemeral",
}
}
}
}
return blocks, nil
}// internal/cacheinject/registry.go
import "github.com/tokenrouter/internal/cacheinject/myvendor"
func init() {
registry.Register(&myvendor.MyVendorInjector{})
}func TestMyVendorInjector_Inject(t *testing.T) {
i := &myvendor.MyVendorInjector{}
blocks := []block.Block{
{
Type: block.BlockSystem,
Messages: []envelope.Message{
{Role: "system", Content: json.RawMessage(`"You are helpful"`)},
},
},
}
result, err := i.Inject(blocks)
assert.NoError(t, err)
assert.NotNil(t, result[0].Messages[0].CacheControl)
assert.Equal(t, "ephemeral", result[0].Messages[0].CacheControl.Type)
}| 厂商 | 缓存机制 | 注入方式 | 响应中提取方式 |
|---|---|---|---|
| Anthropic | cache_control ephemeral | 在 system/tools 上添加 cache_control 字段 | cache_read_input_tokens / cache_creation_input_tokens |
| OpenAI | 自动缓存 | 无需注入(Canonicalizer 保证结构一致性即可) | cached_tokens |
| DeepSeek | 自动缓存 | 兼容 OpenAI 协议,无需显式注入 | 同 OpenAI |
| Context Caching API | 单独调用 caches.create,请求中引用 cachedContent | 无直接返回(需单独查询) |
- 降级处理:
Inject失败时返回原始blocks,不影响正常请求 - 最小修改:只修改需要注入缓存标记的 Block,不改消息内容
- 定价配置:缓存定价在
model_pricing表中维护,不在注入器中硬编码 - 测试覆盖:至少测试
Inject的核心路径和空 Block 边界情况