跳转至

会话摘要(Summary)

概述

随着对话持续增长,维护完整的事件历史可能会占用大量内存,并可能超出 LLM 的上下文窗口限制。会话摘要功能使用 LLM 自动将历史对话压缩为简洁的摘要,在保留重要上下文的同时显著降低内存占用和 token 消耗。

核心特性

  • 自动触发:根据事件数量、token 数量或时间阈值自动生成摘要
  • 增量处理:只处理自上次摘要以来的新事件,避免重复计算
  • LLM 驱动:使用任何配置的 LLM 模型生成高质量、上下文感知的摘要
  • 非破坏性:原始事件完整保留,摘要单独存储
  • 异步处理:后台异步执行,不阻塞对话流程
  • 灵活配置:支持自定义触发条件、提示词和字数限制

基础配置

步骤 1:创建摘要器

使用 LLM 模型创建摘要器并配置触发条件:

import (
    "time"

    "trpc.group/trpc-go/trpc-agent-go/session/summary"
    "trpc.group/trpc-go/trpc-agent-go/model/openai"
)

// 创建用于摘要的 LLM 模型
summaryModel := openai.New("gpt-4", openai.WithAPIKey("your-api-key"))

// 创建摘要器并配置触发条件
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithChecksAny(                     // 任一条件满足即触发
        summary.CheckEventThreshold(20),       // 自上次摘要后新增 20 个事件后触发
        summary.CheckTokenThreshold(4000),     // 自上次摘要后新增 4000 个 token 后触发
        summary.CheckTimeThreshold(5*time.Minute), // 5 分钟无活动后触发
    ),
    summary.WithMaxSummaryWords(200),          // 限制摘要在 200 字以内
)

步骤 2:配置会话服务

将摘要器集成到会话服务(内存或 Redis):

import (
    "time"
    "trpc.group/trpc-go/trpc-agent-go/session/inmemory"
    "trpc.group/trpc-go/trpc-agent-go/session/redis"
)

// 内存存储(开发/测试)
sessionService := inmemory.NewSessionService(
    inmemory.WithSummarizer(summarizer),
    inmemory.WithAsyncSummaryNum(2),                // 2 个异步 worker
    inmemory.WithSummaryQueueSize(100),             // 队列大小 100
    inmemory.WithSummaryJobTimeout(60*time.Second), // 单个任务超时 60 秒
)

// Redis 存储(生产环境)
sessionService, err := redis.NewService(
    redis.WithRedisClientURL("redis://localhost:6379"),
    redis.WithSummarizer(summarizer),
    redis.WithAsyncSummaryNum(4),           // 4 个异步 worker
    redis.WithSummaryQueueSize(200),        // 队列大小 200
)

// PostgreSQL 存储
sessionService, err := postgres.NewService(
    postgres.WithHost("localhost"),
    postgres.WithPassword("your-password"),
    postgres.WithSummarizer(summarizer),
    postgres.WithAsyncSummaryNum(2),       // 2 个异步 worker
    postgres.WithSummaryQueueSize(100),    // 队列大小 100
)

// MySQL 存储
sessionService, err := mysql.NewService(
    mysql.WithMySQLClientDSN("user:password@tcp(localhost:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"),
    mysql.WithSummarizer(summarizer),
    mysql.WithAsyncSummaryNum(2),           // 2个异步 worker
    mysql.WithSummaryQueueSize(100),        // 队列大小 100
)

// ClickHouse 存储
sessionService, err := clickhouse.NewService(
    clickhouse.WithClickHouseDSN("clickhouse://default:password@localhost:9000/default"),
    clickhouse.WithSummarizer(summarizer),
    clickhouse.WithAsyncSummaryNum(2),
)

步骤 3:配置 Agent 和 Runner

创建 Agent 并配置摘要注入行为:

import (
    "trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
    "trpc.group/trpc-go/trpc-agent-go/runner"
)

// 创建 Agent(配置摘要注入行为)
llmAgent := llmagent.New(
    "my-agent",
    llmagent.WithModel(summaryModel),
    llmagent.WithAddSessionSummary(true),   // 启用摘要注入
    llmagent.WithMaxHistoryRuns(10),        // 当AddSessionSummary=false时限制历史轮次
)

// 创建 Runner
r := runner.NewRunner(
    "my-agent",
    llmAgent,
    runner.WithSessionService(sessionService),
)

// 运行对话 - 摘要将自动管理
eventChan, err := r.Run(ctx, userID, sessionID, userMessage)

