news 2026/10/5 8:38:44

把 AgentScope Harness 装进 RuoYi-Vue-Plus:纯 Java AI 平台的集成实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
把 AgentScope Harness 装进 RuoYi-Vue-Plus:纯 Java AI 平台的集成实践

前两篇讲了「是什么」和「权限怎么落地」。这一篇讲工程:智能体内核怎么与一个成熟的 Java 中台对接,以及我们踩过的坑。

一、目标:让中台「长出」智能体内核

我们不想要一个独立的 Agent 服务,再让业务系统去调它。目标是把智能体能力做成中台的一个模块:

  • 一个进程、一个 jar、一套构建;
  • 复用底座的权限、事务、缓存、审计;
  • 前端仍是一个 Vue 工程,通过 SSE 拿流式回答。

落点就是ruoyi-modules/ruoyi-ai,包org.dromara.ai。

二、依赖与版本

分类组件版本
语言 / 运行时Java21
业务框架Spring Boot4.1.1(Jetty)
智能体内核AgentScope Harness2.0.3
MCP官方 MCP Java SDK0.17.2
状态存储Redis(经 Redisson)AgentScopeRedisAgentStateStore
技能仓库PostgreSQLAgentScopePostgresSkillRepository
向量库Milvusv2.6.13
权限Sa-Token1.46.0

三、装配一个 HarnessAgent

装配集中在AgentRegistry。它的职责是:按「智能体定义 × 会话资源集」把模型、工具、权限、中间件、策略拼成一个可复用的HarnessAgent实例,并按指纹缓存——定义或资源组合变化时自动重建,避免每轮对话都新建模型 HTTP 客户端。

装配主干大致是这样:

HarnessAgent.Builderbuilder=HarnessAgent.builder().name(definition.getAgentCode()).description(...).sysPrompt(...)// 智能体提示词(专家装配时追加「技能优先」纪律).model(model)// OpenAI 兼容客户端,含超时与重试.toolkit(toolkit)// 业务工具 + MCP 工具 + 协议工具.stateStore(stateStore)// Redis 状态存储(多轮记忆).workspace(workspace)// 每个智能体一个工作目录.maxIters(maxIters)// 单轮最大迭代次数.toolsConfig(buildToolsConfig(...))// allow / deny 白名单裁剪.permissionContext(...)// 三档授权规则.middlewares(buildMiddlewares(definition));// 关闭平台不需要的能力:无沙箱,禁文件/命令工具builder.disableFilesystemTools().disableShellTool().disableMemoryTools();// 技能来自本项目数据库;关闭框架默认的工作区技能来源builder.skillRepositories(List.of(aiSkillRepository)).skillFilter(SkillFilter.only(skillNames));builder.disableDefaultWorkspaceSkills();

几个值得展开的点。

3.1 工具白名单裁剪(allow / deny)

框架的默认工具箱会附带联网检索、文件、命令等内置工具。企业平台没有沙箱,所以必须双重收敛:

privateToolsConfigbuildToolsConfig(List<String>registeredCodes,List<String>skillNames,McpClientAssemblymcpAssembly){List<String>allow=newArrayList<>(registeredCodes);if(!skillNames.isEmpty()){allow.addAll(SKILL_BUILTIN_TOOLS);// 技能内置工具须显式放行}if(!mcpAssembly.toolNames().isEmpty()){allow.addAll(mcpAssembly.toolNames());// MCP 工具名同样须显式放行}ToolsConfigconfig=newToolsConfig();config.setAllow(allow);// 白名单:非平台工具一律移除config.setDeny(List.of("web_search","web_fetch"));// 显式黑名单:禁联网检索returnconfig;}

教训:技能内置工具、MCP 工具都必须显式放进 allow 名单,否则会被框架的ToolFilter直接裁掉——模型看不到,你还找不到原因。

3.2 中间件注入

平台级中间件对所有智能体自动生效:

  • CurrentTimeMiddleware:每轮现算当前时间(日期 + 星期 + 时分 + 时区)注入 system prompt。模型自己不知道「今天几号」,框架原生注入的又是英文日期。
  • ExpertRosterMiddleware:只对入口智能体注入「本轮候选专家清单」。
  • SlotInheritanceMiddleware:只对入口智能体注入「槽位继承」。

中间件的好处是每轮现算、不进装配指纹——清单变了不必重建 Agent 实例。

四、状态与持久化

4.1 会话状态:Redis

多轮记忆、暂停恢复都依赖状态存储。我们直接复用项目已有的RedissonClient:

@BeanpublicAgentStateStoreaiAgentStateStore(RedissonClientredissonClient){returnRedisAgentStateStore.builder().redissonClient(redissonClient).keyPrefix("bizbuddy:ai:agentscope").build();}

用的是RedisAgentStateStore而非已@Deprecated的RedissonAgentStateStore,两者键布局一致,切换无需数据迁移。

4.2 技能仓库:复用业务表

技能的建表与写入由项目自己的 SQL 与服务负责(保留审计列与生效范围治理),框架侧只读:

returnPostgresSkillRepository.builder(dataSource).schemaName("public").skillsTableName("ai_skill").resourcesTableName("ai_skill_resource").createIfNotExist(false)// 不让框架建表.writeable(false)// 框架只读.build();

五、工具接入:业务工具工厂

平台的工具注册表ai_tool只存元数据(编码、名称、描述、参数 Schema、类型、实现标识),真正的实现必须存在于代码侧:

// 注册页只登记元数据;implName 对应代码侧已实现的 AgentTooltoolFactory.create(definition,kbIds).ifPresent(toolkit::registerAgentTool);

这道设计有意为之:避免出现「裸 SQL / 裸 Shell / 裸 HTTP」的万能工具。目前代码侧登记了 25 个工具实现,覆盖部门 / 用户 / 公告 / 角色 / 菜单权限 / 知识库检索等,其中写入类工具统一走 HITL。

六、MCP 双向集成

6.1 出口:把只读工具暴露出去

平台自建/mcp服务端(不依赖 spring-ai),可把标记为「对外暴露」的只读工具提供给外部调用方,并支持访问口令校验(Authorization: Bearer <token>或X-MCP-Token)。

6.2 接入:外部 MCP 工具

接入外部 MCP Server 时,我们没有走框架的ToolsConfig.mcpServers,而是自行注册「归一化名装饰后」的客户端:

// 与框架 McpServerRegistrar 内部一致:registerMcpClient(...).block()// 且在 build() 之前完成,故仍受 ToolFilter 的 allow 名单管辖toolkit.registerMcpClient(client).block(Duration.ofSeconds(20));

原因:远端工具名可能不满足 LLM 的 function name 规范(例如weather.search_local带点号),在严格校验的模型上会整轮 400。平台因此在装配期做工具名归一化:合法名原样保留,非法字符替换为下划线,撞名时追加原名哈希后缀。这样weather.search_local会以weather_search_local注册,模型即可正常调用。

另外,框架的McpClientManager不负责关闭客户端,所以注册失败时必须由装配方close(),否则连接泄漏。

七、装配单元:从「智能体 × 场景包」到「智能体 × 资源集签名」

入口智能体「小Z」不绑定业务工具,它的工具来自会话级资源集(对话底栏+选择:专家 / 场景包 / 工具 / 技能 / MCP 工具)。

由于工具集是装配期决定的(allow 名单在装配时固定),运行期没有等价的「工具可见面覆盖」通道。所以资源集被压成确定性签名,直接进装配缓存键:

小Z : agentId # R:<sig> // sig = SHA-256(排序后的 type:key 列表) 前 8 字节 专家 : agentId # __expert__ # R:<sig>

好处是完全复用既有的装配与权限机制,改选择后下一轮即重新装配生效;代价是装配实例数随「资源组合种类」增长(同组合的多个会话共享实例)。

八、踩坑记录

坑 1:状态版本 CAS 与实例复用冲突

框架的AgentState走版本化 CAS(saveIfVersion → getVersioned)。当某个实例先服务过某会话、随后该会话状态被另一实例推进时,再复用这个实例会反序列化失败(Failed to get versioned state: agent_state)。

规避方式:为每个会话记录「最近一次装配用的资源集签名」,会话内切换资源组合时主动丢弃目标实例,下次新建即可。

坑 2:MCP 工具名不合规导致整轮 400

见 §6.2。归一化是同款问题的通用解法。

坑 3:框架不释放 MCP 连接

见 §6.2。注册失败路径必须自行关闭。

坑 4:「我配了 MCP 客户端,为什么小Z 还是不知道天气?」

这是一个被真实用户报上来的问题,很典型。用户已经在「MCP 客户端」页配好了天气工具,于是认为小Z 应该会用。排查后发现:

  • 那两个报「不知道」的会话,ai_session_resource里一行资源都没有;
  • 装配日志显示mcpTools=[]、空集签名;
  • 而更早一个会话(当时通过底栏+选中了该 MCP 工具)确实成功调用过天气工具。

结论:MCP 工具是「会话级」的。「MCP 客户端」页的配置只是让工具可被选中,新会话默认是空集,必须在底栏+里勾选。这是平台既定的会话级设计,不是缺陷。

这个坑值得所有做企业 Agent 的团队注意:「配置好了」和「对本轮可用」是两件事,产品上要用 UI 把这个区别讲清楚,否则用户会认为工具坏了。

九、小结

把 Harness 装进中台,核心就三件事:

  1. 装配要可控:allow / deny 白名单 + 装配期权限规则,工具可见面在装配时定死;
  2. 状态要持久:Redis 状态存储承载多轮记忆与 HITL 暂停恢复;
  3. 边界要显式:技能工具、MCP 工具、技能仓库都要显式接入,框架不会替你猜。

再叠加前两篇讲的「三层权限」,一个纯 Java 的企业级 Agent 平台就立起来了。


相关仓库

  • 代码:https://gitee.com/zl3624/biz-buddy

作者:AI架构师张磊

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 8:38:35

灾难片特效幕后:蓝幕微缩模型与AI生成技术拆解

1. 灾难大片是怎么拍的&#xff1f;蓝幕微缩特效幕后拆解 1.1 从“炸了一栋楼”说起&#xff1a;灾难片的视觉真相 很多人看完灾难片&#xff0c;第一反应是“这得烧多少钱”。一栋摩天大楼在镜头前拦腰折断、海啸吞没整座城市、火山灰遮天蔽日——这些画面如果全部实拍&#…

作者头像 李华
网站建设 2026/10/5 8:38:12

国内开源MES框架选型与落地实践:从原理到部署全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 8:37:59

MPU6050姿态解算:避开欧拉角死锁,四元数与Mahony滤波实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 8:37:40

fMRI特征提取本质:ALFF/fALFF/ReHo的信号建模原理与实践

1. 这不是“点几下就能出图”的流程——fMRI特征提取的本质是信号建模&#xff0c;不是图像处理很多人第一次打开DPABI&#xff0c;点开“Preprocessing”菜单&#xff0c;看到ALFF、fALFF、ReHo几个按钮&#xff0c;下意识觉得&#xff1a;“哦&#xff0c;这就是脑功能分析的…

作者头像 李华
网站建设 2026/10/5 8:37:11

上下文模式怎么选?AI工具context-mode原理与省token配置指南

1. context-mode 到底在调什么&#xff1a;先搞清楚这个模式控制的是哪块记忆先说个我自己的经历。早先用 AI 辅助写代码、写文档的时候&#xff0c;经常遇到一种诡异的情况&#xff1a;明明上一个问题它还答得好好的&#xff0c;我补了一句"顺便把刚才那个函数也改了&quo…

作者头像 李华
网站建设 2026/10/5 8:36:52

PSO-LSTM神经网络调整收盘价预测:超参数优化与时序建模实战

简介&#xff1a;基于PSO-LSTM神经网络的股票调整收盘价预测源码包&#xff0c;面向需要完成期末大作业或课程设计的高校学生&#xff0c;也适合刚接触深度学习时序预测的开发者。项目使用粒子群算法自动搜索LSTM最优超参数&#xff0c;包含数据预处理、模型搭建、训练与评估等…

作者头像 李华