当前位置: 首页 > news >正文

为什么你的代码总在返工?因为这120字需求里,藏着5个连产品都没想清楚的坑

一个怎么都做不对的优惠券规则

事情最早看起来很简单。

产品在群里丢了一段话:“会员等级越高优惠越大。金牌会员买东西可以叠加优惠券,银牌不行。下单时如果有可用优惠券就自动选面额最大的那张。但是金牌会员大促期间不能和优惠券叠加,怕亏本。下单金额超过 500 自动用券,不超过的话看用户心情。另外老用户回归也送一张券,回归券能不能叠加再说。前端要有一个券的角标显示。”

不到 120 字。我先后实现了三版。

第一版:所有会员都允许叠加券,上线后金牌会员大促期间叠加,被财务驳回。第二版:禁用大促期间所有叠加,结果普通日金牌会员也无法叠加,被产品打回。第三版:500 阈值写死在后端,产品又说这个阈值要按活动动态调整。

这类需求最麻烦的地方不是技术难,而是每次实现都像已经抓到了规则。过两天,它在另一个活动、另一个会员等级下再次翻车,你才发现之前理解的只是需求的某一面。

我不想再做第四次“凭感觉实现,等产品打回”,于是准备换一种方式:把需求原文、涉及的表结构、现有接口和已经返工的记录全部交给模型,让它先把需求里能确定的事实、互相矛盾的地方和必须由产品确认的问题拆清楚,再讨论怎么写。

这就是“需求炼金炉”的起点。

先认识蓝耘元生代 MaaS

我这次使用的是蓝耘元生代 MaaS 平台。MaaS 是 Model as a Service,也就是把大模型能力封装成可以调用的服务。开发者不需要在自己的电脑上准备 GPU、下载模型权重或维护推理环境,应用通过 API 提交请求,再接收模型返回的结果。

对于这个项目,我需要的不是一个只会把需求复述一遍的聊天页面,而是一个可以嵌入程序、按照固定格式返回澄清契约单的模型接口。需求炼金炉要连续完成事实提取、矛盾标记、开放问题列举、任务拆解和接口契约起草,输出还要能够被前端拆成不同卡片,因此使用 MaaS API 比手动复制对话更合适。

注册并进入 MaaS 平台

注册过程并不复杂:进入蓝耘元生代官网,完成账号注册和登录后,从顶部导航进入“MaaS平台”。控制台左侧可以看到模型广场、文本模型、智能路由、批量推理、用量统计等入口。

我进入“模型广场”,选择文本模型,然后找到 GLM-5.1。模型卡片给出了模型类型、上下文长度、调用名称和 API 示例入口。这几项信息后面都会直接用到。

为什么选 GLM-5.1

需求澄清并不是“把需求翻译成开发能看懂的话”这么简单。为了判断一段模糊需求里到底藏了多少歧义,模型至少需要同时看到以下材料:

  • 需求原文(产品口述或 IM 转录);
  • 涉及的表结构、现有接口和约束;
  • 之前返工过几次、每次卡在哪;
  • 项目对叠加、阈值和回归券的处理边界。

这些内容放在一起,比一段需求长得多。蓝耘模型广场给 GLM-5.1 标注了 198k 上下文,同时将它定位为面向智能体工程、长程任务和代码工作的模型。对“需求炼金”这种需要同时阅读需求文档、表结构和历史返工记录、再分阶段输出结构化契约单的项目来说,这些特性与任务比较匹配。

这里需要说明:本文不测试延迟、吞吐量和价格,也不据此给出性能排名。项目只验证一件事——GLM-5.1 能否根据完整需求材料形成可检查的澄清契约单,并帮助我把返工挡在编码之前。

接入蓝耘 GLM-5.1

在 GLM-5.1 模型卡片中点击“API示例”,可以查看当前平台提供的请求格式。项目没有把密钥写进源码,而是通过环境变量读取:

LANYUN_API_KEY=请填写自己的API密钥 LANYUN_BASE_URL=https://maas-api.lanyun.net/v1 LANYUN_MODEL=/maas/zhipuai/GLM-5.1

项目本身使用 Node.js,因此实际调用代码也直接使用内置fetch

