package tool import "context" // 本文件承载「本回合角色」的 context 通道(切片 KB1)。 // // 背景:一个 Run 里每轮要回答的**角色**由循环解析(loop 的 personaFor: // 会话环境的显式角色,否则循环配置的默认角色),提示词、记忆注入与情绪 // 都按它取值。工具侧此前拿不到这个角色,于是 knowledge 工具把可选参数 // `persona` 缺席时的空串直接透给记忆检索器,由后者以 ErrMissingPersona // 硬失败 —— 模型既不知道该填哪个角色 id(参数说明还写着 optional), // 也就只能对同一堵墙反复重试(安卓侧实测的连续两次 // 「工具调用失败 · knowledge」)。 // // 通道形态与 allowset.go 同族:循环在 Run 顶部播种一次,工具经 // Dispatcher.Invoke 的 ctx(它由本轮 ctx 派生)读取。身份与权限**不** // 走这条通道 —— 它只是「本回合以谁的名义说话」这个作用域元数据,与 // SessionEnv 的注释同一纪律(项目红线:身份由 actor 元数据 + 策略引擎 // 决定,绝不从这里推断)。 // turnPersonaKey 是本回合角色的 context key。 type turnPersonaKey struct{} // WithTurnPersona 把本回合角色挂到 ctx 上(循环在 Run 顶部调用一次)。 // 空角色是空操作:不挂载即「本回合没有解析出角色」,读取侧返回空串, // 让依赖角色作用域的工具据此给出可消化的拒绝,而不是把空角色透给 // 下游存储换回一条无从纠正的错误。 func WithTurnPersona(ctx context.Context, personaID string) context.Context { if ctx == nil || personaID == "" { return ctx } return context.WithValue(ctx, turnPersonaKey{}, personaID) } // TurnPersonaFrom 返回 ctx 上挂载的本回合角色;未挂载(或 ctx 为 nil)时 // 返回空串。 func TurnPersonaFrom(ctx context.Context) string { if ctx == nil { return "" } s, _ := ctx.Value(turnPersonaKey{}).(string) return s }