ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RuoYi-Vue-Plus 集成 AgentScope Harness:在若依后台跑通 AI 智能体编排

RuoYi-Vue-Plus 集成 AgentScope Harness:在若依后台跑通 AI 智能体编排

1. 为什么我要把 AgentScope Harness 塞进一个若依系后台

先交代背景:我在做某公司内部运维工单系统时,用的是 RuoYi-Vue-Plus 这套增强版后台框架。它原生自带用户、角色、菜单、字典、操作日志、代码生成这些能力,出业务 CRUD 页面非常快。但问题是,现在很多需求已经不止“增删改查”了,比如用户直接说一句“帮我查一下这台服务器近三天的告警,并按严重程度整理成报告”,系统就得自己去查告警表、调监控接口、汇总数据、生成文本。这种需求用传统硬编码去做当然也能做,但每来一个新场景就要写一套新链路,非常累。

我当时的想法是:能不能直接在 RuoYi-Vue-Plus 这套 Spring Boot 3 工程里,装一个 AgentScope Harness,让整个项目具备“多智能体编排执行”的能力,同时仍然保持纯 Java 开发,不额外引入 Python 微服务?折腾了一段时间后,效果还不错。这篇就记录一下整个集成决策、实现过程、踩过的坑,以及最终跑起来后的真实表现。如果你正在用若依系框架,想低成本接入 AI Agent 能力,这篇应该能帮你少走不少弯路。

先说清楚 AgentScope Harness 扮演的角色。你可以把它理解成一个“会调用工具的工作流引擎”:上层给它一个任务描述,它负责拆解目标、编排步骤、轮询调用模型接口、触发注册在 Spring 容器里的业务工具方法,再汇总结果。它不关心你到底用的是哪个大模型厂商,也不关心你的业务数据存在哪张表,只负责把“意图”变成“一连串可执行的动作序列”,并且把每一步执行过程和结果都记录下来。这个定位恰好和 RuoYi-Vue-Plus 互补:后台管理系统擅长管人和管数据,而 Harness 擅长把“人的需求”翻译成“数据的操作”。

如果你没有接触过这类东西,可以先回想一下工作流引擎:以前我们用 Flowable 这类 BPM 工具,是先把流程图设计好,再按图执行。Harness 的思路不一样,它没有预设的固定流程图,而是每次根据任务目标和可用工具现场生成执行计划,更像是“动态编排”。这种灵活性在大量非标准化需求场景里特别好用,但同时也带来一个问题:你不能完全信任它的自主决策,必须在关键环节加上人工审核位和熔断机制,这个后面会细说。

2. 落地前的架构选择与模块拆分

2.1 三条集成路线,我为什么选“进程内嵌”

当时摆在我面前的主要有三条路线,我简单做了个对比:

集成方式优点缺点适合场景
Harness 独立部署,后台通过 HTTP 调用隔离性强,Harness 挂掉不影响主应用;可单独扩容需要多维护一个服务;跨服务调用有网络开销;业务工具暴露成 HTTP 接口后权限控制变麻烦团队已经有多服务部署习惯,且需要大规模并发
Harness 作为本地轻进程,主应用通过本地 HTTP 管理请求Java 工程不引入重依赖;调用接口与远端一致还是有个额外进程要维护;进程间通信和调试略麻烦;本质没省多少事不太推荐,两头不讨好
Harness 以 Java 依赖形式嵌入 RuoYi-Vue-Plus 进程部署最简单,一个 jar 全搞定;工具方法直接注入 Spring 容器,天然复用事务、AOP、Sa-Token 权限;调用链短、延迟低类库依赖可能出现冲突;需要关注线程池与主应用资源的隔离;Harness 出问题可能影响主进程中小团队、项目初期、想快速试错落地

我最终选了第三条,进程内嵌。核心原因是:工具调用太方便了。RuoYi-Vue-Plus 里已经写好的 Service、Mapper,只要通过注解暴露成 Agent 可调用工具,Harness 在编排时就能直接调 Spring 容器里的 Bean,连 HTTP 封装都省了。而且后台系统的数据权限已经基于 Sa-Token 登录用户做好了,Agent 调工具时只要带上当前用户上下文,就能继承这套权限,非常自然。

