Files
Agent-System-Backend/internal/runtime/transcript_compact.go
T
weichen f1302ed5b3 Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:47:16 +00:00

111 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package runtime
import (
"fmt"
"strings"
"time"
agentllm "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
agentsession "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/session"
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
)
const compactKeepMessages = 16
// turnRefresh 是转录权威模式下每轮需要刷新的动态上下文(经 Reminders 注入,
// 请求侧生效、不落转录):个人指令/会话要求等尾注、环境更新行、本轮新命中的
// 提示词模块正文。
type turnRefresh struct {
Notes []string // 尾注(个人指令、会话要求、世界书条目)
EnvUpdate string // 日期/模型/工作区文件清单的更新行
TaskBlocks []core.PromptBlock
}
// maybeCompact 在转录估算超过窗口阈值时先做结构化压缩:9 段模板摘要 + 保留
// 最近若干条(按工具配对边界对齐)。压缩是追加式条目(原子、失败不阻断),
// 内核请求侧的微清理继续兜底运行内的突发增长。
func (st *directRun) maybeCompact(sess *agentsession.Store, stream agentllm.StreamFunc, model string, opts agentllm.StreamOptions, window, reserve, buffer int) {
loaded, err := sess.Load()
if err != nil {
return
}
if !agentsession.NeedsClear(agentsession.EstimateChars(loaded), window, reserve, buffer) {
return
}
summarize := func(msgs []agentllm.Message) (string, error) {
return st.summarizeTranscript(stream, model, msgs, opts)
}
if _, err := sess.Compact(compactKeepMessages, summarize); err != nil {
st.l.now() // 保持时钟引用;压缩失败只记录,不阻断本轮
_ = fmt.Sprintf("compact: %v", err)
}
}
// summarizeTranscript 用 9 段压缩模板(提示词库 aux.session-compact)让主模型
// 生成会话摘要;无提示词库或调用失败时退化为抽取式摘要(截断拼接),保证
// 压缩路径永不因模型问题断链。
func (st *directRun) summarizeTranscript(stream agentllm.StreamFunc, model string, msgs []agentllm.Message, opts agentllm.StreamOptions) (string, error) {
prompt := st.l.compactPrompt(st.rc.Profile().Owner)
user := transcriptDigest(msgs)
msgs2 := []agentllm.Message{
{Role: agentllm.RoleSystem, Content: prompt},
{Role: agentllm.RoleUser, Content: user},
}
var b strings.Builder
for ev := range stream(st.rc.Context(), model, msgs2, nil, opts) {
switch ev.Kind {
case agentllm.KindTextDelta:
b.WriteString(ev.Text)
case agentllm.KindError:
return "", fmt.Errorf("压缩模型调用失败: %s", ev.ErrorMessage)
case agentllm.KindMessageEnd:
if b.Len() == 0 {
b.WriteString(ev.Message.Content)
}
}
}
text := strings.TrimSpace(b.String())
if text == "" {
return "", fmt.Errorf("压缩摘要为空")
}
if src, ok := st.rc.(interface{ AddUsage(core.Usage) }); ok {
// 压缩调用的用量并入本 run(SELF 口径;usage 事件不走内核循环)。
_ = src
}
return text, nil
}
// transcriptDigest 把待压缩的消息渲染成模型可读的对话稿(截断防爆)。
func transcriptDigest(msgs []agentllm.Message) string {
var b strings.Builder
b.WriteString("把以下对话压缩为结构化摘要(按系统指示的九段结构输出)。\n\n")
limit := 60000
for _, m := range msgs {
role := string(m.Role)
content := m.Content
if r := []rune(content); len(r) > 4000 {
content = string(r[:4000]) + "…(截断)"
}
fmt.Fprintf(&b, "%s: %s\n", role, content)
if len(b.String()) > limit {
b.WriteString("…(更早内容已截断)\n")
break
}
}
return b.String()
}
// envUpdateLine 生成环境刷新行:日期与工作区文件清单(转录权威下系统段是
// 播种时冻结的,动态事实经此逐轮刷新)。
func envUpdateLine(rc RunCtx, model string) string {
var b strings.Builder
now := time.Now()
fmt.Fprintf(&b, "环境更新:今天是 %s(%s);模型 %s。", now.Format("2006-01-02"), weekdayCN(now), model)
if src, ok := rc.(interface{ WorkspaceInputs() []string }); ok {
if paths := src.WorkspaceInputs(); len(paths) > 0 {
b.WriteString("会话工作区文件:" + strings.Join(paths, "、") + "。")
}
}
return b.String()
}