package loop import ( "context" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model" ) // 本文件拥有循环的 turn 边界:一个用户 turn 落定之处 // (Run 产出了 Result),以及紧随人格的状态 —— // 情绪与好感度,design/01 §2 —— 在循环之外推进。循环 // 本身保持不感知人格与情绪(design/16 §3.3:引擎 // 生成文本;状态与持久化属于其外围分层),因此它 // 只上报该 turn 并记录钩子回传的情绪标签。 // TurnEnd 向循环的 OnTurnEnd 钩子描述一个已落定的 turn。它携带 // 观察者所需的全部信息,而循环无需知道观察者做什么。 type TurnEnd struct { // SessionID 是该 turn 所属的会话。 SessionID string // PersonaID 是该 turn 发言所作为的角色:调用方提供了会话环境 // 解析出的角色时用它,否则用循环配置的默认角色 // (design/22 §4.2)。它使观察者保持人格作用域(design/01 §2):该角色 // 的情绪机与好感度 —— 且仅该角色 —— 推进。 PersonaID string // UserText 是该 turn 最新的用户消息(该 turn 未携带用户文本时 // 为 "",例如恢复的工具轮)。 UserText string // AssistantText 是该 turn 最终的 assistant 文本。 AssistantText string // StopReason 归类该 turn 如何结束。 StopReason string // Steps 是该 turn 经过的模型步数。 Steps int // Env 是会话解析出的隔离环境(owner / group / // stranger),使钩子能让受限会话保持只读。 Env SessionEnv } // TurnEndHook 观察一个已落定的 turn 并返回该 turn 的人格情绪标签 // (调用方不追踪情绪时为 "")。它每个落定的 turn 运行一次 —— 而非 // 每个模型步一次 —— 因此多步工具 turn 的效果恰好应用 // 一次。它不得阻塞:它在答案完成后于 Run 路径上运行, // 其返回值经 Result.Mood 携带至持久化层。 type TurnEndHook func(TurnEnd) string // settleTurn 把落定的 turn 上报给钩子(已接线时),并把 // 返回的情绪标签记录到结果上,使持久化的 assistant 消息携带 // 该 turn 的情绪(design/16 §3.3)。会话环境经 ctx 传递, // 因此群聊/陌生人 turn 以受限身份到达钩子。 func (l *Loop) settleTurn(ctx context.Context, sessionID string, msgs []model.Message, res *Result) { if l.onTurnEnd == nil { return } res.Mood = l.onTurnEnd(TurnEnd{ SessionID: sessionID, PersonaID: l.personaFor(ctx), UserText: lastUserText(msgs), AssistantText: res.Text, StopReason: res.StopReason, Steps: res.Steps, Env: sessionEnvFrom(ctx), }) }