feat(investigation): publish incremental reference graphs
Publish graph deltas using the Server-owned cumulative graph contract. Validate historical comparison chart publications and consolidate Agent documentation.
This commit is contained in:
@@ -0,0 +1,622 @@
|
||||
# 早期 Harness 架构资料
|
||||
|
||||
> 历史归档:由旧 `HARNESS_INTRODUCTION.md` 与 `AGENT_HARNESS_REPORT.md` 合并保存。文中 CLI、render_ref、独立结果引用存储、旧模型默认值和旧会话目录均不能作为当前实现或维护依据。当前架构见 [Agent 架构](../architecture.md),配置见 [仓库 README](../../README.md)。原文中的覆盖声明与外部参考未在本次文档整理中重新验证。
|
||||
|
||||
## 原文件:HARNESS_INTRODUCTION.md
|
||||
|
||||
# TJWaterAgent Harness 框架设计
|
||||
|
||||
> 如果把 AI 大模型比作"大脑",Harness 就是让大脑能**安全、可靠、有条理地**在真实水务业务中工作的整套 **"神经 + 手脚 + 记忆"系统**。
|
||||
|
||||
---
|
||||
|
||||
## 一张图看懂
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor 用户 as 👤 用户
|
||||
participant 前端 as 🖥️ 前端界面<br/>(聊天 / 地图 / 图表)
|
||||
participant 接口 as 🚪 接口层<br/>接收问题、返回结果
|
||||
participant 会话 as 📋 会话层<br/>“你是谁?哪个项目?”
|
||||
participant 上下文 as 📦 上下文层<br/>整理背景资料<br/>历史对话 · 偏好 · 技能
|
||||
participant 适配 as 🔌 适配层<br/>接通AI大脑<br/>(嵌入/远程)
|
||||
participant 工具 as 🔧 工具层<br/>AI的“手和脚”<br/>调CLI · 存渲染引用 · 画图
|
||||
participant 水务 as 🏭 水务系统<br/>管网 / SCADA / 仿真
|
||||
participant 治理 as 🛡️ 治理观测层<br/>日志 · 审计 · 超时 · 容错
|
||||
|
||||
用户->>前端: 💬 “帮我查A片区今天用水量”
|
||||
前端->>接口: 📤 POST 请求
|
||||
接口->>会话: 🔍 验证身份、绑定会话
|
||||
会话->>上下文: 📋 拼装:历史对话+记忆+技能+身份
|
||||
接口->>前端: 📡 建立实时通道 SSE(“电话接通”)
|
||||
接口->>适配: 📤 问题和背景一起发给AI
|
||||
适配->>适配: 🧠 AI思考:推理 → 规划 → 决定调工具
|
||||
适配->>工具: 🔧 “调用 tjwater_cli 查用水数据”
|
||||
工具->>会话: 🔐 回读身份令牌(令牌留在服务端)
|
||||
工具->>水务: 🏭 带上身份去查真实数据
|
||||
水务-->>工具: 📊 返回数据
|
||||
opt 需要渲染大型 junction 分区
|
||||
工具->>工具: 💾 store_render_ref 存 render_ref
|
||||
前端->>接口: 🔎 按 render_ref 回读完整渲染 payload
|
||||
end
|
||||
工具-->>适配: 📦 给AI结果
|
||||
适配-->>前端: 📡 实时推送:文字逐字显示+图表弹窗
|
||||
前端-->>用户: 📲 看到回答和数据
|
||||
适配->>治理: 📝 记录:谁调了什么、为什么、耗时多久
|
||||
会话->>会话: 🧠 同步复盘:对话中实时沉淀记忆/技能
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 主流 Harness 能力覆盖
|
||||
|
||||
AI Agent 领域主流的 Harness 是指大模型外层的工程基础设施。对照这些标准能力项,当前设计全覆盖:
|
||||
|
||||
| 主流 Harness 能力 | 本项目落点 | 覆盖 |
|
||||
|---|---|---|
|
||||
| 运行时编排 | `OpencodeRuntimeAdapter` + `ChatSessionBridge` | ✅ |
|
||||
| 上下文装配 | `buildPromptWithLearningContext` + `runtime context store` | ✅ |
|
||||
| 工具执行与权限隔离 | `.opencode/tools` + `/internal/tools/*` + `x-agent-internal-token`,令牌不出工具 | ✅ |
|
||||
| 状态持久化 | `session` / `UI state` / `transcript` / `runtime context` | ✅ |
|
||||
| 记忆与技能沉淀 | `MemoryStore` + `SkillStore` + `LearningOrchestrator`(对话中同步复盘) | ✅ |
|
||||
| 观测与审计 | `Pino` + `LLM request audit` + `learning audit` + `traceId` | ✅ |
|
||||
| 可靠性 | `abort` / `timeout` / `fallback` / `render_ref TTL` / `cleanup` | ✅ |
|
||||
| 前端动作桥接 | SSE `tool_call` + `UI tools`(地图 / 图表 / 面板) | ✅ |
|
||||
|
||||
---
|
||||
|
||||
**Harness = 大脑负责思考,外壳负责让思考能落地执行。**
|
||||
|
||||
|
||||
## 原文件:AGENT_HARNESS_REPORT.md
|
||||
|
||||
# TJWaterAgent 后端 Harness Engineering 设计整理
|
||||
|
||||
## 1. 先明确概念:什么是 Agent Harness
|
||||
|
||||
在 Agent 系统里,LLM 只是推理核心。Harness engineering 关注的是模型外层的工程系统:它把用户请求、上下文、工具、状态、权限、观测、持久化和恢复机制组织起来,让模型能在真实业务环境里安全地执行多步任务。
|
||||
|
||||
结合近期资料,可以把 harness 概括为:
|
||||
|
||||
- **运行时外壳**:承接请求,驱动模型,多轮执行,判断何时继续、停止或失败。
|
||||
- **上下文装配层**:把用户身份、项目、历史、记忆、技能、业务约束注入到模型可用的上下文中。
|
||||
- **工具执行层**:把模型意图转成受控的 CLI、渲染引用迁移和 UI 动作,并把结果反馈给模型。
|
||||
- **权限与治理层**:隔离用户 token、项目权限、内部工具调用权限和渲染引用回读权限。
|
||||
- **状态与记忆层**:保存 session、transcript、UI state、memory、skills、render refs。
|
||||
- **观测与审计层**:记录工具调用、技能调用、学习决策、trace 和错误。
|
||||
- **可靠性层**:处理 abort、timeout、fallback、cleanup 和渲染引用 TTL。
|
||||
|
||||
因此,TJWaterAgent 的 harness 不是某一个单独文件,而是 `src/` 服务代码、`.opencode/` Agent 资产、工具桥、持久化 store 和运行时适配器共同组成的 Agent 后端工程体系。
|
||||
|
||||
参考资料:
|
||||
|
||||
- TechTarget: [Harness engineering: Agent harnesses as critical infrastructure](https://www.techtarget.com/searchapparchitecture/tip/Harness-engineering-Agent-harnesses-as-critical-infrastructure)
|
||||
- O'Reilly Radar: [Agent Harness Engineering](https://www.oreilly.com/radar/agent-harness-engineering/)
|
||||
- MongoDB: [The Agent Harness: Why the LLM Is the Smallest Part of Your Agent System](https://www.mongodb.com/company/blog/technical/agent-harness-why-llm-is-smallest-part-of-your-agent-system)
|
||||
- LLM Reference: [Harness](https://www.llmreference.com/concept/harness)
|
||||
|
||||
## 2. 项目总体框架
|
||||
|
||||
`TJWaterAgent` 是面向 TJWater 前端和水务后端能力的 Agent 服务层。它对外暴露 HTTP/SSE API,对内启动或连接 opencode runtime,并通过 `.opencode` 下的 agent、tools、skills 资产完成业务推理和工具执行。
|
||||
|
||||
```text
|
||||
TJWaterAgent/
|
||||
src/ 服务宿主、路由、会话、runtime、工具桥、持久化
|
||||
.opencode/ opencode agent 资产
|
||||
agents/instruction.md Agent 角色、行为规则、工具策略
|
||||
tools/*.ts opencode 可调用工具
|
||||
skills/ 可复用 workflow、CLI 使用说明、案例
|
||||
data/ session、memory、render_ref 等运行时数据
|
||||
logs/ LLM/tool/learning 审计日志
|
||||
cli/tjwater-cli 业务 CLI 能力入口
|
||||
opencode.json opencode provider、model、server、default_agent
|
||||
```
|
||||
|
||||
核心技术栈:
|
||||
|
||||
| 类别 | 技术 |
|
||||
| --- | --- |
|
||||
| 服务运行 | Bun、Node.js、TypeScript ESM |
|
||||
| HTTP 层 | Express、CORS、SSE |
|
||||
| 参数校验 | Zod |
|
||||
| Agent runtime | `@opencode-ai/sdk/v2`、opencode embedded/client 两种模式 |
|
||||
| LLM provider | DeepSeek,默认 `deepseek/deepseek-v4-pro` |
|
||||
| 工具扩展 | `@opencode-ai/plugin`、`.opencode/tools/*.ts` |
|
||||
| 持久化 | JSON file store、atomic write、TTL cleanup |
|
||||
| 观测 | Pino、LLM request audit、learning audit |
|
||||
| 后端能力 | `tjwater-cli --auth-stdin` |
|
||||
|
||||
## 3. Harness Engineering 一图总览
|
||||
|
||||
下面这张图把现有后端设计收束成一张 Harness Engineering 视角的总图。重点不是“LLM 调用链”,而是模型外层的工程外壳如何完成请求接入、上下文装配、runtime 编排、工具治理、状态持久化、结果裁剪、审计和恢复。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Client["Client / Frontend"]
|
||||
Chat["Agent Chat UI"]
|
||||
UIAction["Map / Chart / Panel Actions"]
|
||||
end
|
||||
|
||||
subgraph Harness["TJWaterAgent Backend Harness Engineering"]
|
||||
direction TB
|
||||
|
||||
subgraph Entry["1. 接入与协议层"]
|
||||
API["Express REST API<br/>/api/v1/agent/chat/*"]
|
||||
SSE["SSE Contract<br/>progress / token / tool_call / done / error"]
|
||||
end
|
||||
|
||||
subgraph Control["2. 会话与上下文控制面"]
|
||||
Session["ChatSessionBridge<br/>clientSessionId -> opencode sessionId"]
|
||||
ActiveRun["activeRuns + AbortController<br/>并发控制 / 中断 / 重连"]
|
||||
Context["SessionRuntimeContextStore<br/>actor / project / trace / token"]
|
||||
PromptCtx["Learning Context Assembly<br/>recent turns / memory / skills"]
|
||||
end
|
||||
|
||||
subgraph RuntimeLayer["3. Runtime 编排层"]
|
||||
Adapter["OpencodeRuntimeAdapter"]
|
||||
Mode["embedded / client mode"]
|
||||
EventAdapter["Raw Event Adapter<br/>opencode events -> frontend SSE"]
|
||||
end
|
||||
|
||||
subgraph ToolPlane["4. 工具执行与权限边界"]
|
||||
ToolBridge["Internal Tool Bridge<br/>/internal/tools/*"]
|
||||
InternalAuth["x-agent-internal-token"]
|
||||
TokenInject["Server-side Auth Injection<br/>token / project / trace"]
|
||||
RenderRefPolicy["Render Ref Policy<br/>store_render_ref + render_ref"]
|
||||
end
|
||||
|
||||
subgraph State["5. 状态、记忆、沉淀"]
|
||||
SessionStore["Session / UI State / Transcript Store"]
|
||||
ResultStore["ResultReferenceStore<br/>TTL / actor-project-session auth"]
|
||||
MemoryStore["MemoryStore"]
|
||||
SkillStore["SkillStore"]
|
||||
Learning["LearningOrchestrator<br/>gate / review / persistence policy"]
|
||||
end
|
||||
|
||||
subgraph Governance["6. 治理、观测、可靠性"]
|
||||
Audit["LLM Tool Audit / Learning Audit"]
|
||||
Logs["Pino Logs + traceId"]
|
||||
Reliability["timeout / fallback / cleanup / health"]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph Runtime["opencode Runtime Assets"]
|
||||
Agent[".opencode/agents/instruction.md"]
|
||||
OCTools[".opencode/tools/*.ts"]
|
||||
OCSkills[".opencode/skills/*"]
|
||||
LLM["DeepSeek Model"]
|
||||
end
|
||||
|
||||
subgraph Business["TJWater Business Capability"]
|
||||
CLI["tjwater-cli --auth-stdin"]
|
||||
Data["管网 / SCADA / 仿真 / 分析数据"]
|
||||
end
|
||||
|
||||
Chat -->|"POST /stream"| API
|
||||
API --> Session
|
||||
API --> ActiveRun
|
||||
Session --> Context
|
||||
Session --> SessionStore
|
||||
Session --> PromptCtx
|
||||
MemoryStore --> PromptCtx
|
||||
SkillStore --> PromptCtx
|
||||
PromptCtx --> Adapter
|
||||
Adapter --> Mode
|
||||
Mode --> LLM
|
||||
LLM --> Agent
|
||||
LLM --> OCTools
|
||||
LLM --> OCSkills
|
||||
Adapter --> EventAdapter
|
||||
EventAdapter --> SSE
|
||||
SSE -->|"stream events"| Chat
|
||||
SSE -->|"tool_call"| UIAction
|
||||
|
||||
OCTools -->|"sessionID + args + reason"| ToolBridge
|
||||
ToolBridge --> InternalAuth
|
||||
InternalAuth --> Context
|
||||
Context --> TokenInject
|
||||
TokenInject --> CLI
|
||||
CLI --> Data
|
||||
OCTools --> RenderRefPolicy
|
||||
RenderRefPolicy --> ResultStore
|
||||
|
||||
SessionStore --> Learning
|
||||
Learning --> MemoryStore
|
||||
Learning --> SkillStore
|
||||
EventAdapter --> Audit
|
||||
ToolBridge --> Audit
|
||||
Learning --> Audit
|
||||
Audit --> Logs
|
||||
ActiveRun --> Reliability
|
||||
ResultStore --> Reliability
|
||||
Adapter --> Reliability
|
||||
```
|
||||
|
||||
图中可以看出,本项目 Harness Engineering 的核心设计是:
|
||||
|
||||
| 设计域 | 关键职责 | 项目落点 |
|
||||
| --- | --- | --- |
|
||||
| 接入协议 | 把前端请求和流式响应稳定化 | Express routes、SSE event contract |
|
||||
| 会话控制 | 绑定前端会话和 runtime 会话,支持 abort、reconnect、active run 管理 | `ChatSessionBridge`、`activeRuns` |
|
||||
| 上下文装配 | 把用户、项目、trace、token、历史、记忆、技能装配到一次运行中 | `SessionRuntimeContextStore`、learning context |
|
||||
| Runtime 编排 | 屏蔽 opencode embedded/client 差异,统一 prompt、event、messages、abort | `OpencodeRuntimeAdapter` |
|
||||
| 事件适配 | 把 opencode raw events 转成前端稳定协议 | `streamPromptResponse()` |
|
||||
| 工具治理 | 通过内部桥执行真实业务能力,隔离模型和用户 token | `.opencode/tools`、`/internal/tools/*` |
|
||||
| 渲染引用管理 | 大型 junction 渲染 payload 引用化,避免模型读取完整 `node_area_map` | `ResultReferenceStore`、`render_ref` |
|
||||
| 记忆沉淀 | 从对话中异步提取 memory 和 reusable skill | `LearningOrchestrator`、`MemoryStore`、`SkillStore` |
|
||||
| 治理可靠性 | 观测、审计、超时、中断、fallback、TTL cleanup | Pino、audit logs、health、cleanup |
|
||||
|
||||
## 4. Harness 总体架构图
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph FE["TJWater 前端"]
|
||||
ChatUI["聊天 UI / AgentWorkspace"]
|
||||
MapUI["地图 / 图表 / 面板"]
|
||||
end
|
||||
|
||||
subgraph Harness["TJWaterAgent Backend Harness"]
|
||||
Server["Express Server"]
|
||||
ChatRouter["Chat Router<br/>/api/v1/agent/chat/*"]
|
||||
SessionBridge["ChatSessionBridge"]
|
||||
RuntimeAdapter["OpencodeRuntimeAdapter"]
|
||||
StreamAdapter["SSE Stream Adapter"]
|
||||
InternalTools["Internal Tool Bridge<br/>/internal/tools/*"]
|
||||
Stores["Persistent Stores<br/>session / memory / transcript / render_ref"]
|
||||
Learning["LearningOrchestrator"]
|
||||
Audit["Audit Logs"]
|
||||
end
|
||||
|
||||
subgraph Runtime["opencode Runtime"]
|
||||
OCServer["opencode server"]
|
||||
AgentPrompt["agents/instruction.md"]
|
||||
Tools["tools/*.ts"]
|
||||
Skills["skills/workflow/*"]
|
||||
end
|
||||
|
||||
subgraph Business["TJWater 业务能力"]
|
||||
CLI["tjwater-cli"]
|
||||
Data["管网 / SCADA / 仿真 / 分析数据"]
|
||||
end
|
||||
|
||||
ChatUI -->|"POST /stream 或 GET /session/:id/stream"| Server
|
||||
Server --> ChatRouter
|
||||
ChatRouter --> SessionBridge
|
||||
ChatRouter --> Stores
|
||||
SessionBridge --> RuntimeAdapter
|
||||
RuntimeAdapter --> OCServer
|
||||
OCServer --> AgentPrompt
|
||||
OCServer --> Tools
|
||||
OCServer --> Skills
|
||||
Tools -->|"internal callback + sessionID"| InternalTools
|
||||
InternalTools --> Stores
|
||||
InternalTools --> CLI
|
||||
CLI --> Data
|
||||
OCServer -->|"raw events"| StreamAdapter
|
||||
StreamAdapter -->|"progress / token / tool_call / done / error"| ChatUI
|
||||
StreamAdapter --> Audit
|
||||
ChatRouter --> Learning
|
||||
Learning --> Stores
|
||||
ChatUI -->|"tool_call action"| MapUI
|
||||
```
|
||||
|
||||
## 5. Harness 工程分层
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
L1["1. 接口层<br/>Express + REST + SSE"]
|
||||
L2["2. 会话层<br/>ChatSessionBridge + activeRuns + abort"]
|
||||
L3["3. 上下文层<br/>user / project / trace / token 绑定"]
|
||||
L4["4. Runtime 适配层<br/>opencode embedded/client"]
|
||||
L5["5. 事件适配层<br/>opencode raw events -> 前端 SSE 协议"]
|
||||
L6["6. 工具执行层<br/>tjwater-cli / store_render_ref / UI tools"]
|
||||
L7["7. 渲染引用层<br/>render_ref / TTL"]
|
||||
L8["8. 记忆学习层<br/>memory / transcript / skill / learning gate"]
|
||||
L9["9. 治理观测层<br/>auth / audit / logs / timeout / cleanup"]
|
||||
|
||||
L1 --> L2 --> L3 --> L4 --> L5
|
||||
L3 --> L6 --> L7
|
||||
L2 --> L8
|
||||
L6 --> L9
|
||||
L8 --> L9
|
||||
```
|
||||
|
||||
### 5.1 接口层
|
||||
|
||||
主要入口在 `src/server.ts` 和 `src/routes/chat.ts`:
|
||||
|
||||
- `GET /health`:检查服务和 opencode runtime 健康状态。
|
||||
- `POST /api/v1/agent/chat/session`:创建或确保 session metadata。
|
||||
- `GET /api/v1/agent/chat/sessions`:列出用户/项目维度的会话。
|
||||
- `GET /api/v1/agent/chat/session/:sessionId`:读取会话 UI state。
|
||||
- `POST /api/v1/agent/chat/stream`:核心对话入口,返回 SSE。
|
||||
- `GET /api/v1/agent/chat/session/:sessionId/stream`:重新订阅正在运行的 stream。
|
||||
- `POST /api/v1/agent/chat/abort`:中断当前会话运行。
|
||||
- `POST /api/v1/agent/chat/fork`:基于历史 session 分叉。
|
||||
- `GET /api/v1/agent/chat/render-ref/:renderRef`:授权读取渲染结果引用。
|
||||
|
||||
接口层的 harness 价值是给前端一个稳定协议,把复杂的 Agent runtime 行为收敛成会话、消息、状态、流式事件。
|
||||
|
||||
### 5.2 会话与上下文层
|
||||
|
||||
`ChatSessionBridge` 负责把前端 session 和 opencode session 绑定起来,并维护请求级上下文:
|
||||
|
||||
- `clientSessionId`:前端可见的会话 ID。
|
||||
- `sessionId`:opencode runtime session ID。
|
||||
- `actorKey`:由 `x-user-id` 归一化得到的用户隔离键。
|
||||
- `projectKey`:由 `x-project-id` 归一化得到的项目隔离键。
|
||||
- `traceId`:链路追踪 ID。
|
||||
- `accessToken`:来自 `Authorization`,只存入服务端 runtime context,不暴露给模型工具代码。
|
||||
|
||||
每次 `POST /stream` 会:
|
||||
|
||||
1. 解析 headers 和 payload。
|
||||
2. 查询或创建 session metadata。
|
||||
3. 调用 `sessionBridge.resolve()` 创建或复用 runtime session。
|
||||
4. 写入 `SessionRuntimeContextStore`,供内部工具桥回读。
|
||||
5. 为该 session 注册 `AbortController`,避免同一会话并发运行。
|
||||
|
||||
### 5.3 Runtime 适配层
|
||||
|
||||
`OpencodeRuntimeAdapter` 屏蔽 opencode SDK 细节,提供统一方法:
|
||||
|
||||
- `ensureClient()`:懒加载 opencode client。
|
||||
- `health()`:runtime 健康检查。
|
||||
- `createSession()`:创建 runtime session。
|
||||
- `prompt()` / `sendPrompt()`:发送用户消息。
|
||||
- `messages()`:回读消息。
|
||||
- `abortSession()` / `waitForSessionIdle()`:中断和等待空闲。
|
||||
- `subscribeEvents()`:订阅 opencode 事件流。
|
||||
- `dispose()`:关闭 embedded runtime。
|
||||
|
||||
支持两种部署模式:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Mode["OPENCODE_MODE"]
|
||||
Embedded["embedded"]
|
||||
Client["client"]
|
||||
SDKCreate["createOpencode()"]
|
||||
SDKClient["createOpencodeClient()"]
|
||||
Local["本地 opencode CLI 子进程"]
|
||||
Remote["外部 opencode server"]
|
||||
|
||||
Mode --> Embedded --> SDKCreate --> Local
|
||||
Mode --> Client --> SDKClient --> Remote
|
||||
```
|
||||
|
||||
- `embedded`:服务启动本地 opencode server,同时注入 `TJWATER_AGENT_INTERNAL_BASE_URL` 和 `TJWATER_AGENT_INTERNAL_TOKEN` 给 `.opencode/tools`。
|
||||
- `client`:服务连接外部 opencode server,适合 runtime 与 API 服务分离。
|
||||
|
||||
### 5.4 事件适配层
|
||||
|
||||
`streamPromptResponse()` 是前端体验的关键 harness。它订阅 opencode raw events,把内部事件转成稳定 SSE:
|
||||
|
||||
| opencode 事件 | SSE 事件 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `session.status` | `progress` | 展示 busy/retry/idle 等状态 |
|
||||
| `message.part.delta` + text | `token` | 流式输出最终回答 |
|
||||
| `message.part.updated` + reasoning | `progress` | 展示规划和推理状态 |
|
||||
| `message.part.updated` + tool | `progress` / `tool_call` | 展示工具状态,并通知前端执行 UI 动作 |
|
||||
| `todo.updated` | `progress` | 展示计划进度 |
|
||||
| `session.error` | `error` | 返回错误 |
|
||||
| `session.idle` | `done` | 结束本轮 |
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Raw["opencode raw event stream"]
|
||||
Status["session.status"]
|
||||
Reasoning["reasoning part"]
|
||||
TextDelta["text delta"]
|
||||
ToolPart["tool part"]
|
||||
Todo["todo.updated"]
|
||||
Error["session.error"]
|
||||
Idle["session.idle"]
|
||||
|
||||
Progress["SSE progress"]
|
||||
Token["SSE token"]
|
||||
ToolCall["SSE tool_call"]
|
||||
SSEError["SSE error"]
|
||||
Done["SSE done"]
|
||||
|
||||
Raw --> Status --> Progress
|
||||
Raw --> Reasoning --> Progress
|
||||
Raw --> TextDelta --> Token
|
||||
Raw --> ToolPart --> Progress
|
||||
ToolPart --> ToolCall
|
||||
Raw --> Todo --> Progress
|
||||
Raw --> Error --> SSEError
|
||||
Raw --> Idle --> Done
|
||||
```
|
||||
|
||||
适配层还提供:
|
||||
|
||||
- 缺失流式文本时,通过 `runtime.messages()` 兜底补发最终回答。
|
||||
- 工具调用必须提取 `reason`,并写入 LLM request audit。
|
||||
- 对 running/completed/error 做统一归一化,前端不依赖 opencode 内部状态结构。
|
||||
|
||||
### 5.5 工具执行层
|
||||
|
||||
`.opencode/tools/*.ts` 是模型能调用的工具,分为三类:
|
||||
|
||||
| 类别 | 工具 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| 业务数据工具 | `tjwater_cli` | 查询或计算真实后端数据 |
|
||||
| 上下文工具 | `memory_manager`、`session_search`、`skill_manager` | 管理记忆、历史、技能 |
|
||||
| 前端动作工具 | `locate_features`、`view_scada`、`view_history`、`show_chart`、`render_junctions`、`apply_layer_style` | 触发地图、图表、面板展示 |
|
||||
|
||||
工具桥设计重点:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant M as Model / opencode Tool
|
||||
participant Tool as .opencode/tools/*.ts
|
||||
participant Bridge as /internal/tools/*
|
||||
participant Ctx as SessionRuntimeContextStore
|
||||
participant API as TJWater API
|
||||
participant CLI as tjwater-cli
|
||||
participant Store as ResultReferenceStore
|
||||
|
||||
M->>Tool: tool call(args, context.sessionID)
|
||||
Tool->>Bridge: POST internal endpoint + x-agent-internal-token
|
||||
Bridge->>Bridge: 校验内部 token
|
||||
Bridge->>Ctx: 用 session_id 回读用户/项目/trace/token
|
||||
Bridge->>CLI: --auth-stdin 写入 server/token/project
|
||||
CLI-->>Bridge: JSON 或 raw stdout
|
||||
opt 大型 junction 渲染 payload
|
||||
Bridge->>Store: store_render_ref 写入 render_ref
|
||||
Bridge-->>Tool: render_ref + preview
|
||||
end
|
||||
Tool-->>M: tool result
|
||||
```
|
||||
|
||||
这个设计避免了两个常见问题:
|
||||
|
||||
- 模型工具代码不直接持有用户 token。
|
||||
- 大型 junction 渲染 payload 不直接塞进模型上下文,而是存为 `render_ref`,由前端授权回读。
|
||||
|
||||
### 5.6 结果引用层
|
||||
|
||||
渲染引用会估算结果大小:
|
||||
|
||||
- 节点渲染 payload 存入 `ResultReferenceStore`,返回 `render_ref` 和 `preview`。
|
||||
- 前端渲染工具通过 `render_ref` 回读完整 junctions 映射。
|
||||
|
||||
`ResultReferenceStore` 的权限校验维度:
|
||||
|
||||
- `actorKey`
|
||||
- `projectId`
|
||||
- 可选 `clientSessionId`
|
||||
|
||||
并提供:
|
||||
|
||||
- `preview`:列表、对象或标量的摘要。
|
||||
- TTL 清理:`RESULT_REF_TTL_HOURS` 和 `RESULT_REF_CLEANUP_INTERVAL_MS`。
|
||||
- legacy render payload 兼容。
|
||||
|
||||
### 5.7 记忆、历史与技能沉淀
|
||||
|
||||
这部分体现了 harness engineering 的“外部化记忆”和“可复用工作流”:
|
||||
|
||||
- `SessionTranscriptStore`:保存最近对话 turn,支持 `session_search`。
|
||||
- `MemoryStore`:保存用户偏好和 workspace 事实。
|
||||
- `SkillStore`:保存 workflow skill 的模式和参考资料。
|
||||
- `LearningOrchestrator`:在 turn 完成后异步运行 gate/review,判断是否沉淀 memory 或 skill。
|
||||
|
||||
学习链路:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Turn["对话 turn 完成"]
|
||||
Transcript["append transcript"]
|
||||
Cooldown["检查 cooldown"]
|
||||
Gate["learning gate<br/>是否值得复盘"]
|
||||
Review["learning review<br/>生成 memory/skill proposal"]
|
||||
Policy["persistence policy<br/>脱敏与置信度阈值"]
|
||||
Memory["MemoryStore"]
|
||||
Skill["SkillStore"]
|
||||
Audit["Learning Audit"]
|
||||
|
||||
Turn --> Transcript --> Cooldown --> Gate
|
||||
Gate -->|"skip"| Audit
|
||||
Gate -->|"accept"| Review --> Policy
|
||||
Policy --> Memory
|
||||
Policy --> Skill
|
||||
Policy --> Audit
|
||||
```
|
||||
|
||||
Agent 指令中明确区分:
|
||||
|
||||
- `memory_manager`:长期有效的用户偏好或项目事实。
|
||||
- `skill_manager`:可复用操作流程。
|
||||
- `session_search`:历史案例检索,只读。
|
||||
- `tjwater_cli`:真实业务数据源和分析能力。
|
||||
|
||||
## 6. 一次 `/stream` 请求的完整时序
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant U as User
|
||||
participant FE as Frontend
|
||||
participant API as Express Chat Router
|
||||
participant SB as ChatSessionBridge
|
||||
participant Store as Session Stores
|
||||
participant RT as OpencodeRuntimeAdapter
|
||||
participant OC as opencode Runtime
|
||||
participant SSE as Stream Adapter
|
||||
participant Tool as opencode Tool
|
||||
participant Bridge as Internal Tool Bridge
|
||||
participant BE as TJWater API / CLI
|
||||
|
||||
U->>FE: 输入问题
|
||||
FE->>API: POST /api/v1/agent/chat/stream
|
||||
API->>API: Zod 校验 message/session/model
|
||||
API->>Store: 读取或创建 session metadata/ui state/transcript
|
||||
API->>SB: resolve(session, token, user, project, trace)
|
||||
SB->>RT: createSession 或复用 session
|
||||
SB->>Store: 写入 runtime context
|
||||
API->>FE: 建立 SSE
|
||||
API->>Store: 写入初始 user/assistant streaming messages
|
||||
API->>Store: 读取 recent turns + memory
|
||||
API->>SSE: streamPromptResponse(preparedMessage)
|
||||
SSE->>RT: subscribeEvents()
|
||||
SSE->>RT: prompt(sessionId, message, model)
|
||||
RT->>OC: session.prompt
|
||||
OC-->>SSE: status/reasoning/todo events
|
||||
SSE-->>FE: progress
|
||||
OC->>Tool: tool call
|
||||
Tool->>Bridge: internal callback(sessionID, args, reason)
|
||||
Bridge->>Store: 回读 runtime context
|
||||
Bridge->>BE: 注入鉴权上下文后执行 CLI 或存储 render_ref
|
||||
BE-->>Bridge: CLI 结果或 render_ref
|
||||
Bridge-->>Tool: CLI 结果或 render_ref
|
||||
Tool-->>OC: tool result
|
||||
OC-->>SSE: tool/text/session events
|
||||
SSE-->>FE: tool_call/token/done
|
||||
API->>Store: 持久化 UI state、title、transcript
|
||||
API->>Store: 触发异步 learning gate/review
|
||||
```
|
||||
|
||||
## 7. 与 Harness Engineering 能力项的映射
|
||||
|
||||
| Harness 能力项 | 本项目落点 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Runtime orchestration | `OpencodeRuntimeAdapter`、`ChatSessionBridge` | 启动/连接 opencode,创建 session,发送 prompt,等待 idle,abort |
|
||||
| Context assembly | `buildPromptWithLearningContext()`、runtime context store | 拼接历史、记忆、用户/项目/trace/token |
|
||||
| Tool dispatch | `.opencode/tools/*.ts`、`/internal/tools/*` | 模型工具经内部桥执行真实能力 |
|
||||
| Permission boundary | `x-agent-internal-token`、actor/project/session 授权 | token 不进工具代码,render_ref 按用户和项目授权 |
|
||||
| State persistence | metadata、UI state、transcript、runtime context | 支持会话列表、恢复、重连、分叉和历史检索 |
|
||||
| Memory | `MemoryStore`、`memory_manager`、learning review | 用户偏好、项目事实、prompt 注入 |
|
||||
| Skill externalization | `.opencode/skills/workflow`、`skill_manager` | 把验证过的流程沉淀成可复用 workflow |
|
||||
| Observability | Pino、LLM request audit、learning audit、traceId | 记录工具/技能调用理由、耗时、结果和学习决策 |
|
||||
| Reliability | abort、timeout、fallback messages、render_ref TTL | 管控长任务、中断、超时、渲染引用和清理 |
|
||||
| Frontend action bridge | SSE `tool_call` + UI tools | 把 Agent 决策映射为地图、图表、面板动作 |
|
||||
|
||||
## 8. 当前设计亮点
|
||||
|
||||
1. **模型与权限解耦**:用户 token 由服务端 session context 管理,工具只拿 `sessionID` 回调内部接口。
|
||||
2. **前端协议稳定**:opencode 原始事件被统一成 `progress/token/tool_call/done/error`。
|
||||
3. **支持运行时部署弹性**:embedded 和 client 模式兼容本地一体化和外部 runtime。
|
||||
4. **大型渲染上下文友好**:使用 `render_ref`,避免模型读取完整 `node_area_map`。
|
||||
5. **可持续沉淀工作流**:Agent 指令、`skill_manager`、`LearningOrchestrator` 共同支持 workflow 生长。
|
||||
6. **恢复与重连能力**:active run、session UI state、`GET /session/:id/stream` 支持前端重新订阅。
|
||||
7. **审计意识明确**:工具调用 reason、skill event、learning proposal 均有日志记录。
|
||||
|
||||
## 9. 可继续加强的点
|
||||
|
||||
| 方向 | 建议 |
|
||||
| --- | --- |
|
||||
| 工具治理 | 给工具增加风险等级、只读/写入分类、危险动作二次确认 |
|
||||
| 幂等与重试 | 对写操作 API/CLI 增加 idempotency key,区分可重试和不可重试错误 |
|
||||
| Eval harness | 为典型水务任务建立 golden traces,回归验证工具链和 prompt 改动 |
|
||||
| 结构化事件 | 对 `tool_call` 参数增加 schema/version,便于前端兼容升级 |
|
||||
| 结果生命周期 | render_ref 可增加按 session 删除、按项目统计和手动清理接口 |
|
||||
| 安全审计 | 对内部工具桥增加 IP/来源限制、日志脱敏覆盖率检查 |
|
||||
| Runtime 隔离 | client 模式下明确多租户 runtime session 隔离策略 |
|
||||
|
||||
## 10. 一句话结论
|
||||
|
||||
TJWaterAgent 的后端 harness 是一套围绕 opencode runtime 构建的 Agent 工程外壳:它把前端交互、LLM 推理、业务工具、用户权限、会话状态、记忆学习和审计观测组织成可运行、可恢复、可治理的生产级 Agent 服务。
|
||||
Reference in New Issue
Block a user