完成以上配置后,摘要功能即可自动运行。

SessionSummarizer 接口

type SessionSummarizer interface {
    // ShouldSummarize checks if the session should be summarized.
    ShouldSummarize(sess *session.Session) bool

    // Summarize generates a summary without modifying the session events.
    Summarize(ctx context.Context, sess *session.Session) (string, error)

    // SetPrompt updates the summarizer's prompt dynamically.
    SetPrompt(prompt string)

    // SetModel updates the summarizer's model dynamically.
    SetModel(m model.Model)

    // Metadata returns metadata about the summarizer configuration.
    Metadata() map[string]any
}

摘要器选项

触发条件

选项 说明
WithEventThreshold(eventCount int) 当自上次摘要后的事件数量超过阈值时触发
WithTokenThreshold(tokenCount int) 当自上次摘要后的 token 数量超过阈值时触发
WithTimeThreshold(interval time.Duration) 当自上次事件后经过的时间超过间隔时触发

组合条件

选项 说明
WithChecksAll(checks ...Checker) 要求所有条件都满足(AND 逻辑),使用 Check* 函数
WithChecksAny(checks ...Checker) 任何条件满足即触发(OR 逻辑),使用 Check* 函数

注意:在 WithChecksAllWithChecksAny 中使用 Check* 函数(如 CheckEventThreshold),而不是 With* 函数。

// AND 逻辑:所有条件都满足才触发
summary.WithChecksAll(
    summary.CheckEventThreshold(10),
    summary.CheckTokenThreshold(2000),
)

// OR 逻辑:任一条件满足即触发
summary.WithChecksAny(
    summary.CheckEventThreshold(50),
    summary.CheckTimeThreshold(10*time.Minute),
)

摘要生成

选项 说明
WithMaxSummaryWords(maxWords int) 限制摘要的最大字数,包含在提示词中指导模型生成
WithPrompt(prompt string) 自定义摘要提示词,必须包含 {conversation_text} 占位符
WithSkipRecent(skipFunc SkipRecentFunc) 自定义函数跳过最近事件

Hook 选项

选项 说明
WithPreSummaryHook(h PreSummaryHook) 摘要前的 Hook,可修改输入文本
WithPostSummaryHook(h PostSummaryHook) 摘要后的 Hook,可修改输出摘要
WithSummaryHookAbortOnError(abort bool) Hook 报错时是否中断,默认 false(忽略错误)

工具调用格式化

默认情况下,摘要器会将工具调用和工具结果包含在发送给 LLM 进行总结的对话文本中。默认格式为:

  • 工具调用:[Called tool: toolName with args: {"arg": "value"}]
  • 工具结果:[toolName returned: result content]
选项 说明
WithToolCallFormatter(f ToolCallFormatter) 自定义工具调用在摘要输入中的格式。返回空字符串可排除该工具调用
WithToolResultFormatter(f ToolResultFormatter) 自定义工具结果在摘要输入中的格式。返回空字符串可排除该结果
// Truncate long tool arguments
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithToolCallFormatter(func(tc model.ToolCall) string {
        name := tc.Function.Name
        if name == "" {
            return ""
        }
        args := string(tc.Function.Arguments)
        const maxLen = 100
        if len(args) > maxLen {
            args = args[:maxLen] + "...(truncated)"
        }
        return fmt.Sprintf("[Tool: %s, Args: %s]", name, args)
    }),
    summary.WithEventThreshold(20),
)

// Exclude tool results from summary
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithToolResultFormatter(func(msg model.Message) string {
        return ""
    }),
    summary.WithEventThreshold(20),
)

// Include only tool name, exclude arguments
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithToolCallFormatter(func(tc model.ToolCall) string {
        if tc.Function.Name == "" {
            return ""
        }
        return fmt.Sprintf("[Used tool: %s]", tc.Function.Name)
    }),
    summary.WithEventThreshold(20),
)

模型回调(Before/After Model)

summarizer 在调用底层 model.GenerateContent 前后支持模型回调,可用于修改请求、短路返回自定义响应、或在摘要请求上做埋点。

选项 说明
WithModelCallbacks(callbacks *model.Callbacks) 为摘要器的底层模型调用注册 Before/After 回调
callbacks := model.NewCallbacks().
    RegisterBeforeModel(func(ctx context.Context, args *model.BeforeModelArgs) (*model.BeforeModelResult, error) {
        // Modify args.Request, or return CustomResponse to skip the real model call
        return nil, nil
    }).
    RegisterAfterModel(func(ctx context.Context, args *model.AfterModelArgs) (*model.AfterModelResult, error) {
        // Override model output via CustomResponse
        return nil, nil
    })

summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithModelCallbacks(callbacks),
)

Checker 函数

Checker 是用于判断是否需要触发摘要的函数类型:

type Checker func(sess *session.Session) bool

内置 Checker

Checker 说明
CheckEventThreshold(eventCount int) 当自上次摘要以来的增量事件数大于阈值时返回 true
CheckTimeThreshold(interval time.Duration) 当距离最后一个事件的时间大于间隔时返回 true
CheckTokenThreshold(tokenCount int) 当自上次摘要以来的增量事件提取的对话文本估算 token 数大于阈值时返回 true(通过 TokenCounter 估算,而非 event.Response.Usage.TotalTokens
ChecksAll(checks []Checker) 组合多个 Checker,所有都返回 true 时才返回 true(AND)
ChecksAny(checks []Checker) 组合多个 Checker,任一返回 true 时返回 true(OR)

自定义提示词

customPrompt := `Analyze the following conversation and provide a concise summary,
focusing on key decisions, action items, and important context.
Keep it within {max_summary_words} words.

<conversation>
{conversation_text}
</conversation>

Summary:`

summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithPrompt(customPrompt),
    summary.WithMaxSummaryWords(100),
    summary.WithEventThreshold(15),
)

必需占位符

  • {conversation_text}:必须包含,会被对话内容替换
  • {max_summary_words}:当 maxSummaryWords > 0 时必须包含

Token 计数器配置

默认情况下,CheckTokenThreshold 使用内置的 SimpleTokenCounter 基于文本长度估算 token 数量。如果需要自定义 token 计数行为,可以使用 summary.SetTokenCounter 设置全局 token 计数器:

import (
    "context"
    "fmt"
    "unicode/utf8"

    "trpc.group/trpc-go/trpc-agent-go/model"
    "trpc.group/trpc-go/trpc-agent-go/session/summary"
)

// Use the built-in simple token counter
summary.SetTokenCounter(model.NewSimpleTokenCounter())

// Or use a custom implementation
type MyCustomCounter struct{}

func (c *MyCustomCounter) CountTokens(ctx context.Context, message model.Message) (int, error) {
    _ = ctx
    return utf8.RuneCountInString(message.Content), nil
}

func (c *MyCustomCounter) CountTokensRange(ctx context.Context, messages []model.Message, start, end int) (int, error) {
    if start < 0 || end > len(messages) || start >= end {
        return 0, fmt.Errorf("invalid range: start=%d, end=%d, len=%d",
            start, end, len(messages))
    }

    total := 0
    for i := start; i < end; i++ {
        tokens, err := c.CountTokens(ctx, messages[i])
        if err != nil {
            return 0, err
        }
        total += tokens
    }
    return total, nil
}

summary.SetTokenCounter(&MyCustomCounter{})

注意

  • 全局影响SetTokenCounter 会影响当前进程中所有的 CheckTokenThreshold 评估,建议在应用初始化时一次性设置
  • 默认计数器:如果不设置,将使用默认的 SimpleTokenCounter(约每 token 对应 4 个字符)

跳过最近事件

使用 WithSkipRecent 可以在摘要时跳过最近的事件:

// 跳过固定数量的事件
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithSkipRecent(func(_ []event.Event) int { return 2 }), // 跳过最后 2 个事件
    summary.WithEventThreshold(10),
)

// 跳过最近 5 分钟的事件(时间窗口)
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithSkipRecent(func(events []event.Event) int {
        cutoff := time.Now().Add(-5 * time.Minute)
        skip := 0
        for i := len(events) - 1; i >= 0; i-- {
            if events[i].Timestamp.After(cutoff) {
                skip++
            } else {
                break
            }
        }
        return skip
    }),
    summary.WithEventThreshold(10),
)

// 只跳过尾部的工具调用消息
summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithSkipRecent(func(events []event.Event) int {
        skip := 0
        for i := len(events) - 1; i >= 0; i-- {
            if events[i].Response != nil && len(events[i].Response.Choices) > 0 &&
                events[i].Response.Choices[0].Message.Role == model.RoleTool {
                skip++
            } else {
                break
            }
        }
        return skip
    }),
    summary.WithEventThreshold(10),
)

摘要 Hook

PreSummaryHook

在摘要生成前调用,可以修改输入文本或事件:

