feat(channels): 受限车道回合补自身会话历史(群聊/陌生人能连贯理解一段对话;复用共享 transcript,拒绝主会话 id)
This commit is contained in:
1 parent
be6e5f5780
commit
89715d1813
3 files changed
+281
-41
No files matched your search
@@ -4,23 +4,31 @@ import (
|
||||
"context"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/session"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/transcript"
|
||||
)
|
||||
|
||||
// 本文件让 IM 的**主人私聊**回合与 Web 主会话回合的环境逐项一致(主人 2026-10-05 要求:
|
||||
// 处理其他平台的主人消息时和主会话环境完全一致)。
|
||||
// 本文件让 IM 回合带上**该会话自己**最近的对话历史,使她看得懂一段连贯的对话,而不是只
|
||||
// 听得到当次那一条消息(主人 2026-10-05 反馈:「只有 @ 才有反应、不 @ 就不理解意图」——
|
||||
// 此前群里的回合只有「来源系统行 + 当次消息」,前几句她根本没看见)。
|
||||
//
|
||||
// 三条环境在 loop.SessionEnv 上早已一致(ActorEnv=owner、MemWritable=true、PersonaID 取
|
||||
// 主会话绑定,design/22 §7-3),缺的是**历史转录**:Web 主会话回合由编排器播种该会话最近
|
||||
// N 条转录(gateway/session_transcript.go),而 IM 回合此前只有「来源系统行 + 当次消息」。
|
||||
// 于是消息明明落在同一条会话流里,模型看到的历史却不同 —— 她像是第一次见到主人。
|
||||
// 会话环境在 loop.SessionEnv 上早已按车道解析(ActorEnv/MemWritable/PersonaID,
|
||||
// design/22 §7-3/§7-4),缺的一直是**历史转录**:Web 主会话回合由编排器播种该会话最近 N 条
|
||||
// 转录(gateway/session_transcript.go),而 IM 回合此前没有。
|
||||
//
|
||||
// 本文件补上这一半:主人私聊回合在装配完本回合自己的消息之后、交给循环之前,前置**主会话**
|
||||
// 的最近 N 条转录。重建逻辑与条数上限都取自 internal/runtime/transcript —— 与网关同一份
|
||||
// 实现、同一个常量,绝不另写第二套(两份映射必然漂移)。受限车道(群聊/陌生人)**绝不注入**:
|
||||
// 那是隔离会话,只看得到自己会话里的当次消息,主人的主会话历史不得外泄(design/16 §4.6)。
|
||||
// - **主人私聊**:IM 回合镜像进主会话,因此播种**主会话**(`web`)的转录,与 Web 主会话
|
||||
// 看到的历史逐条一致 —— 她不会像第一次见到主人(主人 2026-10-05 要求两条车道环境一致)。
|
||||
// - **群聊 / 陌生人(受限车道)**:播种**该隔离会话自己**(`sess-*`)的最近转录,因此群里
|
||||
// 前几句、以及被最小打扰静默掉而只登记不回的那几条(light.go 的 registerSilenced 已把它们
|
||||
// 落进同一会话)都在她的上下文里,她据此才能理解一段连贯对话的意图。
|
||||
//
|
||||
// 两条车道读的会话不同,重建逻辑与条数上限却是同一份:internal/runtime/transcript
|
||||
// (ContextCount / Read / Message)—— 与网关同一份实现、同一个常量,绝不另写第二套
|
||||
// (两份映射必然漂移)。**受限车道结构上够不到主会话**:目标会话由 inboundTargetFor 结构化
|
||||
// 决定,受限车道拿到的是 `sess-*` 隔离会话,主人的主会话历史一个字都不外泄
|
||||
// (design/16 §4.6 §3、design/02 §3)。
|
||||
|
||||
// turnEnv 返回一条入站消息本轮运行所用的 loop 会话环境。
|
||||
//
|
||||
@@ -37,18 +45,24 @@ func turnEnv(sess session.Session, target inboundTarget, personaID string) loop.
|
||||
return loop.SessionEnv{ActorEnv: sess.Env(), MemWritable: sess.MemWritable, PersonaID: personaID}
|
||||
}
|
||||
|
||||
// seedTurnTranscript 在主人私聊车道上把**主会话**最近的转录置于本回合模型可见消息之前
|
||||
// (与网关编排器的 startTurnMessages 同序:转录在前、本次消息在后),使 IM 回合与 Web 主
|
||||
// 会话看到同一份对话历史。受限(群聊/陌生人)车道原样返回 —— 隔离上下文绝不注入主人的
|
||||
// 主会话历史。
|
||||
// seedTurnTranscript 在本回合模型可见消息之前前置**该会话自己**最近的转录
|
||||
// (与网关编排器的 startTurnMessages 同序:转录在前、本次消息在后):
|
||||
//
|
||||
// - 主人私聊 → 主会话(`web`)的转录,与 Web 主会话逐条一致;
|
||||
// - 群聊 / 陌生人 → 该隔离会话(`sess-*`)的转录,含被静默登记的群成员发言。
|
||||
//
|
||||
// 时刻与网关一致:调用点在本回合用户消息落库**之前**(runInboundTurn 里装配先于
|
||||
// persistToMain),因此当次消息绝不会在上下文里出现两次。
|
||||
// persistToMain / persistIsolated),因此当次消息绝不会在上下文里出现两次。
|
||||
//
|
||||
// 转录读不出来(未接历史存储、读失败、空会话)时同样原样返回,这正是既有行为:主人私聊
|
||||
// 回合退化为「来源行 + 当次消息」,而不是让回合失败。
|
||||
// 转录读不出来(未接历史存储、读失败、空会话)时原样返回,这正是既有行为:回合退化为
|
||||
// 「来源行 + 当次消息」,而不是让回合失败。
|
||||
//
|
||||
// 受限车道另设一道显式守卫:目标会话是主会话 id 时宁可不播种。这在生产里结构上不可能
|
||||
// (inboundTargetFor 只在主人私聊时给出 MainSessionID),但上下文隔离是安全红线 —— 万一
|
||||
// 将来多出别的构造点写错,也绝不能把主人的主会话历史灌进群聊/陌生人上下文
|
||||
// (design/02 §3、design/16 §4.6 §3)。
|
||||
func (b *Bridge) seedTurnTranscript(ctx context.Context, target inboundTarget, msgs []model.Message) []model.Message {
|
||||
if !target.private {
|
||||
if !target.private && target.sessionID == history.MainSessionID {
|
||||
return msgs
|
||||
}
|
||||
seed := transcript.Read(ctx, b.history, target.sessionID)
|
||||
|
||||
Reference in new issue
Block a user