constendpoint=`${process.env.LANYUN_BASE_URL}/chat/completions`;constresponse=awaitfetch(endpoint,{method:"POST",headers:{Authorization:`Bearer${process.env.LANYUN_API_KEY}`,"Content-Type":"application/json",},body:JSON.stringify({model:process.env.LANYUN_MODEL,messages:[{role:"system",content:"你是蓝耘元生代 GLM-5.1 需求分析工程师。必须依据用户提供的需求原文与上下文回答,不得编造未给出的业务规则。",},{role:"user",content:alchemistPrompt},],temperature:0.1,stream:false,}),signal:AbortSignal.timeout(120_000),});constrawBody=awaitresponse.text();if(!response.ok){thrownewError(`模型接口返回${response.status}${rawBody.slice(0,500)}`);}letpayload;try{payload=JSON.parse(rawBody);}catch{thrownewError("模型接口未返回合法JSON响应");}constcontent=payload?.choices?.[0]?.message?.content;if(!content){thrownewError("模型响应中缺少 choices[0].message.content");}

除了请求本身,项目还在两个地方做了兜底。一是接口地址:基础地址末尾有没有/v1、有没有已经带/chat/completions,都交给resolveChatCompletionsUrl统一处理,避免拼出…/v1/v1/chat/completions这种重复路径。二是响应体先按文本读出再解析,这样在接口返回非 JSON 错误(比如网关 502 的 HTML 页面)时,能把前 500 字符带进报错,而不是直接抛一个看不懂的SyntaxError。契约单解析阶段还会校验 7 个必填字段是否齐全,缺字段就返回"澄清契约单缺少字段:xxx",避免一段残缺输出被当成完整契约显示到页面上。

真实 API Key 只保存在本地环境变量中。截图、代码仓库和文章都不应该出现完整密钥。

我没有让模型一上来就写代码

最初版本的提示词很直接:“根据下面的需求实现优惠券叠加。”结果通常也很直接:一段看起来能跑的服务端代码,阈值写死、回归券默认可叠加、大促判断靠一个布尔值。问题是,这些正是我之前已经做过且翻车的事。

后来我把流程拆成四步:

  1. 只从需求原文与上下文中提取已经能确定的事实;
  2. 标记互相矛盾、含糊或多义之处;
  3. 列出必须由产品确认的开放问题;
  4. 在事实足够的前提下再起草接口契约和验收标准。

核心系统提示词如下:

你是一名需求分析工程师。 请把下面材料视为一份待澄清的需求案卷,不要直接假定产品意图,也不要只盯着最后一句结论。 工作顺序: 1. 只从需求原文与上下文中提取已经能确定的事实; 2. 标记互相矛盾、含糊或多义之处; 3. 列出必须由产品确认的开放问题; 4. 在事实足够的前提下拆解任务,标注依赖; 5. 起草接口契约草案,字段类型与约束尽量明确; 6. 给出可验证的验收标准; 7. 提示实现风险。 证据不足时必须明确写出“待确认”,不要补造业务规则。

这个限制很重要。模型很擅长给出“看起来完整”的规则,但需求炼金首先要做的是暴露哪里没说清楚。如果它无法把原文里的矛盾单独列出来,再漂亮的契约单也不值得直接采用。

案发现场:120 字需求里的三处矛盾

为了稳定复现问题,我把产品那段 120 字的需求原样保留,再附上涉及的表结构和现有接口:

members(id, name, level ENUM('bronze','silver','gold'), last_active_at) coupons(id, code, face_value, min_amount, scope, expires_at) orders(id, member_id, amount, coupon_id, created_at) POST /api/orders 创建订单,当前不处理优惠券 GET /api/members/:id/available-coupons 返回该会员可用券列表

只看需求原文,这段话读起来顺:等级越高优惠越大、金牌可叠加、自动选最大面额。但把原文和表结构放在一起,矛盾就藏不住了。

模型需要判断的核心问题是:这段需求里究竟是规则写漏了,还是多处规则互相打架?

我把以下材料一起提交给需求炼金炉:

  • 需求原文;
  • 涉及的表结构、现有接口和约束;
  • 大促活动的控制方式;
  • 前三次返工分别卡在哪。

GLM-5.1 给出的“澄清契约单”

实际调用后,GLM-5.1 先给出了一个很明确的判断:问题不在某一条规则写没写,而在原文里多处规则互相矛盾,且至少 5 个关键点完全没定义。

