告别文字解析:Jev如何用结构化决策重构Agent架构

发布时间:2026/11/4 9:08:14
告别文字解析:Jev如何用结构化决策重构Agent架构

1. 一个Java老兵的困惑:为什么Agent总在“说废话”

我第一次接触Agent这个概念的时候,脑子里想的是:这不就是个高级版的状态机吗?我写了十年代码,从Servlet到Spring Boot,从JDBC到MyBatis,什么流程编排没见过。状态流转、条件分支、异常兜底,这些东西在Java世界里早就被抽象得明明白白。

但当我真正开始用LLM驱动的Agent框架去搭东西的时候,我发现事情不对劲了。

最让我抓狂的一点是:几乎所有的Agent框架,都在让模型“生成文字”。你问它一个问题,它输出一段文字;你让它调用工具,它输出一段JSON;你让它规划下一步,它输出一段自然语言描述。然后框架再拿这段文字去解析、去匹配、去执行。整个链路里,模型的核心动作就是“说话”,而框架的核心工作就是“听懂它在说什么”。

这就有问题了。

做过Java服务端开发的人都知道,让一个不可靠的组件承担核心决策职责,是架构设计的大忌。LLM的输出天然具有不确定性,同样的输入可能产生不同的措辞,同样的意图可能用完全不同的句式表达。你让这样一个组件去决定“下一步该调用哪个工具”“该传什么参数”,然后靠正则或者JSON parser去提取,这跟用字符串匹配做路由有什么区别?

我踩过的坑包括但不限于:模型输出了合法的JSON但字段名跟预期不一致;模型在JSON外面包了一层“好的,我来帮你调用”的前缀;模型把参数值写成了自然语言描述而不是结构化数据;模型在应该返回单个工具调用的时候返回了三个,在应该返回三个的时候只返回了一个。

每一次出问题,你都得去调prompt,加few-shot示例,加格式约束,加后处理逻辑。你花在“让模型好好说话”上的时间,比花在业务逻辑上的时间还多。

直到我看到了Jev这个思路,才觉得有人真的想明白了这件事。

2. Jev到底在做什么:不生成文字的决策模型

2.1 核心思路拆解

Jev的核心主张可以用一句话概括:决策模型不应该生成自然语言,而应该直接输出结构化的决策信号。

什么意思?传统的Agent架构里,决策层和执行层之间是通过“文字”来通信的。模型说“我需要查询天气”,框架解析出“查询天气”这个意图,然后映射到对应的工具。Jev的做法是,决策模型直接输出一个决策向量或者决策编码,这个编码直接对应到具体的动作空间,不需要经过自然语言的中间层。

打个比方。传统Agent就像你让一个翻译官帮你点菜,你用中文说“我想吃辣的”,翻译官翻译成英文告诉服务员,服务员再理解。Jev的做法是你直接指着菜单上的图片说“这个”,不需要翻译。

这个思路在Java开发者看来其实非常熟悉。这就是接口契约的思想。传统Agent的决策层和执行层之间的接口是“自然语言”,这是一个极其模糊、极其不稳定的契约。Jev把这个契约改成了“结构化决策信号”,这是一个明确的、可验证的、类型安全的契约。

2.2 为什么Java开发者应该关注这个

你可能会问,这跟我写Java有什么关系?

关系大了。Java生态里有一套非常成熟的接口驱动设计方法论。我们定义Service接口,定义DTO,定义枚举,定义状态机。这些东西的本质是什么?是把不确定性约束在最小的范围内。

Jev的思路本质上就是把Agent架构里的不确定性从“决策层和执行层的通信”这个环节移除掉。决策模型内部可以是不确定的,但它输出的东西必须是确定的、结构化的、可直接消费的。这跟我们在Java里做的一样:Controller层可以处理各种奇怪的HTTP请求,但它调用Service层的时候,传的参数必须是类型安全的DTO。

而且,Jev的决策模型不生成文字,意味着推理成本大幅降低。生成文字需要自回归地一个token一个token往外蹦,而输出一个决策信号可能只需要几个token,甚至可以直接通过分类头输出。这对于需要高频决策的Agent场景来说,延迟和成本的优化是数量级的。

3. 传统Agent架构的“文字依赖”到底卡在哪里

3.1 文字作为中间表示的三大原罪

我梳理了一下,传统Agent架构里用自然语言作为决策层和执行层之间的中间表示,至少有三大问题。

第一,解析成本高且不可靠。模型输出一段文字,你得写parser去解析。JSON parser算是好的,至少格式相对固定。但很多时候模型输出的不是纯JSON,而是夹杂着解释文字的混合体。你得写正则去提取,写fallback逻辑去兜底。每加一个工具,就得加一套解析规则。工具多了之后,解析逻辑的复杂度是线性增长的,而解析失败的概率是指数增长的。

第二,语义漂移。同一个意图,模型可能用“查询”“获取”“检索”“查找”等不同的词来表达。你的映射表得覆盖所有可能的表达方式。但语言是开放的,你永远覆盖不全。今天模型说“查询天气”,明天可能说“看看天气怎么样”,后天可能说“帮我查一下今天的气温”。你得不断地加同义词,加示例,加约束。

第三,无法做类型检查。自然语言没有类型系统。模型说“温度设为25度”,这个25是整数还是浮点数?单位是摄氏度还是华氏度?如果工具期望的是华氏度,模型给的是摄氏度,框架没法在编译期发现这个问题,只能在运行时报错。而运行时报错意味着用户已经感知到了。

3.2 一个真实的翻车案例

我之前搭过一个客服Agent,需要根据用户问题决定调用哪个后端服务。工具有三个:查订单、查物流、查退款。模型输出的格式要求是JSON,包含tool和params两个字段。

测试的时候一切正常。上线之后,大概每100次调用会出现3到5次解析失败。我拉日志一看,失败的情况五花八门:

  • 模型输出了{"tool": "查订单", "params": {"order_id": "12345"}},但我的映射表里写的是query_order,中文没匹配上。
  • 模型输出了{"tool": "query_order", "params": {"orderId": "12345"}},字段名用了驼峰,我期望的是下划线。
  • 模型输出了好的,我来帮您查询订单:{"tool": "query_order", "params": {"order_id": "12345"}},前面多了一段话。
  • 模型输出了{"tool": "query_order", "params": {"order_id": 12345}},order_id是数字不是字符串。

每一个问题单独看都很蠢,但组合在一起,你的解析逻辑就得写成这样:先去掉可能的前缀,然后尝试解析JSON,如果失败就尝试用正则提取,提取到之后做字段名归一化,再做类型转换,再做枚举值映射。这一套下来,代码量比业务逻辑还多。

这就是“文字依赖”的代价。你把大量的工程复杂度花在了“让模型好好说话”和“听懂模型说的话”上面,而不是花在业务本身。

4. Jev的决策模型是怎么工作的

4.1 从“生成文字”到“输出决策”

Jev的决策模型不生成自然语言,它输出的是一个决策表示。这个表示可以是:

  • 一个离散的动作ID,对应到预定义的动作空间。
  • 一个固定维度的向量,每个维度对应一个决策维度。
  • 一个结构化对象,但字段是预定义的、类型安全的。

关键在于,这个输出是直接可消费的,不需要经过自然语言解析这一层。

我理解这个思路的时候,想到了Java里的枚举。传统Agent就像用字符串来表示状态,String status = "ACTIVE",你得担心大小写、拼写错误、前后空格。Jev就像用枚举,Status status = Status.ACTIVE,编译器帮你检查,IDE帮你补全,不可能出错。

4.2 决策空间的预定义

Jev要求你在搭建Agent之前,先定义好决策空间。也就是说,这个Agent可能做出的所有决策,你都得提前枚举出来。

这听起来像是限制,但做过Java开发的人都知道,预定义接口和实现类是保证系统可靠性的基本手段。你不可能让一个Service随便返回什么类型,你得定义好返回值的类型。同样,你不可能让决策模型随便输出什么动作,你得定义好动作空间。

决策空间的定义包括:

  • 动作列表:这个Agent能执行哪些动作。
  • 每个动作的参数schema:每个动作需要哪些参数,参数的类型是什么,取值范围是什么。
  • 动作之间的约束:哪些动作可以连续执行,哪些动作互斥。

这些定义在Java里可以用枚举、注解、JSON Schema等方式来表达。定义好之后,决策模型的输出就被约束在这个空间里,不可能跑偏。

4.3 训练方式的差异

传统Agent的决策模型通常是通过prompt engineering来引导的。你写一段系统提示词,告诉模型“你是一个Agent,你可以调用以下工具,请以JSON格式输出你的决策”。然后靠few-shot示例来教模型怎么输出。

Jev的决策模型是通过强化学习来训练的。具体来说,它不依赖于人类对“好文字”的偏好,而是依赖于决策结果的好坏。你执行一个动作,环境给你一个反馈,根据反馈来调整决策模型的参数。

这就绕开了RLHF的一个核心问题:人类标注的是文字的质量,而不是决策的质量。在传统Agent里,你用RLHF训练模型,人类标注员看到的是模型输出的文字,他们评判的是“这段文字看起来对不对”。但文字看起来对,不代表决策是对的。模型可能输出了一段非常漂亮的JSON,但选错了工具。

Jev的思路是,直接对决策结果做强化学习。选对了工具,奖励;选错了,惩罚。不需要人类去评判文字质量,因为根本没有文字。

5. 为什么这个思路能颠覆Agent架构

5.1 架构层面的简化

传统Agent架构通常包含这些组件:

  • Prompt构造器:把当前状态、历史对话、工具描述拼成prompt。
  • LLM推理层:调用LLM生成文字。
  • 输出解析器:从文字里提取决策。
  • 决策执行器:根据决策调用工具。
  • 状态更新器:把执行结果更新到状态里。

Jev架构下,这些组件变成了:

  • 状态编码器:把当前状态编码成决策模型能接受的输入。
  • 决策模型:直接输出决策信号。
  • 决策执行器:根据决策信号调用工具。
  • 状态更新器:把执行结果更新到状态里。

Prompt构造器和输出解析器消失了。这两个组件在传统架构里往往是最复杂、最容易出bug的部分。去掉它们,整个系统的可靠性上了一个台阶。

5.2 延迟和成本的优化

生成文字是自回归的,一个token一个token往外蹦。输出一个决策信号可能只需要几个token,甚至可以通过分类头直接输出。这意味着:

  • 推理延迟降低:不需要等模型生成完整的文字描述。
  • 推理成本降低:token数少了,API调用费用或者自托管算力都省了。
  • 吞吐量提升:同样的算力可以支撑更多的并发决策。

对于需要高频决策的Agent场景,比如实时对话、游戏AI、自动化运维,这个优化是决定性的。

5.3 可靠性的提升

没有文字解析,就没有解析失败。决策模型的输出直接对应到动作空间,要么是合法的动作,要么是非法动作。非法动作可以在执行前就被拦截,不会造成副作用。

而且,决策模型的输出可以做类型检查。参数的类型、范围、约束都可以在编译期或者加载期验证。这跟Java的静态类型系统是一个道理:把错误暴露在运行之前。

6. 从Java视角看Jev的工程实现

6.1 决策空间的定义

在Java里,你可以用枚举来定义动作空间:

public enum AgentAction { QUERY_ORDER(OrderParams.class), QUERY_LOGISTICS(LogisticsParams.class), QUERY_REFUND(RefundParams.class); private final Class<?> paramType; AgentAction(Class<?> paramType) { this.paramType = paramType; } public Class<?> getParamType() { return paramType; } }

每个动作对应一个参数类,参数类用Java的record或者Lombok的@Data来定义:

public record OrderParams( @NotNull String orderId, @Min(1) @Max(100) int pageSize ) {}

这样,决策模型的输出就可以直接映射到AgentAction和对应的参数对象。如果模型输出的动作不在枚举里,直接拒绝。如果参数不符合约束,直接拒绝。所有的校验都在执行之前完成。

6.2 决策模型的集成

决策模型可以是一个本地的小模型,也可以是一个远程的API。在Java里,你可以定义一个接口:

public interface DecisionModel { AgentAction decide(AgentState state); <T> T getParams(AgentAction action, AgentState state); }

然后根据具体的实现来选择。如果是本地模型,可以用ONNX Runtime或者DJL来加载。如果是远程API,可以用HTTP客户端来调用。

关键在于,决策模型的输出是类型安全的。你不需要解析文字,不需要做字符串匹配,不需要担心格式问题。

6.3 状态编码

决策模型的输入是Agent的当前状态。状态可以包括:

  • 对话历史
  • 当前任务目标
  • 已执行的动作序列
  • 环境反馈

在Java里,你可以把状态定义成一个对象,然后编码成决策模型需要的格式。如果是神经网络模型,可能需要编码成向量;如果是基于规则的模型,可能直接传对象就行。

6.4 执行与反馈

决策执行器根据决策模型输出的动作和参数,调用对应的工具。执行结果更新到状态里,然后进入下一轮决策。

这个循环在Java里可以用一个简单的while循环来实现:

AgentState state = initialState; while (!state.isTerminal()) { AgentAction action = decisionModel.decide(state); Object params = decisionModel.getParams(action, state); ActionResult result = executor.execute(action, params); state = state.update(result); }

这个循环非常清晰,没有任何文字解析的环节。决策模型负责决策,执行器负责执行,状态负责记录。各司其职。

7. 实操中需要注意的坑

7.1 决策空间的粒度

决策空间的粒度是一个需要仔细权衡的问题。粒度太粗,决策模型的选择太少,灵活性不够。粒度太细,决策空间太大,训练难度增加,而且容易出现无效决策。

我的经验是,从粗粒度开始,根据实际需求逐步细化。比如一开始只定义“查询”“修改”“删除”三个动作,发现“查询”下面有太多不同的查询类型,再拆分成“查询订单”“查询物流”“查询退款”。

7.2 冷启动问题

决策模型需要通过强化学习来训练,但冷启动阶段没有足够的反馈数据。这时候可以用一些启发式规则来引导,或者用传统Agent的日志来初始化训练数据。

我试过的一个方法是,先用传统Agent跑一段时间,把决策日志收集起来,然后用这些日志来预训练决策模型。等模型有了基础能力之后,再切换到强化学习模式。

7.3 与现有Java生态的集成

Jev的思路虽然新,但落地的时候还是要跟现有的Java生态集成。比如:

  • 用Spring Boot来管理Agent的生命周期。
  • 用Spring StateMachine来管理状态流转。
  • 用Micrometer来监控决策延迟和执行结果。
  • 用Resilience4j来做熔断和降级。

这些集成工作不难,但需要你对Java生态有足够的了解。

7.4 决策的可解释性

不生成文字的一个副作用是,决策的可解释性变差了。传统Agent至少能看到模型输出的文字,知道它“为什么”这么决策。Jev的决策模型输出的是一个信号,你很难直接理解它的决策逻辑。

解决方法是,在决策模型之外,单独训练一个解释模型,或者用SHAP、LIME等方法来解释决策。但这会增加系统的复杂度,需要权衡。

8. 常见问题速查

问题可能原因排查思路解决方案
决策模型输出非法动作决策空间定义不完整检查动作枚举是否覆盖所有情况补充动作定义,重新训练
决策延迟高模型推理慢检查模型大小和推理框架模型量化、剪枝,或换更小的模型
决策准确率低训练数据不足检查训练样本数量和分布增加训练数据,调整奖励函数
与现有系统集成困难接口不兼容检查接口定义和数据类型写适配器层,做类型转换
决策不可解释模型是黑盒检查是否有解释机制加解释模型或日志记录

9. 我对这个方向的一些个人判断

我在Java后端领域干了十多年,见过太多“新概念”来了又走。Jev这个思路让我觉得有意思的地方在于,它不是在现有架构上打补丁,而是从根上重新思考了决策层和执行层的关系。

传统Agent架构的问题在于,它把LLM当成了一个“万能翻译器”,既要理解用户意图,又要生成决策文字,还要让框架能解析。这就像让一个翻译官同时做同声传译和会议纪要,还要保证纪要格式能被机器读取。要求太多,出错是必然的。

Jev的做法是把职责拆开:LLM或者决策模型只负责决策,不负责表达。表达的事情交给结构化的接口来做。这跟Java里Controller和Service的职责分离是一个道理。Controller负责处理HTTP协议,Service负责业务逻辑。你不会让Service去解析HTTP报文,也不会让Controller去写业务规则。

当然,这个思路也不是银弹。决策空间的预定义需要你对业务有深入的理解,强化学习的训练需要大量的反馈数据,可解释性的问题也需要额外的机制来解决。但至少,它指出了一个正确的方向:让决策模型做决策,让工程框架做工程。

对于Java开发者来说,这个方向尤其值得关注。因为Java生态里有一套非常成熟的接口驱动设计方法论,有一套非常完善的类型系统和校验机制。这些东西在传统Agent架构里用不上,因为中间隔了一层自然语言。但在Jev架构下,这些工程实践可以直接复用。

我最近在尝试用Java写一个简单的决策模型框架,把动作空间定义、参数校验、执行反馈这些环节都做成可复用的组件。踩了一些坑,也积累了一些经验。等成熟了再单独写一篇分享。

如果你也在做Agent相关的开发,建议你花点时间研究一下Jev的思路。不一定非要完全照搬,但至少可以借鉴它的核心思想:把不确定性约束在最小的范围内,让系统的其他部分保持确定和可靠。这个原则在Java世界里被验证了二十年,在Agent时代同样适用。