feat(server): integrate MCP queries and investigation messaging
Add project-bound OAuth MCP queries and controlled analysis with scoped evidence, limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs. Default nearby searches to 200 meters and five results. Integrate investigation messaging/presentation, health/readiness and typed history; remove duplicate web search and fix online simulation and compressed baseline writes. Include project migrations 023-025; keep local credentials and demo seeding excluded. Validation: 319 focused pytest checks; real PostGIS temporary-table regression; read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
This commit is contained in:
@@ -4,6 +4,17 @@
|
||||
|
||||
## 调查任务队列
|
||||
|
||||
第二轮及后续调查的触发条件、待执行轮次合并、旧运行替代,以及协作与消息的边界,见 [调查轮次与协作边界](operational-tasks.md#调查轮次与协作边界)。
|
||||
|
||||
worker 始终只处理 `event_investigation`,外部预约工单不占用模型并发。
|
||||
执行列表支持可选 `task_type`、`task_created_from` 查询参数;租约恢复请求支持同名字段,
|
||||
均在数据库分页之前过滤。省略字段保持原接口行为。时间下界使用任务创建时间,
|
||||
旧任务产生新 run 不会越过边界,不删除历史或修改旧任务状态来缩小执行范围。
|
||||
|
||||
本机常驻模板位于 `infra/systemd/tjwater-operational-worker.service`,使用已有 `.env`
|
||||
凭据与配置。安装到用户 systemd 后启用,避免同时启动第二个 worker。
|
||||
用户服务未启用 linger 时,不保证退出登录后持续运行。
|
||||
|
||||
当前产品分为定时任务、调查任务、预约任务。定时任务保留前端 mock 数据,不调用后端计划创建或到期调度;调查任务读取数据库并进入本工作队列;预约任务读取数据库中的外围工单投影,以外部同步进度为准。已有后端 schedule API 暂不在当前产品主路径使用。
|
||||
|
||||
从后端仓库运行:
|
||||
@@ -22,13 +33,31 @@ conda run -n server python scripts/run_operational_worker.py
|
||||
| `OPERATIONAL_WORKER_AGENT_URL` | Agent 地址,默认本机 8787 |
|
||||
| `OPERATIONAL_WORKER_RECONCILE_SECONDS` | SSE 中断或漏通知时的对账间隔,默认 30 秒 |
|
||||
| `OPERATIONAL_WORKER_MAX_CONCURRENCY` | 同时投递的 run 上限,默认 4,可设置 1 至 32 |
|
||||
| `OPERATIONAL_WORKER_TASK_CREATED_FROM` | 可选、带时区的任务创建时间下界(包含边界),限制排队、并发计数、凭据刷新和租约恢复;不是 run 创建时间 |
|
||||
| `OPERATIONAL_WORKER_BEARER` | 已具有该项目权限的产品身份,仅用于受控开发或短期运行 |
|
||||
| `OPERATIONAL_WORKER_TOKEN_URL` | 可选 OAuth 客户端凭据取令牌地址 |
|
||||
| `OPERATIONAL_WORKER_DEV_TOKEN_URL` | 仅 `ENVIRONMENT=development` 且非空时覆盖取令牌地址;其他环境忽略,保留正式地址 |
|
||||
| `OPERATIONAL_WORKER_CLIENT_ID`、`OPERATIONAL_WORKER_CLIENT_SECRET` | 长期运行身份;不配置固定 Bearer 时使用,并按有效期刷新令牌 |
|
||||
| `OPERATIONAL_WORKER_TOKEN_REFRESH_SECONDS` | OAuth 缓存上限,默认 300 秒,可设置 30 至 600 秒;同时受令牌 expires_in 限制,应短于后端允许的最大令牌年龄 |
|
||||
|
||||
工作身份必须能通过现有产品身份与项目成员权限校验。配置令牌获取不等于自动创建产品用户、项目成员或授权。权限错误会保留任务用于后续重试,不会绕过认证。
|
||||
|
||||
调查数据读取沿用账号已有的项目权限,不再因 `service` 身份额外移除 `scada.view` 和
|
||||
`simulation.view`。上下文的 `approved_data_scopes` 与数据查询采用同一权限集合;已有读取
|
||||
权限无需逐任务数据审批或委托凭据。此规则不新增角色权限、不放开跨项目读取,也不改变
|
||||
工单审批和设备控制权限。旧的显式批准委托路径保留兼容。
|
||||
|
||||
工作进程每轮从 Server 的 claimed/running 运行快照读取 agent_session_id,并使用当前 OAuth 凭据向 Agent 的 `POST /api/v1/session/{session_id}/credentials` 更新正在执行的会话凭据。令牌只在 header 中传递,body 为 `{}`,不会进入任务、报告或会话持久化文件。Agent 再次校验会话所属用户和项目;工作进程重启后从 Server 快照恢复关联,不依赖浏览器续期。固定 Bearer 仍受自身有效期限制。
|
||||
|
||||
Agent API 统一使用 `/api/v1` 前缀:任务投递为 `POST /api/v1/task-executions`,消息投递为 `POST /api/v1/task-messages/deliver`,凭据续期使用上述会话接口。旧 `/api/v1/agent/chat` 前缀已移除。更新脚本后须重新启动常驻 worker,现有 Python 进程不会自动加载文件变化。本机用户服务使用:
|
||||
|
||||
```bash
|
||||
systemctl --user restart tjwater-operational-worker.service
|
||||
systemctl --user status tjwater-operational-worker.service --no-pager
|
||||
```
|
||||
|
||||
重启后检查日志中的实际请求路径;HTTP 404 属于不可自动重试的投递失败,已失败的 run 不会因重启自动恢复,需通过原有重新调查流程显式创建新一轮。
|
||||
|
||||
长期 OAuth 身份由身份管理员创建支持 `client_credentials` 的专用机密客户端;服务账号令牌应具有后端配置要求的 audience、UUID `sub` 和 `preferred_username`。再由产品管理员把该 `sub` 绑定到启用的产品用户及目标项目成员关系。只赋予本次调查所需项目权限,不授予系统管理员或设备命令权限。当前项目成员策略同时包含工单审批/执行权限,不能将其误称为已经细分的专用最小权限服务角色。
|
||||
|
||||
本机联调配置示意(密钥由管理员通过安全配置填入,禁止粘贴到聊天):
|
||||
@@ -36,7 +65,7 @@ conda run -n server python scripts/run_operational_worker.py
|
||||
```dotenv
|
||||
OPERATIONAL_WORKER_PROJECT_ID=324ff7ee-c2d4-5184-a84b-48fb14a2933e
|
||||
OPERATIONAL_WORKER_SERVER_URL=http://127.0.0.1:8000
|
||||
OPERATIONAL_WORKER_AGENT_URL=http://127.0.0.1:18787
|
||||
OPERATIONAL_WORKER_AGENT_URL=http://127.0.0.1:8787
|
||||
OPERATIONAL_WORKER_TOKEN_URL=https://<identity-host>/realms/<realm>/protocol/openid-connect/token
|
||||
OPERATIONAL_WORKER_CLIENT_ID=<dedicated-service-client>
|
||||
OPERATIONAL_WORKER_CLIENT_SECRET=<inject-securely>
|
||||
@@ -50,8 +79,44 @@ OPERATIONAL_DATA_PERIPHERAL_URL=http://127.0.0.1:8011
|
||||
|
||||
Agent 正常结束必须调用 run `complete`。同一执行代次缺少最终计划或报告时,Server 将 run 以 `ARTIFACT_INCOMPLETE` 明确失败,不依赖租约超时猜测完成状态。心跳只增加 run revision,不改变任务业务 revision。
|
||||
|
||||
最终报告未提供 Mermaid 图(包括 `diagram=null`)时,Server 将同轮、同会话且不晚于报告发布时刻的最新有效进度图保存到报告中;图的证据引用必须已在报告范围内,节点状态保留记录原值,不把“记录时执行中”改成“已完成”。进度快照显式清空图后,不再从更旧的进度恢复。既有无图最终报告在详情、指定版本和历史列表读取时使用相同兼容规则,不改写历史数据库记录,也不借用新轮次的图。
|
||||
|
||||
前端默认跟随最新轮次的进展,新报告发布后自动显示新报告;没有本轮报告时保留并标注上一轮报告。本轮最终报告图优先于过程图。主动选择历史报告或展开阅读报告详情时保留阅读版本,通过“返回实时内容”或“查看最新内容”切换。
|
||||
|
||||
## 事件日志与清理
|
||||
|
||||
### 普通对话与调查运行的私有消息
|
||||
|
||||
启用消息接口前,需要按现有项目数据库发布规则应用
|
||||
`resources/sql/024_agent_messages.sql`;本次代码修改不自动执行迁移或重启服务。
|
||||
该表也是调查启动前收信、运行完成/失败和租约恢复的必需依赖,即使没有协作消息,
|
||||
也必须先迁移再启用调查执行。Server 在领取运行、修改状态前检查消息表;缺失时返回
|
||||
`503 DEPENDENCY_UNAVAILABLE`,提示具体迁移文件,不将排队运行改成“调查中”。
|
||||
若旧运行已因缺表卡住,补齐迁移后现有 worker 会按租约恢复规则创建下一代运行;
|
||||
不要手工改状态或重复提交投诉。应用迁移前须确认允许自动恢复及后续模型调用。
|
||||
迁移保留每条消息的项目、会话归属、明确的 `task_id`/`run_id`、幂等请求 ID
|
||||
及固定原生消息 ID。普通对话只能向排队或有效租约内的调查运行发送消息,
|
||||
调查回复只能沿已存在的消息关系返回原对话。终态运行拒绝新消息,不创建新的运行代次。
|
||||
|
||||
`POST /api/v1/agent-messages` 与 `/{message_id}/reply` 要求项目任务写权限;
|
||||
`GET /api/v1/agent-messages?session_id=...` 按认证用户的对话归属过滤,支持
|
||||
`cursor`、`limit` 与 `pending_only`。Agent 仍须验证实际会话归属,Server
|
||||
不把客户端提交的会话 ID 当成会话所有权证明。服务身份读取任务会话消息时,
|
||||
Server 校验该会话属于当前活动运行。跨用户投递查询与任务回执要求项目工作服务身份。
|
||||
原对话用户可以确认收到的回复,但不能改写目标会话或原生消息 ID。
|
||||
|
||||
既有 worker 每轮最多对账四页、每页 50 条私有待投递消息,即使调查并发已满仍继续对账,
|
||||
不增加进程或配置项。临时网络错误保留消息等待重试;权限拒绝、不存在等确定性
|
||||
拒绝保存失败结果。消息正文、发送人和会话归属不写入项目事件日志。
|
||||
待投递接口按创建时间与消息 ID 返回 keyset 游标,worker 跨轮保留扫描位置;
|
||||
扫到末页后下一轮从头重试。长期空闲对话的待收消息不会一直占住首屏、阻止后续调查投递。
|
||||
|
||||
`pending` 表示等待投递,`enqueued` 表示运行时已接受,`delivered` 表示消息已进入
|
||||
模型上下文;这三者都不能作为业务事项已经处理完成的证明。Agent 应在确认进入
|
||||
上下文后保存回执。同一运行仍有 `pending`/`enqueued` 入站消息时,`complete`
|
||||
返回结构化 `PENDING_AGENT_MESSAGES` 409,Agent 需接收后重试完成。取消、租约
|
||||
超时、失败或被新代次替代会将旧运行未投递的入站消息明确标记为失败。
|
||||
|
||||
`resources/sql/015_workbench_events_and_task_runs.sql` 在项目 BizDB 中增加 `domain_event_log` 和 `operational_task_runs`。后续迁移默认只应用到 `tjwater_v2`,扩展规则见 [`workbench-database-rollout.md`](workbench-database-rollout.md)。事件与业务投影在同一事务提交,`LISTEN/NOTIFY` 只负责唤醒;浏览器与 worker 始终按持久游标补读。每个 API 进程对活跃项目最多保留一条 LISTEN 连接,无订阅者 60 秒后关闭。
|
||||
|
||||
事件默认保留 7 天。清理脚本会从 Metadata DB 解析项目路由,并在发现多个项目共享同一 BizDB 时拒绝运行。先确认 `OPERATIONAL_WORKER_PROJECT_ID` 对应 `tjwater_v2`,然后运行定向 dry-run:
|
||||
|
||||
Reference in New Issue
Block a user