已确认事实: 1. 会员分为 bronze、silver、gold 三个等级,level 字段存于 members 表。 2. 默认仅金牌会员允许优惠券叠加,银牌与铜牌不可叠加。 3. 下单存在可用优惠券时自动选取面额最大者。 4. 大促期间由 ops_activities.is_active 控制,活动订单走独立计价逻辑。 5. 现有 POST /api/orders 不处理优惠券,GET /api/members/:id/available-coupons 已返回可用券列表。 6. 回归券面向流失后回归的老用户发放。 矛盾 / 多义点: 1. 金牌会员大促能否叠加——原文前半句说金牌可叠加, 后半句又说大促不能叠加,未说明该限制是否仅作用于金牌。 2. 500 阈值的适用范围——“超过 500 自动用券,不超过看用户心情” 与“自动选面额最大”冲突:不超过 500 时到底是自动还是手动未定义。 3. 回归券的叠加规则——原文以“再说”收尾, 回归券能否与会员等级券、大促活动叠加完全未定义。 开放问题(需产品确认): 1. 大促期间是否对所有等级统一禁用叠加,还是仅限制金牌? 2. 500 阈值是否随活动动态调整?由哪个字段或配置决定? 3. 不超过 500 时,前端“看用户心情”对应哪种交互:手动勾选还是默认不选? 4. 回归券是否计入 available-coupons 返回?它与会员等级券是否互斥? 5. “面额最大”是否需要同时满足 min_amount 约束?多张券同时满足时是否需做最优组合而非单张最大? 任务拆解: - 叠加规则引擎:输入会员等级、活动状态、券类型,输出是否允许叠加及可叠加券集合 - 大促状态读取:读取 ops_activities.is_active,注入到下单计价链路(依赖叠加规则引擎) - 券选取策略:在可用券中按 min_amount 过滤后选面额最大者,支持手动覆盖(依赖叠加规则引擎) - 回归券发放与校验:入库与互斥规则,待产品确认后实现 - 前端券角标:展示可用券数量与最大面额,区分自动/手动(依赖券选取策略) 接口契约草案: POST /api/orders 请求体:{ member_id: string, items: [{sku, qty}], coupon_id?: string, use_auto_coupon?: boolean } 响应:{ order_id, amount, applied_coupon_id, applied_coupon_face_value, breakdown } 约束: - 服务端按叠加规则引擎计算最终可用券; - use_auto_coupon=false 且未传 coupon_id 视为不使用优惠券; - 大促期间叠加规则引擎返回 is_stacking_blocked=true 时拒绝 coupon_id; - 当 amount < 动态阈值时,忽略自动选券,仅在传入 coupon_id 时校验并应用。 验收标准: 1. 金牌会员非大促期间下单,存在多张满足 min_amount 的券时,服务端自动应用面额最大者。 2. 大促期间下单,叠加规则引擎对所有等级返回 is_stacking_blocked=true,传入 coupon_id 应返回 422。 3. 银牌会员下单传入 coupon_id 与会员等级券叠加时,返回 422 并提示不可叠加。 4. 回归券规则在产品确认前不进入计价链路,available-coupons 暂不返回回归券。 5. 前端券角标展示可用券数量与最大面额,不满足 min_amount 的券不计入。 实现风险: 1. 500 阈值写死会再次被产品要求动态化,应改为读取配置而非常量。 2. “面额最大”不等于“最优”,多券组合可能让用户实际优惠更高,需产品确认是否允许组合。 3. 回归券与会员等级券若互斥规则未定,上线后极易引发资损,建议在契约确认前不实现回归券叠加。 4. 大促与日常逻辑分叉,若共用同一计价入口需注意回归测试覆盖两条路径。

模型给出的契约草案把“是否叠加”收敛成一个叠加规则引擎的输入输出,500 阈值改为读取配置而非常量。它已经抓到了“规则不该写死”这个根因。

但我把它拿给产品确认时,又发现了一个新问题:契约单里把回归券相关任务也拆了出来,可回归券的互斥规则原文根本没定义。如果照着契约单直接实现回归券,等于模型替产品做了一个未被确认的决定。

这次模型找对了矛盾,契约草案却没法覆盖所有分支。最后我把回归券相关任务标记为“待产品确认前不实现”,available-coupons 接口暂不返回回归券。这样既保留了模型整理出的事实和开放问题,又避免把未定义的规则写进代码。

