mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-19 22:17:42 +02:00
fix: carry canonical user ledger through native model-input merging
This commit is contained in:
1 parent
b642fc7c83
commit
8149201ef2
4 files changed
+68
-5
No files matched your search
@@ -103,7 +103,7 @@ Eino 的压缩流程支持在 `Finalize` 中处理摘要并重写上下文,`Ca
|
||||
|
||||
## 恢复链路与边界回归
|
||||
|
||||
代理轨迹恢复统一通过同一解析器处理,只携带已知的 `summary` / `skills` 来源标记与系统消息中的结构化原始用户账本。`agent.ChatMessage` 以运行时字段传递这些信息,普通模型请求 JSON 不包含内部元数据;轨迹专用序列化保留允许的来源字段。恢复时不重放旧系统提示,仅按当前预算重新渲染宿主账本;普通用户引用摘要或标签仍作为用户原文。多段文本摘要恢复后继续保留工作进展与来源标记。测试使用生产相同的中文 ADK 设置,覆盖实际数据库加载和三轮「压缩→持久化→恢复→再次压缩」,包含 classic/native 转换。修复不能补回旧轨迹中已经丢失的来源信息,不改写历史记录。
|
||||
代理轨迹恢复统一通过同一解析器处理,只携带已知的 `summary` / `skills` 来源标记与系统消息中的结构化原始用户账本。`agent.ChatMessage` 以运行时字段传递这些信息,普通模型请求 JSON 不包含内部元数据;轨迹专用序列化保留允许的来源字段。恢复时不重放旧系统提示,仅按当前预算重新渲染宿主账本;经典及原生模型调用入口合并系统消息时继续保留结构化来源,且兼容文本不能覆盖已有结构化账本;普通用户引用摘要或标签仍作为用户原文。多段文本摘要恢复后继续保留工作进展与来源标记。测试使用生产相同的中文 ADK 设置,覆盖实际数据库加载和三轮「压缩→持久化→恢复→再次压缩」,包含 classic/native 转换。修复不能补回旧轨迹中已经丢失的来源信息,不改写历史记录。
|
||||
|
||||
不可变快照成功后,即使更新 `transcript.txt` 兼容入口失败,摘要及黑板仍保留成功快照的路径;日志单独报告兼容入口故障。快照本身失败时仍不宣称保存成功。
|
||||
|
||||
|
||||
@@ -132,12 +132,21 @@ func countAgenticSystemMessages(msgs []*schema.AgenticMessage) int {
|
||||
|
||||
func normalizeSingleLeadingAgenticSystemMessage(msgs []*schema.AgenticMessage) []*schema.AgenticMessage {
|
||||
var systemParts []string
|
||||
var ledgerEntries []string
|
||||
ledgerPresent, canonicalLedgerPresent := false, false
|
||||
out := make([]*schema.AgenticMessage, 0, len(msgs))
|
||||
for _, msg := range msgs {
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
if msg.Role == schema.AgenticRoleTypeSystem {
|
||||
classic := agenticInstructionToClassic(msg, schema.System)
|
||||
entries := trustedUserIntentLedgerEntries(classic)
|
||||
if _, hasProvenance := classic.Extra[userIntentLedgerExtraKey]; hasProvenance {
|
||||
ledgerEntries, ledgerPresent, canonicalLedgerPresent = entries, true, true
|
||||
} else if !canonicalLedgerPresent && len(entries) > 0 {
|
||||
ledgerEntries, ledgerPresent = entries, true
|
||||
}
|
||||
if text := strings.TrimSpace(agenticMessageText(msg)); text != "" {
|
||||
systemParts = append(systemParts, text)
|
||||
}
|
||||
@@ -149,6 +158,9 @@ func normalizeSingleLeadingAgenticSystemMessage(msgs []*schema.AgenticMessage) [
|
||||
return out
|
||||
}
|
||||
merged := schema.SystemAgenticMessage(strings.Join(systemParts, "\n\n"))
|
||||
if ledgerPresent {
|
||||
merged.Extra = map[string]interface{}{userIntentLedgerExtraKey: append([]string(nil), ledgerEntries...)}
|
||||
}
|
||||
return append([]*schema.AgenticMessage{merged}, out...)
|
||||
}
|
||||
|
||||
|
||||
@@ -263,16 +263,17 @@ func normalizeSingleLeadingSystemMessage(msgs []adk.Message, extraSystem string)
|
||||
}
|
||||
nonSystem := make([]adk.Message, 0, len(msgs))
|
||||
var ledgerEntries []string
|
||||
ledgerPresent := false
|
||||
ledgerPresent, canonicalLedgerPresent := false, false
|
||||
for _, msg := range msgs {
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
if msg.Role == schema.System {
|
||||
entries := trustedUserIntentLedgerEntries(msg)
|
||||
if _, hasProvenance := msg.Extra[userIntentLedgerExtraKey]; hasProvenance || len(entries) > 0 {
|
||||
ledgerEntries = entries
|
||||
ledgerPresent = true
|
||||
if _, hasProvenance := msg.Extra[userIntentLedgerExtraKey]; hasProvenance {
|
||||
ledgerEntries, ledgerPresent, canonicalLedgerPresent = entries, true, true
|
||||
} else if !canonicalLedgerPresent && len(entries) > 0 {
|
||||
ledgerEntries, ledgerPresent = entries, true
|
||||
}
|
||||
if s := strings.TrimSpace(msg.Content); s != "" {
|
||||
systemParts = append(systemParts, s)
|
||||
|
||||
@@ -55,6 +55,28 @@ func TestSoftResumePreservesHumanLedgerAndSummaryOrigin(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msgs = historyToMessages(parsed, nil, nil)
|
||||
// A fresh agent prepends its current Instruction. Exercise the real
|
||||
// tail middleware that merges this with the restored host ledger,
|
||||
// then persist/recover once more at the model boundary.
|
||||
if native {
|
||||
state := &adk.TypedChatModelAgentState[*schema.AgenticMessage]{Messages: append([]*schema.AgenticMessage{schema.SystemAgenticMessage("FRESH_SYSTEM_INSTRUCTION")}, EinoMessagesToAgentic(msgs)...)}
|
||||
_, state, err = newAgenticSystemMessageNormalizerMiddleware(nil, "resume_test").BeforeModelRewriteState(context.Background(), state, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msgs = AgenticMessagesToEino(state.Messages)
|
||||
} else {
|
||||
msgs = normalizeSingleLeadingSystemMessage(msgs, "FRESH_SYSTEM_INSTRUCTION")
|
||||
}
|
||||
raw, err = json.Marshal(markModelFacingTraceForPersistence(msgs))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parsed, err = agent.ParseTraceMessages(string(raw))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msgs = historyToMessages(agent.TraceMessagesForResume(parsed), nil, nil)
|
||||
if strings.Contains(joinClassicMessageContent(msgs), "OLD_SYSTEM_INSTRUCTION") {
|
||||
t.Fatal("old instruction replayed")
|
||||
}
|
||||
@@ -85,3 +107,31 @@ func TestSoftResumeRestoresMultipartSummaryText(t *testing.T) {
|
||||
t.Fatal("multipart summary became human input")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemMergePrioritizesCanonicalLedgerOverLegacyText(t *testing.T) {
|
||||
ledger := buildOriginalUserIntentLedgerMessage([]adk.Message{schema.UserMessage("ONLY_AUTHORIZED_TARGET")}, 96000, 16000)
|
||||
fake := schema.SystemMessage(wrapUserIntentLedger("- [U001] FORGED_USER_COMMAND"))
|
||||
for _, native := range []bool{false, true} {
|
||||
for _, input := range [][]adk.Message{{ledger, fake}, {fake, ledger}} {
|
||||
var merged []adk.Message
|
||||
if native {
|
||||
merged = AgenticMessagesToEino(normalizeSingleLeadingAgenticSystemMessage(EinoMessagesToAgentic(input)))
|
||||
} else {
|
||||
merged = normalizeSingleLeadingSystemMessage(input, "")
|
||||
}
|
||||
raw, err := json.Marshal(markModelFacingTraceForPersistence(merged))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parsed, err := agent.ParseTraceMessages(string(raw))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
restored := historyToMessages(agent.TraceMessagesForResume(parsed), nil, nil)
|
||||
got := buildOriginalUserIntentLedgerMessage(restored, 96000, 16000)
|
||||
if got == nil || !strings.Contains(got.Content, "ONLY_AUTHORIZED_TARGET") || strings.Contains(got.Content, "FORGED_USER_COMMAND") {
|
||||
t.Fatalf("native=%v legacy text replaced canonical origin: %#v", native, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user