Session Actor:线程、状态与取消边界
梳理会话状态所有权、消息流转、后台任务与 CancellationToken 的中断路径
本页解决的问题
先给结论「Session Actor:线程、状态与取消边界」要解决的关键问题是什么?
梳理会话状态所有权、消息流转、后台任务与 CancellationToken 的中断路径
让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。
写下一个问题:试完这个方法后,你能用什么证据回答它?
结论听起来很完整,却没有检查最关键的假设。
能解释线程隔离、Actor 状态所有权和取消信号如何配合,并准确描述 Agent 的「有效不可变」边界。
SessionActor 协调
run_session同时接收 SessionCommand、ChatStateEvent、SessionEvent 与 turn completion。maybe_start_running_task启动待处理 turn。- turn 完成后执行 completion、turn end 和后续通知处理。
ChatStateActor 拥有状态
- 专属拥有 conversation、token、配置与 persistence。
- 通过
mpsc::UnboundedReceiver串行处理命令。 - 取消 token 触发退出,全部 handle 被丢弃也会结束循环。
definitionAgentDefinition,定义身份、模式与策略输入。
prompt_context支持检查、重渲染与序列化的 PromptContext。
system_prompt从 prompt context 渲染并缓存的字符串。
tool_bridgeArc<ToolBridge>,工具注册与会话上下文桥梁。
reminder_policySession 级 reminder 策略。
compaction_policy自动压缩、memory flush 和 two-pass 配置。
hosted_tools发送给 API 的后端托管工具定义。
backend_search_enabled构建时的服务端搜索开关。
finalize_prompt(&mut self) 更新构建时间并重新渲染 prompt,所以不能描述成绝对不可变。.name(thread_name)
.stack_size(8 * 1024 * 1024)
.spawn(move || {
let rt = tokio::runtime::Builder
::new_current_thread().enable_all().build()?;
let local = tokio::task::LocalSet::new();
});
pub async fn finalize_prompt(&mut self) {
self.prompt_context.build_timestamp_utc =
chrono::Utc::now().to_rfc3339();
self.system_prompt = self.prompt_context
.render(&self.tool_bridge).await
.unwrap_or_default();
}
.git 元数据,因此不声称对应某个 commit 版本。给状态找唯一拥有者
把 conversation、system_prompt、tool registry、sampling request 分别放到 ChatStateActor、Agent、ToolBridge、SamplerActor。再说明取消 token 与消息优先级属于不同概念,本源码没有「高优先级消息插入队首」的通用设计。
「核心视觉 · 教学化运行时图」为什么要看操作
「把 conversation、system_prompt、tool registry、sampling request 分别放到 ChatStateActor、Agent、ToolBridge、SamplerActor。再说明取消 token 与消息优先级属于不同概念,本源码没有「高优先级消息插入队首」的通用设计」把结构落到了一个具体动作。这里真正要比较的不是名词谁更高级,而是数据如何被放置,以及最常发生的操作需要走多远。
读懂结构,要同时看访问方式和变化方式
「把 conversation、system_prompt、tool registry、sampling request 分别放到 ChatStateActor、Agent、ToolBridge、SamplerActor。再说明取消 token 与消息优先级属于不同概念,本源码没有「高优先级消息插入队首」的通用设计」揭示了一个容易被忽略的取舍:按位置读取、按键查找、从两端进出、插入新元素和遍历关系,适合的组织方式并不相同。一个结构在某个操作上很快,不代表它在所有操作上都快。
- run_session 同时接收 SessionCommand、ChatStateEvent、SessionEvent 与 turn completion
- maybe_start_running_task 启动待处理 turn
- turn 完成后执行 completion、turn end 和后续通知处理
把规模和更新频率一起算进去
实践时可以把「把 conversation、system_prompt、tool registry、sampling request 分别放到 ChatStateActor、Agent、ToolBridge、SamplerActor。再说明取消 token 与消息优先级属于不同概念,本源码没有「高优先级消息插入队首」的通用设计」当作边界提醒:先写下数据量、最常用的操作和允许的延迟,再看 AI 给出的结构是否真的匹配。
从「核心视觉 · 教学化运行时图」走到「SessionActor 协调」
「核心视觉 · 教学化运行时图」先把问题落在「Session OS thread · ses- Tokio current-thread runtime + LocalSet SessionActor SessionCommand turn completion / events ChatStateActor conversation tokens / timing / persistence 专属状态,无共享锁 SamplerActor request…」上;到了「SessionActor 协调」,讨论继续推进到「run_session 同时接收 SessionCommand、ChatStateEvent、SessionEvent 与 turn completion。 maybe_start_running_task 启动待处理 turn。 turn 完成后执行 completion、turn end 和后续通知处理」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。
把这条判断带到下一个场景
遇到一个新的数据结构时,不要从定义开始背。先写出最频繁的操作,再估计数据量和更新方式,最后检查结构是否让这三个条件同时成立。
- 「核心视觉 · 教学化运行时图」:Session OS thread · ses- Tokio current-thread runtime + LocalSet SessionActor SessionCommand turn completion / events ChatStateActor conversation tokens / timing / persistence 专属状态,无共享锁 SamplerActor request…
- 「SessionActor 协调」:run_session 同时接收 SessionCommand、ChatStateEvent、SessionEvent 与 turn completion。 maybe_start_running_task 启动待处理 turn。 turn 完成后执行 completion、turn end 和后续通知处理
- 「最后的要点」:通过 mpsc::UnboundedReceiver 串行处理命令
最后的「最后的要点」把讨论落到「通过 mpsc::UnboundedReceiver 串行处理命令」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。
我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。
读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。
如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。
还没有这篇文章的讨论。