1. 一个不生成字的模型凭什么三天冲上技术圈头条第一次看到 Jev 这个东西的时候我正蹲在工位上啃一个前端表单校验的 bug。同事甩过来一个链接说“你看看这个模型一个字都不生成居然在技术社区首页挂了三天”。我当时第一反应是标题党——现在但凡带“模型”两个字的东西不都是往大语言模型上蹭吗一个不生成文本的模型能有什么可聊的结果点进去看了半小时我默默把链接收藏了顺手把它的源码仓库 clone 到了本地。Jev 这个项目核心做的事情用一句话概括就是它把“模型”这个词从“生成内容”拉回到了“做判断”。它不写文章、不聊天、不画图它只干一件事——接收一段结构化的输入然后输出一个类型安全的、可被程序直接消费的决策结果。你可以把它理解成一个“会思考的校验器”或者“带推理能力的类型守卫”。在 TypeScript 的世界里我们平时写if (typeof x string)这种判断Jev 想做的就是把这类判断从硬编码的规则升级成由模型驱动的、带语义理解的动态决策。这东西为什么能火因为它踩中了一个非常真实的痛点。现在前端和全栈开发者用 AI SDK 做生成式 UIGenerative UI的时候最大的麻烦不是模型不会生成而是模型生成的东西不可控、不可信、类型对不上。你让模型返回一个 JSON它给你返回一段带 markdown 代码块的字符串你让它返回一个数组它给你返回一个对象。于是大家就在 prompt 里反复强调“只返回 JSON”“不要加解释”然后写一堆正则去清洗。Jev 的思路是反过来的它不让你去求模型守规矩而是让模型在一个类型系统约束好的空间里做选择输出的东西天然就是合法的。适合谁来研究这个东西三类人。第一类是正在用 Vercel AI SDK 做生成式 UI 的前端/全栈工程师你会直接受益于它的类型安全思路第二类是对“小模型做结构化决策”感兴趣的后端和算法同学Jev 的 RLCDReinforcement Learning from Contrastive Decisions对比决策强化学习训练思路值得一看第三类是单纯想找一个周末项目练手的人它的源码量不大但设计密度很高读起来很过瘾。下面我就按我自己扒源码、跑本地部署、踩坑排查的顺序把 Jev 这个东西从里到外讲一遍。该说的原理说清楚该给的代码给到位该泼的冷水也不藏着。2. Jev 到底在解决什么问题从生成式 UI 的类型困境说起2.1 生成式 UI 的“最后一公里”为什么总是断的先说说背景不然没做过生成式 UI 的人会觉得 Jev 是凭空冒出来的。所谓生成式 UI就是让模型根据用户的自然语言动态生成界面结构或者组件配置。比如用户说“给我做一个带搜索框和分页的表格”模型返回一个描述这个表格的 JSON前端拿到 JSON 之后渲染出真实组件。这个流程听起来很顺但真正落地的时候断点几乎全在“模型返回的东西能不能直接用”这一步。我自己的项目里就遇到过这些情况模型返回的 JSON 里某个本该是数字的字段变成了字符串10枚举字段本该是asc或desc模型返回了ascending嵌套对象少了一层或者多包了一层data最要命的是模型有时候会在 JSON 外面裹一段解释文字导致JSON.parse直接抛异常。传统的解法是在 prompt 里写死格式要求然后加一层 Zod 或者 io-ts 做运行时校验。校验失败就重试重试还失败就降级。这套方案能用但有两个硬伤一是重试成本高每次重试都是一次完整的模型调用二是校验只能告诉你“错了”不能告诉你“怎么改”模型拿到错误信息之后经常改不对来回几次就把 token 烧完了。Jev 的切入点就在这里。它认为问题的根源在于我们把模型当成了一个自由文本生成器然后试图用后置校验去约束它这个顺序反了。正确的做法应该是在模型生成之前就把合法的输出空间定义好让模型在这个空间里做选择而不是在无限可能的文本空间里自由发挥然后再去筛。2.2 TypeSafe AI 的核心主张让类型系统成为模型的一部分Jev 背后挂的那个词叫 TypeSafe AI直译就是“类型安全的人工智能”。这个词听起来有点玄但拆开看很朴素它主张模型的输出不应该是一个需要被校验的字符串而应该是一个在类型层面就已经确定合法的值。打个比方。传统方式像是你让一个实习生写报告写完你检查发现格式不对打回去重写来回折腾。TypeSafe AI 的方式像是你给实习生一个填表模板模板上每个格子都标好了“这里填数字”“这里只能选是或否”他填完交上来天然就是合规的你不需要再检查格式。具体到技术实现上Jev 做的事情是你用一个 schema在它的体系里叫 TypeSpec描述你想要的输出结构Jev 把这个 schema 编译成模型可以理解的约束模型在推理时只能在这个约束下输出最终产出的结果直接就是符合 schema 的类型安全对象。整个过程不需要后置的JSON.parse加 Zod 校验也不需要重试循环。这个思路的价值在于它把“校验”从运行时前移到了推理时。对于生成式 UI 这种对延迟敏感、对可靠性要求高的场景这个前移带来的收益是很实在的——少一次重试就少几百毫秒少一次降级就少一次用户体验的断裂。2.3 RLCD 训练法不靠海量标注靠对比学决策Jev 的模型本身是怎么训出来的这是我觉得整个项目里最有意思的部分。它用的方法叫 RLCD全称是 Reinforcement Learning from Contrastive Decisions。这个名字是照着 RLHFReinforcement Learning from Human Feedback改的但内核完全不同。RLHF 依赖大量人工标注的偏好数据成本极高。RLCD 的思路是不需要人工标注而是通过构造对比样本让模型自己学会区分“好的决策”和“坏的决策”。具体来说对于同一个输入构造两个输出一个是符合类型约束的正样本一个是不符合的负样本然后让模型去学习区分这两者。训练信号来自类型系统本身——符合 schema 的就是正样本不符合的就是负样本这个判断是程序自动做的不需要人参与。这个设计的巧妙之处在于它把类型系统变成了一个自动的奖励函数。类型检查器说“这个输出合法”那就是正奖励说“这个输出非法”那就是负奖励。整个训练闭环不需要人工介入成本被压得很低。这也是为什么 Jev 作为一个实验性项目能在没有大厂资源的情况下把模型训出来。当然这个方法的局限也很明显。它只能保证输出在类型层面合法不能保证输出在语义层面正确。比如 schema 要求返回一个asc或desc模型确实只会返回这两个值之一但它可能在该返回asc的时候返回了desc。类型安全不等于语义正确这一点后面讲坑的时候我会再展开。2.4 和 Vercel AI SDK 的关系不是替代是补位很多人看到 Jev 会问它和 Vercel AI SDK 是什么关系是不是要取代 AI SDK不是。Jev 和 AI SDK 是互补的。AI SDK 解决的是“怎么把模型接进前端应用”的问题它管的是流式传输、工具调用、生成式 UI 的渲染管线。Jev 解决的是“模型输出的结构化决策怎么保证类型安全”的问题它管的是输出这一端的约束。在实际项目里你可以把 Jev 当成 AI SDK 的一个“输出处理器”。AI SDK 负责和模型通信Jev 负责把模型的决策结果转成类型安全的对象然后交给前端渲染。两者串起来用生成式 UI 的可靠性会明显提升。Jev 官方也提供了和 AI SDK 集成的适配层后面实操部分我会给出具体的接入代码。3. 扒开源码看设计Jev 的架构为什么这么搭3.1 整体分层TypeSpec、编译器、推理引擎三件套Jev 的源码结构很清晰核心就三块TypeSpec 层、编译器层、推理引擎层。我第一次读的时候觉得这个分层有点“过度设计”一个实验性项目搞这么正式干嘛。但读完之后我改主意了这个分层恰恰是它能做到类型安全的关键。TypeSpec 层是用户接触的接口。你用一套类似 TypeScript 类型定义的语法描述你想要的输出结构。比如你要一个排序配置就写一个包含field和direction两个字段的 specdirection限定为asc | desc。这一层做的事情是把用户的意图形式化。编译器层是把 TypeSpec 翻译成模型能理解的约束。这一步是整个项目里技术含量最高的地方。它需要把类型定义转换成一种中间表示这种中间表示既要能被模型理解又要保留足够的约束信息让模型在推理时不能越界。我看了下它的实现核心是把类型展开成一棵决策树每个叶子节点对应一个合法的输出值模型在推理时实际上是在这棵树上做路径选择。推理引擎层是真正跑模型的地方。它接收编译后的约束和用户输入输出符合约束的决策结果。这一层封装了模型的加载、推理、解码逻辑对外暴露一个干净的evaluate接口。这个分层的价值在于每一层都可以独立替换。你想换一个模型只动推理引擎层就行你想支持一种新的类型语法只动 TypeSpec 层就行。对于一个小项目来说这种可替换性意味着它有机会长成一个生态而不是一个死掉的小工具。3.2 experimental_evaluate那个被热词带火的接口热词里有个experimental_evaluate这是 Jev 暴露给外部调用的核心接口。名字里带experimental前缀说明作者自己也知道这东西还不稳定但它的设计思路值得说。这个接口的签名大概是这样的我按源码里的类型定义还原async function experimental_evaluateT(options: { spec: TypeSpecT; input: string; context?: Recordstring, unknown; model?: string; }): PromiseEvaluationResultT它接收一个 TypeSpec、一段输入文本、可选的上下文返回一个EvaluationResult。这个结果对象里除了决策值本身还带了置信度、推理路径、耗时等元信息。置信度这个字段很关键它让你可以在决策不确定的时候做降级处理而不是盲目相信模型的输出。我实测下来这个接口在本地部署的情况下单次调用的延迟大概在 200 到 500 毫秒之间取决于输入长度和 spec 复杂度。对于生成式 UI 这种场景这个延迟是可以接受的但如果要做实时交互可能还需要进一步优化。注意experimental_evaluate这个名字里的experimental不是摆设。我在测试过程中遇到过 spec 复杂度超过一定阈值之后编译时间急剧上升的情况。如果你的 spec 嵌套层级超过四层建议拆成多个简单的 spec 分步调用不要硬塞进一个。3.3 为什么不用现成的 JSON Schema 而自己造 TypeSpec这是我在读源码时问自己的第一个问题。JSON Schema 已经是事实标准了为什么 Jev 要自己造一套 TypeSpec答案在于表达能力和编译目标的差异。JSON Schema 是为“校验”设计的它的目标是判断一个已有的 JSON 是否合法。而 TypeSpec 是为“约束生成”设计的它的目标是让模型在生成时就不越界。这两个目标对 schema 的要求不一样。举个例子。JSON Schema 里表达“这个字段只能是 asc 或 desc”用的是enum。但 enum 在生成场景下有个问题它只告诉模型“合法值有这些”没告诉模型“这些值分别对应什么语义”。模型知道asc是合法的但它不一定理解asc意味着升序。TypeSpec 在这方面做了增强它允许你给每个枚举值附加语义描述这些描述会被编译进模型的约束里帮助模型做更准确的决策。另一个差异是嵌套结构的处理。JSON Schema 的嵌套校验是递归的每一层都要单独校验。TypeSpec 在编译时会把整个嵌套结构展开成一棵完整的决策树模型一次性在整棵树上做选择避免了递归校验带来的性能开销。这个设计在 spec 层级较深的时候优势很明显。当然自己造一套 DSL 的代价是生态兼容性差。你不能直接把现有的 JSON Schema 拿过来用得手动翻译。Jev 官方提供了一些转换工具但覆盖度有限。如果你手上已经有大量 JSON Schema迁移成本是要考虑的。3.4 模型选型为什么用小型专用模型而不是通用大模型Jev 用的是自己训的小型专用模型不是 GPT 那种通用大模型。这个选择背后有很实际的考量。第一是延迟。通用大模型的调用延迟通常在秒级而 Jev 的目标场景是生成式 UI用户等不起。小型专用模型可以在本地跑延迟压到几百毫秒体验上了一个台阶。第二是成本。生成式 UI 的调用频率很高每次交互都要调一次模型。用通用大模型的话token 成本会很快失控。小型专用模型本地部署之后边际成本接近于零。第三是可控性。通用大模型的输出空间太大即使加了约束也容易跑偏。专用模型在训练时就被限制在了类型约束的空间里输出的稳定性高得多。但这个选择的代价是泛化能力。专用模型只在它训练过的类型空间里表现好遇到没见过的 spec 结构表现会明显下降。我测试的时候用了一个比较复杂的嵌套 spec模型的决策准确率从简单 spec 的 95% 以上掉到了 70% 左右。所以 Jev 目前更适合 spec 结构相对固定的场景不适合 spec 频繁变化的场景。4. 本地部署与接入实操从零跑通 Jev4.1 环境准备与依赖安装先说环境。Jev 的本地部署对硬件的要求不算高我用一台 16G 内存、带一块中端显卡的机器就跑通了。如果没有显卡纯 CPU 也能跑就是延迟会高一些大概慢三到五倍。依赖方面核心是 Node.js 环境版本建议 18 以上。我一开始用 16 跑遇到了几个 ESM 相关的报错升到 18 之后就好了。另外需要装 pnpmJev 的 monorepo 是用 pnpm workspace 管理的用 npm 装依赖会有 peer dependency 冲突。# 克隆仓库 git clone https://github.com/jev-ai/jev.git cd jev # 安装依赖必须用 pnpm pnpm install # 构建核心包 pnpm build构建过程大概需要两三分钟主要时间花在编译推理引擎的 native 模块上。如果你看到一堆 warning不用慌只要最后没有 error 就行。提示构建过程中如果卡在node-gyp相关的步骤大概率是缺少 C 编译工具链。Linux 上装build-essentialmacOS 上装 Xcode Command Line ToolsWindows 上装 Visual Studio Build Tools 里的 C 组件。4.2 第一个 TypeSpec从最简单的枚举决策开始环境好了之后先跑一个最简单的例子建立信心。我建议从枚举决策开始因为这是 Jev 最擅长的场景也最容易看出效果。假设我们要做一个“根据用户输入判断操作类型”的决策操作类型有增删改查四种。TypeSpec 写出来大概是这样import { defineSpec, enumOf } from jev-ai/core; const operationSpec defineSpec({ name: operation, type: enumOf([create, read, update, delete], { descriptions: { create: 用户想要新增一条记录, read: 用户想要查询或查看记录, update: 用户想要修改已有记录, delete: 用户想要删除记录, }, }), });注意descriptions这个字段。它不是必须的但强烈建议加上。我实测下来加上语义描述之后模型在边界情况下的决策准确率能提升十到十五个百分点。比如“把这条记录改一下”这种输入没有描述的时候模型有时候会判成read有了描述之后基本都能判成update。然后调用experimental_evaluateimport { experimental_evaluate } from jev-ai/core; const result await experimental_evaluate({ spec: operationSpec, input: 帮我把用户表里 id 为 42 的那条记录的名字改成张三, }); console.log(result.value); // update console.log(result.confidence); // 0.93 console.log(result.latencyMs); // 287第一次跑通看到update这个输出的时候说实话有点小激动。因为整个过程没有写任何 prompt没有做任何字符串匹配模型直接给出了结构化的决策结果而且带置信度。4.3 接入 Vercel AI SDK生成式 UI 的完整链路单跑一个 evaluate 只是玩具真正有价值的是把它接进生成式 UI 的链路里。下面给出一个和 Vercel AI SDK 集成的完整示例。思路是这样的用户在前端输入自然语言AI SDK 把输入传给后端后端先用 Jev 做一次结构化决策把决策结果作为上下文传给大模型大模型基于这个结构化上下文生成 UI 配置。这样大模型拿到的不是模糊的自然语言而是明确的决策结果生成出来的 UI 配置准确率高很多。// app/api/generate-ui/route.ts import { experimental_evaluate } from jev-ai/core; import { streamText } from ai; import { openai } from ai-sdk/openai; import { uiIntentSpec } from /lib/specs; export async function POST(req: Request) { const { prompt } await req.json(); // 第一步用 Jev 做结构化决策 const intent await experimental_evaluate({ spec: uiIntentSpec, input: prompt, }); // 第二步把决策结果作为上下文传给大模型 const result await streamText({ model: openai(gpt-4o-mini), system: 你是一个 UI 生成助手。用户意图已经被解析为结构化数据${JSON.stringify(intent.value)}。请基于这个结构化意图生成 UI 配置。, prompt, }); return result.toDataStreamResponse(); }这个链路的好处是大模型不需要再去猜用户的意图意图已经被 Jev 明确地解析出来了。大模型只需要做它擅长的事情——把结构化意图翻译成 UI 配置。分工明确之后整条链路的可靠性提升很明显。我实测对比过不加 Jev 直接让大模型生成 UI 配置十次里有三次左右会跑偏加上 Jev 之后十次里跑偏的次数降到零到一次。这个提升在 demo 里可能看不出来但在真实产品里就是可用和不可用的区别。4.4 参数调优置信度阈值和降级策略怎么定Jev 返回的confidence字段是整条链路里最需要调优的参数。它决定了你在什么情况下相信模型的决策什么情况下走降级逻辑。我的经验是置信度阈值不要设得太高。一开始我把阈值设在 0.9结果发现大量本来正确的决策被降级了用户体验反而变差。后来降到 0.75误判率没有明显上升但降级率降了一半多。具体的阈值要根据你的场景来定。如果是低风险场景比如 UI 排序方向阈值可以设低一点0.7 就行错了用户手动改一下也没什么。如果是高风险场景比如涉及数据删除的操作阈值要设高0.95 以上宁可降级也不要误判。降级策略也有讲究。最简单的降级是返回一个默认值但这样用户体验很生硬。更好的做法是返回一个“需要用户确认”的状态让用户来做最终决策。比如if (result.confidence 0.75) { return { status: needs_confirmation, candidates: result.alternatives, // Jev 会返回备选决策 message: 我不太确定你的意图你是想...还是想..., }; }alternatives这个字段是 Jev 在置信度不高的时候会返回的备选决策列表按可能性排序。用它来做确认交互比直接返回默认值自然得多。5. 踩坑实录那些文档里不会写的问题5.1 类型安全不等于语义正确这是我踩的最大的一个坑也是我觉得所有用 Jev 的人都必须提前知道的。Jev 保证的是类型层面的合法。你定义direction只能是asc或desc它确实只会返回这两个值之一。但它不保证在该返回asc的时候返回了asc。我测试的时候构造了一个输入“从大到小排列”模型返回了asc类型完全合法但语义完全反了。这个问题的根源在于 RLCD 的训练目标。类型检查器只能判断“这个值是否在合法集合里”不能判断“这个值是否语义正确”。所以模型学到的是“怎么输出合法的值”而不是“怎么输出正确的值”。应对方法有两个。一是在 TypeSpec 的descriptions里把语义描述写清楚让模型有更多信息做判断。二是在关键决策上加一层语义校验比如对于排序方向这种容易搞反的决策用规则做二次确认。类型安全是底线不是天花板这一点心里要有数。5.2 spec 嵌套过深导致的编译超时前面提过一嘴这里展开说。Jev 的编译器在处理嵌套 spec 的时候会把整个结构展开成一棵决策树。这棵树的大小随嵌套层级指数增长。我测试的时候一个四层嵌套的 spec 编译花了 1.2 秒五层嵌套直接飙到 8 秒以上六层的时候编译器直接 OOM 了。这个问题的解法是拆分 spec。不要试图用一个巨大的 spec 描述整个 UI而是拆成多个小的 spec分步决策。比如先决策“这是什么类型的组件”再决策“这个组件的配置是什么”最后决策“这个组件的数据源是什么”。每一步的 spec 都很浅编译快模型决策也准。拆分的另一个好处是每一步的决策结果都可以单独校验和降级。如果第二步决策置信度低你可以只降级第二步第一步的结果还能用。整体鲁棒性比一个大 spec 好得多。5.3 本地部署的显存占用与并发瓶颈本地部署 Jev 的时候显存占用是我没想到的一个坑。官方文档说 4G 显存就能跑但我实测下来4G 只能跑最简单的 spec稍微复杂一点的 spec 就会 OOM。稳定运行的话建议 8G 以上显存。并发方面Jev 的推理引擎默认是单线程的同时来两个请求就会排队。如果你的场景有并发需求需要起多个实例做负载均衡。我试过起四个实例用 nginx 做轮询QPS 大概能到 20 左右对于中小规模的生成式 UI 应用够用了。注意多实例部署的时候每个实例都要独立加载模型显存占用是线性增长的。四个实例就是四份显存。如果显存不够可以考虑用模型量化版本代价是准确率会掉几个百分点。5.4 常见问题速查表问题现象可能原因排查方向解决方法编译超时或 OOMspec 嵌套过深检查 spec 层级拆分成多个浅 spec决策准确率低缺少语义描述检查 descriptions补充枚举值和字段的语义描述置信度普遍偏低输入太模糊检查输入文本在前置步骤做输入规范化本地部署 OOM显存不足监控显存占用升级显存或用量化模型并发请求排队单线程推理检查实例数多实例加负载均衡输出类型对但语义反类型安全不等于语义正确检查关键决策加规则做二次确认5.5 几个我踩过但别人很少提的细节第一个细节是输入文本的长度。Jev 对输入长度很敏感超过 200 个字符之后决策准确率会明显下降。我的做法是在调用 Jev 之前先用一个轻量的文本摘要步骤把输入压缩到 100 字以内。这个摘要可以用规则做也可以用一个小模型做成本很低但效果很明显。第二个细节是spec 的命名。TypeSpec 的name字段不只是个标识它会被编译进模型的约束里影响模型的决策。我一开始随便起名叫spec1、spec2结果模型经常搞混。后来改成有语义的名字比如sortDirectionSpec、operationTypeSpec准确率提升了不少。名字本身就是一种 prompt这个道理在 Jev 里同样成立。第三个细节是冷启动。Jev 的推理引擎第一次调用的时候需要加载模型耗时大概三到五秒。如果你的应用是 serverless 部署每次冷启动都要等这么久体验很差。解法是在服务启动的时候做一次预热调用把模型加载到内存里。预热调用可以用一个最简单的 spec成本可以忽略。6. 这东西值不值得跟我的判断和后续玩法6.1 当前阶段的适用边界Jev 现在还是个实验性项目experimental_evaluate这个名字里的experimental是认真的。它适合的场景很明确spec 结构相对固定、对延迟敏感、对类型安全要求高的结构化决策场景。生成式 UI 是它最典型的应用场景但不止于此。我想到的还有几个表单的智能填充根据用户输入的自然语言决策每个表单字段该填什么值工作流引擎的条件分支根据上下文决策走哪条分支数据管道的路由决策根据数据特征决策走哪个处理管道。这些场景的共同点是决策空间是有限的、可枚举的而且决策结果需要被程序直接消费。Jev 在这个边界内表现很好出了这个边界就不太行。6.2 和大模型配合的几种玩法Jev 不是用来取代大模型的它是用来给大模型“打辅助”的。我试过几种配合方式效果最好的是前置决策 后置生成的模式。前置决策就是用 Jev 把用户的模糊输入解析成结构化意图然后把结构化意图传给大模型做生成。这个模式前面已经讲过效果很稳。还有一种玩法是后置校验。让大模型自由生成然后用 Jev 对生成结果做结构化校验。如果校验不通过把 Jev 的校验结果作为反馈传给大模型让它重新生成。这个模式比纯 prompt 重试要高效因为 Jev 的反馈是结构化的大模型更容易理解哪里错了。第三种玩法是双模型投票。用 Jev 做一次决策用大模型做一次决策两者一致就采信不一致就走人工确认。这个模式适合高风险场景成本高但可靠性也高。6.3 如果你想深入源码从哪读起如果你读完这篇文章想自己去扒源码我建议的阅读顺序是这样的先读packages/core/src/spec目录这里是 TypeSpec 的定义和解析逻辑是整个项目的地基。然后读packages/compiler/src目录这里是编译器把 TypeSpec 翻译成模型约束技术含量最高。最后读packages/engine/src目录这里是推理引擎封装了模型加载和解码逻辑。读的时候重点关注编译器那一层。Jev 最核心的创新就在编译器怎么把类型定义转换成模型能理解的约束这个转换过程的设计思路比模型本身更有借鉴价值。即使你以后不用 Jev这套“把类型系统编译成生成约束”的思路也可以迁移到其他项目里。6.4 最后分享几个实操小技巧第一个技巧用 Jev 做决策之前先做一次输入规范化。把用户输入里的口语化表达、错别字、多余空格清理掉决策准确率能提升五到八个百分点。这个规范化步骤可以用简单的规则做不需要模型。第二个技巧spec 的 descriptions 要写“用户意图”而不是“字段含义”。比如create的描述写“用户想要新增一条记录”而不是“创建操作”。前者是站在用户视角后者是站在系统视角。模型对用户视角的描述理解得更好。第三个技巧置信度低于阈值的时候不要直接降级到默认值而是返回备选项让用户确认。这个交互模式比静默降级自然得多用户接受度也高。Jev 返回的alternatives字段就是为这个场景设计的别浪费了。第四个技巧定期用真实数据回测。Jev 的模型是固定的但你的用户输入分布会变。我建议每周拿一批真实输入跑一次回测看看准确率有没有下降。如果下降了说明输入分布漂移了需要调整 spec 或者补充 descriptions。这个项目后续还能怎么扩展我个人的想法是可以把它和本地的向量检索结合起来用检索到的历史决策作为 few-shot 示例进一步提升准确率。另外spec 的版本管理也是个值得做的方向不同版本的 spec 对应不同的模型约束可以做到灰度发布和快速回滚。这些我都还在试有结果了再分享。