1
2
3
4
5
6
7
8
type PreSummaryHookContext struct {
    Ctx     context.Context
    Session *session.Session
    Events  []event.Event
    Text    string
}

type PreSummaryHook func(in *PreSummaryHookContext) error

PostSummaryHook

在摘要生成后调用,可以修改输出摘要:

1
2
3
4
5
6
7
type PostSummaryHookContext struct {
    Ctx     context.Context
    Session *session.Session
    Summary string
}

type PostSummaryHook func(in *PostSummaryHookContext) error

使用示例

summarizer := summary.NewSummarizer(
    summaryModel,
    summary.WithPreSummaryHook(func(ctx *summary.PreSummaryHookContext) error {
        // 在摘要生成前修改 ctx.Text 或 ctx.Events
        return nil
    }),
    summary.WithPostSummaryHook(func(ctx *summary.PostSummaryHookContext) error {
        // 在摘要生成后修改 ctx.Summary
        return nil
    }),
    summary.WithSummaryHookAbortOnError(true), // Hook 报错时中断(可选)
)

摘要触发机制

自动触发(推荐)

Runner 在每次对话完成后自动检查触发条件,满足条件时在后台异步生成摘要。

触发时机

  • 事件数量超过阈值(WithEventThreshold
  • Token 数量超过阈值(WithTokenThreshold
  • 距上次事件超过指定时间(WithTimeThreshold
  • 满足自定义组合条件(WithChecksAny / WithChecksAll

手动触发

某些场景下,你可能需要手动触发摘要:

// 异步摘要(推荐)- 后台处理,不阻塞
err := sessionService.EnqueueSummaryJob(
    ctx,
    sess,
    session.SummaryFilterKeyAllContents, // 对完整会话生成摘要
    false,                               // force=false,遵守触发条件
)

// 同步摘要 - 立即处理,会阻塞当前操作
err := sessionService.CreateSessionSummary(
    ctx,
    sess,
    session.SummaryFilterKeyAllContents,
    false, // force=false,遵守触发条件
)

// 异步强制摘要 - 忽略触发条件,强制生成
err := sessionService.EnqueueSummaryJob(
    ctx,
    sess,
    session.SummaryFilterKeyAllContents,
    true, // force=true,绕过所有触发条件检查
)

// 同步强制摘要 - 立即强制生成
err := sessionService.CreateSessionSummary(
    ctx,
    sess,
    session.SummaryFilterKeyAllContents,
    true, // force=true,绕过所有触发条件检查
)

API 说明:

  • EnqueueSummaryJob:异步摘要(推荐)

    • 后台处理,不阻塞当前操作
    • 失败时自动回退到同步处理
    • 适合生产环境
  • CreateSessionSummary:同步摘要
    • 立即处理,会阻塞当前操作
    • 直接返回处理结果
    • 适合调试或需要立即获取结果的场景

参数说明:

  • filterKeysession.SummaryFilterKeyAllContents 表示对完整会话生成摘要
  • force 参数
    • false:遵守配置的触发条件(事件数、token 数、时间阈值等),只有满足条件才生成摘要
    • true:强制生成摘要,完全忽略所有触发条件检查,无论会话状态如何都会执行

使用场景:

场景 推荐 API force 参数
正常对话流程 自动触发(无需调用) -
后台批量处理 EnqueueSummaryJob false
用户主动请求 EnqueueSummaryJob true
调试/测试 CreateSessionSummary true
会话结束时 EnqueueSummaryJob true

上下文注入机制

框架提供两种模式来管理发送给 LLM 的对话上下文:

模式 1:启用摘要注入(推荐)

llmagent.WithAddSessionSummary(true)

工作方式

  • 会话摘要合并到已有的系统消息中(如果存在),否则作为新的系统消息插入到开头
  • 这确保了与要求单条系统消息位于开头的模型兼容(如 Qwen3.5 系列)
  • 包含摘要时间点之后的所有增量事件(不截断)
  • 保证完整上下文:浓缩历史 + 完整新对话
  • WithMaxHistoryRuns 参数被忽略

上下文结构

┌─────────────────────────────────────────┐
│ System Prompt                           │
│ (merged with Session Summary)           │ ← 系统提示 + 浓缩历史
├─────────────────────────────────────────┤
│ Event 1 (after summary)                 │ ┐
│ Event 2                                 │ │
│ Event 3                                 │ │ 摘要后的新事件
│ ...                                     │ │ (完整保留)
│ Event N (current message)               │ ┘
└─────────────────────────────────────────┘

模型兼容性

部分 LLM 提供商对系统消息的位置和数量有严格要求:

  • Qwen3.5 系列等模型要求系统消息必须位于对话开头,且不支持多条系统消息
  • 默认的合并行为可避免 System message must be at the beginning 等错误
  • 预加载的内存内容也会通过相同机制合并到系统消息中

模式 2:不使用摘要

llmagent.WithAddSessionSummary(false)
llmagent.WithMaxHistoryRuns(10)  // 限制历史轮次

工作方式

  • 不添加摘要消息
  • 只包含最近 MaxHistoryRuns 轮对话
  • MaxHistoryRuns=0 时不限制,包含所有历史

上下文结构

1
2
3
4
5
6
7
8
┌─────────────────────────────────────────┐
│ System Prompt                           │
├─────────────────────────────────────────┤
│ Event N-k+1                             │ ┐
│ Event N-k+2                             │ │ Last k runs
│ ...                                     │ │ (MaxHistoryRuns=k)
│ Event N (current message)               │ ┘
└─────────────────────────────────────────┘

模式选择建议

场景 推荐配置 说明
长期会话(客服、助手) AddSessionSummary=true 保持完整上下文,优化 token
短期会话(单次咨询) AddSessionSummary=false
MaxHistoryRuns=10
简单直接,无需摘要开销
调试测试 AddSessionSummary=false
MaxHistoryRuns=5
快速验证,减少干扰
高并发场景 AddSessionSummary=true
增加 worker 数量
异步处理,不影响响应速度

摘要格式自定义

默认情况下,会话摘要会以包含上下文标签和关于优先考虑当前对话信息的提示进行格式化:

默认格式

1
2
3
4
5
6
7
Here is a brief summary of your previous interactions:

<summary_of_previous_interactions>
[Summary content]
</summary_of_previous_interactions>

Note: this information is from previous interactions and may be outdated. You should ALWAYS prefer information from this conversation over the past summary.

您可以使用 WithSummaryFormatter 来自定义摘要格式:

1
2
3
4
5
6
7
8
agent := llmagent.New(
    "my-agent",
    llmagent.WithModel(modelInstance),
    llmagent.WithAddSessionSummary(true),
    llmagent.WithSummaryFormatter(func(summary string) string {
        return fmt.Sprintf("## Previous Context\n\n%s", summary)
    }),
)

使用场景

  • 简化格式:使用简洁的标题和最少的上下文提示来减少 token 消耗
  • 语言本地化:将上下文提示翻译为目标语言
  • 角色特定格式:为不同的 Agent 角色提供不同的格式
  • 模型优化:根据特定模型的偏好调整格式

获取摘要

// 获取完整会话摘要(默认)
summaryText, found := sessionService.GetSessionSummaryText(ctx, sess)
if found {
    fmt.Printf("摘要: %s\n", summaryText)
}

// 获取特定 filter key 的摘要
userSummary, found := sessionService.GetSessionSummaryText(
    ctx, sess, session.WithSummaryFilterKey("user-messages"),
)
if found {
    fmt.Printf("用户消息摘要: %s\n", userSummary)
}

Filter Key 支持

  • 不提供选项时,返回全量会话摘要(SummaryFilterKeyAllContents
  • 提供特定 filter key 但未找到时,回退到全量会话摘要
  • 如果都不存在,兜底返回任意可用的摘要

按事件类型生成摘要

在实际应用中,你可能希望为不同类型的事件生成独立的摘要。

使用 AppendEventHook 设置 FilterKey

sessionService := inmemory.NewSessionService(
    inmemory.WithAppendEventHook(func(ctx *session.AppendEventContext, next func() error) error {
        // 根据事件作者自动分类
        prefix := "my-app/"  // 必须添加 appName 前缀
        switch ctx.Event.Author {
        case "user":
            ctx.Event.FilterKey = prefix + "user-messages"
        case "tool":
            ctx.Event.FilterKey = prefix + "tool-calls"
        default:
            ctx.Event.FilterKey = prefix + "misc"
        }
        return next()
    }),
)

FilterKey 前缀规范

⚠️ 重要:FilterKey 必须添加 appName + "/" 前缀。

原因:Runner 在过滤事件时使用 appName + "/" 作为过滤前缀,如果 FilterKey 没有这个前缀,事件会被过滤掉。

1
2
3
4
5
// ✅ 正确:带 appName 前缀
evt.FilterKey = "my-app/user-messages"

// ❌ 错误:没有前缀,事件会被过滤掉
evt.FilterKey = "user-messages"

为不同类型生成摘要

1
2
3
4
5
6
7
8
9
// 为用户消息生成摘要
err := sessionService.CreateSessionSummary(ctx, sess, "my-app/user-messages", false)

// 为工具调用生成摘要
err := sessionService.CreateSessionSummary(ctx, sess, "my-app/tool-calls", false)

// 获取特定类型的摘要
userSummary, found := sessionService.GetSessionSummaryText(
    ctx, sess, session.WithSummaryFilterKey("my-app/user-messages"))

工作原理

  1. 增量处理:摘要器跟踪每个会话的上次摘要时间,后续运行只处理上次摘要后发生的事件
  2. 增量摘要:新事件与先前的摘要组合,生成一个既包含旧上下文又包含新信息的更新摘要
  3. 触发条件评估:在生成摘要之前,评估配置的触发条件。如果条件未满足且 force=false,则跳过摘要
  4. 异步 Worker:摘要任务使用基于哈希的分发策略分配到多个 worker goroutine,确保同一会话的任务按顺序处理
  5. 回退机制:如果异步入队失败(队列已满、上下文取消或 worker 未初始化),系统会自动回退到同步处理

最佳实践

  1. 选择合适的阈值:根据 LLM 的上下文窗口和对话模式设置事件/token 阈值。对于 GPT-4(8K 上下文),考虑使用 WithTokenThreshold(4000) 为响应留出空间
  2. 使用异步处理:在生产环境中始终使用 EnqueueSummaryJob 而不是 CreateSessionSummary,以避免阻塞对话流程
  3. 监控队列大小:如果频繁看到"queue is full"警告,请增加 WithSummaryQueueSizeWithAsyncSummaryNum
  4. 自定义提示词:根据应用需求定制摘要提示词。例如,如果你正在构建客户支持 Agent,应关注关键问题和解决方案
  5. 平衡字数限制:设置 WithMaxSummaryWords 以在保留上下文和减少 token 使用之间取得平衡。典型值范围为 100-300 字
  6. 测试触发条件:尝试不同的 WithChecksAnyWithChecksAll 组合,找到摘要频率和成本之间的最佳平衡

性能考虑

  • LLM 成本:每次摘要生成都会调用 LLM,监控触发条件以平衡成本和上下文保留
  • 内存使用:摘要与事件一起存储,配置适当的 TTL 以管理长时间运行会话中的内存
  • 异步 Worker:更多 worker 会提高吞吐量但消耗更多资源,从 2-4 个 worker 开始,根据负载进行扩展
  • 队列容量:根据预期的并发量和摘要生成时间调整队列大小

完整示例

以下是演示所有组件如何协同工作的完整示例:

package main

import (
    "context"
    "time"

    "trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
    "trpc.group/trpc-go/trpc-agent-go/model"
    "trpc.group/trpc-go/trpc-agent-go/model/openai"
    "trpc.group/trpc-go/trpc-agent-go/runner"
    "trpc.group/trpc-go/trpc-agent-go/session/inmemory"
    "trpc.group/trpc-go/trpc-agent-go/session/summary"
)

func main() {
    ctx := context.Background()

    // Create LLM model for chat and summary
    llm := openai.New("gpt-4", openai.WithAPIKey("your-api-key"))

    // Create summarizer with flexible trigger conditions
    summarizer := summary.NewSummarizer(
        llm,
        summary.WithMaxSummaryWords(200),
        summary.WithChecksAny(
            summary.CheckEventThreshold(20),
            summary.CheckTokenThreshold(4000),
            summary.CheckTimeThreshold(5*time.Minute),
        ),
    )

    // Create session service with summarizer
    sessionService := inmemory.NewSessionService(
        inmemory.WithSummarizer(summarizer),
        inmemory.WithAsyncSummaryNum(2),
        inmemory.WithSummaryQueueSize(100),
        inmemory.WithSummaryJobTimeout(60*time.Second),
    )

    // Create agent with summary injection enabled
    agent := llmagent.New(
        "my-agent",
        llmagent.WithModel(llm),
        llmagent.WithAddSessionSummary(true),
        llmagent.WithMaxHistoryRuns(10),
    )

    // Create runner
    r := runner.NewRunner("my-app", agent,
        runner.WithSessionService(sessionService))

    // Run conversation - summary will be managed automatically
    userMsg := model.NewUserMessage("Tell me about AI")
    eventChan, _ := r.Run(ctx, "user123", "session456", userMsg)

    // Consume events
    for event := range eventChan {
        _ = event
    }
}

参考资源