7.3 IoT DC3 的 Agentic Center 实践
7.3.1 IoT DC3 Agentic Center:当前实现与运行时映射
前文给出的是完整工业 Agent Runtime 的逻辑模型。回到 IoT DC3,需要先区分“当前源码已经具备的能力”和“面向未来的运行时目标”。如果把所有目标能力都写成现状,会高估系统;如果只把 Agentic Center 看成一个聊天接口,又会忽略它已经形成的受控执行基础。
更准确的定位是:当前 Agentic Center 是一个具备会话、模型适配、显式 Tool、租户上下文和人工确认写入的受治理对话式工具运行环境,但还不是通用的长期任务 Agent Runtime。
当前能力如何映射到运行时四平面
决策平面已经具备统一模型调用入口。ChatClientFactory 根据 Provider 与模型配置构建、缓存对应的 ChatClient,上层对话与 Tool 代码不必绑定单一模型厂商。当前决策主要发生在一次会话请求内,还没有独立的长期任务规划器和跨事件调度器。
上下文平面已经具备会话与消息持久化,并在 Tool 调用时携带租户、用户和会话信息。它能够支持多轮对话回放和受身份约束的能力调用,但尚不能等同于完整的长期记忆系统:没有统一的领域 Memory 生命周期、重要性筛选、过期淘汰和跨任务检索策略;RAG 也仍是可扩展能力,而非默认内置的数据通路。
执行平面已经形成显式 Tool 目录。源码中有 10 个 Tool 类,当前 MethodToolCallbackProvider 注册 Tenant、User、Device、Driver、Profile、Point、PointValue 与 System 八类能力;CommandTool 和 EventTool 尚未注册。Tool 通过 Facade 复用平台服务,而不是把设备协议和业务逻辑复制进模型适配层。Gateway 侧的 MCP 入口还提供工具目录、连接授权和白名单,使外部 Agent 能按协议发现经过裁剪的平台能力。
治理平面已经覆盖关键写入链路。ToolContext 提供租户、用户和会话上下文;位号写 Tool 创建有效期有限的 PENDING Action,用户确认后才由 ActionService 进入实际命令链路。MCP 入口有独立的 OAuth、连接授权、工具白名单和确认状态,不能与 Agentic Center 内部 Action 简化成同一个拦截器。现有日志、消息、Action 和命令记录为审计提供了基础,但还缺少统一的 run_id、任务状态机、步骤级 Trace、租约、恢复与补偿语义。
当前成熟度矩阵
| Runtime 能力 | 当前状态 | 准确边界 |
|---|---|---|
| 模型适配 | 已具备 | 支持 Provider/Model 配置与 ChatClient 构建,不等于自动模型路由策略 |
| 会话上下文 | 已具备 | 支持消息持久化与会话连续,不等于长期领域 Memory |
| Tool 注册 | 已具备 | 当前显式注册 8 类 Tool,不是所有带 @Tool 的 Bean 自动可见 |
| MCP 能力暴露 | 已具备 | 支持工具目录、连接授权和白名单;MCP 不负责任务编排 |
| 受控位号写 | 已具备 | 通过 PENDING Action 等待确认,不直接控制设备 |
| 审计与观测 | 部分具备 | 会话、Action、命令与日志分散存在,尚未统一为任务 Trace |
| RAG 与领域 Memory | 部分具备/可扩展 | 书中给出方法,但当前不是 Agentic Center 默认完整链路 |
| Workflow 与 Skill 注册 | 待建设 | 尚无通用的步骤状态、条件、补偿和版本化 Skill 生命周期 |
| 长期任务调度 | 待建设 | 尚无统一 run_id、租约、断点恢复、事件触发和跨进程调度 |
| 运行时恢复 | 待建设 | Action 解决特定确认问题,不等于通用重试、幂等和补偿引擎 |
这张矩阵给出了后文阅读的基准:7.3.2~7.3.4 讨论的 Device、Driver 和 PointValue Tool 属于当前可验证实现;智能告警编排、RAG、长期自主任务和多 Agent 协作属于需要在现有基础上继续演进的能力。评价系统时,应分别回答“今天能运行什么”和“下一步需要补什么”,而不是用一个模糊的“支持 Agent”概括全部成熟度。
7.3.2 DeviceTool:设备检索与控制
设备是物联网平台的核心实体。传统运维界面适合精确配置,但处理告警时,操作员常常只知道设备名称、编码、所属驱动或物模型,需要先搜索设备,再关联状态和位号值。DeviceTool 把这些只读查询暴露为模型工具,让自然语言成为现有设备数据的检索入口。
当前提供的方法
当前 DeviceTool 通过 DeviceFacade、PointFacade、PointValueFacade 和可选的 StatusHealthFacade 访问平台数据,主要方法包括:
lookupDeviceById、lookupDevicesByIds:按 ID 查询单台或批量设备;searchDevices:按设备名称、编码或 Driver ID 分页检索;listDevicesByDriverId、listDevicesByProfileId:按驱动或物模型列设备;getDeviceLatestPointValues:返回设备绑定位号及最新值快照;getDeviceStatusesByIds、getDeviceStatusesByProfileId:查询在线/离线状态。
这些方法都是查询能力。当前 DeviceTool 没有设备创建、属性修改或设备控制方法,也不存在“设备写操作自动二次确认”的注解逻辑。真正的位号写命令由 PointValueTool 准备待确认 Action,不能混写到 DeviceTool 中。
下面的简化代码保留了源码中的关键边界:从 ToolContext 取得租户 ID,构造租户范围内的查询,再通过 Facade 返回结构化结果。
@Tool(description = "Search for devices with optional filters")
public AgenticToolResult<FacadePage<FacadeDeviceBO>> searchDevices(
String deviceName,
String deviceCode,
Long driverId,
int page,
int size,
ToolContext toolContext) {
Long tenantId = AgenticToolContextUtil.requireTenantId(toolContext);
FacadeDeviceQuery query = new FacadeDeviceQuery();
query.setDeviceName(deviceName);
query.setDeviceCode(deviceCode);
query.setDriverId(driverId);
query.setTenantId(tenantId);
query.setPage(AgenticToolUtil.page(page, size));
return AgenticToolResult.ok("Device page loaded", deviceFacade.listByPage(query));
}Tool 方法能从 ToolContext 取到租户,是因为调用方在发起对话时注入了它。装配发生在 ChatClient 一侧,示意如下:
// 示意:ToolContext 在业务侧装配,租户与用户来自受信任的请求上下文(网关注入的主体),
// 不是模型生成字段;参数键名以项目常量定义为准
String answer = chatClient.prompt()
.user(question)
.tools(agenticToolCallbackProvider) // 注册 7.3.1 节所述的八类 Tool
.toolContext(Map.of(
"tenantId", requestContext.getTenantId(),
"userId", requestContext.getUserId(),
"conversationId", conversationId))
.call()
.content();toolContext(...) 接收的键值对会原样传给 Tool 方法签名中的 ToolContext 参数,AgenticToolContextUtil.requireTenantId(...) 等方法即从中取值;会话 ID 同时用于 7.2.4 节的对话记忆与写 Action 的归属。身份来源是平台登录态而非模型输出,这是本节各 Tool 敢于直接信任 ToolContext 的前提。
模型处理“查看三号车间温控器状态”时,可以先用 searchDevices 找到候选设备,再用 getDeviceStatusesByIds 查询状态,最后用 getDeviceLatestPointValues 汇总关键位号。每一步都返回结构化结果,模型只负责选择下一步与组织说明,不直接读取数据库。
DeviceTool 的工程价值是缩短查询路径,而不是取代设备管理界面。批量导入、复杂配置、拓扑编辑仍应留在专业界面或脚本中完成;模型工具更适合临时检索、跨对象关联与解释型结果汇总。
7.3.3 DriverTool:驱动配置与管理
Driver 是协议接入与设备管理之间的关键实体。排查设备离线时,操作员通常要先确认设备属于哪个 Driver,再判断 Driver 自身是否在线,以及其下设备是否普遍异常。DriverTool 把这条诊断链需要的查询能力提供给模型。
当前提供的方法
当前 DriverTool 的能力包括:
lookupDriverById、lookupDriversByIds:按 ID 查询 Driver;lookupDriverByDeviceId:反查设备所属 Driver;searchDrivers:按名称分页检索 Driver;getDriverStatusesByIds:查询 Driver 在线/离线状态;getDriverDeviceStatusSummary:统计某个 Driver 下设备在线与离线数量。
这些方法均为只读查询。当前源码没有 listDriverTypes、configureDriver、toggleDriver 等 Tool 方法,也没有 @WriteOperation(requiresConfirmation = true) 注解。创建、修改或启停 Driver 仍由平台既有管理 API 和界面负责,不能把建议中的未来能力描述成当前实现。
一个符合当前能力边界的对话是:操作员说“为什么设备 S3012 离线?”模型先用 DeviceTool.searchDevices 定位设备,再用 DriverTool.lookupDriverByDeviceId 找到所属 Driver,随后调用 getDriverStatusesByIds 和 getDriverDeviceStatusSummary。如果 Driver 在线但只有该设备离线,结果更指向现场链路或设备自身;如果 Driver 离线且其下设备普遍离线,则应优先检查 Driver 进程、网络和配置。
这类诊断不会直接改变运行状态,却能把设备、Driver 与状态数据串成一条解释链。若后续要开放 Driver 启停,应新增独立的高风险 Action 类型、权限校验、幂等控制和审计记录,而不是简单给查询方法增加一个布尔参数。
7.3.4 PointValueTool:实时数据读写
位号值是物联网运维中最常查询、也最需要谨慎写入的数据。当前 PointValueTool 通过 PointValueFacade、PointCommandFacade 和 ActionService 提供四类能力:
getLatestPointValue:按 Device ID 与 Point ID 查询最新值;getPointValueHistory:查询历史值,并返回可直接绘图的数值序列和统计摘要;readPointValue:提交读命令,让 Driver 从物理设备主动读取指定位号;writePointValue:准备写命令,但不直接执行。
最新值和历史值由 Data Center 统一提供。当前 Data Center 的最新值使用本地 Caffeine 缓存,历史数据写入 PostgreSQL;不能把这里写成 MongoDB、TDengine 或其他未部署的时序数据库。
写入确认的真实流程
writePointValue 没有使用虚构的 @WriteOperation 注解,也不会在 Spring AI 内部自动拦截后执行。它先校验 Device ID、Point ID 与写入值是否为空,再从 ToolContext 取得租户、用户和会话信息,调用 ActionService.createWritePointValueAction 创建一条 10 分钟有效的 PENDING Action,并把 actionId 返回给客户端。
@Tool(description = "Prepare a point write command")
public AgenticToolResult<PointCommandResult> writePointValue(
Long deviceId,
Long pointId,
String value,
ToolContext toolContext) {
RequestHeader.PrincipalHeader header =
AgenticToolContextUtil.requirePrincipalHeader(toolContext);
String conversationId =
AgenticToolContextUtil.requireConversationId(toolContext);
String actionId = actionService.createWritePointValueAction(
conversationId, deviceId, pointId, value, header);
return AgenticToolResult.ok(
"Write command is pending user confirmation",
new PointCommandResult(deviceId, pointId, value, false, true, actionId));
}客户端可查询当前会话的待确认 Action,并调用 Action 接口确认或拒绝。确认时,ActionService 以租户、用户、状态和过期时间为条件原子抢占该 Action;只有仍处于 PENDING 且未过期的记录才能继续。随后服务调用 PointCommandFacade.submitWrite 提交写命令,状态更新为 EXECUTED 或 FAILED。命令再经 Data、RabbitMQ 和对应 Driver 到达物理设备。
这套设计把“模型建议写入”和“平台真正执行”拆成两个明确步骤,确认依据是持久化 Action,而不是模型在自然语言里说了一句“已确认”。若还要增加值域校验、速率限制或多级审批,应继续在平台服务和 Action 流程中实现,不能依赖提示词保证。
7.3.5 自然语言运维:对话替代仪表盘
自然语言运维的价值,是让模型按任务需要组合多个只读查询和受控写入,而不是为平台另造一套业务接口。以“检查三号车间温控器,并把目标温度写为 24”为例,符合当前实现边界的步骤是:
- 调用
DeviceTool.searchDevices找到候选设备; - 调用
DeviceTool.getDeviceStatusesByIds排除离线设备; - 调用
PointTool定位目标温度对应的 Point; - 调用
PointValueTool.getLatestPointValue读取当前值; - 调用
PointValueTool.writePointValue创建待确认 Action; - 客户端展示 Device、Point、目标值与
actionId,用户确认后由 Action 接口执行。
这里不能调用未注册进 Provider 的 Tool(注册清单见 7.3.1 节),也不能把 Driver 查询工具写成 Driver 配置工具。模型负责拆解任务和解释结果,租户边界、参数校验、确认状态、幂等与审计仍由平台代码负责。
Skills 与 CLI 仅作知识对齐
本书引入 Skills 和 CLI,是为了帮助读者理解主流 Agent 工程中的常见概念,不是宣称 IoT DC3 已经实现这两个产品能力。
- Tools 是当前已实现的原子能力,由 Spring AI
@Tool方法和 Gateway 的 MCP Tools 端点分别提供;二者的目录来源不同,不能视为同一份自动同步的工具集。 - Skills 可理解为对多个 Tool、提示模板与输入输出契约的稳定编排,例如“设备晨检”或“离线诊断”。当前源码没有 Skill 类型、注册器或执行器。
- CLI 是终端客户端形态。类似
dc3 agent "查询离线设备"的命令只用于说明理想交互,当前项目没有dc3 agent命令。
如果未来实现 Skills,应在现有 Tool 之上增加显式编排层,并继续复用租户、权限和 Action 确认;如果未来实现 CLI,应只负责参数解析、认证与输出展示,通过现有 HTTP 或 MCP Tools 调用服务端能力,避免复制业务逻辑。
自然语言入口适合查询、跨对象关联和少量受控操作;成百上千台设备的批量配置、毫秒级监控和协议调试仍应使用专业界面、自动化脚本或专用控制系统。
7.3.6 智能告警分析与数据洞察
规则引擎触发告警后,操作员通常要打开设备详情、查询 Driver 状态、翻历史值和维修记录,再凭经验判断原因。把这些信息自动汇聚并交给模型分析,是 Agentic Center 很自然的演进方向,但必须明确:RAG 知识库、自动告警触发、主动推送和异常到动作流水线目前属于参考设计,不是当前默认 Compose 已上线能力。
四阶段参考流水线
一个可落地的智能告警分析方案可以拆成四个阶段:
- 告警接入与上下文汇聚:接收规则引擎事件,按租户读取设备、Driver、Profile、Point 和历史值,形成结构化上下文;
- RAG 检索增强:从受版本管理的 SOP、设备手册和历史工单中检索相似案例,并保留来源与版本;
- LLM 生成诊断报告:输出事实、推断、证据来源、影响范围和建议步骤,明确区分“已观测事实”和“模型推测”;
- 结果推送与人工决策:只读诊断可直接展示,任何写入都转成待确认 Action,不让模型直接控制设备。
当前已注册的 DeviceTool、DriverTool、PointTool 和 PointValueTool 可以提供部分结构化上下文,但项目中尚没有这一流水线所需的 VectorStore、案例入库任务和自动触发编排。实现时应把 RAG 作为独立能力接入,而不是在文稿中假设它已经存在。
数据洞察的现实边界
PointValueTool.getPointValueHistory 已能返回历史值、数值摘要和图表数据,因此模型可以对用户主动发起的查询做趋势解释,例如比较最近窗口的平均值、最大值和变化方向。但“每 15 分钟自动巡检”“预测 30 分钟后越限”“主动推送告警”还需要调度器、阈值配置、回放验证和通知通道,不能仅靠一次 Tool Calling 实现。
工程验证应至少覆盖三类指标:检索是否命中正确版本的资料,模型是否把推断误写成事实,以及建议动作是否被平台 Action 流程拦住。离线日志回放比直接上线试错更安全:先用历史告警评估召回率、误报率和建议可执行性,再决定是否开放自动触发。对于无法撤销的设备动作,即便未来完成自动编排,也应保留人工确认或外部审批。
因此,智能告警分析的正确定位是“用当前 Tool 作为数据入口、按需叠加 RAG 与编排”,而不是把尚未实现的向量库、Command/Event 默认工具和自治执行链写成现状。