外部数据库访问
本章说明当前方案下“数据库访问”到底是怎么做的。
当前统一方案:Backend(后端控制面)负责所有数据库查询和写入,容器内的 Worker / agentcore 不直接连接数据库。 Worker 只需要实现与 Backend 的协议通信。
这意味着:
- Worker 不直接连接 PostgreSQL / MySQL / Redis / 向量数据库
- Worker 不直接打开 checkpoint 数据库文件
- Worker 不持有数据库地址、账号、密码
- 所有数据库访问都由 Backend 统一代理、审计和裁剪
1. 为什么要这样设计
过去最容易出现的误解是:既然容器在本地机器上运行,那是不是可以让容器直接连数据库?
当前方案明确不这么做,原因有四个:
| 原因 | 说明 |
|---|---|
| 安全 | 数据库凭据只留在 Backend,容器内不暴露 |
| 简化容器 | Worker 只实现协议,不需要感知数据库类型、网络地址、驱动差异 |
| 审计一致 | 所有查库、写库都在 Backend 一侧统一记录 |
| 演进容易 | 将来数据库从本地迁到云端,Worker 协议无需改变 |
2. 整体模型:Backend 统一查库
graph TB
subgraph USER_SIDE["用户侧"]
H["Helper / Web UI"]
end
subgraph CONTROL["Backend 控制面"]
API["API + WS 路由"]
SVC["ApplicationService"]
GATE["数据访问网关"]
end
subgraph EXEC["执行面"]
W["Worker / agentcore"]
end
subgraph DATA["数据层"]
DB1["会话数据库"]
DB2["检查点数据库"]
DB3["业务数据库"]
end
H --> API
W -->|"协议请求"| API
API --> SVC --> GATE
GATE --> DB1
GATE --> DB2
GATE --> DB3
一句话概括就是:
Worker 只会说“我要什么数据 / 我要写什么状态”,真正的查库和写库由 Backend 完成。
3. 本地部署时为什么更适合 Backend 查库
当前系统是本地部署:
- 用户的本地机器 = Backend 所在宿主机
- K3s 也在这台机器上运行
因此如果数据库也在本机,Backend 访问数据库最直接:
graph LR
subgraph HOST["用户本地机器 / 宿主机"]
B["Backend"]
DB["本地数据库"]
K["K3s"]
W["Worker Pod"]
end
B -->|"localhost / 本机 socket / 本机地址"| DB
W -->|"协议通信"| B
这样可以直接避免几个典型问题:
- Pod 内
localhost不是宿主机localhost - 容器不需要知道宿主机 IP、Service、Endpoints 这些网络细节
- 容器里不需要再装数据库驱动和连接配置
所以在你们当前的架构里,“宿主机上的数据库怎么让容器访问”这个问题本身就被收敛掉了:不是容器去访问,而是 Backend 去访问。
4. Worker 与 Backend 的数据库协议
既然 Worker 不直接查库,那么就必须通过协议把“我要读什么 / 我要写什么”表达给 Backend。
推荐把协议操作分成四类:
| 协议操作 | 用途 | 语义 |
|---|---|---|
backend_query | 读取业务数据 / 会话数据 / 配置数据 | 同步 |
backend_command | 写入业务状态或执行事务性操作 | 同步 |
checkpoint_get | 拉取恢复所需的检查点 | 同步 |
checkpoint_put | 上报新的检查点快照 | 同步 |
4.1 backend_query(同步)
用于 Worker 需要立刻拿到结果才能继续时,例如:
- 读取某个表的数据
- 获取某个对象详情
- 拉取最新会话状态
- 查询某个权限配置
sequenceDiagram
participant W as Worker
participant B as Backend
participant DB as 数据库
W->>B: backend_query
B->>DB: 执行查询
DB-->>B: 返回结果
B-->>W: backend_query_result
为什么定义成同步?
因为 Worker 的后续推理通常依赖这个结果,不能“先继续、稍后补回来”。
示例:
{
"type": "backend_query",
"id": "query-001",
"timestamp": "2026-04-17T02:30:00Z",
"payload": {
"resource": "customer_profile",
"operation": "get",
"arguments": {
"customerId": "cust-001"
}
}
}
响应:
{
"type": "backend_query_result",
"id": "query-001",
"timestamp": "2026-04-17T02:30:00Z",
"payload": {
"ok": true,
"data": {
"customerId": "cust-001",
"name": "Alice"
}
}
}
4.2 backend_command(同步)
用于 Worker 需要 Backend 执行有副作用的数据库操作,例如:
- 创建一条记录
- 更新某个状态
- 记录审批结果
- 触发一个事务性写入
sequenceDiagram
participant W as Worker
participant B as Backend
participant DB as 数据库
W->>B: backend_command
B->>DB: 执行写入 / 事务
DB-->>B: 返回提交结果
B-->>W: backend_command_result
这类操作也应是同步语义,因为 Worker 需要明确知道写入是否成功,才能决定下一步。
4.3 checkpoint_get(同步)
Worker 启动后,如果需要恢复上一次状态,不直接读本地数据库文件,而是向 Backend 请求:
sequenceDiagram
participant W as Worker
participant B as Backend
participant DB as 检查点数据库
W->>B: checkpoint_get(session_id, run_id)
B->>DB: 查询最新检查点
DB-->>B: 返回 checkpoint snapshot
B-->>W: checkpoint_get_result
这是同步语义,因为恢复流程没拿到 checkpoint 结果就不能继续。
4.4 checkpoint_put(同步)
Worker 在关键步骤结束后,把新的检查点快照通过协议发给 Backend:
sequenceDiagram
participant W as Worker
participant B as Backend
participant DB as 检查点数据库
W->>B: checkpoint_put
B->>DB: 持久化 checkpoint
DB-->>B: 写入成功
B-->>W: checkpoint_put_ack
这同样建议定义成同步语义,因为只有拿到 ACK,Worker 才能认为“这份状态已经可恢复”。
5. 哪些操作是异步的
虽然数据库操作统一走同步语义,但并不是所有协议都同步。
以下仍然是异步事件:
| 协议消息 | 语义 | 原因 |
|---|---|---|
stream_chunk | 异步 | 高频流式输出,不能每个 token 都等确认 |
state_change | 异步 | 状态通知型消息 |
tool_progress | 异步 | 进度展示用,不阻塞主流程 |
error | 异步 | 失败上报,Backend 收到后再统一处理 |
ask_user | 混合 | 发送是异步,Worker 业务流程会暂停等待回复 |
所以整体规则可以简单记为:
查库 / 写库 / checkpoint 恢复与保存 = 同步语义
流式输出 / 状态变化 / 进度通知 = 异步语义
6. run-context.json 在这里扮演什么角色
run-context.json 仍然存在,但它的角色是:
- 给 Worker 提供启动时的初始快照
- 减少刚启动时第一批必须走协议请求的次数
- 让 Worker 在还没发出第一条协议消息前,就能拿到基础上下文
它通常包含:
- 当前会话摘要
- 最近对话历史
- 已授权的输入文件信息
- 最近检查点的元数据摘要
但它不是数据库本身,也不是容器的长期状态源。
长期、权威的数据源仍然是 Backend 管理的数据库。
7. 本地数据库、远程数据库、云数据库有什么区别
在当前方案里,这三种数据库对 Worker 来说没有本质区别。
区别只发生在 Backend 这一层:
| 数据库位置 | 实际连接方 | Worker 是否感知地址差异 |
|---|---|---|
| 宿主机本地数据库 | Backend | 否 |
| 局域网数据库 | Backend | 否 |
| 云数据库 / 托管数据库 | Backend | 否 |
这就是当前方案最大的架构收益之一:Worker 不需要因为数据库部署位置不同而改变协议。
8. 安全与治理收益
| 能力 | 收益 |
|---|---|
| 凭据集中管理 | 数据库用户名、密码、Token 都只保留在 Backend |
| 权限收敛 | Worker 没有直接数据库访问权限 |
| 审计统一 | 所有数据库操作都可以在 Backend 侧统一留痕 |
| 限流与缓存 | Backend 可统一做缓存、熔断、重试和速率控制 |
| 数据裁剪 | Backend 可以只返回 Worker 真正需要的字段 |
9. 技术可行性判断
| 方案 | 可行性 | 复杂度 | 推荐度 |
|---|---|---|---|
| Worker 直接连数据库 | 可行但不推荐 | 中 | 低 |
| Worker 直接读 checkpoint DB 文件 | 可行但不推荐 | 低 | 低 |
| Backend 统一查库 + Worker 协议请求 | 强烈推荐 | 中 | 高 |
推荐 Backend 统一代理的原因不是“唯一能做”,而是它最符合你们当前的本地部署模型和协议化架构。
下一步
理解了数据库访问模型后,建议继续阅读: