☰
纯Java构建企业级Agent Harness:架构设计、状态机与踩坑实践
2026/10/12 15:01:56 网站建设 项目流程

最近这半年,团队里最热的话题已经从“要不要用大模型”变成了“Agent 到底怎么在企业里落地”。我们是个偏传统的企业软件团队,后端几乎全是 Java,跑着几十个内部系统。业务方的诉求很简单:用自然语言查数据、填单据、走审批、盯告警。但真正动手接 Agent 之后我才发现,最难的不是让模型“会说话”,而是让 Agent 在企业的一堆约束条件下稳定地干活、可追踪、可审计、可运维。这个项目代号叫 BizBuddy,是一个用纯 Java 写的企业级 Agent Harness 平台。这篇文章不是讲某个 AI 功能的 Demo,而是把 Harness 的运行时、工具契约、模型网关、治理体系这一整套东西的设计思路和取舍过程记录下来。适合两类人看:一类是在 Java 技术栈里被逼着做 Agent 落地的后端工程师,另一类是纠结“要不要自研 Harness”、想提前知道坑在哪里的架构师。

1. 立项动机:为什么企业里的 Agent 不能只是一堆脚本

1.1 从“给模型接个 API”到“给 Agent 建个运行时”

最开始我们和所有人一样,先写了个 Demo:把几个内部接口封装成方法,模型通过函数调用去触发它们。Demo 跑得很顺,业务方看了很兴奋,紧接着就出事了——多个团队开始各自造轮子。A 组写了一套 Python 脚本做订单查询,B 组在另一个服务里用 Java 拼 Prompt,C 组干脆用 Excel 里的宏去调大模型。三套实现,三套日志格式,三套权限控制,没有一个能回答“上个月 Agent 总共帮谁执行过哪些敏感操作”。

这其实是企业里做 AI 落地最典型的场景:模型能力不是瓶颈,承接模型行为的运行时才是。所谓 Agent Harness,我理解就是承载 Agent 运行时的平台层。一个 Agent 本质上是“模型 + 工具集 + 系统指令 + 执行策略”的组合,而 Harness 负责把这几样东西装配起来,统一管理它们的生命周期、执行过程、上下文、模型访问、权限校验、审计追踪。它不是某个业务系统里的一个库,而是一个独立平台,所有 Agent 都跑在它上面。

1.2 三个绕不开的企业级约束

我们梳理了企业环境里 Agent 落地必须面对的三类约束,这也是 BizBuddy 所有设计的出发点:

  1. 安全合规:Agent 一旦能操作内部系统,就必须回答“谁发起的、调了哪个工具、传了什么参数、结果是什么、有没有经过人审”。这要求每一次工具调用都留下不可篡改的审计记录,越权操作必须有阻断手段。
  2. 稳定可运维:Agent 不是一次性脚本,它要 7x24 小时在线。超时、限流、降级、优雅退出、容量规划这些传统后端服务的基本功,一样都不能少。
  3. 集成与协作:企业里已经有 SSO、权限模型、消息队列、监控告警系统。Agent 平台不能另起炉灶,必须复用这些基础设施,同时还要允许不同团队共享 Agent、共享工具、共享策略。

用脚本堆临时方案,短期内看起来快,但一旦 Agent 数量从 1 个变成 50 个、工具从 5 个变成 200 个,脚本方案就会在运维、安全和协作层面全面失控。所以我们需要的是一个被当作战术平台来建设的产物,而不是一次性胶水代码。

1.3 Harness 和编排框架的边界

很多人会把 Agent Harness 跟 LangChain 这类编排框架混为一谈。实际上在我的理解里,编排框架解决的是“一个 Agent 内部怎么循环”——怎么调模型、怎么触发工具、怎么把结果喂回去;而 Harness 解决的是“一堆 Agent 怎么在企业里被统一托管”——谁来注册 Agent、谁来管理工具权限、执行中的 Trace 发到哪里、失败怎么重试、多租户怎么隔离。

框架是库,嵌入到每个业务代码里;Harness 是平台,反过来托管业务代码。我们在项目初期评估过直接用编排框架的做法,结论是:框架可以解决循环问题,但解决不了企业治理问题。你可能还是得自己做审计、做多租户、做人审、做模型网关,而这些恰恰是工作量的大头。所以 BizBuddy 的定位从一开始就是 Harness,不是又造一个编排框架。

2. 纯 Java 的代价与收益:生态缺课怎么补

