文件管理、挂载与数据流
这一章专门描述“文件从哪里来、到哪里去、run 结束后谁负责保留这些文件”,以及同一 session 内文件与会话怎样保持连续。
1. backend 本地磁盘的目录布局
FileBackedStore(文件型状态存储)在 storage_root 下维护三类核心目录和一个状态文件:
backend/data/
├── state.json
├── uploads/
└── workspaces/
└── <run-id>/
├── in/
├── out/
└── tmp/
含义如下:
state.json:保存SessionRecord、ArtifactRecord、MountGrantRecord、RunRecorduploads/:保存用户上传的原始文件副本workspaces/<run-id>/in/:本轮输入workspaces/<run-id>/out/:本轮输出workspaces/<run-id>/tmp/:本轮临时文件
这意味着当前 backend 采用的是本地持久化控制面。
2. K8s 和 backend 分别保留什么
这部分需要先分清楚。
SessionRecord.history(会话历史)、ArtifactRecord(文件记录)、RunRecord(运行记录)由 backend 保存在state.jsonuploads/和workspaces/由 backend 所在主机磁盘保存Job、Pod、ConfigMap、Secret由 K8s 管理checkpoint文件或状态数据库如果写在挂载卷内,就跟随宿主机目录或 PVC 持久化
因此:
- 对话历史不保存在 K8s
- K8s 负责容器对象生命周期
- backend 负责会话、文件索引和结果索引
- checkpoint 是否可恢复,取决于
agentcore把它写到哪里
3. 用户上传文件怎样进入系统
3.1 HTTP 上传入口
上传接口是:
POST /api/v1/artifacts/upload
它使用 multipart/form-data,当前识别的字段有:
sessionIdfilerelativePath
3.2 backend 落盘逻辑
ApplicationService::upload_artifact_bytes(上传文件字节流)会先校验 session 是否可写,然后调用 FileBackedStore::save_artifact_bytes(保存文件字节流)。后者会:
- 清洗文件名
- 清洗相对路径
- 把文件写到
uploads/ - 在
state.json里写入ArtifactRecord
3.3 relative_path 的意义
relative_path 不会影响原始上传文件在 uploads/ 的落盘位置,但会影响后续 create_workspace(创建工作区)时复制进 /workspace/in 的目录结构。
4. create_workspace 到底做了什么
FileBackedStore::create_workspace(创建工作区)是文件流的核心步骤之一。
4.1 它会创建固定三层目录
<workspace-root>/
├── in/
├── out/
└── tmp/
4.2 它会把 artifact 复制进 in/
- 如果 artifact 有
relative_path,按相对目录复制 - 否则落成
<artifact-id>-<sanitized-file-name>
4.3 它会设置宽松目录权限
当前 Unix 上会把工作区目录权限设成 0o777,目的是避免 backend 进程创建目录后,容器内 UID 1000 无法访问。这个做法解决了当前单机联调中的权限问题,后续如果要提高隔离强度,应改成更细粒度的 owner / group 管理。
5. 容器怎样拿到这些文件
job_builder::build_job(构建 Job)会把宿主机工作区通过 hostPath 挂到容器内的 /workspace。因此当前路径关系是:
用户文件
-> 上传到 backend /uploads
-> create_run 时复制到 /workspaces/<run-id>/in
-> 通过 hostPath 挂到容器 /workspace/in
同一个 Job 还会把 agent package 通过 ConfigMap 挂到:
/opt/agent/package
如果本次运行绑定了 MountGrantRecord(挂载授权记录),还会额外挂一个用户授权目录。
6. run-context.json 怎样生成
在提交 Job 前,ApplicationService::write_run_context(写入运行上下文)会把一个关键文件写到:
/workspace/in/run-context.json
它包含:
sessionIdlatestRunIdhistorypackagebuiltInToolsenvFactsinputArtifactssystemPromptuserPrompt
容器里的 agentcore 会读取这个上下文文件获取本轮所需信息。
7. 会话连续性与文件连续性
这一节从“用户关掉页面后,下次回来还能不能接着聊”这个角度解释当前代码。
7.1 当前已经保证的连续性
当前代码已经保证三类连续性:
- 对话历史连续
SessionRecord.history会持久化保存。 - 上传文件连续
ArtifactRecord和uploads/下的原始文件会保留。 - 历史结果文件连续
RunRecord和workspaces/<run-id>/out/会保留。
7.2 一轮 run 完成后,历史怎样进入下一轮
ApplicationService::sync_session_conversation_history(同步会话历史)会在新 run 创建前刷新同一 session 下旧 run 的状态。
ApplicationService::sync_run_assistant_message(同步助手回答)会在某个 run 完成后读取:
workspaces/<run-id>/out/final-answer.md
然后把其中内容追加为一条 assistant 消息写入 SessionRecord.history。
因此下一次同一 session 再创建 run 时,run-context.json 里已经包含前一轮回答。
7.3 用户关闭前端,再次打开同一 session
如果用户只是关闭前端页面、退出 Helper 或暂时离线,而没有调用 POST /api/v1/sessions/{id}/close,那下面这些内容都会保留:
SessionRecord.historySessionRecord.latest_run_id- 已上传的
artifact - 历史
RunRecord - 历史 run 的
out/结果文件
这时前端只需要重新使用原来的 session_id 调用:
GET /api/v1/sessions/{id}GET /api/v1/runs
随后继续 POST /api/v1/runs 即可。
7.4 session 被关闭后,再回来继续
如果调用了 POST /api/v1/sessions/{id}/close,backend 会把 SessionRecord.status 设为 CLOSED。当前行为是:
- 历史记录保留
- artifacts 保留
- runs 保留
- 结果文件保留
- 新的上传、挂载授权和 run 会被拒绝
如果之后还要继续同一会话,需要先调用:
POST /api/v1/sessions/{id}/reopen
reopen 只会改变 session 的可写状态,不会删除任何历史数据。因此一个月后再打开相同 session,历史和结果文件仍然在。
7.5 目前“连续”的边界在哪里
当前代码已经连续保存:
- 文本对话历史
- 上传文件
- 历史结果文件
当前代码还没有自动连续保存:
- 同一
session的共享工作目录 - 同一
session的checkpoint_root - 上一轮输出文件自动进入下一轮输入
因此今天这套代码能保证“会话历史连续”和“结果文件可追溯”,但“agentcore 内部 checkpoint 的自动恢复”还需要显式的 session 级状态目录设计。
8. checkpoint 文件或状态数据库应写到哪里
如果 agentcore 需要保存:
- checkpoint 文件
- SQLite / RocksDB / LMDB 这类状态数据库
- 工具缓存
- 中间索引
这些数据都应写到持久化挂载目录中。当前最直接的路径是 /workspace 下的子目录,例如:
/workspace/state/
/workspace/checkpoints/
/workspace/checkpoint-db/
如果它们只写在容器根文件系统里,Pod 删除后数据就一起消失。
9. run 结束后,K8s 如何处理文件、对话历史和 checkpoint
这是当前文档必须说清楚的一点。
9.1 K8s 负责的部分
K8s 负责:
- Pod 结束
- Job 结束
- Job TTL 到期后的 Job / Pod 回收
- 显式
cancel时的资源删除
9.2 backend 负责的部分
backend 负责:
state.json中的session、artifact、run元数据uploads/中的原始上传文件workspaces/<run-id>/out/中的结果文件
9.3 ConfigMap / Secret 的当前处理
当前完成态 run 的 ConfigMap 和 Secret 还没有统一的后台 GC。取消 run 时会显式删除;正常完成后主要依赖后续清理策略补齐。
9.4 checkpoint 的当前处理语义
K8s 本身不理解 checkpoint 语义,它只知道卷挂载和容器对象。真正决定 checkpoint 是否保留的是落盘位置:
- checkpoint 写在容器根文件系统:Pod 删除后丢失
- checkpoint 写在挂载卷:Pod 删除后仍保留
当前代码没有独立的 checkpoint 路径索引和恢复 API,所以这部分连续性仍取决于 agentcore 的落盘约定和后续的 session 级状态卷设计。
10. 如果要支持“同一 session 长期继续运行”,推荐的数据模型
从通用运行时设计看,更完整的模型是:
backend/data/
├── state.json
├── uploads/
├── workspaces/<run-id>/
└── session-state/<session-id>/
├── checkpoints/
├── checkpoint-db/
└── tool-cache/
推荐流程是:
- 第一次 run 时,为
session_id创建session-state/<session-id>/ - 后续同一
session的每次 run 都把该目录挂到固定路径 agentcore把 checkpoint 和状态数据库始终写在这个固定路径- backend 在
SessionRecord或新的SessionStateRecord中保存该路径索引
这样可以保证:
- 文本历史连续
- 文件结果连续
agentcorecheckpoint 也连续
当前代码离这一步还差显式元数据和显式挂载规则。