feat: upgrade investigation and presentation runtime
Generic Container CI/CD / test-build-publish (push) Successful in 1m6s
Agent CI/CD / build-test-publish-and-deploy (push) Successful in 1m7s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent 3b52a0ddab
commit 6336157ff4
104 changed files with 6541 additions and 961 deletions
+49 -25
View File
@@ -12,7 +12,7 @@
| API key、服务地址、端口等环境变量 | 仓库根目录 `.local.env` | 本地私有文件,已被 Git 忽略;字段参考 [.env.example](.env.example) |
| 工具权限、外部 MCP 连接 | [opencode.json](opencode.json) | 不再单独配置模型和 provider |
| Agent 的业务指令 | [.opencode/agents/instruction.md](.opencode/agents/instruction.md) | 指令文件不指定模型 |
| 后台调查与只读子代理 | [.opencode/agents/investigation.md](.opencode/agents/investigation.md)、[investigation-reader.md](.opencode/agents/investigation-reader.md) | 主对话禁用子代理,后台任务按可信用途选择独立配置 |
| 后台调查与只读子代理 | [.opencode/agents/investigation.md](.opencode/agents/investigation.md) | 主代理编写每次子任务 prompt;运行时配置负责只读权限和可信用途校验 |
| Agent、插件、Skill 和 MCP 的管理索引 | [.opencode/agent-plugin.json](.opencode/agent-plugin.json) | 描述用途和实际配置入口;管理方法见 [.opencode/README.md](.opencode/README.md) |
| 代码修改规则 | [AGENTS.md](AGENTS.md) | 面向维护代码的 Agent |
@@ -26,7 +26,7 @@
`serviceOpencodeConfig` 区分三个原生配置:`instruction` 用于主对话、人工协作和学习,
禁止子代理;`investigation` 用于服务端标记的后台任务,只能调用 `investigation-reader`;
后者只有任务上下文和任务数据查询两项工具。保持全局 `subagent: deny`,不通过用户提示
后者不加载独立提示词文件,具体 prompt 由主调查 Agent 在每次调用中编写,运行时只开放任务数据查询。保持全局 `subagent: deny`,不通过用户提示
或前端参数放开。父调查使用 `agent-models.json` 的 task 模型,子代理沿用父会话模型。
每个后台调查最多同时两个只读子代理,仅一层,使用并行工具调用批次及前台等待,
@@ -88,6 +88,7 @@ chmod 600 .local.env
```dotenv
HOST=127.0.0.1
PORT=8787
AGENT_INSTANCE_ID=next-tjwater-agent-local
OPENCODE_MODE=embedded
TJWATER_SERVER_BASE_URL=http://127.0.0.1:8000
WATERNETWORK_API_KEY=replace-with-your-key
@@ -95,6 +96,8 @@ WATERNETWORK_API_KEY=replace-with-your-key
默认仅启用 WaterNetwork,填写 `WATERNETWORK_API_KEY` 即可。增加其他 provider 时,还需提供对应凭据,并将默认模型引用指向有效的已配置模型。
`AGENT_INSTANCE_ID` 是后台调查运行的稳定归属标识。同一 Server 可见的每个 Agent 实例必须使用不同值,实例重启后保持不变;未显式配置时仅为本地开发按主机、工作目录和端口生成默认值,复制部署或容器环境必须显式设置。
配置完成后检查模型,再启动服务:
```bash
@@ -291,8 +294,8 @@ Shell 不能联网或访问源码、密钥、其他会话;业务查询和地
| 场景 | 工具或协议 | 说明 |
| --- | --- | --- |
| 查询供水数据 | `mcp_tjwater_*` | 启用 Server MCP 后发现42项项目查询、12项计算和13项预览式写入,供普通项目会话与绑定调查使用 |
| 地图与图表交互 | `locate_features`、`zoom_to_map`、`show_chart`、`view_history` | 通过 `frontend-action@1` 等待浏览器实际回执 |
| SCADA 地图分析 | `render_scada_analysis`、`clear_scada_analysis` | 展示和清除受控叠加层 |
| 地图与图表交互 | `present_view` | 通过 `frontend-action@2` 等待浏览器实际回执 |
| SCADA 地图分析 | `present_view` 的 render_scada_analysis / clear_scada_analysis | 展示和清除受控叠加层 |
| 网页搜索 | OpenCode 原生 `websearch` | 搜索服务由 OpenCode 配置管理,不经过 TJWater Server |
| 地址解析 | `mcp_tjwater_geocode` | 调用 Server 的天地图接口,模型不能指定任意 URL 或鉴权头 |
| 工况任务 | 查询、计划、报告、行动建议和人工证据工具 | 通过 Server 的任务与运行身份访问业务 |
@@ -303,7 +306,7 @@ Shell 不能联网或访问源码、密钥、其他会话;业务查询和地
配置构建器禁用 OpenCode 原生 `opencode.browser` 插件及其子插件,`browser_*` 自动化工具不再注册,浏览器权限也明确拒绝。业务前端继续使用上表的地图、图表和 SCADA 展示插件;`present_answer` / `final_answer` 返回交互界面与最终回答,`publish_task_presentation` 发布后台任务展示事件,`memory_manager` / `skill_manager` 保留完整读写插件接口。
历史查询的时间窗口最长 24 小时,SCADA 字段限 `monitored_value` 和 `cleaned_value`;物理指标和单位以设备元数据为准。历史查询由 Server 校验只读权限,清洗操作仍需清洗权限。`view_history` 当前最多展示 100 个非空样本,不代表完整高频历史;缺失资产和历史缺数会返回实际状态。
历史查询的时间窗口最长 24 小时,SCADA 字段限 `monitored_value` 和 `cleaned_value`;物理指标和单位以设备元数据为准。历史查询由 Server 校验只读权限,清洗操作仍需清洗权限。open_chart 最多展示 100 个样本,历史曲线先通过 MCP 查询;缺失资产和历史缺数会返回实际状态。
网页搜索只使用 OpenCode 原生 `websearch`,搜索服务及其凭据由 OpenCode 管理,保留管理员的服务选择;TJWater Server 的 Bocha 接口、配置和 MCP 搜索工具已移除。搜索结果是外部资料,不自动保存为调查系统证据。`mcp_tjwater_geocode` 返回 `center=[lon,lat]`、`source_crs=EPSG:4326`,可用于地图定位;天地图密钥仍由 Server 管理。
@@ -311,11 +314,11 @@ Shell 不能联网或访问源码、密钥、其他会话;业务查询和地
后台执行由 worker 提交 Server 已保存的 `task_id`、`run_id`、`execution_request_id`。Agent claim 成功后每 20 秒发送心跳,运行正常结束显式调用 `complete`,失败或中止调用 `fail`。Server 决定活动运行和租约状态;计划、报告或 `final_answer` 的发布不代表业务任务完成。执行计划规则见 [工况执行计划](docs/execution-plan.md)。
后台调查在真实步骤开始或路线变化时发布完整进度,Mermaid 节点的显式状态和供水要素引用随进度保存;图可重绘,todo 不决定图或运行状态。资料展示命令支持要素高亮/闪烁、图表开关和证据图层,图层几何与数值由 Server 从已保存证据提取。工具返回 `recorded` 表示展示请求已记录,不表示浏览器显示成功。前端 Dev 实时跟随仅作用于浏览器;无人查看或关闭跟随时调查照常运行,不回放历史展示,也不重新触发调查。普通聊天仍使用现有 `frontend-action@1` 浏览器回执。
后台调查在真实步骤开始或路线变化时发布完整进度,Mermaid 节点的显式状态和供水要素引用随进度保存;图可重绘,todo 不决定图或运行状态。资料展示命令支持要素高亮/闪烁、图表开关和证据图层,图层几何与数值由 Server 从已保存证据提取。工具返回 `recorded` 表示展示请求已记录,不表示浏览器显示成功。前端 Dev 实时跟随仅作用于浏览器;无人查看或关闭跟随时调查照常运行,不回放历史展示,也不重新触发调查。普通聊天使用 `frontend-action@2` 浏览器回执。
交互会话遇到 `credential_refresh_required` 后,前端更新凭据;令牌只通过鉴权头传递,工具请求最多重试一次,403 不触发续期。后台 worker 使用已有 OAuth provider 更新凭据,不等待浏览器;无法写回失败时仍由 Server 的租约恢复处理。
服务保留 `agent-ui@1` UIEnvelope 兼容接口,目前该注册表为空。当前 6 个浏览器动作使用独立的 `frontend-action@1` 注册表;前端需声明能力并回传动作结果。待办使用 `todoread`、`todowrite`,通过 `todo_update` 更新前端。协议见 [Agent UI 协议](docs/agent-ui-protocol.md)。
服务保留 `agent-ui@1` UIEnvelope 兼容接口,目前该注册表为空。当前十种展示命令统一使用 `present_view({command, reason?})`,command 采用扁平 `{action, ...字段}`,与任务展示共用 schema;浏览器动作使用 `frontend-action@2` 注册表;前端需声明能力并回传动作结果。待办使用 `todoread`、`todowrite`,通过 `todo_update` 更新前端。协议见 [Agent UI 协议](docs/agent-ui-protocol.md)。
大结果文件中转、供水瓶颈和服务区算法仍未从旧仓库迁入。
@@ -369,6 +372,8 @@ Shell 不能联网或访问源码、密钥、其他会话;业务查询和地
## 前端与任务接入
回答交互状态使用 `answer-view-state@1`,独立保存在宿主 `chat/answer-view-state.json`,不进入模型工作区或推理上下文。GET 使用 `message_id`、`block_id` 查询;PUT 携带这两个标识以及 `schema_digest`、`expected_revision`、`request_id`、`nodes` 和 `block`。服务端从已授权会话的实际回答校验组件字段、兼容迁移并执行原子版本检查,冲突返回 409 和当前状态。只保存图表视图、筛选选行、表单草稿和证据展开等控件字段,不保存原始数据副本。每块上限 32 KiB UTF-8、每会话文件上限 1 MiB;幂等回执保留最近 32 条且总量最多 128 KiB,过期请求仍受版本检查保护。删除会话同时移除记录,分叉不继承草稿。旧会话首次保存时创建文件,无数据库迁移和新增配置。发布时先更新 Agent,再更新前端;接口不可用时前端保留内存草稿并提示未保存。
Agent 服务的 API 前缀是 `/api/v1`,路径不再包含 `/agent/chat`。生产请求始终携带产品 Bearer,项目会话额外携带项目头;涉及会话的操作还校验用户及个人或项目范围。前端 Agent 面板可选择“个人会话”或“当前项目”,切换会开启新会话,历史分别加载,不改变已有会话归属。任务执行与任务协作始终属于项目。
| 方法 | 路径 | 用途 |
@@ -377,6 +382,7 @@ Agent 服务的 API 前缀是 `/api/v1`,路径不再包含 `/agent/chat`。生
| GET | `/models` | 聊天模型菜单与默认值,不含 provider 凭据 |
| POST | `/session` | 创建会话 |
| GET | `/sessions`、`/session/:session_id` | 列表和会话内容 |
| GET / PUT | `/session/:session_id/ui-state` | 读取或保存已发布回答的交互状态 |
| DELETE | `/session/:session_id` | 删除聊天记录及其会话工作区 |
| POST | `/responses` | Responses 文本请求;stream=true 返回 SSE,否则返回 Response JSON |
| GET | `/session/:session_id/inputs` | 当前会话尚未处理的用户消息 |
@@ -397,13 +403,15 @@ Agent 服务的 API 前缀是 `/api/v1`,路径不再包含 `/agent/chat`。生
运行中的普通聊天和任务协作可继续向 `/responses` 提交非流式文本,指定已有 `conversation`、`tjwater.delivery`(`steer` / `queue`)及稳定的 `tjwater.input_id`(`msg_` 加32位小写十六进制字符)。返回 HTTP 202 和 `object="tjwater.response_input"` 的收件回执,表示输入已由原生收件箱接管;`pending` 不代表模型已读取。前端默认 steer,可选择 queue;steer 在下一步引导当前工作,queue 等待当前工作完成后处理。追加输入不切换模型、审批、任务或授权上下文,也不创建第二条宿主生成循环。原生成流保持连接,`tjwater.user_input` 事件报告输入接收和处理,处理后的用户消息保存到会话历史。
未处理消息通过 `/session/:session_id/inputs` 查询,可 PATCH `{ "delivery": "queue" }` 或 `{ "delivery": "steer" }` 切换;已取走时返回409,前端刷新列表。每会话最多20条待处理消息,按次最多接收500条追加输入;停止会话会取消尚未处理的本类输入。接口校验用户、项目和会话归属,不能直接追加或修改后台 `task_execution` 调查输入,也不能借此更改代理协作消息。会话收尾或启动尚未就绪时返回409,不能自动重试为新一轮。
提问等待或续接失败时也接受上述 steer/queue 输入;没有活动宿主轮次时以 `resume=false` 保存到原生收件箱,等待后续受控执行,不自行启动推理,也不将追加消息当作表单回答。续接确认丢失时,读取提问状态会核对原生回答已消费及关联输出已完成,再恢复确认;部分输出或原生记录不可用时不推断成功。
未处理消息通过 `/session/:session_id/inputs` 查询,可 PATCH `{ "delivery": "queue" }` 或 `{ "delivery": "steer" }` 切换;已取走时返回409,前端刷新列表。每会话最多20条待处理消息,活动轮次最多接收500条追加输入;停止会话会取消尚未处理的本类输入。接口校验用户、项目和会话归属,不能直接追加或修改后台 `task_execution` 调查输入,也不能借此更改代理协作消息。只有入队前明确返回 `409/RESPONSE_FINISHED` 时,前端可刷新状态并按新一轮发送;其他收尾、启动冲突或不确定错误不自动重发。
单条撤回使用 DELETE,同样校验归属与用户消息来源。原生取消对已消费 ID 也可能返回成功,因此适配器取消后还会核对原生消费记录;消费先发生则返回409,不能将已经送出的内容当作撤回草稿。成功后通过 `tjwater.user_input` 发布 `cancelled`,队列跟踪器释放等待状态。前端默认展示待处理正文,可在消息菜单中编辑或关闭排队;编辑仅在服务端确认撤回后恢复原文,保留输入框中已有草稿,等待用户手动重发。
这里是现有 HTTP/SSE 服务的 `tjwater` 扩展,映射 OpenCode 原生用户收件箱;不等同于 OpenAI WebSocket 的 `response.steer` 事件,官方协议边界见 [Mid-turn steering](https://developers.openai.com/api/docs/guides/steering)。验证命令 `bun tests/integration/response-input-smoke.ts` 使用隔离 OpenCode 和本机确定性模型,验证模式切换及队列后续输出,不调用付费推理。
工具说明统一使用 `reason`,填写时用一句话说明具体目的或业务依据。普通查询不额外注入该参数,地图、图表和 SCADA 展示工具的说明可选;缺少可选说明不会触发警告。数据权限申请、处置提案和计划变更保留必填理由;`memory_manager` / `skill_manager` 的 `list` 可省略,其余修改操作在内部桥接接口强制校验非空理由。说明随调用进入审计(`reasonProvided` 仅表示是否提供),进度中有值才显示“说明”,不替代授权、审批或执行回执。新工具只使用 `reason`,旧事件别名仅兼容读取。
工具说明统一使用 `reason`,填写时用一句话说明具体目的或业务依据。普通查询不额外注入该参数,`present_view` 的说明可选;缺少可选说明不会触发警告。数据权限申请、处置提案和计划变更保留必填理由;`memory_manager` / `skill_manager` 的 `list` 可省略,其余修改操作在内部桥接接口强制校验非空理由。说明随调用进入审计(`reasonProvided` 仅表示是否提供),进度中有值才显示“说明”,不替代授权、审批或执行回执。新工具只使用 `reason`,旧事件别名仅兼容读取。
流使用 `response.created`、`response.in_progress`、`response.output_item.added/done`、`response.content_part.added/done`、`response.output_text.delta/done` 和终态 `response.completed/failed/incomplete`,带单调递增的 `sequence_number`。中止返回 incomplete;未知原因不伪造标准 incomplete reason。进度、审批、问题、凭据续期、地图和富文本展示通过 `tjwater.<事件名>` 扩展携带 `response_id`。前端使用 Responses transport 接入现有 useChat 和 UIMessage 渲染;UIMessage 不再是生成接口的网络协议。标准流语义见 [OpenAI Responses streaming](https://developers.openai.com/api/docs/guides/streaming-responses)。
@@ -452,7 +460,7 @@ volumes:
`POST /api/v1/suggestions` 接收可选的已配置聊天模型 `model` 和最多 12 条 `activities`。每条活动包含 `id`、`kind`(alert/task/analysis/session)、`title`、`occurred_at`、`origin`(backend/agent/demo),以及可选的 `summary`、`status`;它们是当前工作台已加载的快照,不替代 Server 的权威业务记录。
接口沿用聊天路由的登录身份与会话范围校验。OpenCode 在临时原生上下文中执行一次性文本生成,不派发聊天轮次或业务工具,也不写入用户会话历史;完成或失败后删除该上下文及其专用工作区。结果为 `suggestions`(最多三条 question/activity_id)及 `generated_at`,活动引用必须来自请求,演示活动的问题始终标记为演示。
接口沿用聊天路由的登录身份与会话范围校验。OpenCode 在临时原生上下文中使用独立的 `prompt-suggestions` 角色执行一次性文本生成:该角色与会话均禁止所有工具,仅返回 JSON,不继承业务聊天的 `final_answer` 收尾协议,也不写入用户会话历史;完成或失败后删除该上下文及其专用工作区。模型沿用统一配置中的聊天默认值或请求指定的聊天模型。结果为 `suggestions`(最多三条 question/activity_id)及 `generated_at`,活动引用必须来自请求,演示活动的问题始终标记为演示。
推荐按已验证用户、项目、模型和活动快照合并并发请求并缓存 5 分钟,失败不缓存。推理超时为 20 秒;生成失败返回 503,前端保持输入可用,不展示固定问题作为生成结果。不需要增加 provider、密钥或独立模型默认值。
@@ -471,10 +479,22 @@ OPENCODE_MODE=embedded OPENCODE_PORT=0 bun run tests/integration/prompt-suggesti
### 本地交互式聊天回答
支持 `host-question@1` 的客户端使用自有 `ask_user` 提问。宿主在会话 `chat/interaction-requests.json` 保存问题、选项草稿、已提交答案和固定消息 ID 的续跑记录;切换会话不取消提问。文件最多 2 MiB、1000 个请求,不保存登录凭据。接口位于 `/api/v1/session/:session_id/interactions`,提供列表、单项、草稿、reply/skip/cancel/resume;保存答案与模型继续分别显示状态。续跑通过现有 `/responses` 的 `tjwater.interaction_id` 使用服务端答案,不接受调用者替换答案文字。
续跑的用户消息保留同一个 `delivery.native_message_id`,保存可读文本及 `data-interaction_reply` 已提交回答快照;给模型的协议前缀仅用于推理输入。会话详情、分页和恢复流首次状态按当前会话的投递 ID 投影旧记录,提供相同的回答表示,不猜测文本前缀、不改写历史文件。客户端可在独立提问状态接口返回前还原回答卡片和关联过程,随后按修订更新投递状态。
原生 question 保留兼容入口,新增 `GET /api/v1/session/:session_id/questions/:request_id` 核对状态;原生请求缺失时显示失效,已持久化的回答终态保留。服务重启后,在有效用户授权下核对已保存的续跑记录;固定 ID 已被消费时不重发。无法核实对应的已完成回复时显示继续失败,不能把上一轮回答当作恢复结果。模型服务与插件需要同步更新,现有实例不会因源码修改自动完成发布。
聚焦验证:`bun test tests/sessions/interactionRequest.test.ts tests/routes/interactionRequestRoutes.test.ts tests/routes/legacyQuestionRoutes.test.ts tests/routes/hostQuestionStream.test.ts tests/routes/chatInteractionBridge.test.ts tests/runtime/hostQuestion.test.ts`。无推理的原生隔离检查可运行 `RUN_HOST_QUESTION_NATIVE_SMOKE=1 bun tests/integration/hostQuestionNativeSmoke.ts`,使用独立临时数据根及进程,验证固定输入 ID 去重、重启恢复和 form 状态边界。该检查不证明模型实际选择 ask_user 或完整业务续跑已验收。
原生插件通过 `present_answer` 发布 `answer-ui@1` 内容块,现有聊天消息流输出
`data-answer_ui`,允许文字与本地指标、表格、图表、证据、地图定位和补充资料表单交替出现。
相同 block_id 更新本轮内容;消息持久化与历史恢复保留结构化部分。最终答复包含
完整报告文字及 ui_block_ids 引用的已发布组件,其余过程默认折叠。已发布界面的
常驻指令要求按阅读任务主动选择组件:多对象比较优先 Table,数值趋势优先 Chart,
简单解释和简短确认保留文字。用户要求纯文字时遵从。聊天图表保留分析依据;
`present_view` 的 `open_chart` 命令用于用户要求打开的工作台独立图表,避免默认重复展示。
需要保留的组件直接显式指定 phase=final,无须先发布 process。
相同 block_id 仅更新本轮同 phase 内容,默认 phase=process;最终组件需独立 ID 并明确 phase=final。消息持久化与历史恢复保留结构化部分。最终答复包含
完整报告文字及 ui_block_ids 允许引用的 final 组件,使用独立一行 `[[ui:block_id]]` 安排位置;process 组件与其余过程默认折叠。已发布界面的
聊天在最终答复完成且无待完成工具时收尾,不代替后台任务的完成接口。后台调查执行
不发布聊天界面块,继续使用原任务报告和展示通道。`publish_task_report` 的报告可携带
`ui_blocks`,每项包含相同 `answer-ui@1` 内容块及 `evidence_refs`。最多 8 块,总 UTF-8
@@ -492,9 +512,23 @@ UI 托管服务配置或数据库迁移。详见
原生工具发现验证:`bun run tests/integration/answer-ui-smoke.ts`,使用独立临时数据库,
不调用模型推理,不接管既有服务。
自然问题选择验收:
```bash
TJWATER_ANSWER_UI_EVAL=1 bun run tests/integration/answer-ui-selection.ts
```
该命令用真实模型验证三个场景:管道比较、压力趋势、简单概念解释。提示不指定工具名
或组件名,检查最终答复保留的组件、输入数值与引用关系,以及简单问题是否保持文字。
默认使用配置中的聊天模型,可用 `TJWATER_ANSWER_UI_EVAL_MODEL=provider/model` 选择已启用的
聊天模型。每个场景使用独立会话,OpenCode 进程、数据库和工作目录均为临时资源,结束后清理。
只开放 present_answer 与 final_answer,使用演练资料并模拟宿主提问策略接口;不连接业务 MCP,
不执行地图动作、Shell 或业务写入。这验证原生模型的组件选择,不代替浏览器渲染、真实查询
或多工具场景的验收;单次通过也不代表稳定使用率。未设置启用变量时不发起模型请求。
## 第一方产品 MCP 查询
新增10项受控分析:标准水力模拟、爆管侦测、爆管定位、阀门隔离、DMA漏损识别、污染扩散、关阀影响、冲洗分析、调泵影响和压力监测点选址。普通项目会话与调查主Agent可按权限调用,reader保持只读。计算不保存正式方案或下发设备指令;调查结果和输入假设沿用证据链保存。计算工具支持Agent按任务指定 `timeout_seconds`,默认120秒,上限由Server工具schema公布(默认1小时);HTTP、宿主和OpenCode同步使用相应预算,普通查询仍为30秒。修改Server上限后需重启Agent以重新发现目录并更新原生连接。长计算超时不会自动重试。能力、单位、模板隔离和发布边界见[分析工具说明](../next-tjwater-server/docs/mcp-analysis-tools.md)。
新增10项受控分析:标准水力模拟、爆管侦测、爆管定位、阀门隔离、DMA漏损识别、污染扩散、关阀影响、冲洗分析、调泵影响和压力监测点选址。普通项目会话与调查主Agent可按权限调用,reader保持只读。计算不保存正式方案或下发设备指令;调查结果和输入假设沿用证据链保存。长计算采用提交作业、查询状态、读取结果的队列流程。HTTP 返回预算由目录元数据和宿主管理,模型不再填写 `timeout_seconds`。Server 默认120秒、Agent默认180秒;Server上限240秒,Agent加60秒余量并封顶270秒,外层原生MCP总时限300秒。后台执行时限由服务端独立配置(默认3600秒),爆管定位没有单独的长超时。修改Server请求预算后需重启Agent以重新发现目录。状态查询遇到临时网络/服务错误最多退避重试3次,新完成作业会先返回模型处理;计算不会自动重试。每项目最多100个未结束作业,排队期限1小时,结果保留7天;结果过期保留任务记录与已归档证据。部署前追加Server的027队列生命周期迁移。能力、单位、模板隔离和发布边界见[分析工具说明](../next-tjwater-server/docs/mcp-analysis-tools.md)。
本地开发手动启动Agent,读取项目 `.local.env`,已有系统环境变量优先。MCP随Server运行。仓库保留 [tjwater-agent.service](infra/systemd/tjwater-agent.service) 作为可选部署模板;用户级Server/Agent服务已移除,不与手动进程同时启用。选择systemd部署时,私有 `~/.config/next-tjwater/agent.env` 会覆盖项目同名配置,必须与Server及前端的认证环境一致,并复用已有原生会话数据库。
@@ -502,7 +536,7 @@ UI 托管服务配置或数据库迁移。详见
项目及宿主只读查询统一由原生连接 `mcp_tjwater` 提供,工具 ID 为 `mcp_tjwater_<name>`。本地 MCP 使用官方 Server SDK 2.3.1,固定 MCP 2026-07-28,默认提供地理编码、会话搜索、调查任务列表与详情、消息读取和待办读取六项查询,启用上游后合计73项。公开网页搜索使用 OpenCode 原生 `websearch`。`memory_manager`、`skill_manager` 保留完整读写插件;消息发送、报告发布和地图展示等也保留插件形式。
`mcp_tjwater_query_nearby_elements` 查询当前授权项目的模型管网:将 `mcp_tjwater_geocode` 返回的 EPSG:4326 `center: [经度,纬度]` 原样传入,后端负责坐标转换。`element_type` 默认 `pipe`,`radius_m` 默认200、最大10000米,`limit` 默认5、最大100,支持 `offset`。结果按点到完整几何的 WGS84 椭球最短距离排序,返回模型 `element_id`、米制 `distance_m` 和 `properties`;可用 `locate_features` 定位。原始 GIS 的 `source_fid` 不在此查询范围。空结果不自动扩大半径,空间邻近不代表连通或供水归属。普通查询预算仍为30秒,用户、项目和调查证据链沿用现有 MCP 规则。
`mcp_tjwater_query_nearby_elements` 查询当前授权项目的模型管网:将 `mcp_tjwater_geocode` 返回的 EPSG:4326 `center: [经度,纬度]` 原样传入,后端负责坐标转换。`element_type` 默认 `pipe`,`radius_m` 默认200、最大10000米,`limit` 默认5、最大100,支持 `offset`。结果按点到完整几何的 WGS84 椭球最短距离排序,返回模型 `element_id`、米制 `distance_m` 和 `properties`;可用 `present_view` 的 `locate_features` 命令定位。原始 GIS 的 `source_fid` 不在此查询范围。空结果不自动扩大半径,空间邻近不代表连通或供水归属。普通查询的Server默认HTTP预算为120秒,用户、项目和调查证据链沿用现有 MCP 规则。
事件业务处置 Skill 由 Server 维护并经任务 MCP 分发:`get_task_context` 摘要返回 `handling_skill` 名称、版本和内容摘要,`section="skill"` 返回按已绑定任务 workflow 选择的完整流程,不保存为事件证据。Agent 本地 Skill 维护通用执行方法,不复制或覆盖 Server 业务规则;权限、人工审批与动作执行仍由 Server 控制。报告和进度的人类可读时间统一使用 Asia/Shanghai(UTC+8),API 时间保留原始时刻。更新 Agent 指令或 Server 工具 schema 后须重启 Agent 重新加载。
@@ -510,7 +544,7 @@ UI 托管服务配置或数据库迁移。详见
三类 Agent 均优先单对象、有过滤条件和必要短时间窗的查询。集合默认20条,按需翻页,不自动遍历全网;offset 上限10000,超过时缩小时间窗或目标。历史组合查询按序列、时间对数据点分页;读取证据优先指定 data_path 和小页。DEMO 样本超过整份摘要校验的8MiB预算时需拆分数据源,反复缩小时间窗不能解除此限制。截断或非零offset均不能证明完整范围无异常。
历史曲线展示调用从每页500个数据点起步,遇到 `RESULT_TOO_LARGE` 原位减半重试;最多读取1000条、发送16次请求,共享120秒超时(单次普通查询仍为30秒),展示最多100个有效点。空值页不等于没有历史数据,预算内仍有未读取数据时会明确说明。此预算独立于模型首次查询默认20条和时序接口单页10000条上限。
历史曲线展示调用从每页500个数据点起步,遇到 `RESULT_TOO_LARGE` 原位减半重试;最多读取1000条、发送16次请求,共享120秒超时(单次请求同时受该剩余总预算约束),展示最多100个有效点。空值页不等于没有历史数据,预算内仍有未读取数据时会明确说明。此预算独立于模型首次查询默认20条和时序接口单页10000条上限。
`get_task_context` 默认返回固定摘要和可读章节;完整任务、指定报告、证据、动作、进度、授权申请、计划和运行信息通过 section/data_path 分页读取。预览带 `preview_only`,不能当作完整报告。详情每页默认20项、最多500项,长文本按字符分页,数据库返回前限制8KB,超限时缩页或读取更深路径。上游大结果的证据资源链接在本地转成 `evidence_read` 工具参数,原证据编号与来源保留,不要求 OpenCode 直接读取上游资源。
@@ -546,13 +580,3 @@ UI 分页只影响读取窗口,服务端继续保存完整消息并从完整
主题是当前用户在个人/业务项目范围内的会话组织单元,与业务 `project_id` 独立。`GET/POST /api/v1/topics` 读取/创建主题,`PATCH/DELETE /api/v1/topics/:topic_id` 重命名/移除主题,`PATCH /api/v1/session/:session_id/topic` 以 `topic_id`(UUID 或 null)调整会话归属。主题名 1–80 字符,当前主题列表存于 `SESSION_METADATA_STORAGE_DIR/topics/<scope-hash>.json`,与元数据目录一起持久化。删除主题保留会话,遗留成员 ID 在读取时视为未分类;不会改写业务项目、原生会话或授权。旧会话默认未分类,无需迁移。普通会话和任务协作可归类,后台任务执行会话不进入用户主题。
`POST /session` 或首次 `/responses` 的 `tjwater.topic_id` 可指定已授权范围内的主题;后续续聊沿用原会话,移动使用上述独立接口。主题只组织记录,本轮不自动拼接其他会话或引入主题共享指令/附件。正文搜索在通过所有权校验的记录内读取完整消息 JSON(活动会话使用当前运行消息),匹配可见文本而非工具输入或推理;分页仍按更新时间及 ID 排序,不另建搜索服务。
## 可恢复提问与回答展示
支持 `host-question@1` 的客户端使用自有 `ask_user` 提问。宿主在会话 `chat/interaction-requests.json` 保存问题、选项草稿、已提交答案和固定消息 ID 的续跑记录;切换会话不取消提问。文件最多 2 MiB、1000 个请求,不保存登录凭据。接口位于 `/api/v1/session/:session_id/interactions`,提供列表、单项、草稿、reply/skip/cancel/resume;保存答案与模型继续分别显示状态。续跑通过现有 `/responses` 的 `tjwater.interaction_id` 使用服务端答案,不接受调用者替换答案文字。
续跑的用户消息保留同一个 `delivery.native_message_id`,保存可读文本及 `data-interaction_reply` 已提交回答快照;给模型的协议前缀仅用于推理输入。会话详情、分页和恢复流首次状态按当前会话的投递 ID 投影旧记录,提供相同的回答表示,不猜测文本前缀、不改写历史文件。客户端可在独立提问状态接口返回前还原回答卡片和关联过程,随后按修订更新投递状态。
原生 question 保留兼容入口,新增 `GET /api/v1/session/:session_id/questions/:request_id` 核对状态;原生请求缺失时显示失效,已持久化的回答终态保留。服务重启后,在有效用户授权下核对已保存的续跑记录;固定 ID 已被消费时不重发。无法核实对应的已完成回复时显示继续失败,不能把上一轮回答当作恢复结果。模型服务与插件需要同步更新,现有实例不会因源码修改自动完成发布。
聚焦验证:`bun test tests/sessions/interactionRequest.test.ts tests/routes/hostQuestionStream.test.ts tests/routes/interactionRequestRoutes.test.ts tests/routes/chatInteractionBridge.test.ts tests/runtime/hostQuestion.test.ts`。