Files
zk-data-agent/docs/architecture.zh-CN.md
2026-07-26 21:32:12 +08:00

7.4 KiB
Raw Permalink Blame History

架构设计

中文 · English

设计目标

K1412 Agent 是一个多用户网页编码 Agent,其 Agent 循环可以独立于账户系统和用户界面进行修改。架构将高成本的实验面控制在较小范围内:研究者应当能够改变上下文、记忆、调度、工具、委派或完成策略,而不必分叉鉴权、聊天记录、Docker 生命周期或整个前端。

系统有意只保留一条 Agent 路径。早期曾计划在自定义 Work 循环之外并存一条轻量 Chat 循环,但这会重复产品行为、混淆模型选择,并增加前后端必须保持兼容的代码量,因此已经移除。

系统上下文

flowchart LR
    U["浏览器用户"] --> P["DNS、TLS 与 Nginx Proxy Manager"]
    P --> W["Open WebUI<br/>鉴权、RBAC、历史记录、界面"]
    W --> R["Agent Runtime<br/>循环、上下文、记忆、模型路由"]
    R --> M["模型提供方<br/>K1412 API 或 DeepSeek"]
    R --> G["Workspace Gateway<br/>身份与执行策略"]
    G -->|Tailscale + SSH Docker| X["每用户工作区容器<br/>home-node-itx"]
    W --> DB["PostgreSQL<br/>用户与对话"]
    R --> DB
    W --> Q["Redis<br/>协调"]

只有 Web 对公网开放。Runtime、Gateway、PostgreSQL 和 Redis 位于私有 Compose 网络中。工作区容器运行在另一台物理 Docker 主机上,不接收应用密钥,也不暴露主机端口。

职责边界

负责 不应负责
Open WebUI 注册、登录、会话、管理员审批、RBAC、对话持久化、渲染 Agent 调度、模型提供方密钥、Docker 访问
Agent Runtime 模型目录、上下文策略、Agent 循环、工具、调度、委派、记忆、计划、运行事件、证据门禁 浏览器会话、Docker socket
Workspace Gateway 签名身份校验、工作区路径策略、每用户锁、Docker 生命周期、文件交付 模型调用、公开鉴权界面
工作区容器 用户代码、依赖、生成文件、命令执行 其他用户的数据、服务凭据、Docker socket
PostgreSQL Open WebUI 持久化状态,以及 Runtime 事件、计划和记忆 代码执行
共享基础设施 TLS、镜像仓库、源码托管、私有路由、模型服务 Agent 行为

这种分层允许 Web UI 按自己的节奏升级,同时让 Agent 循环可以频繁迭代。

请求生命周期

Agent 请求

sequenceDiagram
    participant B as 浏览器
    participant W as Open WebUI
    participant R as Runtime
    participant M as 模型提供方
    participant G as Gateway
    participant X as 用户工作区

    B->>W: 已鉴权的对话请求
    W->>R: OpenAI 兼容请求 + 服务密钥 + 签名用户身份
    R->>R: 校验身份、构建上下文、记录 run.created
    loop 直到有证据支持完成,或预算耗尽
        R->>M: 最近上下文 + 工具 + 提供方策略
        M-->>R: 助手内容和/或工具调用
        R->>R: 规范化、校验、限流并调度调用
        R->>G: 工具请求 + 服务密钥 + 新签名身份
        G->>G: 校验身份并绑定用户工作区
        G->>X: 在资源和路径约束下执行
        X-->>G: 退出码、输出或文件结果
        G-->>R: 结构化工具结果
        R->>R: 持久化事件并评估完成证据
    end
    R-->>W: SSE 回答和已完成的工具详情块
    W-->>B: 渲染后的 Agent 响应

Open WebUI 发送的身份在 Runtime 接受请求时校验一次。之后,Runtime 会为每一次 Gateway 调用重新签发已经校验过的身份。这样可以避免一次耗时较长的 Agent 运行,在模型或工具工作期间继续复用已经过期的身份令牌。

工作区文件请求

