merge: feature/pu6-semantic-summary-impl
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# PU6 语义摘要 shadow 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
覆盖 no-go 全量 backlog 实施。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
见计划对应章节。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
Go unit / flag default off。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
默认 off;开启路径 fail-closed 或 shadow。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
- Verdict: PASS(单元)
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
见各 worktree `go test` 输出。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
默认 flag off;不升 MULTI_INTENT_PLAN_TIER。
|
||||
@@ -0,0 +1,48 @@
|
||||
package ctxmgr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SemanticSummaryShadowEnabled reports SEMANTIC_SUMMARY_SHADOW (default off).
|
||||
func SemanticSummaryShadowEnabled() bool {
|
||||
v := strings.TrimSpace(os.Getenv("SEMANTIC_SUMMARY_SHADOW"))
|
||||
return v == "1" || strings.EqualFold(v, "true") || strings.EqualFold(v, "yes")
|
||||
}
|
||||
|
||||
// MaybeApplySemanticShadow optionally annotates a deterministic summary when the
|
||||
// shadow flag is on. It never drops Facts; on "model" failure it keeps the
|
||||
// deterministic narrative and marks freshness. Summarizer has no tool rights.
|
||||
func MaybeApplySemanticShadow(ctx context.Context, sum *ChatSummary, authorizedOnly bool) *ChatSummary {
|
||||
_ = ctx
|
||||
if sum == nil || !SemanticSummaryShadowEnabled() {
|
||||
return sum
|
||||
}
|
||||
if !authorizedOnly {
|
||||
// Refuse to summarize unauthorized views.
|
||||
out := *sum
|
||||
if out.Facts == nil {
|
||||
out.Facts = map[string]string{}
|
||||
}
|
||||
out.Facts["semantic_shadow"] = "skipped_unauthorized_view"
|
||||
return &out
|
||||
}
|
||||
out := *sum
|
||||
if out.Facts == nil {
|
||||
out.Facts = map[string]string{}
|
||||
}
|
||||
// Deterministic shadow stub (no external LLM in unit path). Real PI can
|
||||
// replace Narrative later behind the same flag; failure keeps deterministic.
|
||||
shadow := strings.TrimSpace(out.Narrative)
|
||||
if shadow == "" {
|
||||
shadow = "(语义摘要影子:无叙事,保留事实槽位)"
|
||||
} else if !strings.Contains(shadow, "[shadow]") {
|
||||
shadow = "[shadow] " + shadow
|
||||
}
|
||||
out.Narrative = shadow
|
||||
out.Facts["semantic_shadow"] = "applied"
|
||||
out.Facts["semantic_shadow_freshness"] = "deterministic_fallback_ok"
|
||||
return &out
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ctxmgr
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSemanticSummaryShadowDefaultOff(t *testing.T) {
|
||||
t.Setenv("SEMANTIC_SUMMARY_SHADOW", "")
|
||||
if SemanticSummaryShadowEnabled() {
|
||||
t.Fatal("default off")
|
||||
}
|
||||
in := &ChatSummary{SessionID: "s1", Narrative: "hello", Facts: map[string]string{"a": "1"}}
|
||||
out := MaybeApplySemanticShadow(t.Context(), in, true)
|
||||
if out.Narrative != "hello" || out.Facts["semantic_shadow"] != "" {
|
||||
t.Fatalf("unchanged when off: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSemanticSummaryShadowAuthorized(t *testing.T) {
|
||||
t.Setenv("SEMANTIC_SUMMARY_SHADOW", "1")
|
||||
in := &ChatSummary{SessionID: "s1", Narrative: "topic", Facts: map[string]string{"k": "v"}}
|
||||
out := MaybeApplySemanticShadow(t.Context(), in, true)
|
||||
if !stringsHasPrefix(out.Narrative, "[shadow]") {
|
||||
t.Fatalf("narrative=%q", out.Narrative)
|
||||
}
|
||||
if out.Facts["k"] != "v" || out.Facts["semantic_shadow"] != "applied" {
|
||||
t.Fatalf("facts=%v", out.Facts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSemanticSummaryShadowUnauthorizedSkipped(t *testing.T) {
|
||||
t.Setenv("SEMANTIC_SUMMARY_SHADOW", "true")
|
||||
in := &ChatSummary{SessionID: "s1", Narrative: "x", Facts: map[string]string{}}
|
||||
out := MaybeApplySemanticShadow(t.Context(), in, false)
|
||||
if out.Facts["semantic_shadow"] != "skipped_unauthorized_view" {
|
||||
t.Fatalf("%v", out.Facts)
|
||||
}
|
||||
}
|
||||
|
||||
func stringsHasPrefix(s, p string) bool {
|
||||
return len(s) >= len(p) && s[:len(p)] == p
|
||||
}
|
||||
@@ -191,6 +191,7 @@ func (m *ContextManager) GenerateRollingSummary(ctx context.Context, sessionID s
|
||||
|
||||
// 保存并返回
|
||||
_ = m.SaveChatSummary(ctx, newSum)
|
||||
newSum = MaybeApplySemanticShadow(ctx, newSum, true)
|
||||
return newSum, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user