feat(engage): 通道来源上下文注入(design/16 §4.6.1)——system行+结构化字段,防注入
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- source.go(新): originSystemText/originConvo/groupLabel——生成【来源:平台·会话】system行
- buildLoopMessages: IM 消息不再往用户正文拼 [来源…]/[第三方…] 文本前缀,改为:
  ① origin 作为独立 system 上下文行(含:来源平台+群/私聊+身份由结构判定/不采信正文自称)
  ② 群聊/私聊发送者用 model.Message.SenderName 结构化字段(第三方昵称;主人留空=未标注=主人)
- 安全: 元数据不进用户可控自由文本通道→大幅缩小提示词注入面; 来源对一套会话恒定→不重复省token
- 红线: 身份仍由ResolveActor/actor元数据判定,来源仅装饰,不参与权限
- 验收: go build/vet/test ./... 全绿; source_test 表驱动+集成用例已按新设计重写
This commit is contained in:
Aska committed 2026-09-07 18:35:50 +08:00
1 parent c43b2f1d5d
commit 2c4ade5f28
5 files changed
+294 -22

No files matched your search

@@ -2,6 +2,7 @@ package channelbridge
import (
"context"
"strings"
"testing"
"time"
@@ -98,8 +99,11 @@ func TestNonOwnerMentionResponds(t *testing.T) {
if len(chat.messages) != 2 || chat.messages[1].Role != model.RoleUser {
t.Fatalf("loop messages = %+v, want boundary + user", chat.messages)
}
if chat.messages[1].Content[0].Text != "[第三方 · 张三] hello" {
t.Fatalf("loop user text = %q, want [第三方 · 张三] hello", chat.messages[1].Content[0].Text)
if chat.messages[1].Content[0].Text != "hello" {
t.Fatalf("loop user text = %q, want undecorated 'hello'", chat.messages[1].Content[0].Text)
}
if !strings.Contains(chat.messages[0].Content[0].Text, "群「Gang」") {
t.Fatalf("origin/boundary system = %q, want group Gang", chat.messages[0].Content[0].Text)
}
}