// 契约单驱动下的任务分流:回归券在产品确认前不进入计价链路constreport=parseAlchemistReport(content);consttasks=report.task_breakdown;// 1. 把依赖“回归券发放与校验”的任务单独拎出来,暂不进入开发列constblocked=tasks.filter(task=>task.depends_on?.includes("回归券发放与校验"));constready=tasks.filter(task=>!blocked.includes(task));// 2. 把开放问题回执给产品,逐条确认后才解锁 blocked 任务constopenQuestions=report.open_questions;// sendToProduct(openQuestions) -> 等回执 -> unblock(blocked)// 3. 在此期间 available-coupons 不返回回归券,避免前端误用constavailableCouponFilter=(coupon)=>coupon.scope!=="returning-user";

契约单不能代替产品确认

模型暴露矛盾、起草契约,只完成了一半工作。真正决定这份契约单是否可信的,是产品对开放问题的逐条确认。

我最后保留了四个最关键的验证场景。在app目录执行npm test即可跑全部 12 项测试,用的是 Node.js 内置测试框架,不需要额外装依赖:

cd app npm test

三种模拟对应三种开发策略,差别只在“歧义点是在编码前解决,还是拖到联调阶段爆炸”:

// 盲开发:5 个歧义点一个不澄清,每个都变成一次返工exportasyncfunctionsimulateBlindImplementation(ambiguityCount=5){// 编码 -> 联调暴露第一个歧义 -> 返工 -> 暴露下一个 -> … -> 带病上线return{reworkCycles:ambiguityCount,// 5 次返工defectsShipped:1,// 带病上线 1 个deliveredOnTime:false,};}// 澄清后:编码前把歧义点逐条和产品确认,实现阶段零返工exportasyncfunctionsimulateClarifiedImplementation(ambiguityCount=5){// 澄清(逐条 resolved) -> 编码 -> 联调通过 -> 上线return{ambiguitiesResolvedBeforeCoding:ambiguityCount,reworkCycles:0,defectsShipped:0,deliveredOnTime:true,};}

模拟没有只检查“函数没有抛错”,而是直接比较返工轮次、带病上线和歧义点解决数。例如澄清后开发的核心断言是:

constresult=awaitsimulateClarifiedImplementation(5);assert.equal(result.ambiguityCount,5);assert.equal(result.ambiguitiesResolvedBeforeCoding,5);assert.equal(result.reworkCycles,0);assert.equal(result.defectsShipped,0);
模拟场景实际结果结论
盲开发:5 个歧义点不澄清直接编码返工 5 次,带病上线 1 个成功复现需求返工
只澄清一半:5 个歧义点确认 2 个返工 3 次,带病上线 1 个剩余歧义点照样返工
全部澄清后开发:5 个歧义点逐条确认返工 0 次,带病上线 0 个编码阶段零返工
契约单驱动:回归券任务暂缓回归券不进入计价链路未定义规则不写进代码

本地完整运行流程:配好环境变量后在app目录执行npm start,浏览器访问http://127.0.0.1:4174。页面左栏是需求案卷,可切换"需求原文 / 已有上下文 / 返工记录"三个标签并直接编辑;中栏点"盲开发模拟"看 5 个歧义点全部变成返工的时间线,点"澄清后模拟"看返工归零的对比;右栏点"送入 GLM-5.1 炼丹炉"会实时调用模型生成契约单,点"载入实测报告"则直接展示已保存的真实结果。未配置密钥时模拟和载入报告均可正常使用,只有实时调用才需要 API Key。

这一步也给“需求炼金炉”划了一条边界:它负责提取事实、暴露矛盾和起草契约,但不能替产品回答开放问题。产品没确认之前,契约单只是一份分析意见。

用完整案卷代替一句话需求

以前接到需求,我经常只看产品最后发的那段话,再凭经验补全细节。模型当然可以给出规则,但它看到的只是需求最表面的几句话。

这次把材料按“案卷”组织后,体验发生了变化。真正有用的信息往往不在最后一句结论里,而在更早的位置:哪些字段已经存在、哪些接口已经定义、之前返工过几次、哪些规则只在某个活动下才成立。

蓝耘 GLM-5.1 在这个项目里做的事比起草一段接口契约多。模型广场给了清晰的调用名称和 API 示例,MaaS 接口让我能把事实提取、矛盾标记、开放问题列举和契约起草串进同一个应用,模型输出也就从聊天窗口走进了需求工作流。

