第14章 工程全景:API、控制台、数据与测试
把学习手册落到仓库协作:看清接口、界面、数据、测试与发布
14.1 本章导读
前 13 章沿着“跑起来 → 架构 → 核心机制 → 扩展 → 治理 → 生产”的主线读源码。这一章换成工程视角,把散落在 README、Maven 模块、Controller、SQL、静态资源和 CI 中的事实收拢成一张全景图。读完后,你应该能独立回答三个问题:接口契约在哪里;修改一个功能要动哪些模块;提交前怎样验证不破坏主线。
本章定位:不是重复讲解 ReAct、插件和审批原理,而是给已经理解机制的人提供工程地图。你可以把它当作代码评审、功能交接和上线前的检查底稿。
14.2 Maven 模块矩阵
项目由 7 个业务模块加 2 个插件工程组成。业务代码的依赖方向遵守六边形架构:trigger 只转协议,case 编排用例,domain 持有业务与端口,infrastructure 实现技术适配。
| 模块 | 工程职责 | 代表性内容 | 快照规模 |
|---|---|---|---|
trigger | HTTP/SSE、鉴权拦截器、DTO 绑定 | AgentController、命令/查询 Controller、ApiKeyAuthInterceptor | 25 个 Java 文件,1,398 行 |
api | 用例接口与 API 契约 | IAgentApi、查询/命令 API、请求与响应记录 | 56 个 Java 文件,2,040 行 |
case | 用例编排与责任链 | Agent 意图/分发/收集节点,任务提交与权限检查 | 45 个 Java 文件,1,761 行 |
domain | 业务内核、聚合、端口与领域服务 | ReactLoopAgent、SessionLog、工具、插件、MCP、工作流 | 344 个 Java 文件,17,861 行 |
infrastructure | 数据库、LLM、执行环境和外部网关适配 | MyBatis DAO、DeepSeek 网关、插件/工具网关、本地工作流引擎 | 88 个 Java 文件,9,424 行 |
types | 插件 SPI 与公共契约 | ToolDefinition、执行输入输出、运行上下文 | 21 个 Java 文件,998 行 |
app | Spring Boot 装配与静态控制台 | DeepseekHarnessJavaApplication、预置插件装载器、static/ | 3 个 Java 文件,155 行 |
plugins/sample-tools-plugin | Java Native 插件样例 | 示例工具、插件入口与打包 | 可安装 JAR |
plugins/plugin-archetype | 插件脚手架 | Maven Archetype、插件主类、示例工具模板 | 新插件生成器 |
行数只是导航用的工程快照,不是质量指标。真正需要记住的是边界:domain 不依赖 Spring Web,infrastructure 承接可替换适配器,trigger 不写业务规则。
14.3 REST 契约全景
HTTP 层按“资源 + 动作”分组。下表来自 trigger/http 中的实际路由,可以直接对照 Controller 评审新增接口。
| 能力 | 端点 | 说明 |
|---|---|---|
| Agent | POST /api/agent/message | 阻塞式对话,返回完整消息结果 |
POST /api/agent/stream | SSE 流式对话,输出 meta、reasoning、chunk、step_break、tool_result、finish、done | |
GET /api/agent/{agentId}/status | 读取 Agent 运行状态 | |
POST /api/agent/{agentId}/cancel | 取消正在执行的 Agent | |
| 工作区 | GET /api/agent/workspaces | 读取工作区列表 |
GET /api/agent/workspaces/list | 按可选路径读取目录列表 | |
POST /api/agent/workspaces | 创建工作区 | |
DELETE /api/agent/workspaces/{name} | 删除工作区 | |
PATCH /api/agent/workspaces/{name} | 重命名工作区 | |
PUT /api/agent/workspaces/order | 更新工作区排序 | |
| 任务与审批 | POST /api/harness/tasks/submit | 提交 Harness Task |
GET/POST /api/harness/goals/{sessionId} | 查询或推进目标状态 | |
GET /api/harness/approvals/pending | 读取提交期待审批项 | |
POST /api/harness/approvals/{sessionId}/approve | 通过提交期审批 | |
| 运行期审批 | GET /api/harness/approvals/runtime/pending | 轮询 Agent 挂起的实时审批 |
POST /api/harness/approvals/runtime/{approvalId}/resolve | ALLOW_ONCE、ALLOW_SESSION、DENY 或 CANCEL | |
| 模型配置 | GET/POST /api/harness/settings/models | 读取或保存模型设置 |
DELETE /api/harness/settings/models/{channelCode} | 删除模型渠道 | |
POST /api/harness/settings/models/discover | 从模型网关发现模型 | |
GET /api/harness/runtime/models | 读取运行时可选模型 | |
| 插件 | GET /api/harness/plugins | 插件列表 |
GET /api/harness/plugins/{pluginId}/status | 插件运行状态 | |
POST /api/harness/plugins/install|activate|run | 安装、激活和执行插件 | |
POST /api/harness/plugins/{pluginId}/enable | 启用插件 | |
POST /api/harness/plugins/{pluginId}/disable | 停用插件 | |
POST /api/harness/plugins/{pluginId}/uninstall | 卸载插件 | |
| 终端 | POST /api/harness/terminal/sessions | 打开终端会话 |
GET /api/harness/terminal/sessions | 终端会话列表 | |
POST /api/harness/terminal/sessions/{id}/send | 发送命令 | |
GET /api/harness/terminal/sessions/{id}/read | 增量读取输出 | |
POST /api/harness/terminal/sessions/{id}/close | 关闭终端 | |
| 工作流 | POST /api/workflow/start | 启动工作流,返回 runId |
POST /api/workflow/{runId}/cancel | 取消运行 | |
POST /api/workflow/stream | 以 SSE 方式启动并输出运行状态 | |
| 控制台回放 | GET /api/harness/console/sessions | 分页历史会话列表 |
GET /api/harness/console/sessions/{sessionId}/messages | 历史消息回放 | |
POST /api/harness/sessions/{sessionId}/restore | 恢复历史会话并重建消息轨迹 |
curl -X POST http://localhost:8090/api/harness/approvals/runtime/{approvalId}/resolve \
-H 'Content-Type: application/json' \
-d '{"verdict":"ALLOW_ONCE"}'
鉴权由 ApiKeyAuthInterceptor 统一处理:静态资源和 /actuator 放行;harness.auth.api-keys 为空时本地全放行;非空时接受 X-API-Key 或 Authorization: Bearer。新增公开路径前必须重新评估安全边界。
14.4 Web 控制台与前端资产
内置控制台位于 deepseek-harness-java-app/src/main/resources/static,不是打包后的前端工程,也没有 Node 构建链。核心文件只有两个:app.js 负责状态、请求、会话渲染、工具卡片、推理过程和会话管理;app.css 负责界面样式。第三方能力全部本地化:marked 解析 Markdown,purify 过滤 HTML,highlight-core 与 highlight-common 高亮代码。
| 资产 | 职责 | 工程要点 |
|---|---|---|
index.html | 控制台骨架 | 轻量入口,适合随 Spring Boot 一起发布 |
app.js | 调用 Agent、工作区、模型、插件、审批、控制台回放等 API | 无框架依赖;修改时保持事件边界清晰 |
app.css | 完整 UI 样式与响应式布局 | 避免引入运行时 CSS 框架 |
lib/* | 本地 Markdown、XSS 过滤和代码高亮 | 升级前核对许可证、体积和安全公告 |
控制台的核心体验包括:新建/切换/重命名/删除工作区,创建、恢复、重命名、删除和清空会话,分页加载历史会话,懒加载历史消息,普通或流式对话,渲染工具调用卡片与推理过程,取消运行,管理模型,启停插件,审批任务,以及查看历史消息。排查 UI 问题时不要只看后端日志,应先在 Network 面板确认请求路径、响应码和 SSE 帧,再进入 Controller 或用例层。
14.5 数据与事件模型
MySQL 初始化脚本定义 12 张表;standalone profile 使用 H2 文件库并以 MODE=MySQL 兼容运行,两套数据源共用同一 schema。事件相关表支持过程记录、界面投影与历史回放。
| 表 | 作用 | 工程关注点 |
|---|---|---|
harness_session | 会话聚合基础状态 | 大多数记录通过 session_id 关联 |
harness_session_header | 会话头、目录、父会话、委托深度等上下文 | 影响 Agent 运行边界与提示组装 |
harness_session_event | 会话过程事件 | 注意 JSON 载荷体积 |
harness_session_event_log | 带序号的事件日志与投影操作 | session_id + seq 唯一,是回放顺序的关键 |
harness_task | 任务提交与执行状态 | 任务队列与审批链路的入口记录 |
harness_goal_state | 目标状态 | 支撑长期任务与目标恢复 |
harness_plugin_installation | 插件安装元数据 | 保存来源、版本和运行类型 |
harness_plugin_runtime_binding | 插件运行绑定 | 隔离插件生命周期与安装记录 |
harness_model_setting | 模型与 Provider 配置 | 密钥应注入环境,不提交仓库 |
harness_tool_catalog | 工具目录 | 初始化文件、Shell、Web、MCP 工具 |
harness_tool_profile_binding | 工具与 Profile 绑定 | 控制不同运行形态可见的工具集 |
迁移注意:当前初始化脚本偏向首次建库,修改已有表结构时不要只改 CREATE TABLE;应引入可重复执行的迁移脚本或使用 Flyway/Liquibase,并在 H2 与 MySQL 上同时验证。
14.6 测试与质量门禁
当前共有 23 个 Java 测试类,分布在六个模块:api 1 个,case 5 个,domain 8 个,infrastructure 7 个,app 1 个,trigger 1 个。它们覆盖 ReAct 循环、意图与分发、工具执行、Hook、超时、提示组装、目标、终端、待办、插件桥接、MCP 配置、扩展配置、审批链路和若干查询/命令用例。
# 全量测试
mvn test
# 只验证领域内核
mvn -pl deepseek-harness-java-domain test
# 快速打包
mvn package -DskipTests
# 完整发布前验证
mvn clean verify
当前缺口:app 与 trigger 没有 Java 测试,HTTP/SSE 层缺少端到端契约测试;CI 也没有独立 Maven 测试工作流。改动 Controller、鉴权、SSE 或 Dockerfile 时,必须本地补跑构建并手工验证控制台关键路径。
建议按变更类型选择验证范围:
| 变更 | 最低验证 | 更完整验证 |
|---|---|---|
| 领域服务、Agent、工具 | mvn -pl deepseek-harness-java-domain test | 全量 mvn test + 本地对话 |
| Controller/SSE/API DTO | 启动应用,curl 验证请求与响应 | 为 trigger 增加 MockMvc/WebTestClient 测试 |
| SQL/DAO | H2 standalone 启动 | MySQL 容器启动 + 数据迁移验证 |
| 静态 UI | 打开控制台完成一次对话 | 验证工具卡片、取消、审批和历史回放 |
| 插件/MCP | 启动后查看插件列表 | 安装、启用、执行、停用、卸载全链路 |
14.7 配置、发布与观测
本地最小配置在 deepseek-harness-java-app/src/main/resources/harness.yml:默认端口 8090 由 application.yml 提供,模型网关默认 http://127.0.0.1:8777/v1。模型、审批、沙箱、Skills、MCP 和插件使用同一配置入口。发布镜像时至少注入 DEEPSEEK_API_KEY,并按环境覆盖数据库与认证配置。
| 场景 | 关键配置 | 检查点 |
|---|---|---|
| 本地零依赖 | --spring.profiles.active=standalone | H2 文件、自动 schema、无外部 MySQL |
| 开发 MySQL | spring.datasource.* | 可连接、schema 可初始化、日志可读 |
| 生产 API | harness.auth.api-keys | 非空、可轮换、不把服务直接暴露公网 |
| 模型调用 | base-url、api-key、default-model | 网关兼容、模型可用、密钥来自环境 |
| 审批沙箱 | harness.approval.*、harness.sandbox.* | 高风险工具默认受限,容器和目录权限收紧 |
| 有效配置 | GET /api/harness/config/effective | 启动后检查合并结果;敏感项应被脱敏 |
| 扩展生态 | harness.extensions.* | Skills、MCP 和插件统一装配与校验 |
发布链路由 .github/workflows/docker-build-push.yml 处理:推送 tag-v* 标签(例如 tag-v0.1.6)或手动触发时,Workflow 使用多阶段 Dockerfile 构建 JAR,并同时推送到 fuzhengwei/deepseek-harness-java 与 registry.cn-hangzhou.aliyuncs.com/xfg-studio/deepseek-harness-java。标签构建会发布去掉 tag-v 前缀的版本号与 latest;手动触发时可输入 0.1.7、v0.1.7 或 tag-v0.1.7,留空则发布 latest 与短 SHA 标签。仓库当前没有独立的 Maven CI 工作流,这是后续工程化最值得补的一环。
上线后至少观测四类信号:JVM 与 HTTP 指标、模型请求耗时与失败率、工具执行耗时与拒绝原因、数据库连接池和事件表增长。/actuator 方便排障,但公网部署必须放在认证、网关或内网之后。
14.8 工程协作与变更检查清单
项目已经很大,单靠“看一下代码”很难保证变更质量。建议把下面清单放进 PR 描述,评审时逐项确认。
| 阶段 | 检查项 |
|---|---|
| 定位 | 明确属于 Trigger 协议、Case 编排、Domain 业务、Infrastructure 适配、Types 契约或 App 装配中的哪一层。 |
| 契约 | 新增 API 先定义 request/response record;字段命名、错误码、状态语义与旧接口保持一致。 |
| 架构 | 不把 HTTP 类型引入 Domain;不把 DAO 直接暴露给 Trigger;外部能力都通过端口进入领域层。 |
| 安全 | 评估路径、输入、Shell、文件、插件、MCP、密钥和日志泄漏;高风险动作必须接入权限或审批。 |
| 数据 | 确认索引、唯一键、事件顺序、JSON 大小、H2/MySQL 兼容和历史数据迁移。 |
| 并发 | 确认线程池、取消、超时、重试、SSE 生命周期和阻塞点;避免无界资源无限增长。 |
| 测试 | 补领域测试或 HTTP 契约测试;跑 mvn test;必要时完成手工冒烟。 |
| 文档 | 更新 README、相关章节或插件指南;配置变化要有默认值和风险说明。 |
| 发布 | 确认镜像标签、数据库迁移、回滚方式、监控告警和事件表归档。 |
14.9 常用仓库地图
| 文件/目录 | 用途 |
|---|---|
README.md | 项目介绍、快速启动、架构、机制、API、配置、边界与运维清单 |
pom.xml | Maven 模块清单和 Java/Spring Boot 版本管理 |
Dockerfile | 依赖缓存、构建镜像、运行镜像三层结构 |
docker-compose.yml | 挂载 data、plugins、.dsh、workspaces 并运行 standalone |
docs/dev-ops/mysql/sql/deepseek_harness_java.sql | MySQL 建库脚本和初始工具数据 |
docs/html | 本学习手册的静态站点与构建脚本 |
docs/md | 架构图、领域设计与插件开发补充文档 |
.github/workflows | Docker 镜像构建与推送流水线 |
14.10 小结与下一章预告
第 14 章把学习视角切回仓库:模块矩阵决定修改边界,REST 表决定前后端契约,数据表决定事实存放方式,测试命令决定提交信心,CI 和 Docker 决定交付路径。你现在既有“为什么这样设计”的原理链,也有“怎么安全地改”的工程链。
实践建议:不要把第 14 章当作终点。挑一个真实需求,沿着 API → Case → Domain → Infrastructure → Test → 文档走一遍;如果一次变更无法清楚说出自己跨越了哪些模块,通常说明设计还没有收敛。
下一章继续进入平台能力:工作流如何启动、取消和收敛结果,子代理又如何把复杂任务委派给另一个执行者。