news 2026/10/31 17:40:01

RuoYi-Vue-Plus 集成 AI Agent 运行时:Harness 实践与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RuoYi-Vue-Plus 集成 AI Agent 运行时:Harness 实践与避坑指南

1. 为什么要在 RuoYi-Vue-Plus 里塞进一个 AI Agent 运行时

RuoYi-Vue-Plus 这套脚手架在国内 Java 后端圈子里出镜率极高,权限模型、数据权限、多租户、代码生成器这些基础设施都打磨得比较成熟,很多团队拿它当企业级中后台的起点。但这两年有个明显的变化:业务方开始不满足于"点按钮查列表"这种交互,他们想要的是"我说一句话,系统自己把事办了"。比如在工单系统里输入"把上周所有超时未处理的工单按负责人汇总一下,顺便给每个负责人发条站内提醒",传统做法是写一个接口加一个前端页面,而 Agent 化的做法是让模型理解意图、调用工具、编排流程。

问题就出在这里。绝大多数 Java 团队不想为了一个 AI 能力把整套技术栈换成 Python,也不想引入一堆 Node 服务做中间层。大家想要的是:在现有的 Spring Boot 单体或微服务里,直接跑一个 Agent 运行时,复用已有的 Service、Mapper、权限上下文,让 Agent 调用的"工具"就是项目里现成的业务方法。AgentScope 的 Harness 概念正好切中这个需求——它把 Agent 的执行循环、工具注册、消息管理、状态持久化抽象成一套可嵌入的运行时,而不是一个必须独立部署的服务。

这篇内容面向的是已经用过 RuoYi-Vue-Plus、对 Spring Boot 自动装配和线程模型有基本认知的 Java 开发者。我会把整个集成过程拆开讲:Harness 到底解决了什么问题、它的执行模型和 Spring 的 Bean 生命周期怎么对齐、工具方法怎么安全地暴露给模型、多租户和权限上下文在 Agent 调用链里怎么传递、以及实测中那些文档里不会写的坑。整套方案是纯 Java 的,不依赖任何外部运行时,最终形态是 RuoYi-Vue-Plus 的一个业务模块,跟着主应用一起启动。

需要先说明一点:AgentScope 本身有 Python 和 Java 两条线,这里讨论的是 Java 侧的 Harness 能力。它的核心价值不在于"又一个 LLM 调用封装",而在于把 Agent 的推理-行动循环做成了可插拔的组件,你可以只用它最薄的那一层,也可以把记忆、规划、多 Agent 协作都挂上去。集成到 RuoYi-Vue-Plus 这种重业务系统里,关键是克制——不要一上来就搞多 Agent 编排,先把单 Agent 加工具调用跑通,再逐步加能力。

2. Harness 的执行模型与 Spring 容器的对齐方式

2.1 Harness 到底封装了哪几件事

很多人第一次接触 Harness 会把它和"LLM 客户端"混为一谈,其实两者差得远。一个裸的 LLM 客户端只负责发请求、收响应,而 Harness 要管的是一次完整任务的生命周期。具体来说,它至少封装了四层东西:

第一层是消息与上下文管理。Agent 和模型的交互不是单轮问答,而是多轮的消息序列,包含 system prompt、历史对话、工具调用请求、工具返回结果。Harness 负责维护这个序列的格式正确性,尤其是不同模型对 tool call 消息格式的要求不一样,有的要求 assistant 消息里带 tool_calls 字段,有的要求独立的 tool 角色消息,这些差异由 Harness 抹平。

第二层是工具注册与调度。你告诉 Harness "有哪些工具可用",它负责把工具描述转成模型能理解的 schema,模型返回工具调用意图后,它负责找到对应的方法、反序列化参数、执行、把结果塞回消息序列。这一层是集成的重点,因为 Java 的方法签名和模型需要的 JSON Schema 之间需要映射。

第三层是执行循环控制。模型可能连续调用多个工具,也可能在拿到工具结果后继续推理,Harness 要控制这个循环的终止条件——是模型主动说"我完成了",还是达到最大轮次,还是某个工具返回了终止信号。没有这层控制,代码就会写成一大坨 while 循环加状态判断。

