## 两件事,同一页 接手 persona 这条线时对着 `main` 实测出来的两个缺陷。都不是「功能没做」,是**做出来的那半与宣称的语义不一致**。 ### 一、`default` 人格卡会无条件进所有没建卡的身份(包括宿主里跑的外部 bot) `src/user-settings.js` 之前的寻址(原住在 `src/settings.js` 的 `getPersonaFor`)是: - 身份命中 `byAgent` 卡且卡开着 → 用这张卡; - **身份没有卡、或有卡但卡被关掉 → 一律落 `default` 卡**。 第二条让两件事失效: 1. `default` 卡开着时,宿主里任何自带身份与设定的 bot(DS Bot 那类)每一帧都会收到一份「你的人格名字:…」,与它自己的身份声明撞在同一轮里。而 `agent_scope` 是**用户可给的参数**,不是可信边界,挡不住。 2. 「把这张卡关掉」在界面上有开关、实际不生效——关掉的卡仍然通过 `default` 拿到一份人格。 面板上那句 `未启用——这个身份的会话将使用默认卡` 说明这不是不知道的行为,是当时定的口径;问题是这个口径把「解析不到身份」与「有身份但没配置」当成同一件事。 ### 二、面板「Agent 眼中的你(注入生效文本)」不是生效文本 `lib/client.js` 里那段是**前端自己拼的**:只有 `profile` 与 `rules` 两条列表,没有 `[用户设置]` 表头、没有 `- 用户画像:` 前缀、**完全没有人格三行**(注释自己写着「纯前端拼接…零后端新请求」)。而真正的渲染在注入器里,两处各写各的,标题却声称是同一件事的读数——这类「两张皮」在本仓是被反复钉过的(#411 的回收口径、#179 的注入预览)。 后果很具体:用户开着人格卡,看预览说「没有人格」,就会去怀疑注入没生效;反过来预览里也不会暴露转义后的真实形状。 ## 修法(一条 PR) 1. 渲染整体搬到 `src/user-settings.js`:`escapePromptVars` / `resolvePersonaCard` / `renderUserSettingsBlock`,注入器与预览调同一份;`src/inject.js` 的 `renderUserSettings` 只剩一行委托,同名转义闭包改为委托(其余调用点不动)。 2. 新增 opt-in 键 `personaStrictAddressing`(默认 `false` = 历史行为逐字节不变):打开后「身份没有卡」与「卡被显式关掉」都不注入人格,`default` 退回它的本意——只兜**解析不到会话身份**的无名会话。`settings.js` 白名单成对注册,不进 `LIGHT_MODE_OFF`(纯判定分支)。 3. `GET /api/dsh-mneme/persona` 增 `preview` 字段(`text` / `source` / `agent` / `configGate`),支持 `?agent=<身份>` 逐张卡预览;面板只显示后端给的文本,读失败时 fail-closed 显示「未知」,绝不回退到前端拼的版本。 4. 删掉 `settings.getPersonaFor`(改完后生产代码零调用点),寻址只留一处;原锁它的两条用例改指 `resolvePersonaCard`,并补一条 strict 用例。 ## 明确不在这里做 - **画像/规则没有独立开关**:它们比 persona 早,`personaEnabled` 只管人格行。给它们加闸是行为变更,影响所有用户,另议。 - **「从记忆里提取画像/人格」**:本条提出时确实不存在(无路由、无按钮、无 LLM 调用点),所以列在了「不做」里;维护者随后指定要做,**已作为 PR #498 的第三段落地**:新键 `profileExtractEnabled|默认关` + `POST /api/dsh-mneme/profile/extract`。形态上有两条硬边界:**只出草稿**(模块连 settings 都不注入,采用是面板上另一次显式 PUT),以及**空库与解析失败都报错**、绝不回一张空草稿冒充「这次没提炼出东西」。 - **人格卡单选互斥**:运行时每轮本来只会用一张卡;把存储层做成互斥会牺牲 v1.2 多卡(不同 agent 用不同卡)的意义,等 strict 口径上线后看是否还需要。 ## 验证口径 回归锁:`test/persona-addressing.test.js`(strict 四例 + 一条现状锁)、`test/user-settings-preview.test.js`(预览与注入器逐字节同源 + 寻址读数 + 总闸关闭如实说)、`test/persona-settings.test.js`(寻址唯一实现)、`test/client.test.js`(预览必须由后端给、前端拼接形态不许回来、双语齐全)。变异自证两条:摘掉 strict 分支 → 4 条红;预览忽略 `agent` 参数 → 3 条红(含逐字节同源那条)。
Activity
modusensus commentedon Oct 21, 2026
PR #498 落地了本条描述的两处修补(严格寻址 + 预览同源)。有一条已知边界留在这里,不藏在代码注释里:
严格寻址的判据是会话身份标签(
session.header.agentPreset)。如果一个外部 bot 与用户自己的会话共用同一个 preset 标签,那么这一格只能做到「没建卡的身份不注入」;而一旦为该标签建了卡(用户想让日常会话有人格),同一标签下的 bot 会话也会一起拿到那张卡。要把这两者分开,需要的是按 workspace 维度的注入排除,它会把政策轴的语义牵进来——而 #471 的
scopeState目前只管记忆行的可见性,不管user_settings(画像/规则/人格)。那是另一个议题,不在 #498 里顺手做。另外两点与「人格为什么看着没起作用」有关,属于使用面而不是缺陷:
personaEnabled管辖。如果画像正文里写了身份句(例如「你是某某」),它在每个会话每一帧都注入,与人格卡的开关、与严格寻址都无关;要按身份分持人格,这类句子应该待在卡片字段里。minimal预设的会话由宿主整体压制注入(runtime contexts (ctx.systemPrompt.context) never reach the model — dsh 0.1.5-rc.1 #175 的定论,面板状态页有提示),那种会话里人格、画像、记忆都不会出现,与寻址口径无关。fix(persona): drop stale preview responses and label the fallback hon…
feat(profile): draft the user profile from memory on demand (#497)
fix(profile): verify saves, name the missing-model case, and correct …
fix(persona)+feat(profile): 收回 default 卡兜底、生效文本单源渲染,并新增画像提取草稿(#497) (#…