Files
AskaEth da3a386daf
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(cyrene,config,loop): 护栏/压缩/身份知识三条落地(P0-5/P0-10/P0-11)+ 身份注入收窄到主人会话
- P0-5 护栏不再是空壳:挂 WithSensitiveInfo(阻断) + 长度上限(输入 262144 / 输出 65536 字符,依据=
  组装完成的请求体量级与「不误伤正常对话」) + 关键词**仅在配置时**挂;正常对话有用例证明放行
- P0-11 上下文压缩默认启用:CONTEXT_MAX_TOKENS 默认 32768(=loop.DefaultMaxContextTokens,两常量由测试锁死),
  0/负 = 关闭(行为退回接线前);长对话真压缩且摘要消息进请求首位、最近一轮保留
- P0-10 身份说明书(owner.yaml identityFile)注入人格上下文,并且**收窄为仅主人会话**:
  · 新增最小钩子 loop.Options.PersonaKnowledgeFor(env):**按该轮会话环境**解析(不看提示词、不看会话 id),
    块只进每轮视图、**不进 messages**(不进 checkpoint、不进记忆提取面)
  · 开关 OWNER_IDENTITY_INJECTION:owner_only(默认) / always(仅自测) / off;非法值与读不动 → WARN + 回退默认(不静默)
  · **顺手关掉第二条同型泄漏**:channelbridge 群聊参与度判定器的 PersonaPrompt 此前带身份正文 ⇒ 现改为纯人格指令
  · 回归:缺文件/目录/未配置逐字节等于旧输出;静态指令与角色池 Instructions() 全不含正文;
    文件里点名的陌生账号仍 IsOwner==false(身份知识绝不参与权限判定)
- 复核:build/vet 绿 + cmd/config/loop 包测试绿;四类断言(主人含 / **群聊与陌生人不含** / always·off 两档 /
  回归不变)+ 4 条变异反证(含把判定改成恒真 → 提示词里真的出现身份说明书)均已还原
- 未做(登记):knowledge 块启动时读一次(改文件需重启);`.env.example` 的 OWNER_IDENTITY_INJECTION 行随后补;
  `internal/user/owner.go` 注释与 design/01 §2.2、design/11 §0 待同步
2026-10-04 14:30:26 +08:00

67 lines
2.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"fmt"
"strings"
)
// OwnerIdentityInjectionEnv 是「主人身份说明书注入范围」开关的环境变量名
// (design/11 §0 红线;见 cmd/cyrene/persona_identity.go 的消费点)。
const OwnerIdentityInjectionEnv = "OWNER_IDENTITY_INJECTION"
// 注入范围的三档取值。默认(也是唯一安全的一档)是 OwnerIdentityOwnerOnly:
// 只有主人会话能拿到那段知识,群聊与陌生人私聊的上下文里根本不出现。
const (
// OwnerIdentityOwnerOnly 只在主人会话注入(默认)。
OwnerIdentityOwnerOnly = "owner_only"
// OwnerIdentityAlways 所有会话都注入,含群聊与陌生人私聊。
// 它显式开启红线例外,只应在本地自测/排障时短暂使用。
OwnerIdentityAlways = "always"
// OwnerIdentityOff 完全关闭注入:连主人会话也没有这段知识。
OwnerIdentityOff = "off"
)
// LoadOwnerIdentityInjection 读取注入范围开关,优先级与其余配置完全一致:
// 进程环境 > .env > 默认 owner_only。返回值已去空白(空串按默认处理),
// 由调用方用 ParseOwnerIdentityInjection 判定合法性 —— 非法值必须被显式
// 报告(记日志 + 回退默认),绝不静默生效或静默失效。
//
// 为什么需要这个读取函数,而不是给 Config 加一个字段:Config 的反射解码器
// 只做标量赋值,承载不了「三档 + 非法值判定」的语义;而直接 os.Getenv 会
// 让只写在 .env 里的声明被静默忽略(.env 由 parseDotEnv 读进独立 map,
// 并不会写回进程环境)。这里复用同一套优先级与同一份 .env 解析,因此两种
// 写法都真实生效。envPath 为空表示不看 .env(与 Load 的约定一致)。
func LoadOwnerIdentityInjection(envPath string) (string, error) {
fromEnv := map[string]string{}
if envPath != "" {
m, err := parseDotEnv(envPath)
if err != nil {
return "", fmt.Errorf("config: read %s: %w", envPath, err)
}
fromEnv = m
}
raw := strings.TrimSpace(lookupRaw(OwnerIdentityInjectionEnv, OwnerIdentityOwnerOnly, fromEnv))
if raw == "" {
raw = OwnerIdentityOwnerOnly
}
return raw, nil
}
// ParseOwnerIdentityInjection 把开关取值判定成三档之一(大小写不敏感,
// 允许首尾空白)。空串按默认 owner_only 处理;其余无法识别的取值返回错误,
// 由调用方明确回退到 owner_only 并把非法值记进日志。
func ParseOwnerIdentityInjection(raw string) (string, error) {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "", OwnerIdentityOwnerOnly:
return OwnerIdentityOwnerOnly, nil
case OwnerIdentityAlways:
return OwnerIdentityAlways, nil
case OwnerIdentityOff:
return OwnerIdentityOff, nil
default:
return OwnerIdentityOwnerOnly, fmt.Errorf(
"config: %s=%q is not one of %s|%s|%s",
OwnerIdentityInjectionEnv, raw, OwnerIdentityOwnerOnly, OwnerIdentityAlways, OwnerIdentityOff)
}
}