Summarization

概述

Summarization 中间件会在对话的 token 数量超过配置阈值时,自动压缩对话历史。这有助于在长对话中保持上下文连续性,同时控制在模型的 token 限制范围内。

💡 本中间件在 v0.8.0 版本引入。

快速开始

import (
    "context"
    "github.com/cloudwego/eino/adk/middlewares/summarization"
)

// 使用最小配置创建中间件
mw, err := summarization.New(ctx, &summarization.Config{
    Model: yourChatModel,  // 必填:用于生成摘要的模型
})
if err != nil {
    // 处理错误
}

// 与 ChatModelAgent 一起使用
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
    Model:       yourChatModel,
    Middlewares: []adk.ChatModelAgentMiddleware{mw},
})

配置项

字段类型必填默认值说明
Modelmodel.BaseChatModel
  • 用于生成摘要的聊天模型
    ModelOptions[]model.Option
  • 传递给模型生成摘要时的选项
    TokenCounterTokenCounterFunc约 4 字符/token自定义 token 计数函数
    Trigger*TriggerCondition190,000 tokens触发摘要的条件
    UserInstructionstring内置 prompt自定义摘要指令
    TranscriptFilePathstring
  • 完整对话记录文件路径
    GenModelInputGenModelInputFunc
  • 自定义摘要模型输入的预处理函数
    FinalizeFinalizeFunc
  • 自定义最终消息的后处理函数
    CallbackCallbackFunc
  • 在 Finalize 之后调用,用于观察状态变化(只读)
    EmitInternalEventsboolfalse是否发送内部事件
    PreserveUserMessages*PreserveUserMessagesEnabled: true是否在摘要中保留原始用户消息

    TriggerCondition 结构

    type TriggerCondition struct {
        // ContextTokens 当总 token 数量超过此阈值时触发摘要
        ContextTokens int
    }
    

    PreserveUserMessages 结构

    type PreserveUserMessages struct {
        // Enabled 是否启用保留用户消息功能
        Enabled bool
        
        // MaxTokens 保留用户消息的最大 token 数
        // 只保留最近的用户消息,直到达到此限制
        // 默认为 TriggerCondition.ContextTokens 的 1/3
        MaxTokens int
    }
    

    配置示例

    自定义 Token 阈值

    mw, err := summarization.New(ctx, &summarization.Config{
        Model: yourChatModel,
        Trigger: &summarization.TriggerCondition{
            ContextTokens: 100000,  // 在 100k tokens 时触发
        },
    })
    

    自定义 Token 计数器

    mw, err := summarization.New(ctx, &summarization.Config{
        Model: yourChatModel,
        TokenCounter: func(ctx context.Context, input *summarization.TokenCounterInput) (int, error) {
            // 使用你的 tokenizer
            return yourTokenizer.Count(input.Messages)
        },
    })
    

    设置对话记录文件路径

    mw, err := summarization.New(ctx, &summarization.Config{
        Model:              yourChatModel,
        TranscriptFilePath: "/path/to/transcript.txt",
    })
    

    自定义 Finalize 函数

    mw, err := summarization.New(ctx, &summarization.Config{
        Model: yourChatModel,
        Finalize: func(ctx context.Context, originalMessages []adk.Message, summary adk.Message) ([]adk.Message, error) {
            // 自定义逻辑构建最终消息
            return []adk.Message{
                schema.SystemMessage("你的系统提示词"),
                summary,
            }, nil
        },
    })
    

    使用 Callback 观察状态变化**/存储**

    mw, err := summarization.New(ctx, &summarization.Config{
        Model: yourChatModel,
        Callback: func(ctx context.Context, before, after adk.ChatModelAgentState) error {
            log.Printf("Summarization completed: %d messages -> %d messages", 
                len(before.Messages), len(after.Messages))
            return nil
        },
    })
    

    控制用户消息保留

    mw, err := summarization.New(ctx, &summarization.Config{
        Model: yourChatModel,
        PreserveUserMessages: &summarization.PreserveUserMessages{
            Enabled:   true,
            MaxTokens: 50000, // 保留最多 50k tokens 的用户消息
        },
    })
    

    工作原理

    flowchart TD
        A[BeforeModelRewriteState] --> B{Token 数量超过阈值?}
        B -->|否| C[返回原始状态]
        B -->|是| D[发送 BeforeSummarize 事件]
        D --> E{有自定义 GenModelInput?}
        E -->|是| F[调用 GenModelInput]
        E -->|否| G[调用模型生成摘要]
        F --> G
        G --> H{有自定义 Finalize?}
        H -->|是| I[调用 Finalize]
        H -->|否| L{有自定义 Callback?}
        I --> L
        L -->|是| M[调用 Callback]
        L -->|否| J[发送 AfterSummarize 事件]
        M --> J
        J --> K[返回新状态]
    
        style A fill:#e3f2fd
        style G fill:#fff3e0
        style D fill:#e8f5e9
        style J fill:#e8f5e9
        style K fill:#c8e6c9
        style C fill:#f5f5f5
        style M fill:#fce4ec
        style F fill:#fff3e0
        style I fill:#fff3e0
    

    内部事件

    当 EmitInternalEvents 设置为 true 时,中间件会在关键节点发送事件:

    事件类型触发时机携带数据
    ActionTypeBeforeSummarize生成摘要之前原始消息列表
    ActionTypeAfterSummarize完成总结之后最终消息列表

    使用示例

    mw, err := summarization.New(ctx, &summarization.Config{
        Model:              yourChatModel,
        EmitInternalEvents: true,
    })
    
    // 在你的事件处理器中监听事件
    

    最佳实践

    1. 设置 TranscriptFilePath:建议始终提供对话记录文件路径,以便模型在需要时可以参考原始对话。
    2. 调整 Token 阈值:根据模型的上下文窗口大小调整 Trigger.MaxTokens。一般建议设置为模型限制的 80-90%。
    3. 自定义 Token 计数器:在生产环境中,建议实现与模型 tokenizer 匹配的自定义 TokenCounter,以获得准确的计数。