当然,一次案例不能证明它能处理所有需求。会员、优惠券和叠加只是业务规则中的一类。涉及计费、权限、风控和跨部门流程时,需求材料会更复杂,模型给出的契约也更需要人工和产品核对。

最后:这次挡住的不是某一次返工

这段需求前三次实现都围绕某一层规则展开:全部允许叠加、全部禁用叠加、阈值写死。它们没有完全错,只是没有碰到真正的盲区。返工只在规则互相打架时发生,根因自然也藏在规则的交叉处。

“需求炼金炉”没有替我承担最终决策。它做的是另一件事:逼我把一段口语需求整理成完整案卷,再按照事实、矛盾和开放问题去看需求。GLM-5.1 给出的契约最终是否进入编码,仍然由产品对开放问题的确认决定。

这套流程比“把需求贴进聊天框,复制第一段代码”慢一点,却更像真正的需求澄清。

模型找出了 3 处矛盾和 5 个开放问题,产品确认又替它补上了回归券的未定义分支。比起直接生成一段“标准实现”,我更放心这种分工:GLM-5.1 把理解偏差缩小、把候选契约摆出来,能不能进项目则由产品确认说了算。

http://www.jsqmd.com/news/1304516/

相关文章:

  • 苏州服务不错的钢结构车间制造厂:2026年升级 - 品牌推广大师
  • 2026年DIN 6926非金属嵌件六角法兰锁紧螺母厂家有哪些?嘉兴莱翔等企业特点分析 - 品牌排行榜
  • 【2027最新】基于SpringBoot+Vue的购物推荐网站管理系统源码+MyBatis+MySQL
  • 彻底解决Python相对导入错误:从原理到实践的完整指南
  • 2026年8月防水维修/防水工程处理公司哪家权威_广州执盾建筑防水工程有限公司 - 品牌宣传支持者
  • 花朵授粉算法(FPA)的改进与工程实践
  • 寒武纪股权激励计划解析:AI芯片人才争夺战的新策略
  • Windows C++程序通过ADO连接Oracle数据库的完整实践指南
  • RS-485与Modbus协议:从物理层到应用层的工业通讯核心解析
  • 微信聊天记录AI分析:原理、应用与隐私安全实践指南
  • AI扁平化设计合规性警报:GDPR/无障碍WCAG 2.2双标验证失败率高达67%(含自动检测脚本)
  • 2026年8月_321不锈钢板/不锈钢板行业厂家推荐_无锡苏杭不锈钢有限公司 - 品牌宣传支持者
  • 2026年哈尔滨饲料厂家实力之选:浓缩料/全价料/颗粒料/精补料/预混料养殖增效综合评估 - 优企名品
  • HP Chromebox G1刷入Coreboot开源固件全攻略:解锁硬件白名单与虚拟化
  • 【LangChain 核心指南】从自然语言到结构化对象:Output Parser 与 Pydantic 实战解析
  • FPGA高速收发器GTX核心原理、Vivado配置与上板调试全攻略
  • Java编译器优化:JIT与字节码转换实战指南
  • CCS铁魄EVA二号机二式深度评测:高端合金模型选购与养护全指南
  • 仅限头部AI基建团队内部流通:AI网络请求生命周期管理矩阵(含请求溯源ID、意图标签、可信度置信区间三元组标准)
  • 从幻觉到可信:构建企业级AI智能体的工程化实践与多层防御体系
  • 2026这6款王炸降AI率网站大起底,一键让AIGC率断崖式下跌!
  • 2026优选指南:定兴全屋定制品牌怎么选?本地化服务与落地能力拆解 - 装修教育财税推荐2026
  • 永磁同步电机损耗建模与优化:从理论到PLECS仿真与台架测试
  • 2026年口碑好的工业洗地机有哪些? - 品牌排行榜
  • AI学习进度跟踪不是打卡!20年一线教学验证的“认知负荷-技能增量”双轴评估模型
  • GPT-6前瞻:多模态融合、超长上下文与智能体架构的技术演进与实战准备
  • 终极指南:5分钟快速掌握COMET翻译质量评估工具
  • PHP继承实战:从Vehicle类到Car/Bicycle子类的educoder题目解析
  • Python自动化WiFi安全测试:pywifi库实战与WPA2-PSK原理剖析
  • ComfyUI-Inpaint-CropAndStitch:终极智能局部修复指南