2.1 先承认:Python 生态确实领先

这个项目立项时,团队里最响亮的一个质疑声就是:“大模型的生态明明在 Python 那边,为什么非要用 Java 自虐?”我完全认同这个判断的事实部分。Python 生态里针对 LLM 的工具链确实丰富,从请求封装到编排框架到评估工具,几乎每个月都有新东西。而 Java 这边,成熟的 LLM SDK 相对少,函数调用支持参差不齐,流式输出兼容性也更差。

但我们团队的第一性原则是:平台要长在已有的企业土壤里,而不是凭空造一套孤岛。我们的核心系统是 Java 写的,部署在 Kubernetes 上,配置中心、熔断限流、监控链路全是 Java 技术栈的;如果 Harness 用 Python 写,等于把 AI 业务和整个企业基础设施之间的集成工作全部推给一条黑乎乎的 Python 服务去承担,最后还得自己补 SSO 对接、补日志规范、补发布流程,成本一点都不低。

2.2 Java 生态真正能打的是什么

把 Java 的账算清楚之后,会发现它在企业级 Agent 平台上有几个 Python 短期追不上的优势:

  • 类型系统:几十人协作的代码库里,强类型对重构和维护是巨大的保护。工具契约、事件模型、状态机这些核心抽象,用 Java 写出来可读性和可维护性都更稳。
  • 运行时可控性:JVM 的 GC 调优、线程转储、性能剖析、Metrics 埋点都是几十年沉淀下来的工程能力。AI 应用的特点是长尾延迟高、资源消耗波动大,JVM 的观测手段帮我们省了很多排查时间。
  • 基础设施集成:Spring 配置体系、数据库连接池、消息队列客户端、分布式事务方案,企业里现成的组件拿来就能用。Python 在这些方向不是没有,但要接进现有 Java 体系,中间总得隔一层传输协议。

更重要的是,我们评估了当时 Java 侧 LLM SDK 的成熟度,发现它们大多只解决了“能调用”的问题,没解决“可控、可观测、可治理”的问题。与其在别人的半成品上打补丁,不如自己掌握核心抽象,只把纯协议下载层用最薄的方式包一层。

2.3 虚拟线程带来的调度模型重构

选 Java 21 是我们一个关键决策,核心理由就是虚拟线程。Agent 执行有一个很反直觉的特点:相比传统 API 网关,Agent 的一次执行可能要十几秒甚至几分钟,其中绝大部分时间都在等模型响应或者等工具执行结果。如果用传统线程模型,一个线程等一个请求,上千个并发 Agent 就能把线程池打爆。

Java 21 的虚拟线程让我们能写“同步阻塞”风格的高并发代码,不需要引入复杂的响应式框架。这对团队意义重大:我们不需要全员学习 Reactive 编程心态,业务逻辑读起来就是用普通 Java 写的顺序代码,而并发能力却能轻松支撑数千个 Agent 实例同时等待模型响应。我们也因此把 Java 版本直接锁定在 21 LTS,不接受降级到 17 的方案。

2.4 我们放弃的东西和坚持的东西

为了“纯 Java”,我们明确放弃了几样东西:不引入 Python 作为辅助 Agent 运行时;不在主链路里接响应式框架;不在 Harness 内部塞一个嵌入式 Python 解释器去执行“Agent 代码”。我们坚持的是:Agent 的循环逻辑、工具注册机制、模型网关、治理管道全部用 Java 写;外部 AI 能力只通过标准 HTTP + SSE 协议访问模型供应商。

这套取舍在短期确实增加了工作量——很多模型供应商的 Java 示例远没有 Python 示例完善,我们只能对着文档抠协议细节。但它换来了长期收益:整个平台只有一套技术栈,开发、测试、运维、审计全套链路无需跨语言维护,这对企业信任度至关重要。

3. BizBuddy 的总体架构:控制面、运行时与模型网关三平面

3.1 为什么必须拆三个平面

BizBuddy 的第一版架构我把所有逻辑塞在一个 Spring Boot 服务里,结果代码很快乱成一团:注册 Agent 的代码和执行 Agent 的代码混在一起,管理端改个配置都可能影响正在跑的会话。后来我们参考传统服务里“控制面与数据面分离”的思路,把平台拆成三个平面:

  • 控制面:负责 Agent 的注册、工具元数据管理、策略配置、模型路由规则、配额设置。它面向管理员和开发同学,低频但要求谨慎。
  • 运行时平面:负责 Agent 实例创建、执行业务循环、维护会话上下文、调用工具、处理人审等待。它面向真实用户请求,高频、延迟敏感。
  • 模型网关:负责对接所有模型供应商,统一消息格式,处理限流、重试、计量。它隔离了“模型供应商的多样性”和“Harness 核心逻辑的稳定性”。

