主人 2026-10-03 拍板「注释一律中文」;本模块按长期分批协议(一个模块一次派工)执行。 注释行数 1329 → 1329 逐行 1:1(含中文 33 → 1304);行尾注释 5 条一并译。 校验:机械校验(非注释改动行)= 空;独立 Go AST 程序(剥注释复渲)证明 44/44 文件代码全等; 全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。 gofmt:capacity_loop_test.go / flow.go 改动前即未格式化,按铁律保留原样未动。
97 lines
3.7 KiB
Go
97 lines
3.7 KiB
Go
package loop
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/restrict"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/capacity"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/memory"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||
)
|
||
|
||
// seedL4Habit 写入一条规范的 intent record 记忆(由
|
||
// capacity.RecordIntent 产出的闭环回写格式),记录 "搞一下这个部署" 是
|
||
// persona-a 的任务。
|
||
func seedL4Habit(t *testing.T, store *memory.Store) {
|
||
t.Helper()
|
||
ctx := context.Background()
|
||
if _, err := store.Add(ctx, memory.MemoryItem{
|
||
PersonaID: "persona-a",
|
||
Scope: memory.ScopeUser,
|
||
Kind: memory.KindRelationship,
|
||
Content: "intent:isTask=true;taskKind=ops;confidence=0.85;text=搞一下这个部署",
|
||
}); err != nil {
|
||
t.Fatalf("seed L4 habit memory: %v", err)
|
||
}
|
||
}
|
||
|
||
// TestLoop_Capacity_L4_WakesOnLearnedHabit_OwnerEnv 证明 L4 输入:在
|
||
// 检索到记录该短语是任务的用户习惯后,一个原本未决的
|
||
// turn 在 owner 全权限环境中唤醒,且模型被广告 awake 工具集。
|
||
func TestLoop_Capacity_L4_WakesOnLearnedHabit_OwnerEnv(t *testing.T) {
|
||
t.Parallel()
|
||
store := newTestMemoryStore(t)
|
||
seedL4Habit(t, store)
|
||
td := capacityToolSet(t)
|
||
ad := &mockAdapter{stream: []mgResp{{resp: model.Response{Content: "好的"}}}}
|
||
a := newTestAgent(t, ad, td)
|
||
dec := capacity.New(capacity.Config{Enabled: true, MinConfidence: 0.6})
|
||
l := New(Options{
|
||
Agent: a,
|
||
MaxRounds: 8,
|
||
OnStream: func(model.StreamEvent) {},
|
||
Capacity: dec,
|
||
Retriever: memory.NewRetriever(store),
|
||
PersonaID: "persona-a",
|
||
})
|
||
ctx := WithSessionEnv(context.Background(), OwnerEnv())
|
||
if _, err := l.Run(ctx, "sess", []model.Message{model.TextMessage(model.RoleUser, "搞一下那个部署")}); err != nil {
|
||
t.Fatalf("Run: %v", err)
|
||
}
|
||
if len(ad.streamReqs) == 0 {
|
||
t.Fatal("no streaming request captured")
|
||
}
|
||
set := toolSet(ad.streamReqs[0].Tools)
|
||
// L4 读到了学到的习惯 → 唤醒 → 广告出 web-search(awake 集)。
|
||
if !hasAll(set, "terminal", "file", "web-search") {
|
||
t.Fatalf("owner L4 tools = %v, want awake set incl web-search", toolNames(ad.streamReqs[0].Tools))
|
||
}
|
||
}
|
||
|
||
// TestLoop_Capacity_L4_RestrictedEnv_NoOwnerHabitRead 证明 L4 隔离红线:
|
||
// 在群聊/陌生人上下文中循环不传任何召回,因此判定器回退到
|
||
// L1/L2,主人的习惯记忆绝不被用来唤醒意图。
|
||
func TestLoop_Capacity_L4_RestrictedEnv_NoOwnerHabitRead(t *testing.T) {
|
||
t.Parallel()
|
||
store := newTestMemoryStore(t)
|
||
seedL4Habit(t, store)
|
||
td := capacityToolSet(t)
|
||
ad := &mockAdapter{stream: []mgResp{{resp: model.Response{Content: "好的"}}}}
|
||
a := newTestAgent(t, ad, td)
|
||
dec := capacity.New(capacity.Config{Enabled: true, MinConfidence: 0.6})
|
||
l := New(Options{
|
||
Agent: a,
|
||
MaxRounds: 8,
|
||
OnStream: func(model.StreamEvent) {},
|
||
Capacity: dec,
|
||
Retriever: memory.NewRetriever(store),
|
||
PersonaID: "persona-a",
|
||
})
|
||
ctx := WithSessionEnv(context.Background(), SessionEnv{ActorEnv: restrict.ActorGroupMember, MemWritable: false})
|
||
if _, err := l.Run(ctx, "sess", []model.Message{model.TextMessage(model.RoleUser, "搞一下那个部署")}); err != nil {
|
||
t.Fatalf("Run: %v", err)
|
||
}
|
||
if len(ad.streamReqs) == 0 {
|
||
t.Fatal("no streaming request captured")
|
||
}
|
||
set := toolSet(ad.streamReqs[0].Tools)
|
||
// 受限环境 → L4 静默 → 该 turn 保持未决,且 design/11 §3
|
||
// 安全门隐藏所有未标记 AllowInGroup 的工具(terminal/file
|
||
// 未标记)。因此模型什么也看不到:web-search 缺席(主人的习惯
|
||
// 未被读取),连 quiet 集工具也对群聊会话关闭。
|
||
if len(set) != 0 {
|
||
t.Fatalf("restricted tools = %v, want none (security gate closes unmarked tools)", toolNames(ad.streamReqs[0].Tools))
|
||
}
|
||
}
|