带 Open WebUI 会话的浏览器
  -> /api/v1/k1412/workspace/*
  -> Open WebUI 校验当前登录用户
  -> Web 签发短期用户身份
  -> Gateway 校验服务密钥和签名身份
  -> Gateway 只选择该用户对应的哈希容器与数据卷
  -> 返回浏览、下载或流式归档响应

浏览器永远不会获得 Gateway 服务密钥,也不会直接连接 Docker。

Runtime 内部结构

从 Open WebUI 的视角看,Runtime 是一个 OpenAI 兼容的模型提供方,但普通聊天补全实际由 K1412 循环实现:

  • ModelProvider 将内部模型规格转换为提供方请求,并统一流式补全响应。
  • ContextPolicy 移除已渲染的工具详情标记,前置系统契约和持久记忆,并在每个模型的字符预算内保留最新的可见消息。
  • ToolRegistry 声明工具 schema,将工作区工具路由到 Gateway,将状态工具路由到 RuntimeStore
  • AgentLoop 负责迭代、工具规范化、校验、调度、恢复、委派和基于证据的完成判定。
  • RuntimeStore 持久化运行事件、每对话计划和用户记忆。

更多细节参见 Agent 循环实现

工作区架构

local-dockerssh-docker 实现同一套 ExecutionProvider 契约。生产环境使用 ssh-docker:Gateway 通过只读挂载的专用 SSH 配置,连接 home-node-itx 上的 Docker。

每个不可变的 Open WebUI 用户 ID 都映射到一个由 SHA-256 派生的工作区 ID,并且恰好对应一组:

  • 名为 k1412-ws-<workspace-id> 的容器;
  • 名为 k1412-ws-data-<workspace-id> 的数据卷;
  • 名为 k1412-ws-net-<workspace-id> 的桥接网络。

容器以 UID/GID 1000 运行,根文件系统只读,移除全部 Linux capabilities,启用 no-new-privileges,设置 PID/CPU/内存限制,不挂载 Docker socket,也不发布端口。/workspace 是唯一持久可写的文件系统。/tmp 可写,但以 noexec 方式挂载。

Python 环境位于 /workspace/.venv。用户级 home 与软件包缓存位于 /workspace/.agent 下。镜像会把虚拟环境和用户二进制目录放在 PATH 前部。命令在启用 pipefail 的 Bash 中运行,因此失败的安装不会被成功的 tail 或类似管道末端命令掩盖。

WORKSPACE_IMAGE 改为新的不可变标签后,Gateway 会在下一次访问时替换过期容器,但重新挂载已有命名数据卷。因此,镜像升级可以改变执行环境而不删除用户文件。

网络与部署

生产环境以一个 Unraid Compose Manager 项目运行:

  • publicWeb 入口;
  • internal:私有服务间通信;
  • egress:Runtime 访问模型提供方,以及 Gateway 的 SSH 访问。

只有 Web 发布 NAS 端口 12004。VPS 上的 Nginx Proxy Manager 为 agent.k1412.top 终止 TLS,并通过 Tailscale 转发。镜像以 linux/amd64 构建,使用不可变的“时间戳-提交”标签推送到 docker.k1412.top/wuyang/*,部署时只更新相关服务。

文档门户打包在 Web 镜像中,通过 /doc/ 提供,不会增加新的公开服务、端口、数据库或代理规则。

稳定面与实验面

稳定的平台契约:

  • 已鉴权的 Web 到 Runtime 请求;
  • Runtime/Web 到 Gateway 的签名身份;
  • ExecutionProvider 工作区语义;
  • 每用户数据卷隔离;
  • 运行事件持久化;
  • 公开模型 ID
  • 工作区文件 API。

预期持续实验的部分:

  • 提示词与上下文策略;
  • 记忆选择;
  • 工具目录与意图路由;
  • 调度器与并行;
  • 委派;
  • 恢复与证据规则;
  • 模型预算与路由。

所有对实验结果有影响的行为,都应在 run.created 中带版本号,使实现变化前后的结果仍然可比较。