第四层是状态与可观测性。每一步的输入输出、耗时、token 消耗、工具调用链路,这些都需要记录,否则线上出问题根本没法排查。Harness 通常提供回调或事件机制,让你把这些信息接到自己的日志或监控体系里。

理解了这四层,就能明白为什么不能简单地"在 Service 里调一下模型 API"——那样写出来的代码在单轮场景能跑,一旦涉及多工具协作就会失控。

2.2 把 Harness 做成 Spring Bean 的两种思路

集成到 RuoYi-Vue-Plus 里,第一个决策是 Harness 实例的生命周期怎么管。这里有两种思路,各有适用场景。

思路一:单例 Harness + 无状态工具。整个应用共享一个 Harness 实例,工具方法都是无状态的,所有上下文通过参数传入。这种做法的好处是资源占用低、启动快,适合工具逻辑纯粹、不依赖请求上下文的场景。但 RuoYi-Vue-Plus 的业务方法大量依赖SecurityUtils.getLoginUser()、DataScope这类 ThreadLocal 上下文,单例模式下如果工具方法直接调这些,会拿到错误的用户信息。

思路二:请求级 Harness + 上下文透传。每次 Agent 调用创建一个 Harness 实例,把当前请求的用户、租户、权限信息作为初始上下文注入。工具方法执行时,Harness 负责把这些上下文恢复到当前线程。这种做法更贴合 RuoYi-Vue-Plus 的模型,代价是每次调用有轻微的对象创建开销,但相比一次 LLM 请求的网络延迟,这点开销可以忽略。

我最终选的是思路二,但做了一点折中:Harness 的核心组件(模型客户端、工具注册表)是单例的,只有执行上下文(ExecutionContext)是请求级的。这样既避免了重复初始化,又保证了上下文隔离。具体做法是定义一个AgentExecutionContext持有LoginUser、tenantId、deptId等字段,在工具方法执行前通过一个包装器把这些值塞进对应的 ThreadLocal,执行完再清理。

public class AgentExecutionContext { private final LoginUser loginUser; private final String tenantId; // 构造时从当前请求捕获 public static AgentExecutionContext capture() { return new AgentExecutionContext( SecurityUtils.getLoginUser(), TenantHelper.getTenantId() ); } // 在工具执行线程中恢复上下文 public void restore() { SecurityContextHolder.setContext(...); TenantHelper.setDynamic(tenantId); } public void clear() { SecurityContextHolder.clearContext(); TenantHelper.clear(); } }

这段代码的关键在于restore和clear必须成对出现,且要放在 try-finally 里。我见过有团队忘了 clear,结果线程池复用线程时上下文串了,A 用户的 Agent 调用读到了 B 用户的数据,这种问题在测试环境很难复现,上线后就是数据泄露事故。

2.3 线程模型:Agent 循环跑在哪个线程上

这是集成中最容易被低估的问题。Harness 的执行循环本质上是"调模型 → 等响应 → 执行工具 → 再调模型"的串行过程,如果直接跑在 Web 请求线程(Tomcat 的工作线程)上,一次复杂任务可能占用线程几十秒甚至几分钟,并发一上来线程池就爆了。

我的做法是把 Agent 执行放到独立的线程池,Web 层只负责提交任务和返回一个任务 ID,前端通过轮询或 SSE 获取进度。线程池的配置有几个讲究:

  • 核心线程数不宜过大。Agent 任务是 IO 密集型(等模型响应),但每个任务占用的内存不小(消息序列、上下文),核心线程数按CPU 核数 * 2起步比较稳妥,最大线程数根据模型服务的并发限制来定。
  • 队列要有界。用LinkedBlockingQueue无界队列会导致任务堆积到 OOM,我一般用ArrayBlockingQueue配合合理的拒绝策略,拒绝时给用户明确的"系统繁忙"提示。
  • 线程命名要可识别。给线程工厂加上agent-worker-前缀,排查问题时一眼就能从线程 dump 里区分出来。
@Bean("agentExecutor") public ThreadPoolTaskExecutor agentExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(4); executor.setMaxPoolSize(16); executor.setQueueCapacity(100); executor.setThreadNamePrefix("agent-worker-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; }

注意:CallerRunsPolicy在 Agent 场景下要慎用。它会让提交任务的线程(通常是 Tomcat 线程)自己去执行 Agent 循环,等于把压力转移到了 Web 线程池。如果模型响应慢,Web 线程会被长时间占用。更稳妥的是自定义拒绝策略,直接返回失败让前端重试。

3. 把 RuoYi 的业务方法安全地暴露成 Agent 工具

3.1 工具注册的三种粒度和选型

工具是 Agent 和业务系统之间的桥梁,粒度选错了,要么模型不会用,要么权限失控。我实践下来把工具分成三种粒度:

粗粒度工具:一个工具对应一个完整的业务动作,比如createWorkOrder、queryOrderList。优点是模型容易理解,参数少;缺点是灵活性差,稍微换个查询条件就得加新工具。

细粒度工具:把业务能力拆成原子操作,比如queryOrderByStatus、queryOrderByOwner、countOrderByStatus。优点是组合灵活;缺点是工具数量膨胀,模型选择困难,而且每个工具都要单独做权限校验。

中间粒度工具:按业务领域聚合,一个工具接受一个结构化的查询对象,内部根据字段组合走不同的查询逻辑。比如queryOrders(OrderQuery query),query 里可以带状态、负责人、时间范围。这种粒度在 RuoYi-Vue-Plus 里最合适,因为它的 Mapper 本来就支持条件构造器,天然适合这种模式。

我最终采用的是中间粒度为主、粗粒度为辅的策略。写操作一律用粗粒度(避免模型拼出危险的组合操作),读操作可以用中间粒度(灵活且风险可控)。

3.2 用注解驱动工具注册,避免手写 Schema

手写工具的 JSON Schema 是件痛苦的事,参数一多就容易和实际方法签名对不上。我的做法是自定义一套注解,在应用启动时扫描带注解的方法,自动生成工具描述和参数 Schema。

@AgentTool( name = "queryOrders", description = "根据条件查询工单列表,支持按状态、负责人、时间范围过滤" ) public List<OrderVO> queryOrders( @AgentParam(description = "工单状态,可选值:PENDING/PROCESSING/DONE") String status, @AgentParam(description = "负责人用户ID", required = false) Long ownerId, @AgentParam(description = "开始时间,格式 yyyy-MM-dd", required = false) String startDate ) { // 复用现有 Service return orderService.queryByCondition(status, ownerId, startDate); }

启动时用一个BeanPostProcessor扫描所有 Bean 的方法,遇到@AgentTool就解析参数注解,生成 Schema 注册到 Harness 的工具表里。这里有几个细节要注意:

  • 参数类型映射。Java 的Long、Integer、Boolean、String、List<String>这些基本类型要能正确映射到 JSON Schema 的integer、boolean、string、array。枚举类型建议直接用 String 接收,在方法内部转换,避免模型传错枚举值导致反序列化失败。
  • 必填与可选。required = false的参数在 Schema 里不能出现在 required 数组里,同时方法内部要有默认值处理逻辑。
  • 描述要写给模型看。description 不是写给同事看的注释,要写得具体、包含可选值枚举、包含格式示例。模型对描述的质量非常敏感,描述模糊的工具它基本不会正确调用。

3.3 权限校验不能依赖模型自觉

这是安全上的红线。模型可能会被诱导调用它本不该调用的工具,也可能因为理解偏差调用了越权的方法。所有工具方法内部必须做权限校验,不能假设"模型只会调它有权限的工具"。

我的做法是在工具执行的前置拦截器里做两层校验:

第一层是工具级权限。每个@AgentTool可以声明需要的权限标识,比如@AgentTool(permission = "order:list"),执行前用 RuoYi 的PermissionService校验当前用户是否有这个权限。没有就直接返回"无权限执行该操作",而不是抛异常中断整个 Agent 循环——让模型知道这个工具不可用,它可能会换一个方式完成任务。

第二层是数据级权限。RuoYi-Vue-Plus 的@DataScope注解会在 SQL 层面拼接数据权限条件,工具方法只要走正常的 Service 调用链,这层就自动生效。但要注意,如果工具方法里用了自定义 SQL 或者直接操作 Mapper 绕过了 Service,数据权限就会失效。我的原则是工具方法只调 Service,不直接调 Mapper。

@Component public class ToolPermissionInterceptor implements ToolInterceptor { @Override public ToolResult beforeExecute(ToolInvocation invocation, AgentExecutionContext ctx) { AgentTool annotation = invocation.getAnnotation(); if (StringUtils.isNotBlank(annotation.permission())) { if (!permissionService.hasPermi(annotation.permission())) { return ToolResult.rejected("当前用户无权限执行 " + annotation.name()); } } return ToolResult.proceed(); } }

提示:返回"拒绝"而不是抛异常,是为了让 Agent 循环能继续。模型收到拒绝信息后,可能会尝试其他工具或告知用户无法完成,这比整个任务崩掉体验好得多。

4. 多租户与上下文在 Agent 调用链里的传递

4.1 租户隔离为什么在 Agent 场景下更棘手

RuoYi-Vue-Plus 的多租户是通过TenantHelper配合 MyBatis 拦截器实现的,正常情况下一个请求进来,租户 ID 从登录信息或请求头解析出来,放进 ThreadLocal,SQL 执行时拦截器自动加上租户条件。这套机制在同步的 Web 请求里工作得很好,但 Agent 场景引入了两个新变量:

一是执行线程的切换。Agent 循环跑在独立线程池里,ThreadLocal 不会自动传递。如果不做处理,工具方法执行时TenantHelper.getTenantId()返回 null,SQL 拦截器要么报错要么查出所有租户的数据。

二是异步工具调用。有些工具本身是异步的(比如发起一个耗时的报表生成),执行线程又切了一次,上下文传递链更长。

4.2 上下文透传的完整实现

我的方案是在AgentExecutionContext里捕获租户信息,在工具执行的包装器里恢复。但仅仅恢复 ThreadLocal 还不够,因为 Harness 内部可能用线程池执行工具,需要把上下文作为参数显式传递,而不是依赖 ThreadLocal 的隐式传递。

具体实现分三步:

第一步,捕获。在 Web 层提交 Agent 任务时,从当前请求捕获完整的上下文快照。

public AgentTask submit(String userInput) { AgentExecutionContext ctx = AgentExecutionContext.capture(); String taskId = IdUtil.fastSimpleUUID(); agentExecutor.execute(() -> { try { ctx.restore(); harness.run(userInput, ctx); } finally { ctx.clear(); } }); return new AgentTask(taskId); }

第二步,工具执行时恢复。Harness 调用工具方法前,通过拦截器把上下文恢复到当前线程。如果工具内部又开了异步线程,需要在异步任务里再次传递 ctx。

第三步,清理。无论成功失败,finally 里必须清理,防止线程池复用导致上下文泄漏。

这里有个容易忽略的点:RuoYi 的TenantHelper有动态租户和忽略租户两种模式。Agent 执行过程中,某些系统级工具(比如查询字典、查询配置)可能需要忽略租户,这时候要用TenantHelper.ignore(() -> {...})包起来。但忽略租户是危险操作,我建议只在明确的系统工具上使用,并且加注释说明原因。

4.3 用户身份在工具链中的一致性

除了租户,用户身份也要贯穿整个调用链。有个场景值得警惕:Agent 在执行过程中调用了工具 A,工具 A 内部又触发了另一个 Agent 任务(比如子任务分解),如果子任务没有继承父任务的用户身份,就会出现"子任务以系统身份执行"的问题。

我的处理原则是用户身份只减不增。子任务继承父任务的用户身份,但可以进一步收窄权限(比如只保留只读权限),绝不能放大。实现上,AgentExecutionContext支持derive()方法创建一个权限收窄的副本,子任务用副本执行。

public AgentExecutionContext derive(Set<String> allowedPermissions) { return new AgentExecutionContext( this.loginUser, this.tenantId, allowedPermissions // 收窄后的权限集 ); }

5. 实测中那些文档不会写的坑

5.1 模型返回的工具参数类型不匹配

这是最高频的问题。模型返回的 JSON 里,数字可能是字符串形式("123"而不是123),布尔值可能是"true"字符串,数组可能是单个值。如果直接用 Jackson 反序列化到强类型参数,会抛异常。

我的处理是在参数绑定层做一次宽松转换:数字类型的参数,如果收到字符串就尝试 parse;布尔类型同理;数组类型如果收到单值就包装成单元素数组。这个转换逻辑要放在工具调用的参数绑定阶段,而不是让每个工具方法自己处理。

private Object coerce(Object raw, Class<?> targetType) { if (raw == null) return null; if (targetType.isInstance(raw)) return raw; if (targetType == Long.class && raw instanceof String) { return Long.parseLong(((String) raw).trim()); } if (targetType == Boolean.class && raw instanceof String) { return Boolean.parseBoolean(((String) raw).trim()); } // 其他类型转换... return raw; }

注意:转换失败时不要抛异常,而是返回一个明确的错误信息给模型,比如"参数 ownerId 需要是数字,收到的是 abc"。模型看到这个信息后通常会自我纠正重试。

5.2 工具描述里的枚举值模型记不住

我在工具描述里写了"状态可选值:PENDING/PROCESSING/DONE",但实测模型经常传pending、Pending、处理中这些变体。解决办法是在参数绑定层做一次归一化:把输入转成大写,去掉空格,然后和允许的枚举值做匹配,匹配不上再报错。

更彻底的做法是把枚举值也放进参数的 Schema 里,用enum关键字约束。但不是所有模型都严格遵守 enum 约束,所以归一化这层还是不能省。

5.3 长对话导致 token 超限

Agent 执行多轮后,消息序列会越来越长,最终超过模型的上下文窗口。Harness 一般提供消息裁剪策略,但默认策略未必适合业务场景。

我的做法是分层裁剪:系统提示词永远保留,最近 N 轮对话完整保留,更早的工具调用结果做摘要压缩。工具结果往往是最占 token 的部分(比如查询返回了几十条记录),压缩时只保留关键字段和数量统计,而不是把整个 JSON 塞进去。

private String summarizeToolResult(String toolName, Object result) { if (result instanceof Collection) { Collection<?> col = (Collection<?>) result; return String.format("工具 %s 返回 %d 条记录,首条摘要:%s", toolName, col.size(), firstItemSummary(col)); } return String.valueOf(result); }

5.4 工具执行超时拖垮整个 Agent

某个工具调用了外部接口,接口卡住不返回,整个 Agent 循环就挂在那里。必须给每个工具调用设置超时,超时后返回"工具执行超时"给模型,让它决定是重试还是换方案。

超时时间怎么定?我的经验是按工具类型分档:纯数据库查询 3 秒,调用内部服务 5 秒,调用外部接口 10 秒。超过这个时间基本可以判定有问题,继续等没有意义。

Future<ToolResult> future = toolExecutor.submit(() -> tool.invoke(args)); try { return future.get(tool.getTimeoutSeconds(), TimeUnit.SECONDS); } catch (TimeoutException e) { future.cancel(true); return ToolResult.error("工具执行超时,请稍后重试或换一种方式"); }

5.5 日志里打印了敏感数据

Agent 的调试日志通常会打印完整的消息序列和工具参数,这里面可能包含用户手机号、身份证、订单金额等敏感信息。上线前一定要做日志脱敏,对已知的敏感字段做掩码处理。

我的做法是定义一个敏感字段清单,在日志输出前统一过滤。同时,工具方法的参数在进入日志前也要过一遍脱敏,不能因为"这是内部日志"就放松。

6. 从单 Agent 到多 Agent 的渐进式扩展

6.1 什么时候该引入多 Agent

单 Agent 加工具调用能解决大部分"理解意图 + 调用接口"的场景。但遇到以下情况时,单 Agent 会力不从心:

  • 任务需要不同领域的专业知识。比如一个任务既涉及财务规则又涉及库存规则,塞进一个 system prompt 会让提示词臃肿且互相干扰。
  • 任务步骤多且需要并行。单 Agent 是串行执行的,多个独立子任务并行能显著缩短总耗时。
  • 需要不同权限级别的操作。比如查询用只读 Agent,写操作用高权限 Agent,通过 Agent 隔离权限比在工具层做校验更清晰。

6.2 在 RuoYi 里落地多 Agent 的架构选择

多 Agent 有两种常见模式:编排式(一个主 Agent 分解任务,分派给子 Agent)和协作式(多个 Agent 平等对话,协商完成)。在 RuoYi-Vue-Plus 这种业务系统里,我推荐编排式,因为它的控制流更清晰,权限边界更好划定。

编排式多 Agent 的落地要点:

  • 主 Agent 只做任务分解和结果汇总,不直接调用业务工具。它的工具就是"调用某个子 Agent"。
  • 子 Agent 各自持有独立的工具集和 system prompt,权限按子 Agent 的职责划定。
  • 子 Agent 之间不直接通信,所有协调通过主 Agent 完成,避免调用链失控。
@AgentTool(name = "delegateToFinanceAgent", description = "将财务相关问题委派给财务 Agent") public String delegateToFinance(String subTask) { AgentExecutionContext subCtx = currentContext.derive(FINANCE_PERMISSIONS); return financeAgent.run(subTask, subCtx); }

6.3 多 Agent 带来的新问题

引入多 Agent 后,可观测性变得更重要。一次用户请求可能触发十几个 Agent 调用和几十个工具调用,没有完整的调用链追踪根本没法排查问题。我的做法是给每个 Agent 任务分配一个 traceId,所有子任务继承同一个 traceId,日志里带上 traceId 和 Agent 名称,这样能还原出完整的调用树。

另一个问题是成本控制。多 Agent 意味着多次模型调用,token 消耗成倍增长。我一般会设置一个任务级别的 token 预算,超过预算就强制终止并返回部分结果,避免一个失控的任务烧掉大量额度。

7. 部署与运维层面的几个实际考量

7.1 模型服务的连接配置

模型服务的地址、密钥、超时这些配置不要硬编码,走 RuoYi 的配置体系。我一般放在application.yml里,用@ConfigurationProperties绑定,同时支持通过环境变量覆盖,方便不同环境部署。

agent: model: base-url: ${AGENT_MODEL_URL:http://localhost:8080} api-key: ${AGENT_MODEL_KEY:} timeout-seconds: 60 max-retries: 2 executor: core-size: 4 max-size: 16 queue-capacity: 100

密钥这类敏感配置不要提交到代码仓库,用环境变量或配置中心注入。RuoYi-Vue-Plus 本身支持 Nacos 配置中心,直接复用即可。

7.2 健康检查与降级

模型服务不可用时,Agent 功能应该优雅降级,而不是让整个应用启动失败。我的做法是把模型客户端的初始化做成懒加载,启动时不强依赖模型服务可达。同时提供一个健康检查端点,运维可以监控模型服务的可用性。

降级策略上,模型不可用时直接返回"AI 助手暂时不可用,请稍后重试",而不是让请求挂起等超时。前端也要有对应的提示,避免用户反复点击。

7.3 监控指标该看哪些

Agent 功能的监控和普通接口不一样,除了常规的 QPS、响应时间、错误率,还要关注几个特有指标:

指标含义告警阈值建议
平均工具调用轮次一次任务平均调用几个工具突增说明模型理解变差
工具调用失败率工具执行失败的比例超过 10% 需排查
平均 token 消耗单次任务的 token 用量突增说明上下文膨胀
任务超时率超过最大轮次或时间限制的比例超过 5% 需优化
上下文裁剪次数触发消息裁剪的频率频繁触发说明对话过长

这些指标通过 Harness 的事件回调采集,打到 Micrometer 或直接写日志,接入现有的监控体系。

8. 一些个人实践下来的取舍心得

整套集成做下来,我最大的体会是克制比功能更重要。AgentScope Harness 提供了很多能力——记忆、规划、多 Agent、反思,但业务系统里真正需要的往往只是"理解意图 + 调用工具 + 返回结果"这一条最朴素的链路。每加一层能力,就多一层不确定性,多一层排查成本。

工具的数量也要克制。我见过有团队一口气注册了上百个工具,结果模型选择困难,经常调错。我的经验是单个 Agent 的工具控制在 10 到 15 个以内,超过就考虑拆分 Agent 或者合并工具粒度。工具描述要反复打磨,把模型调错的 case 收集起来,针对性优化描述,这比换更强的模型见效更快。

权限这块再怎么强调都不为过。模型是不可信的输入源,它可能被用户诱导,也可能自己产生幻觉。所有工具方法内部的权限校验、数据权限、参数校验一个都不能少,不能因为"这是内部系统"就放松。我甚至建议对写操作工具加一道人工确认,模型生成操作意图后,前端弹出确认框让用户点一下再执行,这在企业场景里用户接受度反而更高。

最后是成本。Agent 功能的 token 消耗比普通对话高一个数量级,因为每轮都要带上完整的历史和工具定义。上线前一定要做成本测算,设置好预算上限和告警。我一般会先在小范围灰度,观察一周的实际消耗再决定是否全量开放。

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

Agency Agents多智能体协作系统:从架构设计到研发落地的全过程实战

这几年我带过的研发团队有个共同的怪现象&#xff1a;人数越加&#xff0c;沟通成本涨得越快。一个支付模块的改造需求&#xff0c;产品先讲一遍业务背景&#xff0c;技术负责人再讲一遍约束条件&#xff0c;后端开发进场前还要拉三个同事确认字段口径——每个人都在做"上…

作者头像 李华
网站建设 2026/10/27 22:47:03

PS5全攻略:从选购、M.2扩容到散热与系统维护

"AnyPS5"这六个字母&#xff0c;说实在的&#xff0c;是过去很长一段时间里&#xff0c;我各大平台搜索栏里的常客。如果你也跟我一样&#xff0c;喜欢折腾游戏主机&#xff0c;对这个名字应该不陌生。它涵盖了从"我该买哪台"到"我该怎么把它用回本&q…

作者头像 李华
网站建设 2026/10/29 1:21:25

Coze模型接入OpenAI兼容网关:协议转换与流式输出实战

最近团队里有条业务线需要把 Coze 上已经调好的自定义模型收进统一网关&#xff0c;让业务方只认 OpenAI Chat Completions 这一套接口。换句话说&#xff0c;外面看起来就是一个普通的 OpenAI 兼容模型端点&#xff0c;实际Request进来之后&#xff0c;由中间层转发给 Coze 的…

作者头像 李华
网站建设 2026/10/24 18:36:37

ATOMPAW生成Ni的PAW_PBE赝势:参数解析与实操校验

做第一性原理计算的人都知道&#xff0c;赝势的质量基本决定了整个计算链路的可信度。镍&#xff08;Ni&#xff09;这个元素看起来简单&#xff0c;但在PAW_PBE赝势里藏着不少细节&#xff1a;3d电子局域性、磁矩、半芯态选择&#xff0c;哪一步没处理干净&#xff0c;后面晶格…

作者头像 李华
网站建设 2026/10/23 16:07:38

空气质量数据异常值处理与Pandas线性插值实践

1. 空气质量数据预处理&#xff0c;为什么总是绕不开异常值城市空气质量数据大概是环境数据里最"闹腾"的一种。采集设备分布在城市各个监测站点&#xff0c;受传感器漂移、通信故障、扬尘干扰甚至人为维护影响&#xff0c;拿到的原始数据几乎不可能是一张干干净净的表…

作者头像 李华
网站建设 2026/10/24 8:01:50

Pygame跳房子实战:掌握事件驱动与状态机设计

1. 项目概述&#xff1a;用Python和Pygame复刻经典跳房子&#xff0c;为什么这个小项目值得花两小时认真做一遍“Hopscotch小游戏”这个词乍看简单&#xff0c;但在我带过的几十个零基础编程训练营里&#xff0c;它始终是学员完成度最高、课后自发优化最多的一个项目。不是因为…

作者头像 李华