现在我们把前几节串起来。
当前已有:
SessionManager
WorkingMemory
ContextBuilder
Agent Engine
一个新请求进来时,流程应该变成:
请求携带 session_id
↓
SessionManager.get_session()
↓
取出 messages
working_memory
tool_history
↓
ContextBuilder.build()
↓
Agent Engine
Agent 执行完以后:
新增 messages
更新 working_memory
追加 tool_history
关联 run_id
↓
SessionManager 保存
这就是一个完整闭环。
真正接回 Agent 时,可以把链路理解成下面这样:请求先根据 session_id 找回状态,再由 ContextBuilder 组装上下文,Agent 执行结束后再把新的状态写回同一个 Session。
![图片[1]-小节四:把 Session 接回 ContextBuilder 和 Agent Engine——真正实现“继续刚才那个任务”-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1542.png)
这个闭环解决的是“下一次请求怎么接着上一次继续”。输入侧恢复 messages / working_memory / tool_history,输出侧再追加新消息、更新 Memory、记录 Tool 和 Run。这样 Session 才不是静态存档,而是整个多轮交互的状态中枢。
新用户第一次请求
如果没有 session_id:
create_session()
返回:
session_id
客户端以后都带这个 ID。
老用户继续请求
请求:
{
"session_id": "sess_xxx",
"message": "继续刚才的任务"
}
服务端:
get_session
↓
恢复上下文
↓
继续 Agent
一个完整内存版 Demo
下面实现一个非常简单的“可恢复对话”。
第二次输出能够直接知道:
当前 Goal
当前 Next Action
这就是 Session + Working Memory 联合的价值。
这段代码还缺什么?
非常多。
例如:
真正持久化
多用户
并发安全
Session 过期
Run Manager
数据库
权限
但架构已经成立。
后面服务化时只要替换底层存储,不需要重写整个 Agent 逻辑。
继续聊天与继续执行,需要不同的信息
用户下午回来问“接着上午的排查继续”,我们可以从 Session 恢复目标和历史。但如果上午的任务停在文件写入中途,仅靠聊天记录无法确认文件是否已经改变。恢复对话需要上下文,恢复执行还需要动作记录、资源状态和检查点,两条链路要分别设计。
另一个常见情况是用户开了两个页面,同时向同一 Session 提交任务。如果两个 Run 都读到旧版本,再各自写回 Memory,后提交的结果可能覆盖前一个。教学实现可以先限制同一 Session 串行执行;需要并行时,再使用版本检查或明确分支,避免默默合并冲突。
Session 的归属、过期和访问权限应在读取与写入时检查。会话编号只是标识,不是授权凭据。长期记忆也不能因为出现在同一个数据库,就被其他用户的 Session 自动检索到。
动手推演:两次写入谁留下来
让两个模拟请求都读取版本 3,第一个更新到版本 4,第二个仍以版本 3 提交。参考结果是第二个被告知冲突,重新读取后再决定如何合并。若最后只剩其中一份内容而没有任何错误,我们就发现了一次丢失更新。
这就是典型的“丢失更新”问题。用版本号把两个请求放到同一条时间线上看,会更直观:
![图片[2]-小节四:把 Session 接回 ContextBuilder 和 Agent Engine——真正实现“继续刚才那个任务”-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1543.png)
expected_version 的意义不是增加一个字段,而是明确告诉服务端:“我的修改是基于哪个快照算出来的。”如果 Session 已经从 3 变成 4,旧版本写入就应该被拒绝,让客户端重新读取后再决定是否合并,而不是静默覆盖最新状态。
网络重试还会带来另一类重复
并发之外,客户端超时重试也很常见。第一次请求可能已经成功追加消息,只是响应在网络途中丢了;客户端再次提交同一内容,如果服务端没有 request_id 或幂等键,就会得到两条相同消息,甚至启动两个 Run。
一个实用做法是让每次写请求携带唯一 request_id,Session Store 记录已经处理过的 ID。再次收到相同 ID 时返回第一次结果,不重复追加消息或创建 Run。这里不要用消息正文充当幂等键——用户连续发送两句相同的话,完全可能是合法行为。
网络重试和并发冲突不是一回事。这里的问题是“同一个写请求可能被发送两次”,所以更适合用独立的 request_id 做幂等控制:
![图片[3]-小节四:把 Session 接回 ContextBuilder 和 Agent Engine——真正实现“继续刚才那个任务”-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1544.png)
第一次请求如果其实已经写成功,只是响应丢在网络途中,第二次带着相同 request_id 再来时,服务端应该直接返回第一次结果。这样既不会重复追加同一条消息,也不会因为一次客户端重试额外创建一个 Run。
在配套项目里找到这一节
课程中的可运行实现位于:
src/agent_harness/context/session.py
projects/05_context_memory_session/main.py
项目版保留了短方法名 create()、get()、close()、restore(),同时提供 create_session()、get_session() 等同义入口,方便和本节正文对应。它已经支持关闭后保留历史、恢复写入、按用户筛选、Session 过期,以及用 expected_version 拦截旧版本覆盖。
进入 agent_harness_course_projects 后运行:
python3 --version
python3 projects/05_context_memory_session/main.py
第一条命令确认当前使用 Python 3.9。第二条命令会创建 Session、通过 Manager 追加消息,再完成 Working Memory 的保存、恢复和 Context 组装。这里仍是内存版教学实现:刷新浏览器后,只要服务进程和对象索引还在,Session 可以继续;服务进程重启后则无法恢复,真正的跨重启持久化要等后面的 SQLite 章节接上。
















请登录后查看评论内容