diff --git a/README.md b/README.md
index 9ee177e..7871d6c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# K1412 Agent
+[中文文档](README.zh-CN.md) · English
+
K1412 Agent is a multi-user web coding Agent. Every user request enters the
same independently evolvable Agent loop; its scheduler, context policy, memory,
tools, evidence rules, and child Agents are owned by this repository.
@@ -122,6 +124,7 @@ Open WebUI and re-signed for the Workspace Gateway; no internal key is exposed.
## Documentation
+- [中文文档索引](docs/README.zh-CN.md)
- [Documentation index](docs/README.md)
- [Architecture and Agent loop](docs/architecture.md)
- [Agent loop implementation](docs/agent-loop.md)
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 0000000..6b4df70
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,143 @@
+# K1412 Agent
+
+中文 · [English](README.md)
+
+K1412 Agent 是一个多人 Web 编码 Agent。每一次用户请求都会进入同一套可独立演进的
+Agent Loop;调度器、上下文策略、记忆、工具、证据规则和子 Agent 均由本仓库实现和维护。
+
+生产环境:
+
+架构、Agent 实现细节和实验台账:
+
+## 架构
+
+```text
+浏览器
+ |
+ v
+Open WebUI(鉴权、RBAC、对话历史、界面)
+ |
+ v
+Agent Runtime(模型网关 + Agent Loop)
+ |
+ v
+Workspace Gateway(身份、策略、审计)
+ |
+ v
+专用执行主机上,每个 Open WebUI 用户对应一个 Docker 工作区
+```
+
+Open WebUI 被固定到明确版本并进行少量补丁修改。它的选择器只展示四个模型名称,以及一个
+独立、只读的思考状态。Provider URL、上游模型 ID、API Key、工具服务设置、系统提示词和
+Runtime 参数全部保留在服务端。
+
+模型身份与思考能力有意分开表达:
+
+| 界面模型 | Provider 模型 | 思考 | 推理强度 |
+| --- | --- | --- | --- |
+| Luna | `ChatGPT-5.6:Luna` | 开启,不分强度档位 | — |
+| Terra | `ChatGPT-5.6:Terra` | 开启,不分强度档位 | — |
+| Sol | `ChatGPT-5.6:Sol` | 开启,不分强度档位 | — |
+| DeepSeek V4 Pro | `deepseek-v4-pro` | 开启 | 极高(`max`) |
+
+三个 Ollama 模型会声明 `thinking` 能力,但它们属于仅提供思考开关的 Qwen 系列模型,
+没有 GPT-OSS 风格的低、中、高推理强度。Luna、Terra、Sol 是三个不同的模型,不是同一个
+模型的三个推理设置。
+
+## 本地开发
+
+1. 将 `.env.example` 复制为 `.env` 并填写密钥。绝不能提交 `.env`。可使用
+ `./scripts/init-secrets.sh` 生成新值。
+2. 构建用户工作区镜像:
+
+ ```bash
+ docker compose --profile build-only build workspace-image
+ ```
+
+3. 启动完整服务栈:
+
+ ```bash
+ docker compose up --build
+ ```
+
+4. 打开 。新用户注册后的角色为 `pending`,必须由初始化管理员批准。
+
+Provider 凭据不会保存在仓库中,只能放在受保护的部署 `.env` 中。
+
+## 测试
+
+```bash
+python3 -m venv .venv
+.venv/bin/pip install -e '.[dev]'
+.venv/bin/pytest
+```
+
+运行包含真实 Docker 隔离测试的完整本地验证:
+
+```bash
+./scripts/verify.sh
+```
+
+在唯一、可销毁、禁用网络的 Docker 工作区中运行真实 Agent 评测:
+
+```bash
+set -a
+source /path/to/protected/deepseek.env
+set +a
+.venv/bin/python scripts/eval-live-work.py --model deepseek-v4-pro
+```
+
+评测器会报告延迟、Token 使用、工具调用次数、完成证据和生成文件列表。除非传入
+`--keep-workspace`,否则结束时只会删除本次评测唯一对应的容器、卷和网络。
+
+运行使用确定性假模型 Provider 的一次性六服务集成栈,验证注册审批、Agent Loop、
+工作区隔离和文件交付:
+
+```bash
+./scripts/verify-e2e.sh
+```
+
+E2E 服务栈及其测试专用卷会在退出时删除。只有需要检查运行中容器时才设置
+`E2E_KEEP_STACK=1`。
+
+审计所有最终服务镜像和工作区镜像,要求 Python 环境依赖一致、没有已知 Python 漏洞,
+且不存在可修复的 High/Critical 镜像漏洞:
+
+```bash
+./scripts/audit-images.sh
+```
+
+生成文件可通过模型选择器旁边的“工作区文件”按钮获取。用户可以浏览目录、下载单个文件,
+或者将当前目录下载为 `.tar.gz` 归档。浏览器请求先由 Open WebUI 鉴权,再为 Workspace
+Gateway 重新签名;任何内部密钥都不会暴露给浏览器。
+
+## 文档
+
+- [中文文档索引](docs/README.zh-CN.md)
+- [架构与 Agent Loop](docs/architecture.zh-CN.md)
+- [Agent Loop 实现](docs/agent-loop.zh-CN.md)
+- [实验体系](docs/experiments.zh-CN.md)
+- [项目历史与决策](docs/project-history.zh-CN.md)
+- [Open WebUI 集成](docs/openwebui-integration.zh-CN.md)
+- [基础设施地图](docs/infrastructure.zh-CN.md)
+- [开发与验证](docs/development.zh-CN.md)
+- [运维手册](docs/operations.zh-CN.md)
+- [安全模型](docs/security.zh-CN.md)
+- [English documentation](docs/README.md)
+
+## 部署
+
+生产环境使用 `docker.k1412.top/wuyang/*` 中不可变的 `linux/amd64` 镜像、Unraid Compose
+Manager 项目、私有服务网络,以及唯一的公开 HTTPS 入口
+。
+
+用户工作区通过 SSH 运行在专用 Docker 主机上;应用服务和数据库仍运行在 NAS。
+
+## 许可证与 Open WebUI 归属
+
+Web 服务派生自 Open WebUI v0.9.6。Open WebUI 的版权、许可证和归属继续遵循上游
+Open WebUI License。K1412 原创的 Runtime、Gateway、部署、测试和文档使用 Apache-2.0。
+因此本仓库是混合许可证仓库;派生自 Open WebUI 的 Web 层不会被重新许可为 Apache-2.0。
+
+生产部署使用 Open WebUI 针对滚动 30 天内不超过 50 名最终用户的品牌移除例外。公开构建
+默认保留上游品牌。详见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
diff --git a/docs/README.md b/docs/README.md
index 57c6cb3..e584ae6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,5 +1,7 @@
# K1412 Agent documentation
+[中文](README.zh-CN.md) · English
+
This directory is the canonical technical record for K1412 Agent. The public
documentation portal at is a curated rendering
of the same design. When behavior, infrastructure, or an experiment changes,
diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md
new file mode 100644
index 0000000..f4111c7
--- /dev/null
+++ b/docs/README.zh-CN.md
@@ -0,0 +1,48 @@
+# K1412 Agent 中文文档
+
+中文 · [English](README.md)
+
+本目录是 K1412 Agent 的权威技术记录。公开文档门户
+ 是同一套设计的精选呈现。行为、基础设施或实验发生变化时,
+应在同一个 commit 中同时更新对应的 Markdown 文档和门户摘要。
+
+## 从这里开始
+
+| 文档 | 适合读者 | 回答的问题 |
+| --- | --- | --- |
+| [架构](architecture.zh-CN.md) | 所有人 | 哪些组件运行在哪里?为什么这样划分边界? |
+| [Agent Loop](agent-loop.zh-CN.md) | Agent 研究者、后端工程师 | 上下文、工具、调度、证据、记忆和子 Agent 如何工作? |
+| [实验体系](experiments.zh-CN.md) | Agent 研究者 | 如何修改 Loop,同时保持实验可比性和生产安全? |
+| [项目历史](project-history.zh-CN.md) | 维护者 | 已经做出了哪些产品和架构决策? |
+| [Open WebUI 集成](openwebui-integration.zh-CN.md) | 前端、平台工程师 | 哪些职责属于 Open WebUI,哪些仍由 K1412 负责? |
+| [安全模型](security.zh-CN.md) | 安全、平台工程师 | 用户、凭据、工作区和 Docker 如何隔离? |
+| [基础设施地图](infrastructure.zh-CN.md) | 运维人员 | 哪些是共享基础设施,哪些属于 Agent 核心? |
+| [开发与验证](development.zh-CN.md) | 贡献者 | 如何运行、测试并安全修改系统? |
+| [运维手册](operations.zh-CN.md) | 运维人员 | 如何构建、部署、验证、备份和迁移生产环境? |
+
+## 当前产品约定
+
+- 用户只看到一种模式:K1412 Agent Loop。早期的 Chat/Work 双模式已被有意移除。
+- Open WebUI 负责账户、会话、审批、对话存储和聊天外壳;K1412 负责 Agent 行为。
+- 服务端定义四个模型选项:Luna、Terra、Sol、DeepSeek V4 Pro。模型身份和思考能力
+ 分开显示。
+- 每个 Open WebUI 用户都会在专用物理执行主机上获得独立的 Docker 容器、网络和持久卷。
+- 生成文件可以通过已鉴权的 Web UI 浏览和下载,内部依赖目录默认隐藏。
+- Provider 凭据、内部提示词、Docker 权限、SSH 配置和工具服务凭据都不能在浏览器配置。
+
+## 哪些部分属于实验
+
+稳定的平台边界包括鉴权、用户/工作区隔离、持久存储和公开的 OpenAI 兼容 Runtime API。
+计划持续实验的范围包括:
+
+- 上下文选择与压缩;
+- 记忆检索与生命周期;
+- 工具描述、规范化和路由;
+- 并行调度与变更串行化;
+- 子 Agent 委派策略;
+- 证据和完成门禁;
+- 恢复提示词与重试策略;
+- 模型路由与各档预算。
+
+每项实验都应在运行事件中记录版本,并在进入生产环境前针对固定任务集完成评测。详见
+[实验体系](experiments.zh-CN.md)。
diff --git a/docs/agent-loop.md b/docs/agent-loop.md
index ecb2757..dafa72f 100644
--- a/docs/agent-loop.md
+++ b/docs/agent-loop.md
@@ -1,5 +1,7 @@
# Agent loop implementation
+[中文](agent-loop.zh-CN.md) · English
+
## Purpose
The K1412 loop turns a model completion API into an accountable coding Agent.
diff --git a/docs/agent-loop.zh-CN.md b/docs/agent-loop.zh-CN.md
new file mode 100644
index 0000000..52a577d
--- /dev/null
+++ b/docs/agent-loop.zh-CN.md
@@ -0,0 +1,178 @@
+# Agent 循环实现
+
+中文 · [English](agent-loop.md)
+
+## 目标
+
+K1412 循环把模型补全 API 转变为一个可追责的编码 Agent。它最核心的规则很简单:文字声明不能证明工作真的发生过。文件、命令、报告和测试都必须由本次运行中成功的工具事件提供证据。
+
+当前事件元数据使用以下标识:
+
+- 策略:`agent-loop-v3`;
+- 调度器:`safe-parallel-v1`;
+- 上下文策略:`recent-visible-v1`。
+
+这些标识是实验契约的一部分,不是营销版本号。只要行为变化可能影响评测结果,就应更新对应标识。
+
+## 运行状态机
+
+```mermaid
+stateDiagram-v2
+ [*] --> BuildContext
+ BuildContext --> RequestModel
+ RequestModel --> ValidateCalls: 存在工具调用
+ RequestModel --> CheckCompletion: 没有工具调用
+ ValidateCalls --> Schedule
+ Schedule --> Execute
+ Execute --> RecordEvidence
+ RecordEvidence --> RequestModel
+ CheckCompletion --> RequestModel: 证据不足且仍有预算
+ CheckCompletion --> Completed: 证据充分
+ CheckCompletion --> Unverified: 证据不足且重试预算耗尽
+ Completed --> [*]
+ Unverified --> [*]
+```
+
+每次顶层运行都有唯一的 `run_id`。所有事件都包含用户、对话、单调递增序号、时间戳、事件类型和净化后的载荷。
+
+## 上下文策略
+
+`ContextPolicy.prepare` 会:
+
+1. 只接受 system、developer、user 和 assistant 消息;
+2. 从旧的助手消息中移除已渲染的 `` 块,避免把 UI 标记再次送入模型上下文;
+3. 前置 K1412 系统契约;
+4. 最多附加八条持久用户记忆作为上下文,并明确说明它们不是更高优先级的指令;
+5. 从新到旧遍历可见消息,直到达到该模型的字符预算;
+6. 记录被丢弃的旧消息数量。
+
+这一策略有意保持简单且可检查。它目前不会总结旧的对话分支、检索语义化工作区上下文,也不会估算不同提供方的具体 token 切分方式。这些都是未来可以实验的方向。
+
+## 模型契约
+
+`ModelSpec` 是以下配置在服务端的唯一事实来源:
+
+- 公开模型 ID 和提供方模型 ID;
+- 提供方选择;
+- 是否支持思考以及显示标签;
+- 提供方支持时所使用的 reasoning effort;
+- 最大输出 token 数;
+- 最大循环迭代次数;
+- 上下文字符预算。
+
+Luna、Terra 和 Sol 是三个不同的本地模型,各自以布尔值表示是否支持思考;它们不是同一模型的三档推理强度。DeepSeek V4 Pro 使用 DeepSeek 提供方,启用思考,并设置 `reasoning_effort=max`。
+
+如果提供方协议要求,Runtime 会在多轮工具调用之间保留 `reasoning_content`,但不会把隐藏推理发布为用户可见内容。
+
+## 工具目录
+
+工作区工具:
+
+- 获取状态,列出、读取、搜索、写入和补丁修改文件;
+- 执行前台命令;
+- 检查 Git 状态与 diff;
+- 启动、轮询和取消后台进程。
+
+Runtime 状态工具:
+
+- 更新每个对话的计划;
+- 记住、回忆和遗忘持久用户记忆。
+
+委派工具:
+
+- 使用角色、任务和明确的写入策略启动一个有边界的子 Agent。
+
+工具 schema 使用 `additionalProperties: false`,让格式错误的模型参数尽早失败。参数会在策略检查前完成规范化。内容正文和补丁正文不会写入公开运行事件的载荷。
+
+## 基于意图的工具选择
+
+根循环并不总是发送全部工具。轻量级请求分类器会判断任务是否涉及产物、执行、源码、报告、比较、进程、Git、记忆或委派。筛选后的工具目录可以降低模型进行工具决策的难度,同时保留核心工作区工具。
+
+这只是启发式路由,不是权限控制。Gateway 仍然是实际的强制执行边界。
+
+## 调度器
+
+每次模型响应最多请求八个工具调用。调用按原始顺序解析,并划分为连续的组:
+
+- 标为 `parallel_safe` 的工具并发执行;
+- 写操作和其他非并行工具串行执行;
+- 只读子 Agent 可以并行运行;
+- 拥有写权限的子 Agent 串行执行;
+- 子 Agent 不能再次委派。
+
+Gateway 还会按用户串行化工作区写操作。第二层锁非常重要,因为多个 Runtime 请求或浏览器操作可能同时访问同一个工作区。
+
+即使并行组中的任务完成顺序不同,调度器仍会保持结果的原始顺序。
+
+## 防御性规范化与重试保护
+
+循环会修复少量常见的模型格式错误,然后应用策略:
+
+- 拒绝 `python3`、`bash`、`node` 这类没有参数的交互式命令;
+- 拒绝把文档或数据文件当作可执行源码运行;
+- 在写入完整 Python 文件之前拒绝语法无效的内容;
+- 解码弱模型生成的、重复转义的源码布局换行;
+- 跳过内容完全相同且此前成功的写入;
+- 在内容变化前阻止重复执行完全相同的失败写入;
+- 在发生写操作或实质不同的诊断前,阻止重复执行没有变化的失败命令;
+- 对同一批次里的重复命令去重;
+- 限制生成长度、迭代次数和工具批次大小。
+
+在执行层,Bash 启用 `pipefail` 并返回真实退出码。前台工具默认最长运行 900 秒;后台进程具有明确的启动、轮询和取消生命周期。
+
+## 基于证据的完成门禁
+
+循环会从用户请求中推导所需证据。
+
+| 请求类型 | 最低证据要求 |
+| --- | --- |
+| 具体产物 | 一次成功的文件写操作,以及之后一次成功的验证 |
+| 执行/测试/分析 | 至少一次成功的执行或检查 |
+| 源码请求 | 单独的可执行源文件 |
+| 报告请求 | 在执行之后单独写入的报告 |
+| 基准测试/比较报告 | 从成功执行输出中复制的精确数值测量 |
+
+验证可以是之后的文件读取、Git diff/status、命令执行或后台进程轮询。失败的命令会一直保持未解决状态,直到后续执行成功。
+
+如果模型过早尝试完成任务,Runtime 会发出 `completion.rejected`,向模型返回聚焦的恢复指令,并继续循环。经过多次被拒绝的完成检查或迭代次数耗尽后,Runtime 会返回明确的未完成结果,而不是把未经验证的声明包装成成功。
+
+## 委派
+
+根 Agent 可以委派一个边界明确的独立任务。子 Agent 会获得:
+
+- 一个角色和精确任务;
+- 最多八次迭代的缩减预算;
+- 不包含委派工具;
+- 默认仅有只读工具,除非明确请求了写权限。
+
+子 Agent 事件与根运行共用同一事件流,但 `depth` 会增加。当前实现是在单个 Runtime 进程内递归执行,不是分布式队列,也不是持久自治 worker。
+
+## 记忆与计划
+
+持久记忆按用户 ID 隔离。当前优先返回最近记忆,并支持可选的、不区分大小写的子字符串过滤。计划按用户和对话隔离,并且最多只允许一个 `in_progress` 项。
+
+记忆很有价值,但系统有意保持保守:不会从每次对话自动提取事实,也暂未实现向量检索、置信度评分、过期和冲突解决。
+
+## 运行事件模型
+
+重要事件类型包括:
+
+- `run.created`、`context.built`、`run.completed`、`run.failed`、`run.cancelled`;
+- `model.requested`、`model.responded`;
+- `tool.started`、`tool.completed`、`tool.batch_limited`;
+- `completion.rejected`、`completion.unverified`;
+- `agent.spawned`、`agent.completed`。
+
+事件流目前用于驱动 UI 工具详情块,也是未来实现回放、评测、成本分析和 A/B 分组的基础。事件仅存储公开参数和简短摘要,不存储凭据、完整写入正文或隐藏推理。
+
+## 已知限制
+
+- 上下文压缩会直接丢弃旧消息,而不是总结。
+- 记忆检索基于文本而不是语义。
+- 工具意图分类器基于正则表达式。
+- 调度器只并行连续的安全调用,且没有资源成本模型。
+- Runtime 重启后,子 Agent 不会持久存在。
+- 尚未实现实验分组和聚合仪表盘。
+- 提供方返回的 token 统计会被保留,但尚未转换成统一成本模型。
+
+这些都是有意保留的实验方向,记录在[实验方法](experiments.zh-CN.md)中。
diff --git a/docs/architecture.md b/docs/architecture.md
index 0cc284c..b4848f3 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -1,5 +1,7 @@
# Architecture
+[中文](architecture.zh-CN.md) · English
+
## Design objective
K1412 Agent is a multi-user web coding Agent whose loop can be changed
diff --git a/docs/architecture.zh-CN.md b/docs/architecture.zh-CN.md
new file mode 100644
index 0000000..8a923de
--- /dev/null
+++ b/docs/architecture.zh-CN.md
@@ -0,0 +1,150 @@
+# 架构设计
+
+中文 · [English](architecture.md)
+
+## 设计目标
+
+K1412 Agent 是一个多用户网页编码 Agent,其 Agent 循环可以独立于账户系统和用户界面进行修改。架构将高成本的实验面控制在较小范围内:研究者应当能够改变上下文、记忆、调度、工具、委派或完成策略,而不必分叉鉴权、聊天记录、Docker 生命周期或整个前端。
+
+系统有意只保留一条 Agent 路径。早期曾计划在自定义 Work 循环之外并存一条轻量 Chat 循环,但这会重复产品行为、混淆模型选择,并增加前后端必须保持兼容的代码量,因此已经移除。
+
+## 系统上下文
+
+```mermaid
+flowchart LR
+ U["浏览器用户"] --> P["DNS、TLS 与 Nginx Proxy Manager"]
+ P --> W["Open WebUI
鉴权、RBAC、历史记录、界面"]
+ W --> R["Agent Runtime
循环、上下文、记忆、模型路由"]
+ R --> M["模型提供方
K1412 API 或 DeepSeek"]
+ R --> G["Workspace Gateway
身份与执行策略"]
+ G -->|Tailscale + SSH Docker| X["每用户工作区容器
home-node-itx"]
+ W --> DB["PostgreSQL
用户与对话"]
+ R --> DB
+ W --> Q["Redis
协调"]
+```
+
+只有 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 请求
+
+```mermaid
+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 运行,在模型或工具工作期间继续复用已经过期的身份令牌。
+
+### 工作区文件请求
+
+```text
+带 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 循环实现](agent-loop.zh-CN.md)。
+
+## 工作区架构
+
+`local-docker` 与 `ssh-docker` 实现同一套 `ExecutionProvider` 契约。生产环境使用 `ssh-docker`:Gateway 通过只读挂载的专用 SSH 配置,连接 `home-node-itx` 上的 Docker。
+
+每个不可变的 Open WebUI 用户 ID 都映射到一个由 SHA-256 派生的工作区 ID,并且恰好对应一组:
+
+- 名为 `k1412-ws-` 的容器;
+- 名为 `k1412-ws-data-` 的数据卷;
+- 名为 `k1412-ws-net-` 的桥接网络。
+
+容器以 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 项目运行:
+
+- `public`:Web 入口;
+- `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` 中带版本号,使实现变化前后的结果仍然可比较。
diff --git a/docs/development.md b/docs/development.md
index 870745f..829fa23 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -1,5 +1,7 @@
# Development and verification
+[中文](development.zh-CN.md) · English
+
## Repository layout
```text
diff --git a/docs/development.zh-CN.md b/docs/development.zh-CN.md
new file mode 100644
index 0000000..51fa816
--- /dev/null
+++ b/docs/development.zh-CN.md
@@ -0,0 +1,123 @@
+# 开发与验证
+
+中文 · [English](development.md)
+
+## 仓库结构
+
+```text
+agent_platform/
+ runtime/ Agent API、Loop、上下文、模型传输和工具
+ gateway/ 已鉴权的工作区和文件服务
+ auth.py 内部服务与签名身份验证
+ models.py 公开模型目录与服务端预算
+ store.py 运行事件、记忆和计划
+docker/
+ web/ Open WebUI 组件与可复现补丁集
+ *.Dockerfile Web、Runtime、Gateway 和 Workspace 镜像
+docs/
+ site/ /doc/ 的静态源码
+deploy/ 生产 Compose 文件和 SSH Override
+e2e/ 确定性全栈验证器与假 Provider
+scripts/ 验证、评测、审计和密钥辅助脚本
+tests/ 单元测试与真实 Docker 集成测试
+```
+
+## 本地环境
+
+```bash
+python3 -m venv .venv
+.venv/bin/python -m pip install -e '.[dev]'
+cp .env.example .env
+./scripts/init-secrets.sh
+```
+
+真实 Provider 凭据必须保存在受保护的本地环境文件或部署文件中。绝不能将其加入 Git、
+Docker 构建参数、前端代码、测试 Fixture 或日志。
+
+构建用户工作区并启动服务栈:
+
+```bash
+docker compose --profile build-only build workspace-image
+docker compose up --build
+```
+
+打开 。新注册用户的角色为 `pending`,直到初始化管理员批准。
+
+## 快速反馈
+
+```bash
+.venv/bin/ruff check agent_platform tests e2e
+.venv/bin/ruff format --check agent_platform tests e2e
+.venv/bin/pytest
+```
+
+单元测试是确定性的,不需要模型 Provider。
+
+## 真实 Docker 集成
+
+```bash
+docker build -f docker/workspace.Dockerfile \
+ -t k1412-agent-workspace:test .
+RUN_DOCKER_INTEGRATION=1 \
+ .venv/bin/pytest tests/test_docker_workspace.py
+```
+
+测试会创建名称唯一的容器、网络和卷,清理时只删除这些测试资源。
+
+## 完整栈 E2E
+
+```bash
+./scripts/verify-e2e.sh
+```
+
+该脚本会构建一次性六服务栈,使用确定性模型桩,执行鉴权与隔离检查,并在结束时删除测试栈和
+测试卷。
+
+## 真实 Agent 评测
+
+```bash
+set -a
+source /path/to/protected/provider.env
+set +a
+.venv/bin/python scripts/eval-live-work.py --model deepseek-v4-pro
+```
+
+只有检查失败用例时才使用 `--keep-workspace`;检查结束后应删除该名称唯一的评测工作区。
+
+## 镜像审计
+
+```bash
+./scripts/audit-images.sh
+```
+
+审计要求 Python 环境依赖一致,并且最终镜像中不存在可修复的 High/Critical 漏洞。
+Web 镜像还会接受 Python 依赖漏洞审计。
+
+## 文档门户
+
+`docs/site/` 是不依赖构建工具的静态 HTML、CSS、JavaScript 和 JSON。Open WebUI 前端构建
+完成后,`docker/web.Dockerfile` 会将它复制到 `/app/build/doc`。
+
+本地验证:
+
+```bash
+python3 -m http.server 4173 --directory docs/site
+```
+
+生产路径是 `/doc/`,因此所有内部资源都使用相对 URL。Markdown 文档和门户内容必须在同一个
+commit 中保持一致。通过 `docs/site/experiments.json` 添加实验卡片。
+
+## 变更检查清单
+
+推送前:
+
+1. 不要改动工作树中无关的用户变更;
+2. 更新架构或行为文档;
+3. 为策略变更添加确定性测试;
+4. 运行单元测试和格式检查;
+5. Workspace/Gateway 变更必须运行 Docker 集成测试;
+6. 公开模型、鉴权、Loop 或文件交付变更必须运行 E2E;
+7. 审计每个重新构建的镜像;
+8. 生产环境只使用不可变镜像标签;
+9. 验证公网健康状态和一个代表性用户流程;
+10. 在验证完成前保留上一镜像和部署备份。
diff --git a/docs/experiments.md b/docs/experiments.md
index 3b71539..73b2a62 100644
--- a/docs/experiments.md
+++ b/docs/experiments.md
@@ -1,5 +1,7 @@
# Agent experiment system
+[中文](experiments.zh-CN.md) · English
+
## Goal
The project exists to make Agent-loop iteration cheap, observable, and
diff --git a/docs/experiments.zh-CN.md b/docs/experiments.zh-CN.md
new file mode 100644
index 0000000..1bf4877
--- /dev/null
+++ b/docs/experiments.zh-CN.md
@@ -0,0 +1,119 @@
+# Agent 实验体系
+
+中文 · [English](experiments.md)
+
+## 目标
+
+这个项目的目标,是让 Agent 循环的迭代成本更低、过程可观察、结果可回滚。一次实验应当只改变一个行为假设,同时保持鉴权、工作区隔离和交付基础设施不变。
+
+公开门户 中包含一个整理过的实验看板。这份 Markdown 文档定义长期有效的流程,门户目录只是展示层。
+
+## 实验单元
+
+每个实验都应定义:
+
+1. **问题**——希望改善的行为。
+2. **假设**——具体的循环改动和预期方向。
+3. **变体标识**——记录在 `run.created` 中的稳定字符串。
+4. **任务集**——固定提示词、初始工作区夹具和模型档位。
+5. **指标**——成功率、质量、延迟、模型/工具使用量和回归。
+6. **安全约束**——绝不能变差的行为。
+7. **决策规则**——推广、继续迭代或拒绝。
+8. **产物**——代码提交、配置、原始事件导出和报告。
+
+不要比较使用不同任务输入的变体,也不要在没有记录的情况下更换模型、提示词、上下文预算或工作区夹具。
+
+## 推荐指标
+
+主要指标:
+
+- 通过明确证据检查的任务成功率;
+- 已验证交付物比例;
+- 未解决工具失败率;
+- 人工或量表质量评分。
+
+效率指标:
+
+- 端到端延迟;
+- 模型调用与迭代次数;
+- 可用时记录提示和补全 token 数;
+- 工具调用次数;
+- 重复或被策略拦截的调用;
+- 子 Agent 数量与并发度;
+- 估算的模型提供方费用与电费。
+
+可靠性与安全指标:
+
+- 是否正确拒绝了不完整的完成检查点;
+- 假成功率;
+- 工作区逃逸或跨用户访问失败;
+- 是否错误地把非零命令退出码记录为成功;
+- 身份/鉴权失败;
+- Runtime 或工作区重启后的恢复能力。
+
+## 评测层次
+
+### 单元与策略测试
+
+快速、确定性的测试覆盖解析、工具规范化、证据门禁、鉴权、上下文选择、事件持久化和提供方响应规范化。
+
+### 真实 Docker 集成
+
+集成测试创建两个一次性用户,并验证:
+
+- 容器、网络和数据卷彼此独立;
+- 工作区路径受到约束;
+- 文件浏览、下载和归档行为;
+- 根文件系统只读且 capabilities 已移除;
+- Python 虚拟环境可以持久保存;
+- 前台和后台命令都能通过 `pipefail` 返回真实退出码。
+
+### 一次性全栈 E2E
+
+E2E 环境使用确定性的伪模型提供方,覆盖:
+
+- 注册与管理员审批;
+- 精确的四模型公开目录;
+- 自定义 Agent 循环;
+- 有证据支持的文件生成;
+- 文件交付;
+- 每用户隔离。
+
+### 真实模型评测
+
+`scripts/eval-live-work.py` 会让真实模型在一个唯一的一次性工作区中运行,并记录延迟、token 使用、事件、工具数量和输出文件。真实模型评测用于补充确定性测试,不能替代它们。
+
+### 生产冒烟测试
+
+每次部署后,使用低影响的已鉴权任务,确认公网 Web、Runtime、Gateway、远程工作区主机和文件获取都正常。验证后只删除这次冒烟测试产生的临时文件。
+
+## 当前基线
+
+| ID | 领域 | 状态 | 结果 |
+| --- | --- | --- | --- |
+| `loop-v3-evidence` | 完成策略 | 生产基线 | 产物必须先发生写操作,再经过验证;报告和基准测试要求更强证据。 |
+| `safe-parallel-v1` | 调度器 | 生产基线 | 连续只读调用和只读委派可以并发,写操作保持串行。 |
+| `recent-visible-v1` | 上下文 | 生产基线 | 在每模型字符预算内保留最近可见消息,并附加最多八条记忆。 |
+| `workspace-python-v1` | 执行 | 已验证 | 持久 `.venv`、持久用户缓存、900 秒工具上限,以及真实的管道退出码。 |
+| `fresh-gateway-identity-v1` | 鉴权 | 已验证 | Runtime 为每次工具请求重新签发已验证身份,使长任务不受原始令牌过期影响。 |
+
+## 初始待办
+
+1. 具有冲突和过期策略的语义或混合记忆检索。
+2. 结构化旧上下文总结,并通过回放比较效果。
+3. 基于依赖的工具 DAG 调度,而不只是连续安全组。
+4. 子 Agent 结果契约与预算分配。
+5. 按运行记录实验分组,并提供聚合比较 API。
+6. 统一核算本地电费与付费提供方 token 成本。
+7. 工作区配额、出站策略、滥用监控和备份演练。
+
+## 添加实验
+
+1. 把假设和指标加入本文,或新增专门的 `docs/experiments/.md`。
+2. 将实验加入 `docs/site/experiments.json`,用于公开看板。
+3. 在 `run.created` 中新增或更新相关策略、调度器或上下文标识。
+4. 在真实模型运行前先添加确定性测试。
+5. 执行标准验证并保存报告。
+6. 使用不可变镜像标签推广,并保留上一个标签用于回滚。
+
+实验产物中绝不能放入提供方密钥、私有事件载荷、用户标识、真实用户提示词或内部凭据。
diff --git a/docs/infrastructure.md b/docs/infrastructure.md
index 95657cd..9974a73 100644
--- a/docs/infrastructure.md
+++ b/docs/infrastructure.md
@@ -1,5 +1,7 @@
# Infrastructure map
+[中文](infrastructure.zh-CN.md) · English
+
This document separates shared infrastructure from the parts that define
K1412 Agent behavior.
diff --git a/docs/infrastructure.zh-CN.md b/docs/infrastructure.zh-CN.md
new file mode 100644
index 0000000..8ab0735
--- /dev/null
+++ b/docs/infrastructure.zh-CN.md
@@ -0,0 +1,73 @@
+# 基础设施地图
+
+中文 · [English](infrastructure.md)
+
+本文档用于区分共享基础设施与真正决定 K1412 Agent 行为的组件。
+
+## 请求路径
+
+```text
+Internet
+ -> DNS / TLS
+ -> Nginx Proxy Manager
+ -> Unraid NAS:K1412 Web 服务栈
+ -> Tailscale + SSH
+ -> home-node-itx:每用户 Docker 工作区
+```
+
+## 共享基础设施
+
+这些服务让应用可以被访问或部署,但修改它们不会改变 Agent Loop:
+
+| 服务 | 职责 |
+| --- | --- |
+| `git.k1412.top` | 公开源码仓库 |
+| `docker.k1412.top` | 私有不可变容器镜像 |
+| Nginx Proxy Manager | HTTPS 入口和反向代理 |
+| k1412 首页 | 服务发现入口 |
+| Tailscale | NAS 到执行主机的私有网络 |
+| Unraid Compose Manager | NAS 上的应用生命周期管理 |
+| Ollama + 32 GB NVIDIA GPU | Luna、Terra、Sol 模型服务 |
+
+NAS 上的其他应用是相互独立的租户。仅仅因为共享同一台主机,并不意味着它们是 K1412
+Agent 的依赖。
+
+## NAS 上的 K1412 平台服务
+
+| 容器 | 职责 | 是否属于 Agent 核心 |
+| --- | --- | --- |
+| `k1412-agent-web` | Open WebUI 鉴权、RBAC、历史和界面 | 产品外壳 |
+| `k1412-agent-runtime` | Agent Loop 和 Provider 网关 | 是 |
+| `k1412-agent-gateway` | 身份、执行策略、工作区生命周期 | 是 |
+| `k1412-agent-postgres` | Open WebUI 和 Runtime 持久状态 | 状态层 |
+| `k1412-agent-redis` | Open WebUI 协调和缓存 | 状态层 |
+| `k1412-agent-bootstrap` | 幂等的管理员/工具初始化任务 | 运维 |
+
+只有 Web 会发布宿主机端口。Runtime、Gateway、PostgreSQL 和 Redis 均位于私有 Compose
+网络。
+
+## 物理执行主机
+
+`home-node-itx` 是一台专用、可替换的 Docker 执行节点:
+
+- Intel Core i3-12100F,4 核 8 线程;
+- 约 14 GiB 可用内存;
+- 120 GB NVMe 系统盘;
+- 500 GB SSD,格式化为 ext4 并挂载到 `/srv/k1412-data`;
+- Docker Root 位于 `/srv/k1412-data/docker`;
+- 用户工作区卷保存在同一块数据盘。
+
+NAS Gateway 使用专用 Ed25519 密钥和严格的主机密钥校验进行连接。执行主机不保存 Web
+或数据库密钥。更换执行主机只需要迁移 Docker 卷并修改 SSH 配置,不需要修改 Agent Loop。
+
+## Agent 核心
+
+用于持续实验的代码包括:
+
+- `agent_platform/runtime/loop.py`:Loop 和证据策略;
+- `agent_platform/runtime/context.py`:上下文策略;
+- `agent_platform/runtime/tools.py`:工具和调度元数据;
+- `agent_platform/store.py`:事件、计划和记忆;
+- `agent_platform/gateway/`:隔离执行 Provider。
+
+前端品牌、反向代理配置和 Registry 自动化是重要的产品/运维工作,但不属于 Agent 智能。
diff --git a/docs/openwebui-integration.md b/docs/openwebui-integration.md
index 31b884d..f17b35e 100644
--- a/docs/openwebui-integration.md
+++ b/docs/openwebui-integration.md
@@ -1,5 +1,7 @@
# Open WebUI integration
+[中文](openwebui-integration.zh-CN.md) · English
+
## Responsibility split
Open WebUI is the product shell. It owns account registration, login sessions,
diff --git a/docs/openwebui-integration.zh-CN.md b/docs/openwebui-integration.zh-CN.md
new file mode 100644
index 0000000..c26cc0e
--- /dev/null
+++ b/docs/openwebui-integration.zh-CN.md
@@ -0,0 +1,50 @@
+# Open WebUI 集成
+
+中文 · [English](openwebui-integration.md)
+
+## 职责划分
+
+Open WebUI 是产品外壳,负责账户注册、登录会话、RBAC、管理员审批、对话历史和渲染。
+K1412 不会把这些职责再次分叉到第二套鉴权系统或聊天数据库中。
+
+K1412 Runtime 负责唯一面向用户的 Agent Loop:上下文选择、持久记忆、计划、工具 Schema、
+调度、并行读取、子 Agent、证据门禁、实验和运行事件持久化。Workspace Gateway 负责绑定
+用户身份的执行策略和 Docker 生命周期。
+
+这种划分缩小了成本最高的实验面。更换调度器或上下文策略只需要修改 Runtime,不需要同时
+修改账户系统或整个前端。
+
+## 工具与进度展示
+
+Open WebUI 原生能够理解工具调用和推理详情块;对于在其自身后端内部执行的 Loop,它还提供
+更丰富的实时状态机制。K1412 Runtime 是一个外部 OpenAI 兼容 Provider,因此不能直接修改
+Open WebUI 内部的 Socket 状态对象。
+
+因此,K1412 会输出与 Open WebUI 兼容的“已完成工具详情块”。每个工具只展示一次,其中包括:
+
+- 本地化的动作名称;
+- 清理后的参数(不展示文件内容和 Patch 正文);
+- 成功或失败状态;
+- 简洁的纯文本结果,而不是内部 JSON 包装。
+
+K1412 不会暴露隐藏的思维链。界面展示执行进度、工具证据、完成检查和简洁的 Agent 摘要。
+这些信息足以检查行为,又不会把模型私有推理当成产品 API。
+
+早期实现会为每次工具调用分别追加一个“未完成”和一个“已完成”的 `` 元素。由于
+标准 Provider 流是只追加的,Open WebUI 会将它们渲染成重复卡片。Runtime 现在只输出完成
+卡片,工具等待期间由正常的生成指示器表示。
+
+## 补丁与升级成本
+
+Open WebUI 固定为 v0.9.6 和一个精确 commit。Web 镜像在可复现构建中应用一组小型补丁。
+产品专用 Svelte 组件和已鉴权的工作区路由位于 `docker/web/`。
+
+升级 Open WebUI 需要:
+
+1. 更新标签和 commit 固定值;
+2. 使用 `git apply` 重新应用每个补丁;
+3. 构建 Svelte 生产 Bundle;
+4. 运行鉴权、Agent Loop、隔离和文件浏览器 E2E;
+5. 检查上游许可证变化。
+
+K1412 Agent Loop 没有嵌入 Open WebUI,因此大多数 Agent 实验不承担这部分升级成本。
diff --git a/docs/operations.md b/docs/operations.md
index 63e4cf8..3746871 100644
--- a/docs/operations.md
+++ b/docs/operations.md
@@ -1,5 +1,7 @@
# Operations runbook
+[中文](operations.zh-CN.md) · English
+
## Production layout
- Public URL: `https://agent.k1412.top`
diff --git a/docs/operations.zh-CN.md b/docs/operations.zh-CN.md
new file mode 100644
index 0000000..8898ba5
--- /dev/null
+++ b/docs/operations.zh-CN.md
@@ -0,0 +1,106 @@
+# 运维手册
+
+中文 · [English](operations.md)
+
+## 生产布局
+
+- 公开地址:`https://agent.k1412.top`
+- 公开文档:`https://agent.k1412.top/doc/`
+- NAS 项目:`/boot/config/plugins/compose.manager/projects/k1412-agent`
+- 公开主机端口:`12004`
+- 执行提供方:`ssh-docker`
+- 执行主机 Docker root:`/srv/k1412-data/docker`
+- Runtime 模型 API 路由:`http://172.31.0.1:3000`(从 `k1412-agent_egress` 网络访问 NAS 主机网关,绕过公网代理)
+- 执行主机工作区数据卷:名为 `k1412-ws-data-` 的 Docker volume
+
+本文和仓库中都不应存放任何凭据。
+
+## 部署
+
+构建并推送不可变的 `linux/amd64` 镜像:
+
+```bash
+docker buildx build --platform linux/amd64 -f docker/web.Dockerfile \
+ -t docker.k1412.top/wuyang/k1412-agent-web: --push .
+docker buildx build --platform linux/amd64 -f docker/runtime.Dockerfile \
+ -t docker.k1412.top/wuyang/k1412-agent-runtime: --push .
+docker buildx build --platform linux/amd64 -f docker/gateway.Dockerfile \
+ -t docker.k1412.top/wuyang/k1412-agent-gateway: --push .
+docker buildx build --platform linux/amd64 -f docker/workspace.Dockerfile \
+ -t docker.k1412.top/wuyang/k1412-agent-workspace: --push .
+```
+
+把 `deploy/docker-compose.yml`、`deploy/docker-compose.override.yml` 和 `deploy/docker-compose.ssh.yml` 复制到 NAS 项目。只修改受保护 `.env` 中的不可变镜像标签和非敏感设置,使用 `docker compose config` 校验,然后拉取并重建服务。
+
+NAS 部署中的 `MODEL_API_BASE_URL` 应设置为上面列出的内部模型 API 路由。`https://api.k1412.top` 仍是外部 API 入口,但 Runtime 不应让耗时较长的 Agent 推理经过公网反向代理。
+
+设置 `DEEPSEEK_API_BASE_URL=https://api.deepseek.com`,并且只把 `DEEPSEEK_API_KEY` 放在受保护的部署 `.env` 中。这个密钥只由 Runtime 调用 DeepSeek V4 Pro 使用,绝不能加入浏览器、Compose 文件、镜像、仓库或日志。
+
+SSH override 会从 Gateway 中移除 `/var/run/docker.sock`,并把专用 SSH 目录只读挂载到 `/root/.ssh`。
+
+文档门户复制到现有 Web 镜像的 `/app/build/doc`。它和 Open WebUI 使用相同的主机端口与代理路由,因此不需要额外的 Compose 项目、NAS 端口、DNS 记录或 Nginx Proxy Manager 主机。
+
+Workspace 镜像把用户 home、软件包缓存和 Python user base 放在 `/workspace/.agent` 下;约定的项目虚拟环境是 `/workspace/.venv`。将 `WORKSPACE_IMAGE` 更新为新的不可变标签后,Gateway 会在每个过期工作区下次被访问时替换其容器,同时保留用户的命名数据卷。
+
+## Ollama 模型常驻
+
+NAS Ollama 服务通过以下配置让 Luna、Terra 和 Sol 保持常驻:
+
+- `OLLAMA_KEEP_ALIVE=-1`;
+- `OLLAMA_MAX_LOADED_MODELS=3`;
+- `OLLAMA_NUM_PARALLEL=1`;
+- `OLLAMA_GPU_OVERHEAD=2147483648`;
+- 启用 Flash Attention 和 `q8_0` KV cache。
+
+三个 32K 上下文 runner 的实测 GPU 分配量分别约为 2.88 GB、4.12 GB 和 18.44 GB,合计约 25.44 GB,占用 32 GB GPU。除非重新进行同时加载测试并为推理开销预留容量,否则不要增加上下文长度或单模型并行度。
+
+持久化 Unraid 模板位于 `/boot/config/plugins/dockerMan/templates-user/my-ollama.xml`。`/boot/config/plugins/dynamix/k1412-ollama.cron` 中有一个带锁、幂等的五分钟检查任务,在主机或容器重启后只预热缺失的模型。恢复后应确认 `/api/ps` 列出全部三个模型,并且到期时间为无限。
+
+## 工作区迁移
+
+更换执行主机前:
+
+1. 停止 Gateway,阻止新的工作区写操作开始;
+2. 使用 `app.k1412.component=user-workspace` 标签/名称前缀枚举容器和数据卷;
+3. 停止每个工作区容器;
+4. 将每个命名数据卷以 tar 流传输到新 Docker 主机上名称完全相同的数据卷;
+5. 在新主机拉取精确的 Workspace 镜像;
+6. 使用新的 SSH 目标启动 Gateway;
+7. 对每个迁移后的数据卷验证文件列表和至少一次读取;
+8. 在验证窗口结束前保留旧数据卷。
+
+数据卷名称只包含用户 ID 哈希,不包含邮箱或显示名称。
+
+## 验证
+
+每个版本都要执行:
+
+```bash
+.venv/bin/pytest
+RUN_DOCKER_INTEGRATION=1 .venv/bin/pytest tests/test_docker_workspace.py
+./scripts/verify-e2e.sh
+./scripts/audit-images.sh
+```
+
+生产冒烟检查必须覆盖:
+
+- 匿名请求被重定向或拒绝;
+- 已批准用户只能看到精确的四个 Agent 模型;
+- 每个模型都经过自定义 Agent 循环;
+- Agent 创建并验证一个真实文件;
+- 文件浏览器能列出并下载该文件;
+- 第二个用户无法看到它;
+- Gateway 健康检查报告 `ssh-docker`;
+- 工作区容器存在于物理执行主机,而不是 NAS。
+
+## 备份优先级
+
+依次备份:
+
+1. PostgreSQL;
+2. Open WebUI 数据卷;
+3. PostgreSQL 中的 Runtime 状态;
+4. 执行主机上的每用户工作区数据卷;
+5. 通过私有基础设施备份流程保存受保护的部署 `.env` 与 SSH 目录。
+
+Redis 是可以重建的协调状态,优先级低于数据库和工作区数据卷。
diff --git a/docs/project-history.md b/docs/project-history.md
index 524cf56..16342e7 100644
--- a/docs/project-history.md
+++ b/docs/project-history.md
@@ -1,5 +1,7 @@
# Project history and decisions
+[中文](project-history.zh-CN.md) · English
+
This record summarizes the rebuild that began on 2026-07-26. Git remains the
source of truth for exact changes; this document captures product intent and
architecture decisions that are otherwise difficult to reconstruct from
diff --git a/docs/project-history.zh-CN.md b/docs/project-history.zh-CN.md
new file mode 100644
index 0000000..3777ea8
--- /dev/null
+++ b/docs/project-history.zh-CN.md
@@ -0,0 +1,105 @@
+# 项目沿革与决策
+
+中文 · [English](project-history.md)
+
+这份记录总结了从 2026-07-26 开始的重构。Git 仍是精确变更的事实来源;本文记录产品意图和架构决策,因为这些信息很难从单个提交中还原。
+
+## 1. 从零重建网页 Agent
+
+原有的业务/数据专用应用被明确作为一个不要求向后兼容的新项目重建。业务 Skill、领域工作流和用户可配置的模型提供方设置都被移出范围。保留的核心需求是通用编码 Agent 能力,以及可以独立演进的 Agent 循环。
+
+决策:
+
+- 构建一个通用的多用户网页 Agent;
+- 保留编码、Shell、文件、Git、进程、计划、记忆和委派能力;
+- 将基础设施和产品外壳与 Agent 智能分离。
+
+## 2. 以 Open WebUI 作为产品外壳
+
+项目没有继续维护第二套完整的账户/聊天前端,而是固定使用 Open WebUI v0.9.6,并做少量补丁。它提供注册、登录、RBAC、管理员审批、对话历史和熟悉的聊天交互。
+
+K1412 保留 Agent 循环、提供方路由、记忆、工具、调度、事件和工作区执行的所有权。这样既降低了 Agent 实验成本,也没有让循环依赖 Open WebUI 内部实现。
+
+## 3. 单一 Agent 路径,而不是 Chat 与 Work
+
+早期设计曾考虑在 Open WebUI 原生 Chat 循环和 K1412 Work 循环之间切换。最终产品简化为一条 Agent 路径,原因是:
+
+- 用户不应被迫理解两套编排引擎;
+- 在两种模式间继续对话会产生含糊状态;
+- 两个循环会让前端状态和验证行为翻倍;
+- 自定义循环才是项目最重要的研究资产。
+
+保留下来的界面是 Agent 优先的聊天界面,模型选择由服务端管理。
+
+## 4. 模型与思考语义
+
+原先的“轻度/中/高推理”标签把不同模型和可调推理强度混为一谈。现在 UI 和后端会区分模型身份与思考能力:
+
+- Luna、Terra 和 Sol 是三个不同的 K1412/Ollama 本地模型;
+- 每个模型都标为支持思考,但没有可调节的强度档位;
+- DeepSeek V4 Pro 是云端极高档位,使用最大推理强度。
+
+提供方模型 ID、URL、凭据和调优参数都保留在服务端。
+
+## 5. 多用户远程工作区
+
+每个用户都有一个由不可变用户 ID 的哈希派生出的专属 Docker 容器、网络和持久数据卷。执行环境通过 SSH Docker 从 NAS 迁移到物理机 `home-node-itx`。
+
+物理节点的准备包括:
+
+- 一块挂载到 `/srv/k1412-data` 的 ext4 500 GB 数据盘;
+- Docker root 位于 `/srv/k1412-data/docker`;
+- 严格的 SSH 主机校验;
+- 不存放应用或数据库密钥。
+
+本地与 SSH 执行提供方保持同一套接口,因此以后更换主机时不需要改变 Agent 行为。
+
+## 6. 文件交付与聚焦的界面
+
+前端围绕 Agent 进行了简化:
+
+- K1412 产品名称与视觉资产;
+- 紧凑的模型选择器,并单独显示思考状态;
+- 不向用户暴露提供方、工具或系统提示词设置;
+- 已鉴权的工作区文件浏览器;
+- 单文件下载和目录流式归档。
+
+Open WebUI 的署名与许可证要求仍有完整记录。
+
+## 7. 从失败任务分析中加固循环
+
+真实的排序脚本/报告任务暴露了模型能力弱点和平台缺陷。循环随后逐步强化:
+
+- 要求生成真实文件,而不是只做文字声明;
+- 要求源码与报告分别形成文件;
+- 先执行源码,再写报告;
+- 要求使用实测的基准数值;
+- 拒绝无效 Python 和转义换行造成的版式损坏;
+- 从失败的验证中恢复;
+- 防止无变化的重试循环和重复批次;
+- 即使输出经过管道,也保留前序命令的失败状态;
+- 限制迭代、工具批次、模型输出和重试预算。
+
+这些约束并不被假定为永久最优。它们是当前基线,只应通过有测量结果的实验来删除或修改。
+
+## 8. Python 环境与长任务可靠性
+
+生产调试发现了三个平台问题:
+
+1. 容器根文件系统只读,但 `pip` 默认写入 `/home/agent`;
+2. `/tmp` 以 `noexec` 挂载;
+3. Shell 管道返回最后一个命令的状态,掩盖了安装失败。
+
+修复方案把 home、缓存、用户软件包和虚拟环境移动到持久工作区,启用 Bash `pipefail`,把工具超时延长到 900 秒,并让 Agent 使用受支持的 `.venv` 工作流。
+
+同一次排查还发现,五分钟有效期的身份令牌会在一次长任务的每个工具调用中重复使用。现在 Runtime 会在原始请求身份校验通过后,为每次 Gateway 调用重新签发短期身份。
+
+生产验证实际创建了 `.venv`,安装并导入 `psutil 7.2.2`,通过 Web API 下载了生成的证明文件,随后删除了临时证明文件。
+
+## 9. 文档与实验门户
+
+仓库文档围绕架构、Agent 实现、安全、运维、项目沿革、开发和实验重新组织。整理后的公开门户打包在现有 Web 镜像中,通过 `/doc/` 提供,因此无需第二个服务或代理规则。
+
+## 当前方向
+
+平台现在已经适合开展受控的 Agent 循环实验。下一阶段应优先建设评测数据、上下文/记忆实验、调度器设计、委派契约、成本核算,以及针对恶意多租户环境的加固,而不是增加特定业务工作流。
diff --git a/docs/security.md b/docs/security.md
index 52b81ce..f03d77d 100644
--- a/docs/security.md
+++ b/docs/security.md
@@ -1,5 +1,7 @@
# Security model
+[中文](security.zh-CN.md) · English
+
## Trust boundaries
Only Open WebUI is public. Runtime, Gateway, PostgreSQL, and Redis are on an
diff --git a/docs/security.zh-CN.md b/docs/security.zh-CN.md
new file mode 100644
index 0000000..f54a4fe
--- /dev/null
+++ b/docs/security.zh-CN.md
@@ -0,0 +1,67 @@
+# 安全模型
+
+中文 · [English](security.md)
+
+## 信任边界
+
+只有 Open WebUI 对公网开放。Runtime、Gateway、PostgreSQL 和 Redis 位于内部 Compose
+网络。
+
+Open WebUI 会转发一个短期 HS256 用户 JWT。Runtime 和 Gateway 会验证其签名、签发者、
+过期时间和主体,并且还要求独立的服务 Bearer Key。因此,仅复制用户身份 Token 不能调用
+任何内部服务。Runtime 在接受公开请求时验证一次身份,之后每次调用 Gateway 都会基于已验证
+身份重新签发一个新的短期 Token。长时间运行的 Agent 不会复用已经过期的原始请求 Token。
+
+浏览器永远不会获得:
+
+- 上游 Provider URL 或 API Key;
+- Provider 模型 ID;
+- Workspace Gateway 服务密钥;
+- 用户身份签名密钥;
+- Docker、SSH、MCP/OpenAPI、提示词或实验配置。
+
+## 工作区隔离
+
+每个用户都会获得独立容器和命名卷,其名称来自不可变 Open WebUI 用户 ID 的 SHA-256 哈希。
+容器:
+
+- 以 UID/GID 1000 运行;
+- 使用只读根文件系统和可写 `/workspace` 卷;
+- 将 Python 虚拟环境、用户软件包和缓存状态保存在持久化 `/workspace` 卷中;
+- 允许出网时使用专用的每用户 Bridge 网络;
+- 丢弃全部 Linux Capability;
+- 启用 `no-new-privileges`;
+- 设置内存、CPU 和 PID 限制;
+- 不挂载 Docker Socket;
+- 不向宿主机发布任何端口。
+
+只有 Workspace Gateway 在本地模式下获得 Docker Socket。Runtime 和 Open WebUI 永远不会
+获得它。远程模式会移除 Socket 挂载,将指定 SSH 配置只读挂载,并把 Docker 权限转移到通过
+SSH 连接的执行主机。
+
+工作区路径会被规范化,任何逃逸 `/workspace` 的路径都会被拒绝。文件浏览和下载使用同一
+身份边界。单文件下载默认限制为 128 MiB,目录归档采用流式传输。
+
+## 部署密钥
+
+密钥只存在于 Git 之外、权限为 600 的部署 `.env` 中。镜像不包含 Provider 或基础设施凭据。
+应用代码不会把 Provider 凭据返回浏览器、写入日志或提交到 Git。
+
+Web 镜像从固定 commit 的 Open WebUI 版本构建,Node 和 Python 基础镜像均固定 Digest。
+RAG/向量、云存储、媒体模型、浏览器自动化和未使用的密码学依赖被排除。发布验收会同时审计
+仓库环境与最终 Web 镜像。`scripts/audit-web-image.sh` 要求:
+
+- `pip check` 报告 Python 环境依赖一致;
+- `pip-audit` 报告零个已知 Python 漏洞,且不忽略任何漏洞 ID;
+- Trivy 报告零个可修复的 High/Critical 操作系统或库漏洞。
+
+`scripts/audit-images.sh` 对 Web、Runtime、Gateway 和用户 Workspace 镜像应用相同的
+High/Critical 可修复漏洞门禁。截至 2026-07-26,四个镜像均通过。Trivy 还在 Web 镜像中
+报告了 38 个 Debian 尚未提供修复的 High/Critical 问题;发布门禁通过
+`--ignore-unfixed` 单独记录它们,而不是假装应用层修改能够修复。
+
+## 面向恶意公网用户前仍需加强
+
+工作区运行在专用物理 Docker 主机上,与公网 Web 和数据库服务分离,但 Docker Daemon
+仍是高价值边界。在将系统视为恶意多租户基础设施之前,还应加入每工作区出网策略、镜像签名、
+集中审计保留、备份恢复演练、配额和资源滥用告警。
diff --git a/docs/site/index.html b/docs/site/index.html
index 5d8dddb..095e5a1 100644
--- a/docs/site/index.html
+++ b/docs/site/index.html
@@ -537,29 +537,29 @@
- 打开 Git 仓库 ↗
+ 打开中文文档 ↗
diff --git a/tests/test_docs_translations.py b/tests/test_docs_translations.py
new file mode 100644
index 0000000..0455bf8
--- /dev/null
+++ b/tests/test_docs_translations.py
@@ -0,0 +1,52 @@
+from __future__ import annotations
+
+from pathlib import Path
+
+REPOSITORY_ROOT = Path(__file__).parents[1]
+ENGLISH_DOCUMENTS = (
+ Path("README.md"),
+ Path("docs/README.md"),
+ Path("docs/agent-loop.md"),
+ Path("docs/architecture.md"),
+ Path("docs/development.md"),
+ Path("docs/experiments.md"),
+ Path("docs/infrastructure.md"),
+ Path("docs/openwebui-integration.md"),
+ Path("docs/operations.md"),
+ Path("docs/project-history.md"),
+ Path("docs/security.md"),
+)
+
+
+def _chinese_counterpart(english: Path) -> Path:
+ return english.with_name(f"{english.stem}.zh-CN{english.suffix}")
+
+
+def test_every_project_document_has_a_chinese_counterpart() -> None:
+ for english_relative in ENGLISH_DOCUMENTS:
+ chinese_relative = _chinese_counterpart(english_relative)
+ english = REPOSITORY_ROOT / english_relative
+ chinese = REPOSITORY_ROOT / chinese_relative
+
+ assert english.is_file(), english_relative
+ assert chinese.is_file(), chinese_relative
+ assert chinese.stat().st_size > 500, chinese_relative
+ assert f"]({chinese.name})" in english.read_text(), english_relative
+ english_link = "README.md" if chinese.name == "README.zh-CN.md" else english.name
+ assert f"]({english_link})" in chinese.read_text(), chinese_relative
+
+
+def test_public_portal_links_to_the_chinese_documentation() -> None:
+ html = (REPOSITORY_ROOT / "docs" / "site" / "index.html").read_text()
+
+ assert "打开中文文档" in html
+ assert "docs/README.zh-CN.md" in html
+ for relative in (
+ Path("README.md"),
+ Path("docs/agent-loop.md"),
+ Path("docs/architecture.md"),
+ Path("docs/experiments.md"),
+ Path("docs/operations.md"),
+ Path("docs/security.md"),
+ ):
+ assert _chinese_counterpart(relative).as_posix() in html