三平面各跑各的进程,通过消息队列和数据库共享状态。控制面可以随时发布新的 Agent 配置,运行时平面感知到配置变更后优雅地热生效,不影响正在执行的会话。这个分离带来的直接好处是:模型供应商升级接口或调整限流策略时,我们只需要改模型网关,控制面和运行时完全不用动。

3.2 核心模块与一次完整的执行流程

BizBuddy 的内部模块大致如下:

模块职责关键设计点
Agent RegistryAgent 配置、版本、启停多版本并存,灰度发布
Tool Registry工具元数据、权限标签启动时扫描并生成 JSON Schema
Session Manager会话创建、上下文存取每个会话独立状态,TTL 过期回收
ExecutorAgent 循环执行状态机驱动,支持中断与恢复
Model Gateway模型统一接入流式、重试、配额、计量
Approval Center人审节点管理与 IM/门户联动,超时策略
Trace & Audit链路追踪与审计异步导出,本地聚合第一版落地
Quota Center配额与成本分摊按租户、按 Agent、按用户三级维度

一次典型的执行流程是这样的:用户请求进来后,控制面的接口服务创建一个 Session,把 Agent 配置、上下文、初始参数装配好,交给 Executor。Executor 进入 Agent 主循环:组装消息列表发给模型网关,网关拿到模型返回后可能的工具调用,Executor 对每个工具调用做权限校验,然后通过工具注册表找到对应实现去执行;执行完的结果作为 ToolMessage 再发给模型,如此循环,直到模型给出面向用户的最终回复。

3.3 Agent 实例的状态机:为什么必须显式建模

为 Agent 执行设计状态机,是我认为整个项目里最值得的一笔投入。早期我们用一个布尔变量“isRunning”表示执行状态,出了大问题:Agent 在等工具结果的时候,用户可能想取消任务;Agent 调用了一个危险工具,需要等人审批,审批期间会话不能被别的请求复用;模型供应商超时了,Executor 得知道当前到底是“正在等模型”还是“正在等工具”。布尔变量根本表达不了这些状态。

BizBuddy 里的 Agent 执行状态机如下:

public enum AgentState { CREATED, // 会话已创建,尚未开始执行 READY, // 等待被调度 RUNNING, // 正在调用模型或处理消息 WAITING_TOOL, // 正在等待工具执行结果 WAITING_APPROVAL, // 工具需要人工审批,挂起等待 BLOCKED, // 触发安全策略,被阻断 DONE, // 正常完成 FAILED, // 执行失败 TIMED_OUT, // 超过整体时限 TERMINATED // 用户或管理员主动终止 }

状态之间的转移规则我建议用表来固化,因为它能逼着你想清楚所有边界场景:

当前状态事件迁移目标
READY开始执行RUNNING
RUNNING发出工具调用WAITING_TOOL
RUNNING模型回复最终内容DONE
WAITING_TOOL工具结果返回RUNNING
WAITING_TOOL工具耗时超阈值BLOCKED + 告警
WAITING_APPROVAL审批通过RUNNING
WAITING_APPROVAL审批拒绝BLOCKED
任意运行态用户取消请求TERMINATED
任意运行态全局执行超时TIMED_OUT

状态机同时是观测的基础:我们在每个状态迁移时都记录一次时间戳,后期排查“Agent 为什么这么慢”时,可以直接按状态迁移时间线分析,是模型响应慢、工具执行慢、还是人审等太久,一目了然。

4. 工具契约:用 Java 类型系统驯服 LLM 的意图

4.1 从 Java 方法到 JSON Schema

Agent 要干活,靠的是工具。BizBuddy 里工具的定义方式,是让开发者用一个注解标注普通 Java Bean 方法,系统自动把它变成模型能理解的 JSON Schema。这样做的好处是,工具开发者不需要懂 LLM 的底层协议,写一个普通方法就可以被 Agent 使用。

@AgentTool(name = "queryOrder", description = "根据订单号查询订单的基础信息") public OrderResult queryOrder( @ToolParam(description = "订单号,例如 ORD20250618001", required = true) String orderId, @ToolParam(description = "是否返回明细行", defaultValue = "false") boolean withItems) { // 实际业务逻辑 }

启动时,Tool Registry 会扫描带有@AgentTool注解的 Spring Bean,通过反射读取方法的参数名、类型、注解描述,再用 Jackson 生成标准的 JSON Schema。比如上面的方法会生成:

{ "name": "queryOrder", "description": "根据订单号查询订单的基础信息", "parameters": { "type": "object", "properties": { "orderId": { "type": "string", "description": "订单号,例如 ORD20250618001" }, "withItems": { "type": "boolean", "description": "是否返回明细行", "default": false } }, "required": ["orderId"] } }

这段 Schema 会被作为工具定义传给模型。模型据此前生成符合结构的调用参数。我们这边再用同一份 Schema 做入参校验,双保险。类型映射是这里最容易出错的地方,我们踩了很多坑,后面会专门讲。

4.2 工具调用的错误回传与自纠错

企业环境里工具调用是肯定会出错的:参数格式可能不对、底层接口可能暂时不可用、权限校验可能不过。关键在于,Agent 遇到错误后不能原地崩溃,也不能给用户一个含糊的“出错了”。BizBuddy 的处理原则是:把错误当作一种合法结果回传给模型,让模型基于错误信息自行纠错。

工具执行结果被封装成统一类型:成功数据、业务失败说明、系统异常说明、需要用户补充信息。比如工具返回“订单号不存在”,我们会把这句提示作为 ToolMessage 回传给模型,模型通常会主动换一种方式追问用户或者纠正参数再试一次。如果模型连续多次都无法正确处理同一个错误,状态机会进入 BLOCKED,把会话交给管理员介入。

4.3 上下文、会话状态与记忆窗口

企业对话还有一个常见需求:用户在多个轮次中反复查询某个订单、修改某个审批单,Agent 必须记住上下文。BizBuddy 里每个 Session 独立维护一个上下文容器,里面同时保存两类内容:一类是消息历史,另一类是你精心设计的业务状态对象——比如“当前用户正在操作哪个订单”“上一次查询的客户编号是多少”。

把业务状态单独建模,而不是全部塞在消息历史里,是一个很重要的取舍。纯靠消息历史 + 模型摘要来记忆,Token 消耗大且容易丢失关键信息;把关键业务上下文放进对象里,每一轮循环都持久化一次,Agent 需要时可以精确读取。我们给上下文设置了 TTL 和大小上限,超过上限的老消息会被自动压缩成摘要,而业务状态对象始终完整保留。

5. 模型网关:隔离供应商差异的最后一道防线

5.1 统一消息模型:屏蔽各家协议的手续

企业里几乎不会只用一个模型。价格、延迟、能力各有不同,硬编码任何一家都是灾难。模型网关的第一责任是把各家供应商的 API 差异全部吞掉,向上层只暴露一个稳定接口。

网关内部定义了一套统一消息模型,大致是这样的结构:

public sealed interface ChatMessage permits SystemMessage, UserMessage, AiMessage, ToolMessage {} public record AiMessage( String content, List<ToolCall> toolCalls) implements ChatMessage {} public record ToolCall( String callId, String name, String argumentsJson) {}

各家供应商的响应,无论它的返回形态是什么样的,都在网关的 Adapter 层被转换成这套统一模型。上层 Executor 永远只认这一套模型,供应商 A 换供应商 B,对业务逻辑完全透明。我们在 Adapter 里还做了响应归一化:有的供应商在content里放文本、在tool_calls里放工具调用;有的供应商把工具调用包在更复杂的结构里;还有的可能一条 SSE 消息拆成很多个碎片。归一化是把这些差异收敛掉的唯一办法。

5.2 流式输出的两个处理要点

企业级场景里,流式输出几乎是必须的,用户受不了十几秒干等。但流式会带来两个麻烦。第一个麻烦是流式文本的拼接:SSE 一条条到达,每个 chunk 可能只是半句甚至半个词。我们要做的是在网关层就完成流式缓冲,把完整句子按语义边界逐步释放给前端,不能让上层业务逻辑看到半截 JSON。

第二个麻烦更隐蔽:流式模式下,工具调用的参数也是分片到达的。比如模型决定调用queryOrder,参数{"orderId": "ORD2025...可能被拆在三个 SSE chunk 里。处理方案是给每个 ToolCall 建一个累积缓冲器,以callId为维度,把argumentsJson的碎片拼全,等供应商发来该工具调用的结束标记后再统一交给 Executor。这个我们第一版没处理好,结果经常出现 JSON 解析异常。

5.3 限流、配额与成本:把模型当成企业资源

模型调用是有成本的,企业里必须做计量。BizBuddy 的 Quota Center 按“租户 → Agent → 用户”三级做配额:租户维度控制整体预算上限,防止某个部门把全公司预算烧光;Agent 维度控制单个 Agent 的调用频率;用户维度限制单人在单位时间内的请求数。超配额时,网关不是直接报错,而是返回明确的配额超限信息,让 Executor 能给出“当前配额已满,请稍后再试”这类对用户友好的提示。

重试策略上我们做了分级:连接超时、读超时、HTTP 5xx、限流 429,分别走不同的退避策略。比如 429 要尊重供应商的Retry-After头,而连接超时可以直接换一个同能力的备选供应商重试。这里必须注意:如果请求已经发出但我们没收到响应,不能盲目重发,因为模型那边可能已经生成了工具调用指令,重发一次等于业务行为重复触发。我们的原则是:工具执行的幂等性由工具自己声明,网关层面只对有幂等标记的请求做自动重试,其他情况一律向上抛异常,交给执行策略决定怎么办。

6. 企业治理:Trace、审计与多租户隔离

6.1 一次 Agent 执行的完整 Trace

Agent 执行的链路比传统 API 长得多:一次对话可能包含多轮“模型调用 → 工具执行 → 结果回填”的循环。如果只用传统日志,排查问题时你会面对一堆毫无关联的散乱日志。BizBuddy 从第一天起就在每个 Session 上绑定 TraceId,并且把 Agent 状态机的每次迁移都嵌入 OpenTelemetry 的 Span。

在执行链路里,我们手动埋了四类 Span:agent.execute是总跨度,覆盖整个会话执行;llm.call记录一次模型请求的耗时和 Token 用量;tool.execute记录某一次工具调用的耗时与结果;approval.wait专门记录人审等待的时长。每类 Span 上都挂了结构化属性:Agent 名称、模型名称、工具名称、耗时、Token 数、错误码。排查“为什么这次回答特别慢”的时候,直接按 Trace 时间线展开看,到底是模型层 8 秒还是工具层 5 秒,一清二楚。

这里我特意选择手动埋点,而不是全自动的字节码增强。原因很简单:Agent 场景下的语义埋点需要业务知识,比如“等待人审”这种状态,自动增强工具根本无从知晓。手动埋点控制在七个关键位置,代码侵入量很小,换来的是整个业务语义的可观测性。

6.2 人审节点:不让 Agent 直接扣款

企业 Agent 和消费级聊天机器人最大的区别在于:它要操作真实业务,能下单、能改价、能撤销审批单。BizBuddy 的安全设计采取分级控制:把工具分成只读和写两类,写工具还可再细分是否需要人审。默认情况下,查询类工具直接执行;低风险写操作(比如修改备注)由用户确认后执行;高风险写操作(比如取消订单、修改金额),必须走人审节点。

人审节点就是状态机里的WAITING_APPROVAL状态。Executor 撞到需要审的工具时,先把工具调用挂起,把审批请求推送到企业的 IM 或门户系统,审批人看到的是完整的参数快照和调用上下文,同意或拒绝后,结果回传到 Harness,状态机继续执行。审批链接有有效期,超时后自动按“拒绝”处理并通知发起人。这个机制让 AI 的能力边界和企业的管理红线真正对齐了。

6.3 审计日志、密钥管理与 Prompt 注入防护

审计是硬合规要求。BizBuddy 把每次工具调用都写入一个独立审计主题:谁在什么时间,通过哪个 Agent,调用了哪个工具,参数是什么,结果是什么,审批人是谁,审批意见是什么。审计日志只增不改,数据库层面做了权限隔离,普通业务开发没有清理权限。采用 Outbox 模式写审计:业务执行在本地事务里顺带落一条审计事件,后台异步批量导出,避免审计动作拖慢主链路。

密钥管理上,我们严禁在配置里明文写任何模型供应商的 API Key,统一走企业密钥服务,Harness 进程启动时申请临时凭证,用完即释放。Prompt 注入是 Agent 安全里最容易被低估的一环:模型很可能读到用户转发来的邮件、网页内容,这些外部内容里可能夹带“忽略之前指令”“立刻执行 XX”之类的恶意指令。我们的做法是:外部内容一律打上外部数据标签,在上文被分隔符明确包裹,系统指令里反复强调外部内容只是数据不可当指令;同时所有工具调用都要过权限校验,即使模型被误导,也不可能越过 ACL 执行越权操作。

7. 踩坑复盘:几个差点翻车的设计决策

7.1 流式工具调用的参数拼接:第一版就踩的坑

第一版我们天真地以为 SSE 每个 chunk 里的arguments就是完整的 JSON,直接转成对象调用工具。结果上线第一天,工具调用失败率高达 30%。排查半天发现,供应商在流式模式下会把参数 JSON 切成任意大小的碎片,我们拿到的往往是一个被切断的局部字符串。后来改成按callId做累积缓冲,直到收到调用结束标记才解析。这里给后来者一个建议:永远不要假设供应商的流式分片边界和你的业务边界一致,所有流式数据必须以语义完整性为消费单位。

7.2 JSON Schema 自动生成的类型陷阱

从 Java 方法反射生成 Schema,听起来平平无奇,实际处处是雷。我们最先遇到的问题在Optional<T>上:反射拿到的泛型信息比想象中复杂,Optional<String>一度被生成成object类型,模型死活填不对参数。还有Set和List:反射里都是Collection,但我们希望List映射成array、Set映射成array并加uniqueItems。以及枚举类型:默认生成string时,模型的自由度太大,会填出枚举里没有的值;改成enum之后错误率骤降。

最后我们给 Tool Registry 加了两个保险:一是注解上允许开发者手动覆盖类型提示;二是建了一批“Schema 黄金测试”,把每个工具的 Schema 生成结果做快照对比,任何改动导致 Schema 变化时测试都会亮红,人工确认后才能更新。

7.3 虚拟线程被“钉住”的情况不是传说

Java 21 虚拟线程确实好,但“钉住”问题是真实存在的。我们有一个模块用了老的同步库,内部有synchronized块,调用频繁时线程转储发现很多虚拟线程被钉在载体线程上,并发吞吐不升反降。

排查手段就是上生产环境的线程转储,看哪个栈反复出现在Monitor持有状态。找到之后,把那个模块的同步策略改成ReentrantLock,或者把该调用挪到固定线程池里做隔离,问题就解决了。这个坑提醒我们:用了虚拟线程不等于可以无视同步瓶颈,团队里所有同步代码仍然要遵循“能不加锁就不加锁、要加锁用显式锁”的纪律。

7.4 测试 Agent 的确定性难题

Agent 的最大特点就是不确定性。同一个 Prompt,模型两次输出可能不一样,这导致所有依赖真实模型的测试都不可靠。我们花了很大力气搭了一套确定性测试体系:Mock Model Gateway 不访问真实模型,而是从录制好的响应脚本里读取预定义输出;测试用例严格定义“输入消息序列 → 期望状态迁移 → 期望工具调用顺序”。

回归测试能做到:一旦工具 Schema 变了,黄金测试立刻暴露;一旦某个工具的异常分支没处理好,状态机测试立刻暴露。确认平台逻辑稳定之后,我们才在预发环境放开真实模型,跑一批带人工评判的评测用例。没有这套确定性测试,靠肉眼盯真实模型输出做回归,效率低到没法接受。

写在最后的一点体会

BizBuddy 从立项到现在,最大的收获不是代码量,而是我们终于想明白了一件事:在企业里做 Agent,本质上是做一套有状态的、需要治理的分布式系统,只不过它的“业务逻辑”有一部分是模型在生成。纯粹的 Java 并不是最优解,而是最适合我们土壤的解法。如果你所在的团队也在类似的 Java 技术栈里,我的建议是:先把状态机、工具契约、模型网关这三件地基打牢,再去追生态里的新花样。地基稳了,上面长什么都快;地基不稳,再多花活都是给自己埋雷。

最后再分享一个小细节:设计状态机时,我们最早没考虑WAITING_APPROVAL这个状态,觉得业务方不会频繁用高风险的写操作。结果上线第二周就有客户提了需求,要把“批量修改价格”接进来。那一次如果状态机没有留好人审扩展点,整个执行框架都要动。所以,给企业级 Agent 做设计,宁可一开始多几个看似冗余的状态,也不要等到线上事故来教育你。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询