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
111 lines
4.1 KiB
Go
111 lines
4.1 KiB
Go
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()
|
||
}
|