当然,进程内嵌也不是没有代价。最明显的问题是依赖管理:Harness 传递过来的第三方库很容易和项目里原有的依赖打架,这个我在第 5 节会专门讲。另外,如果 Agent 任务里有非常吃 CPU 的模型推理逻辑,它确实会占用主应用线程。我们的解决方式是给 Harness 单独配一个线程池,并把模型调用超时卡死,避免个别慢请求拖垮整个应用。

2.2 新建 agent-harness 模块,别在原工程里到处乱插

若依增强版的工程结构通常是多模块 Maven 项目,我建议不要直接在 admin 或 system 模块里写 Agent 编排代码,而是单独建一个ruoyi-agent-harness模块,让业务模块依赖它。这样做的好处很直接:以后 Harness 升级版本,或者你想把它摘出去变成独立服务,改动范围都被限制在一个模块里,不会搞得整个工程都是散落的 AI 代码。

我的模块结构大致如下:

ruoyi-agent-harness/ ├── pom.xml ├── src/main/java/.../agent/ │ ├── core/ // 与 Spring 无关的纯编排核心 │ ├── spring/ // Spring Boot Starter 装配 │ ├── tool/ // @AgentTool 注解与扫描器 │ ├── execution/ // 任务执行器、步骤状态机 │ ├── context/ // 会话上下文、用户上下文 │ └── web/ // 给管理端用的 Controller └── src/main/resources/ └── META-INF/spring/... // 自动装配配置

这里有个容易被忽略的点:Harness 的核心编排逻辑最好做成跟 Spring 无关的纯 Java 类,Spring 相关的东西只是“适配层”。这样万一以后要把它独立出去,或者想单测,都不用启动整个 Spring 容器。

2.3 数据表设计:四张表把编排过程全部落库

Agent 执行和普通 CRUD 不一样,它是个“过程型”任务:用户提一个目标,Harness 要拆成多个步骤,每个步骤可能要调不同的工具,中间还可能失败重试。所以数据库里得把整个执行过程记录下来,否则出问题都没法定位。

我设计了四张核心表,都放在agent前缀下:

表名作用关键字段
agent_bot智能体登记表,一个智能体对应一套系统提示词和可用工具集bot_key、name、system_prompt、tool_keys、status
agent_tool工具登记表,记录有哪些业务方法被暴露给 Agenttool_key、bean_name、method_name、description、param_schema
agent_job任务表,一次用户请求就是一个 jobjob_no、bot_key、user_id、task_desc、status、created_at
agent_step步骤执行记录表,每个编排步骤一条记录job_no、step_no、tool_key、input_json、output_json、status、retry_count

有了这几张表,管理端页面直接查数据就能看到某个任务执行到哪一步了,哪一步调了哪个工具、传了什么参数、返回了什么结果,全部可追溯。这也是我敢让 Agent 动业务数据的前提——每一步都留痕,出了问题能复盘。

3. 核心集成实操:从依赖到第一个 Agent 任务

3.1 Maven 依赖与版本锁定

既然选择进程内嵌,第一步就是把 Harness 的包引进来。我当时在处理依赖时踩过一次坑:Harness 的某个传递依赖把项目里的guava从 33 降到了 31,结果一个定时任务模块当场编译报错。后面我统一在父 POM 里用 dependencyManagement 锁版本,再对 Harness 的传递依赖做排除。

<dependency> <groupId>io.agentscope</groupId> <artifactId>agentscope-harness-spring-boot-starter</artifactId> <version>2.4.1</version> <exclusions> <exclusion> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </exclusion> <exclusion> <groupId>io.netty</groupId> <artifactId>netty-common</artifactId> </exclusion> </exclusions> </dependency>

然后在父 POM 的 dependencyManagement 里统一定义 guava 版本:

<dependencyManagement> <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>33.0.0-jre</version> </dependency> </dependencies> </dependencyManagement>

这个做法的原则是:子模块不要自己乱写版本号,所有第三方版本尽量收口到父 POM 统一管理。特别是有 Spring Cloud Alibaba 或其它全家桶的项目,Harness 的自动配置类里可能引用了不同版本的 Jackson 或 Netty,必须在集成早期就做一次依赖树分析。

3.2 application.yml 配置

Harness 集成进来后,需要配置模型端点、线程池、超时时间这些核心参数。我贴一份当时线上用的核心配置片段:

agentscope: harness: enabled: true # 模型服务配置,兼容 OpenAI 风格接口 model: endpoint: http://127.0.0.1:8899/v1 api-key: sk-placeholder model-name: deepseek-chat temperature: 0.3 max-tokens: 2048 timeout: 30s # 执行线程池 executor: core-pool-size: 4 max-pool-size: 8 queue-capacity: 200 keep-alive: 60s # 任务全局超时,避免一个任务跑太久 task-timeout: 10m # 最大编排步数,防止模型无限拆步骤 max-steps: 15 # 工具调用失败重试次数 tool-retry: 2

temperature我给的 0.3,不是 0。原因很实际:Agent 编排任务如果完全确定性,遇到工具返回结果的写法稍有变化就容易死板;但温度太高又容易编出超出工具范围的东西。0.3 是平衡点。max-steps必须设置,宁可任务做不完也不让它无限循环,否则模型会为了同一个目标反复尝试同一个工具,非常烧钱还占线程。

3.3 用 @AgentTool 把普通 Service 变成 Agent 能力

这是整个集成里最优雅的一环。RuoYi-Vue-Plus 里封装好的业务方法,比如“查工单列表”“更新工单状态”“发送站内通知”,我只要写一个类,把这些方法暴露出来:

@Component public class OrderAgentTools { private final IOrderService orderService; private final NoticeService noticeService; public OrderAgentTools(IOrderService orderService, NoticeService noticeService) { this.orderService = orderService; this.noticeService = noticeService; } @AgentTool( key = "query_order", name = "查询工单", description = "根据关键词或状态查询工单列表,返回工单编号、标题、当前状态", paramSchema = "{\"type\":\"object\",\"properties\":{\"keyword\":{\"type\":\"string\"},\"status\":{\"type\":\"string\"}},\"required\":[\"keyword\"]}" ) public List<OrderVO> queryOrder(String keyword, String status) { // 直接复用已有的 service 查询逻辑 return orderService.search(keyword, status); } @AgentTool( key = "notify_user", name = "发送站内通知", description = "给指定用户发送站内消息通知,需要提供用户名和通知内容", paramSchema = "{\"type\":\"object\",\"properties\":{\"username\":{\"type\":\"string\"},\"content\":{\"type\":\"string\"}},\"required\":[\"username\",\"content\"]}" ) public void notifyUser(String username, String content) { noticeService.send(username, content); } }

用注解而不是用配置文件注册工具,最大的好处是类型安全和编译期检查。工具方法就是普通的 Java 方法,参数签名写死,idea 里能直接跳转,改起来也放心。配置文件的方式看起来灵活,实际上 paramSchema 写错了都不知道,等运行时模型传参才发现,那感觉太糟了。

启动时扫描器会把这些带@AgentTool的方法注册进 Harness 的工具表,同时在agent_tool表里维护一份元数据。我建议工具描述写详细一点,因为模型是靠 description 来判断“什么时候调用这个工具”的。描述含糊,模型就不会用,编排效果会大打折扣。

3.4 触发编排与执行生命周期

用户从前端点一下“开始处理”,后端 Controller 收到请求后,调用agentHarnessService.touchForHandle(...)发起任务。整个执行过程我描述成一段文字流程,比代码更容易看懂:

  1. 前端传入botKey和taskDesc,例如 botKey 为ops_assistant,taskDesc 为“帮我查一下近三天服务器告警并按严重程度排序”。
  2. 后端取出智能体配置和可用工具列表,生成job记录,状态为RUNNING。
  3. Harness 把 taskDesc 和工具描述一起交给模型,模型返回一个执行计划,包含若干步骤,每个步骤指定调用哪个工具、传什么参数。
  4. 执行器按顺序执行步骤:先从 Spring 容器取对应 Bean,反射调用标注的方法,然后把输出 JSON 塞回上下文。
  5. 如果一个步骤执行完后,模型认为需要根据结果继续下一步,就再走一次“模型 -> 工具调用”循环,直到达到max-steps或者模型给出最终总结。
  6. 最终结果被写入agent_job,状态变为SUCCESS或FAILED。

这里有个小细节:步骤执行不一定是严格串行的。有些步骤之间没有依赖关系,Harness 可以并行去调多个工具。但我在项目初期没有开并行,原因很简单——工具方法里可能操作同一张表,并行带来并发问题之后不好排查。先把串行跑通,再逐步优化性能。事实证明这个决策很明智,后面排查问题的时候全靠串行日志才捋清了调用链。

触发任务的调用代码大致这样:

public AgentJobVO submitTask(String botKey, String taskDesc, Long userId) { BotConfig bot = botService.getByKey(botKey); if (bot == null || !bot.isEnabled()) { throw new ServiceException("智能体不存在或已停用"); } AgentJob job = agentJobService.create(bot, userId, taskDesc); agentHarnessService.asyncExecute(job.getJobNo()); return AgentJobVO.from(job); }

异步执行用的是 Harness 自带的线程池,没有直接在主请求线程里跑,否则前端请求会长时间阻塞。返回 jobNo 给前端,前端用它订阅 WebSocket 查看执行进度。

3.5 异步线程里保留用户上下文

RuoYi-Vue-Plus 基于 Sa-Token 做登录认证,用户信息存在 ThreadLocal 里。问题来了:Harness 的执行线程是异步线程池,进去之后StpUtil.getLoginId()直接报“未登录”。数据权限、操作日志、用户归属全部拿不到,工具查询也没法按用户隔离。

我当时比较粗暴的办法是在提交任务时把 userId 存进AgentJob,工具方法需要用户信息时手动查一遍。但这样太啰嗦了,每个工具都要写获取用户参数的逻辑。后来改用线程装饰器,把主线程里的 LoginContext 复制到异步线程:

public class UserContextTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { String loginId = StpUtil.getLoginIdAsString(); String tokenValue = StpUtil.getTokenValue(); return () -> { try { // 创建临时登录会话 StpUtil.setTokenValue(tokenValue); StpUtil.setLoginId(loginId); runnable.run(); } finally { StpUtil.logout(); } }; } }

然后在配置 Harness 线程池时传入这个 TaskDecorator。这一步解决得很关键,不然所有带用户权限过滤的工具方法在 Agent 执行时都会静默失效,而且测试还不容易发现,因为只查几条数据时结果看着挺正常。

4. 控制台接入与操作收敛

4.1 菜单、权限与数据字典

Agent 能力集成进来后,必然要给管理员一个管理界面。RuoYi-Vue-Plus 的菜单权限都是入库的,所以我在 SQL 脚本里初始化了“AI 控制台”菜单,下面分三个页面:智能体管理、任务执行记录、工具注册列表。页面对应的权限标识分别是agent:bot:list、agent:job:list、agent:tool:list。

这里要特别处理的一个业务点是“谁能看谁的任务”。如果是协同办公场景,用户 A 发起的 Agent 任务,不能让用户 B 也看到全部上下文,否则就是越权。我实现的方式是在查询列表时自动加数据权限过滤,普通用户只能查user_id = 当前登录用户的任务,管理员可以看全部。这一步其实不是技术问题,而是产品设计问题:别等上线了才发现 Agent 任务记录里的业务数据泄露给了不该看的人。

工具注册列表我一开始是只读的,因为工具是通过 Java 注解注册的,运行期动态加工具需要额外的 agent 执行机制,复杂度高。先做只读,等稳定了再考虑可视化注册。管理员能看懂“哪些服务已经暴露给模型”,这个信息比“能改工具的参数结构”更重要。

4.2 WebSocket 流式推送执行进度

任务异步执行后,用户不可能一直刷新列表。我用 WebSocket 把执行步骤推送到前端。后端核心代码其实不复杂:

@Component public class AgentStepWebSocket { private final SimpMessagingTemplate messagingTemplate; public void pushStep(AgentStep step) { AgentStepVO vo = AgentStepVO.from(step); // 按 jobNo 发到对应订阅频道 messagingTemplate.convertAndSend("/topic/agent/" + step.getJobNo(), vo); } }

这里我踩过一个坑:RuoYi-Vue-Plus 本身没有启用 Spring Messaging,直接引入spring-boot-starter-websocket后和 Sa-Token 的鉴权过滤器顺序有冲突。耗时一晚上才定位到是握手阶段的拦截器没有放行 WebSocket 请求,导致前端一直在 pending。解决方式是在 Sa-Token 配置里把 WebSocket 端点路径加入排除列表,握手阶段不鉴权,连接建立后再在回调里校验 token。

前端部分我用的是 Vue 3 的原生 WebSocket 封装,没有引额外的库,因为就一个订阅场景,引库反而加重。前端收到步骤事件后,直接在页面上渲染一个“步骤时间线”,每步显示工具名、输入参数摘要、执行状态。用户能实时看到 Agent 在干什么,就减少了“卡死还是正在思考”的困惑。

4.3 限流、熔断与人工审批位

Agent 调用模型接口是要花钱的,而且一次任务可能循环调用多次模型,所以必须有限流。我做了两层:一是 Nginx 层对/agent/**接口按 IP 做请求频率限制,二是应用层用 Redisson 的分布式限流器,对每个用户的并发任务数做限制,默认一个用户同时最多 2 个任务,超过直接报“任务太密集,请稍后再试”。

比限流更重要的是人工审批位。虽然 Harness 能编排工具调用,但我并不信任它直接执行所有高危险动作。举个例子:工单的状态改成“已完成”这种事,不能让模型直接调工具改。我的做法是在 AgentJob 状态机里加一个WAIT_CONFIRM状态:当工具注册表里某个工具被标记为need_confirm=true时,执行器不会真的调用,而是把参数存到待审批表里,用户在前端点“确认执行”后才会真正触发调用。这其实就是人在回路,本质上是在自主性和可控性之间取平衡。

5. 实战踩坑与故障修复实录

5.1 依赖冲突:Netty、Guava、Jackson 三连击

这是集成过程中最折磨人的问题。Harness 底层用的 Netty 版本和 RuoYi-Vue-Plus 引入的 WebSocket 相关依赖版本不一致,启动时出现过NoSuchMethodError。这类报错的排查思路很简单:先看异常类是从哪个 jar 加载的,然后在 IDEA 的 Maven 面板里跑dependency:tree,直接查冲突节点。

我的做法是写一份固定版本的依赖清单,把容易打架的 jar 全部显式声明:

依赖锁定版本说明
com.google.guava:guava33.0.0-jre若不锁,可能被传递依赖降级
io.netty:netty-common4.1.100.FinalWebSocket 与 Harness 都需要
com.fasterxml.jackson.core:jackson-databind2.15.3防止序列化行为不一致
com.alibaba:fastjson2.0.42RuoYi 家喜欢用 fastjson,务必显式声明

顺便说一句,RuoYi-Vue-Plus 老版本里大量使用 fastjson,Harness 内部用的是 Jackson,两边在序列化工具类上经常互相干扰。我的建议是所有对外 JSON 返回统一用 Jackson,内部临时对象序列化可以看情况,但不要混用两个框架去处理同一个对象图,否则会出现“A 接口正常,B 接口字段丢了”的诡异问题。

5.2 多线程环境下 @Transactional 失效

这个坑是典型的 Spring 多线程事务问题。我一开始把整条 Agent 执行链路都包在一个大事务方法里,想着“要么全成功,要么全回滚”。实际上 Harness 是异步线程池执行,主线程把任务丢给线程池后,事务就断了。而且工具方法里操作数据库时,如果工具方法本身没有加事务注解,那每条 SQL 是自动提交的,任务中途失败根本没法回滚前面的操作。

后来我调整了事务策略:

  • 主任务创建和状态更新:单独事务,快速提交。
  • 每个工具调用:在工具方法上加@Transactional(rollbackFor = Exception.class),保证单个工具内部的数据库操作原子性。
  • 步骤记录表:用REQUIRES_NEW单独事务写入,不管业务操作成不成功,执行轨迹都必须留下来。

这个策略有个代价:整个任务不是原子性的。但仔细想一下就明白,Agent 参与的任务几乎不可能全链路原子化——它已经调用了外部模型接口,模型调用没法回滚,工具调用的副作用也没法完全撤销。所以不要追求“任务级事务”,应该追求“可追踪、可人工修正”,这才是 Agent 集成和普通接口事务最大的思维差异。

5.3 模型工具参数解析失败:JSON 序列化的兼容性

模型返回的工具调用参数是 JSON 字符串,Harness 要把这个 JSON 反序列化成 Java 方法的实际参数。本来这是框架内部做的事,但我自己写的paramSchema和实际的方法参数不完全对应,比如方法参数是Integer,schema 里写成string,模型传了个"2024"进来,反序列化直接就报类型不匹配。

这个问题排查起来很费劲,因为模型输出是不稳定的,不会每次都在同一个地方失败。我的解决思路是两条腿走路:

第一,在 schema 里严格定义类型,而且尽量简单,能用字符串就不搞对象嵌套,能平铺就不搞复杂结构,降低模型的理解成本。第二,加一个“工具调用参数校验”的兜底方法,在反射调用前把参数 JSON 解析成JsonNode,再根据方法签名做一次宽松转换,比如数字字符串转 Integer,空值给默认值。这种方法治标,但能极大提升成功率。

经验是:给模型用的工具接口一定要设计得“蠢一点”,参数越少越好,每个参数含义越直白越好。如果你把业务对象二十个字段全部对外暴露,模型绝对会选错或漏传,那不是模型问题,是你接口设计问题。

5.4 循环依赖与启动失败

Harness 的编排服务如果放在一个AgentOrchestrator里,它要依赖业务 Service;但业务 Service 里可能也有一个“手动调用 Agent 生成报告”的功能,反过来依赖AgentOrchestrator。在 Spring 启动阶段直接注入就会出现Currently in creation循环依赖异常。

解决方式我用了三层:

@Service public class OrderServiceImpl implements IOrderService { // 用 ObjectProvider 延迟获取,避免启动期循环 private final ObjectProvider<AgentOrchestrator> orchestratorProvider; public OrderServiceImpl(ObjectProvider<AgentOrchestrator> orchestratorProvider) { this.orchestratorProvider = orchestratorProvider; } public String generateReport(Long orderId) { AgentOrchestrator orchestrator = orchestratorProvider.getIfAvailable(); //... } }

这里不建议直接无脑加@Lazy解决所有循环引用。@Lazy只是在代理上绕了一下,如果两个 Bean 之间运行时确实会双向调用,还是会出现代理失效的问题。ObjectProvider的方式更明确:它表达的是“我不是一定需要这个依赖,只是需要的时候再取”,语义更清晰。

5.5 模型接口超时导致的线程耗尽

有段时间线上的任务频繁失败,看监控发现 Harness 的核心线程池线程数量一直处于满占用状态。日志里全是某模型接口的超时异常。排查后发现是配置的模型服务本身问题,但更根本的原因是整个网关线程池都被慢调用拖住了。

修复措施有两个:

  • 场景一:把单个模型调用的超时缩短到 10 秒,全局任务超时 5 分钟。宁可任务失败也不要让线程无限挂起等着模型响应。
  • 场景二:给 Agent 任务用独立线程池,和业务主线程池分离,Maven 侧配置 Harness 时不要直接用默认的全局线程池。

你可能会问:任务失败了怎么办?我的答案是允许 Agent 任务失败,然后由系统发送失败通知给发起人,发起人重新组织描述后再发起一次。别强求高成功率,更重要的是失败能被快速发现、快速重试。

6. 跑起来之后的一些真实体会

集成跑通后,我最大的感受是“工具注册的边界,决定了 Agent 能力的边界”。前期我只暴露了三四个工单查询类工具,Agent 的表现只能说中规中矩,能完成单步查询;后面我陆续把工单流转、通知发送、告警查询、报表生成这些工具都注册进去,Harness 能自己组合出“查告警 -> 归并同类项 -> 生成工单 -> 通知负责人”这样四步链路,才真正感觉到这套东西的威力。

但边界感也很重要。我现在把工具分成三类:只读查询类工具可以全自动执行;状态变更类工具需要人工确认;外部写入类工具,比如发短信、调用第三方接口,默认禁用,除非管理员手工开启。这个分类不是技术上的,而是业务风险上的。Agent 可以帮你想、帮你查、帮你建议,但“动手改系统状态”这件事必须保留人的决定权。

如果你也想在若依系工程里做类似集成,我的建议是三步走:第一步先只接一个查询类工具,把 Harness 的编排链路完整跑通;第二步再加状态变更类工具,配合人工确认机制测试;第三步再考虑多个 Agent 协同或者把 Harness 拆成独立服务去应对更大并发。全部跑通之后你会发现,RuoYi-Vue-Plus 不再只是一个后台管理系统脚手架,而是一个能听懂业务语言的业务操作平台。这个转变的价值,比单纯多几个 AI 界面大得多。

最后再分享一个小技巧:集成后的日志配置一定要单独给agent包开一个 logger 级别,控制台里只输出任务摘要,详细的工具调用入参出参写进独立日志文件。排查问题时,grep jobNo就能看到一次 Agent 任务从开始到结束的完整生命周期,比翻全量日志高效太多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进