2026/4/18 9:14:11
网站建设
项目流程
东莞网站制作公司报价,高端网站设计简介,民政网站建设情况汇报,温州平台公司在严肃的生成类场景里#xff0c;LLM 只负责内容填充#xff0c;模板负责格式约束#xff0c;代码负责逻辑校验。这是我做过多个生成类项目后一直坚持的原则#xff0c;这个项目也很好地印证了这一点。
两个月前#xff0c;我给一家做了十几年水处理设备集成的企业#…在严肃的生成类场景里LLM 只负责内容填充模板负责格式约束代码负责逻辑校验。这是我做过多个生成类项目后一直坚持的原则这个项目也很好地印证了这一点。两个月前我给一家做了十几年水处理设备集成的企业交付了一套售前报价系统。第一期先做的是历史报价单的检索就是让老板能从 7000 多份历史 Excel 格式的报价单里几秒钟找到最相似的参考案例。刚交付没多久老板整体反馈很不错但也提了个新需求能不能连改都帮我改好我输入客户需求系统直接给我一份可以七七八八的报价单草稿演示视频链接https://mp.weixin.qq.com/s/HidTZt2KtSch6alJRmIH4A这就是本文要讲的第二期项目一个售前报价生成 Agent。用户输入自然语言描述的需求系统自动解析参数、检索相似历史案例、生成报价草稿最后渲染成可直接下载的 Excel 文件。整个工作流用 LangGraph(1.0) 管理状态其中有几个关键的循环节点比如 SKU 校验失败后会路由回 LLM 进行自修正追问流程中会根据用户补充的信息重新解析需求。这篇试图说清楚先回顾下项目背景和痛点然后讲讲如何从 7000 多份历史报价中提炼出 20 种报价模式接着讲在没有 BOM 表的情况下如何构建 SKU 库之后是 7 个核心工程决策的详细拆解再到评测体系的设计以及为什么不做价格预测最后是一些通用思路和感想。以下enjoy:1、项目背景与痛点回顾先简单回顾一下一期项目。这家企业做水处理设备集成主营业务是给工厂、楼宇提供恒压供水泵组、循环水加药装置这类非标设备。十几年下来老板电脑里攒了 7000 多份历史报价单全是 Excel 格式命名五花八门内容结构也不统一。一期项目的核心是检索。我用向量相似度 关键词的混合检索方案让老板输入一段需求描述系统能在几秒钟内从 7000 多 份文件里找到最相似的 3-5 份历史案例。这个功能上线后老板反馈以前找一份参考案例要翻十几分钟现在几秒钟搞定。但实际用了没多久客户觉得已经找的比较准了反馈希望能基于找到的历史案例生成一版新业务的报价单草稿。回顾这个老板的实际工作流程是这样的收到客户的需求通常是一份 Word 或者微信聊天记录→ 用一期系统找到相似的历史案例 → 打开那份 Excel → 逐行对比差异手动调整设备型号、参数、数量 → 重新核算价格 → 另存为新的报价单发给客户。整个过程一期系统只帮他省了找的时间改的时间一点没省。而且改的过程很容易出错。比如漏改管径、忘记调整变频器功率、金额算错这些都是常见问题。所以二期的目标很明确让系统不仅能找还能改。用户输入需求系统直接输出一份可以直接用的报价草稿。老板审核一下微调几处就能发给客户。另外老板还提到一个更长远的诉求希望等这套系统跑稳了他想把它放到网上通过抖音、百度投流获客。潜在客户在线上描述需求系统给出一个大致的报价区间既能筛选客户又给后续销售跟进留足谈判空间。这个目标目前还没做但在架构设计时已经考虑进去了。2、从 7000 多份到 20 份报价模式的提炼二期项目的核心知识库不是那 7000 多份历史报价单而是从中提炼出的 20 份标准报价模式。这一节讲清楚为什么要做这个提炼以及具体怎么做的。2.1、为什么不直接用历史报价单一期项目已经可以从 7000 多份里检索最相似的案例了二期直接复用这个检索结果不行吗实际测试下来确实不行。因为 7000 多份历史数据里噪音太多版本冗余一个项目可能有 v1预算版、v2砍价版、v3最终成交版实际只有 v3 是有效的复制粘贴客户之前习惯找一份旧单子改个抬头就用导致大量重复特例单有部分历史报价单是老板朋友的人情单价格不具参考性有些是特殊工况的非标配置。而且这些无法直接剔除。过时数据五年前的型号可能已经停产价格也早已变化如果让 Agent 直接参考这些数据生成报价它会把这些错误、特例、过时信息一并学进去。所以需要先做一轮数据清洗和提炼。2.2、聚类分析发现业务场景的收敛性第一步是对 7000 多份报价单做聚类分析看看这些数据在特征空间里是否存在自然的聚集。特征提取每份报价单用 LLM 提取结构化特征主要包括设备类型恒压供水 / 循环水加药 / 锅炉加药 / 污水处理 / 其他核心参数流量、扬程、功率等品牌档次全进口 / 混搭 / 全国产价格区间向量化与聚类把提取的特征转为文本描述用 Embedding 模型用的 bge-m3生成向量然后用 HDBSCAN 做聚类。选择 HDBSCAN 而不是 K-Means是因为不需要预设聚类数量让算法自己发现数据的自然分布。from sentence_transformers import SentenceTransformer import hdbscan # 加载 embedding 模型 model SentenceTransformer(BAAI/bge-m3) # 每份报价单的特征描述示例 descriptions [ 恒压供水泵组流量15T扬程50m威乐泵ABB变频器标准配置, 循环水加药装置SEKO计量泵带PH监测中型规模, # ... 7000 份 ] # 生成向量 embeddings model.encode(descriptions) # HDBSCAN 聚类 clusterer hdbscan.HDBSCAN(min_cluster_size50, min_samples10) labels clusterer.fit_predict(embeddings) # 统计聚类结果 n_clusters len(set(labels)) - (1 if -1 in labels else 0) print(f发现 {n_clusters} 个聚类) # 输出发现 22 个聚类聚类结果显示7000 多份报价单在向量空间里自然聚成了 20-25 个簇。这个数字验证了之前的业务直觉虽然每份报价单看起来都不一样但底层的业务模式其实是收敛的。结合聚类分析结果和老板又进行了一次比较深度的访谈。发现其实非标集成的水处理设备业务表面看似像搭乐高一样有N种组合但底层逻辑其实极度标准。首先是核心场景有限无非是循环水冷却、补水锅炉、供水恒压、污水加药/絮凝、纯水反渗透这 5 大类。其次是规模分级每一类分小、中、大3 级。再加上少量的“特殊材质全不锈钢”或“特殊控制PLC上位机”的变种20 个聚类中心Centroids刚好覆盖了该企业 90% 的业务成交额。2.3、业务维度的交叉验证聚类结果是纯数据驱动的但还需要从业务角度验证这些聚类是否有意义。通过分析每个聚类的样本我们发现数据自然地按照三个维度分布维度取值说明场景循环水加药 / 恒压供水 / 锅炉加药 / 污水处理 / 特殊应用决定设备类型和工艺流程规模小型 / 中型 / 大型影响设备容量和数量档次经济型 / 标准型 / 豪华型决定品牌选择和配置水平理论上 5 × 3 × 3 45 种组合但实际业务中很多组合是稀疏的比如大型锅炉加药经济型几乎不存在。最终有效的组合约 20 种和聚类结果吻合。另外不同场景的业务占比是不均匀的场景占比说明循环水加药40%主营业务恒压供水25%第二主力锅炉加药15%常规业务污水处理10%偶发业务特殊应用10%长尾需求所以在构建知识库时我在高频场景循环水、恒压供水上做了更细的拆分保证 Agent 在 80% 的请求中都能匹配到高度相关的参考案例。2.4、关联规则挖掘从数据中发现业务约束聚类解决了有哪些模式的问题但 Agent 生成报价时还需要知道设备之间怎么搭配。这些业务约束比如11KW 的泵要配 11KW 或 15KW 的变频器是怎么来的一开始我想让老板把这些规则写下来但发现很难执行。老板的知识是隐性的让他系统化表达比让他直接报价还难。于是我换了个思路从历史数据里挖掘这些规则。方法一共现频率分析统计设备组合在历史报价中的共现概率from collections import Counter from itertools import combinations # 每份报价单的设备列表 quotations [ [格兰富泵, ABB变频器, 施耐德元器件], [南方泵业, 汇川变频器, 正泰元器件], [格兰富泵, ABB变频器, GF仪表], # ... 更多 ] # 统计两两共现 co_occurrence Counter() for items in quotations: for pair in combinations(sorted(items), 2): co_occurrence[pair] 1 # 计算共现率 total_grundfos sum(1 for q in quotations if 格兰富泵 in q) grundfos_abb co_occurrence[(ABB变频器, 格兰富泵)] print(f格兰富泵 ABB变频器 共现率: {grundfos_abb / total_grundfos:.1%}) # 输出格兰富泵 ABB变频器 共现率: 87.3%这个分析发现格兰富泵和 ABB 变频器几乎是绑定的Agent 在生成豪华配置时应该遵循这个搭配。方法二数据分箱与统计分析对于数值型的匹配关系如流量 vs 管径我做了简单的统计分析import pandas as pd # 历史数据中的流量-管径配对 data pd.DataFrame({ flow_rate: [5, 8, 15, 20, 25, 40, 60, 80, 100], # 吨/小时 pipe_diameter: [25, 32, 50, 50, 65, 65, 80, 100, 100] # DN }) # 按流量区间统计管径分布 bins [0, 10, 20, 40, 60, 100, 150] data[flow_bin] pd.cut(data[flow_rate], bins) print(data.groupby(flow_bin)[pipe_diameter].agg([mean, mode]))最终提炼出的匹配规则脱敏示例流量 (T/H)推荐管径≤10DN25-DN4010-20DN5020-40DN6540-60DN8060-100DN100100DN125这些规则的好处在于不是拍脑袋定的而是正儿八经的数据分布统计结果。2.5、验证让老板审核当然数据驱动生成的 20 份报价模式和业务规则最终还是要让老板验证。们把这 20 份模式和提炼出的规则打印出来给老板看。老板翻了一遍只改了三四处细节例如某个国产泵的型号已经停产换成了新型号还有就是某个仪表品牌的价格涨了更新了参考价。对于其他 90% 的内容老板表示基本没问题实际他也是这么配的。这个验证过程也印证了我在做类似项目的一些技巧与其让业务专家从零开始写规则不如先用算法从历史数据中读出规则再让专家做增量修订。这种方式实测确实可以很大程度降低垂直场景的领域知识沉淀的门槛。3、没有 BOM 表怎么办SKU 库的构建上一节解决了有哪些报价模式的问题但 Agent 生成报价时还需要知道有哪些设备可以选。这就涉及到 SKU 库的问题。3.1、中小企业的常态没有产品数据库在大企业选型和报价通常依赖 ERP 系统里面有完整的产品目录、BOM 表、标准价格库。但在这家企业这些都没有。这个客户的老板的日常报价逻辑是这样的先找一份类似的历史案例看里面用了什么设备然后打电话问供应商最新价格最后再手动调整后报价。这种模式下产品知识是分散在老板脑子里和 7000 多份历史 Excel 里的并没有一个集中的产品目录。3.2、两阶段萃取策略一开始我想让老板自己整理一份产品清单但发现执行很困难。老板知道格兰富的 CR 系列泵但让他列出所有用过的型号、参数、价格他根本没时间也没耐心做这件事。那能不能直接从 7000 多份历史报价单里提取呢也不行。因为历史数据里噪音太多五年前用过但现在已经停产的型号、录入时打错的型号、临时替代用的非标设备……如果把这些都提取进来SKU 库会变得又脏又乱反而会误导 LLM。于是我换了个思路。既然前面已经从 7000 多份里提炼出了 20 个业务场景聚类我可以回溯这 20 个聚类簇背后的原始高频数据提取每个场景下常用的设备型号。这 20 份标准报价模式本身是“代表性清单”每份都包含了该场景下可能用到的典型配置类似“超级 BOM”而不是某个具体项目的单一清单。这样提取出来的都是企业当前真正在用的、经过老板确认没问题的设备型号。import pandas as pd import json from pathlib import Path def extract_sku_from_templates(template_dir: str) - dict: 从 20 份报价模式中提取 SKU 库 sku_library {} for excel_file in Path(template_dir).glob(*.xlsx): df pd.read_excel(excel_file) for _, row in df.iterrows(): # 提取设备信息 category row.get(设备类型, 其他) model row.get(型号, ) brand row.get(品牌, ) params row.get(参数, ) price row.get(单价, 0) if not model: continue # 按类别组织 if category not in sku_library: sku_library[category] [] # 去重 existing_models [item[model] for item in sku_library[category]] if model not in existing_models: sku_library[category].append({ model: model, brand: brand, params: params, reference_price: price }) return sku_library # 执行提取 sku_library extract_sku_from_templates(data/templates/) # 保存为 JSON with open(config/sku_library.json, w, encodingutf-8) as f: json.dump(sku_library, f, ensure_asciiFalse, indent2) print(f提取完成{sum(len(v) for v in sku_library.values())} 个 SKU) # 输出提取完成110 个 SKU最终提取出 110 个 SKU覆盖 13 个类别类别SKU 数量典型品牌离心泵18格兰富、威乐、南方泵业计量泵12SEKO、普罗名特变频器8ABB、汇川PLC/触摸屏6西门子、三菱仪表传感器15GF、NEART阀门管件20远禾、埃美柯其他31-3.3、这种方式的好处第一SKU 库里的设备都是企业真正在用的。因为 SKU 是从 20 份标准报价模式里提取的而这 20 份本身就是经过清洗和老板确认的干净数据。那些过时的、错误的、临时替代的型号在聚类清洗阶段就已经被过滤掉了不会进入 SKU 库。第二Agent 生成的方案被严格限制在 SKU 库范围内。在 Prompt 里明确告诉 LLM 只能使用 SKU 库范围内的型号。在代码层面还有一道校验如果 LLM 生成了不存在的型号系统会自动替换成 SKU 库里最接近的型号。这样双重约束下基本杜绝了 LLM 编造型号的问题。第三企业不需要停下来做数据治理。传统的数字化转型往往要求企业先花几个月整理产品数据库这对中小企业来说是巨大的负担。而这种从历史数据中逆向提取的方式让企业可以边用边积累不需要前置的重资产投入。4、核心工程决策这一节讲几个在开发过程中做的关键选择以及背后的踩坑经历。4.1、Excel 模板 JSON 填充而不是让 LLM 直接生成 Excel在生成类场景中如果让 LLM 端到端生成完整的文档包括格式、公式、金额计算通常会遇到格式不稳定每次生成的列顺序可能不一样。以及金额计算经常出错LLM 的数学能力确实不靠谱。还有就是没法保留企业原有的模板样式和法律条款。所以在这个项目里我从一开始就采用了模板 JSON 填充的方式让 LLM 只负责内容生成设备清单、参数、数量格式和计算交给代码处理。具体实现是一个三层结构第一层Excel 模板。这是老板原来就在用的报价单模板里面已经有固定的样式、表头、法律条款、以及金额计算公式如 数量*单价。我把它作为骨架不让 LLM 碰。第二层LLM 生成的 JSON。LLM 只需要输出一个结构化的 JSON包含设备清单和差异分析。{ bom_items: [ { name: 增压泵, model: CR45-4, brand: 格兰富, params: 流量60T/H 扬程80m 功率18.5KW, quantity: 3, unit: 台, unit_price: 12500 }, { name: 变频器, model: ACS550-18.5KW, brand: ABB, params: 18.5KW 380V, quantity: 2, unit: 台, unit_price: 4800 } ], diff_analysis: 根据用户需求相比参考案例调整了泵的数量从2台增加到3台两用一备 }第三层渲染器。一个 Python 脚本负责把 JSON 数据注入到 Excel 模板里。模板里的公式会自动计算总价。def render_quote_to_excel(draft_json: dict, output_path: str): 将 JSON 草稿渲染到 Excel 模板 # 复制模板 template_path templates/quote_template.xlsx shutil.copy(template_path, output_path) wb openpyxl.load_workbook(output_path) ws wb.active # 找到 BOM 起始行通过标记行识别 start_row find_bom_start_row(ws) # 逐行写入设备数据 for i, item in enumerate(draft_json[bom_items]): row start_row i ws.cell(rowrow, column1, valuei 1) # 序号 ws.cell(rowrow, column2, valueitem[name]) # 设备名称 ws.cell(rowrow, column3, valueitem[model]) # 型号 ws.cell(rowrow, column4, valueitem[brand]) # 品牌 ws.cell(rowrow, column5, valueitem[params]) # 参数 ws.cell(rowrow, column6, valueitem[quantity]) ws.cell(rowrow, column7, valueitem[unit]) ws.cell(rowrow, column8, valueitem[unit_price]) # 总价由模板公式自动计算 wb.save(output_path)这种方式的好处是职责分离LLM 专注于理解需求和生成内容格式稳定性由模板保证金额正确性由公式保证。业务人员想调整模板样式直接改 Excel 模板就行不用动代码。4.2、SKU 校验的三道防线LLM 在这个场景里的幻觉主要体现在可能会编造不存在的设备型号。一开始我在 Prompt 里写请使用以下型号列表中的设备但 LLM 经常无视这个约束自己编一个看起来很像的型号出来。于是我设计了三道防线第一道Prompt 强约束。把措辞从建议使用改成必须且只能使用并且把 SKU 库直接嵌入 Prompt。第二道代码事后校验。LLM 生成 JSON 后代码遍历每个设备型号检查是否存在于 SKU 库。def validate_sku(draft_json: dict, sku_library: dict) - list: 校验 BOM 中的型号是否合法 invalid_items [] for item in draft_json[bom_items]: model item.get(model, ) category item.get(name, ) # 在对应类别中查找 valid_models [sku[model] for sku in sku_library.get(category, [])] if model not in valid_models: invalid_items.append({ name: category, invalid_model: model, valid_options: valid_models[:5] # 给出可选项 }) return invalid_items第三道自动替换兜底。如果检测到非法型号系统会自动用 SKU 库里最接近的型号替换而不是直接报错。为什么选择自动替换而不是让用户手动修正因为在这个场景里型号不合法通常是 LLM 的拼写错误或编码混淆比如把CR45-4写成CR454本质上 LLM 想选的就是那个最接近的型号。而且这是一个“草稿”系统最终报价还要经过老板审核自动替换只是让草稿能用不影响最终决策。当然这里要注意一个风险就是模糊匹配容易“看起来很像但其实差很多”比如 CR-10 和 CR-100 字符接近但规格可能完全不是一个量级。我把它定位为拼写纠错兜底并加两道约束——只有相似度超过阈值才自动修正所有自动修正都在草稿页高亮提示且可撤销。不满足阈值的情况直接回退到 LLM 自修正/人工确认避免静默误替换。from rapidfuzz import process, fuzz def fix_sku(invalid: str, valid: list[str], threshold: int 90): best, score, _ process.extractOne(invalid, valid, scorerfuzz.ratio) return best if score threshold else None4.3、场景化的追问策略Agent 需要判断用户输入的参数是否完整不完整就要追问。但追问太多会让用户烦追问太少又可能生成不靠谱的结果。一开始我设计的追问逻辑很死板只要流量、扬程、品牌、预算任何一个没提到就追问。结果用户说做个加药装置Agent 问请提供流量和扬程——这就很傻因为加药装置根本不需要流量扬程这些参数。这里就体现出了前面数据分析的价值。在聚类分析阶段已经发现了业务自然分成几大场景恒压供水、循环水加药、锅炉加药等而且分析了每个场景的特征数据泵组场景恒压供水、增压泵是强物理属性的流量和扬程是泵选型的核心依据缺一不可。加药场景是强工艺属性的只要知道应用场景循环水/锅炉/污水药箱容积、计量泵流量等都有标准配置可以默认。基于这个理解我把追问逻辑改成了场景化的追问策略不同场景有不同的必填参数。def check_requirement_completeness(state: dict) - dict: 场景化的参数完整性检查 params state.get(parsed_requirement, {}) scenario params.get(scenario, unknown) missing_params [] # 泵组场景必须有流量和扬程物理硬约束 if scenario in [constant_pressure, booster_pump]: if not params.get(flow_rate): missing_params.append(流量吨/小时) if not params.get(head): missing_params.append(扬程米) # 加药场景只需要知道应用场景其他可以用默认值 elif scenario in [dosing_system, circulating_water]: if not params.get(application): missing_params.append(应用场景循环水/锅炉/污水) # 药箱容积、计量泵流量等可以默认 # 未知场景问清楚要什么设备 else: missing_params.append(设备类型) return { missing_params: missing_params, need_clarification: len(missing_params) 0 }核心思路是物理硬约束必须问没有流量扬程泵根本选不出来业务软约束可以默认药箱容积默认 200L用户不满意再改。4.4、不维护对话历史很多 Agent 教程会教你维护一个对话历史列表每次把完整历史传给 LLM。但在这个场景里我选择不维护对话历史。原因是用户修改报价单通常是把泵换成国产的这种局部调整。如果把之前所有的对话都传进去不仅浪费 Token还可能让 LLM 受到历史对话里错误信息的干扰。我的做法是每次修改只传两个东西当前的报价草稿 JSON用户的修改意见。def revise_draft(current_draft: dict, user_instruction: str) - dict: 基于当前草稿和用户意见生成新草稿 prompt f 当前报价草稿 {json.dumps(current_draft, ensure_asciiFalse, indent2)} 用户修改意见{user_instruction} 请根据用户意见修改草稿输出新的 JSON。 response llm_client.generate(prompt) return parse_json(response)这样每次修改都是“无状态”的不会有幻觉累积的问题调试也更容易。当然这种设计也有 trade-off如果用户说“还是恢复到上一步吧”或者“就像刚开始那版一样”这种涉及多轮上下文引用的指令在无状态架构下会失效。但在实际使用中这类场景很少出现而且用户可以直接修改草稿 JSON综合权衡下来无状态设计的稳定性和低 Token 消耗更符合改完即走的工具属性。4.5、强制反馈才能下载在 B 端产品里收集用户反馈是很难的用户没有动力去点点赞或者填问卷。但反馈数据对于后续优化非常重要。我的做法是把反馈作为下载的前置条件。用户生成报价草稿后必须先选择满意或不满意才能下载 Excel 文件。当然这也是征得了老板的同意。如果选择不满意还需要选择具体问题多选设备型号不对、价格偏差太大、缺少必要设备、有多余设备、其他。这样设计虽然稍微牺牲了一点用户体验但能保证每次使用都留下数据。这些数据后续可以用于分析 Agent 的常见错误模式还可以挖掘用户的隐性偏好比如老板总是把 A 品牌换成 B 品牌以及长远一点的作为微调的数据支撑。4.6、业务规则外置到配置文件在开发过程中我发现很多业务规则是逐渐浮现的。比如11KW 的泵要配 11KW 或 15KW 的变频器流量 10-20 吨要用 DN50 的管径。一开始我把这些规则写成 if-else 硬编码在代码里结果规则越来越多代码变得难以维护。而且老板经常说这个规则不对应该是这样每次改规则都要改代码、重新部署。于是我把业务规则抽出来放到 JSON 配置文件里{ pump_inverter_matching: { description: 泵与变频器功率匹配规则, rules: [ {pump_power_max: 3, inverter_power: 3}, {pump_power_max: 5.5, inverter_power: 5.5}, {pump_power_max: 11, inverter_power: 11}, {pump_power_max: 18.5, inverter_power: 18.5}, {pump_power_max: 30, inverter_power: 30} ] }, flow_pipe_matching: { description: 流量与管径匹配规则, rules: [ {flow_max: 10, pipe_diameter: DN40}, {flow_max: 20, pipe_diameter: DN50}, {flow_max: 40, pipe_diameter: DN65}, {flow_max: 60, pipe_diameter: DN80}, {flow_max: 100, pipe_diameter: DN100} ] } }代码只负责读取配置和执行校验不关心具体规则是什么def validate_pump_inverter_match(pump_power: float, inverter_power: float) - bool: 校验泵与变频器功率是否匹配 rules load_config(business_rules.json)[pump_inverter_matching][rules] for rule in rules: if pump_power rule[pump_power_max]: return inverter_power rule[inverter_power] return False这样改规则只需要改 JSON 文件不用动代码也不用重新部署。4.7、先 HTML 验证再 React 生产在做前端时一个常见的纠结是直接上 React TypeScript 的生产级架构还是先做一个简单的 Demo如果一开始就上 React 全家桶可能会过度工程化。毕竟后端逻辑还在调试交互流程还在摸索花两周搭一个精致的 React 项目结果老板看了说流程不对要改那之前的工作就白费了。所以我一般的做法是先用单文件 HTML 快速验证。用一个 500 行左右的 HTML 文件配合 Alpine.js 实现基本交互调通后端 API跑通完整流程。这样可以快速给老板演示收集反馈确认方向对不对。等老板拍板说可以了再正式投入 React 开发。这个做法的好处是平衡效果和开发成本HTML Demo 开发快、修改成本低适合早期验证。老板在自己电脑上直接双击打开 HTML 就能用不需要装任何环境确认方向正确后再投入 React 开发这时候需求已经稳定不容易返工。当然HTML Demo 有它的局限性。比如复杂的状态管理、动态表单、loading 动画这些用原生 HTML 实现起来很痛苦。所以生产版还是用了 React Vite Ant Design有完整的组件化和工程化支持。这也是在实际项目中形成的一个小经验不要一上来就追求最佳实践先用最小成本验证方向确认可行后再逐步升级技术栈。5、评测体系怎么量化 Agent 的效果做 Agent 项目有个常见的困境怎么知道系统是变好了还是变差了光靠感觉不行需要有量化的评测指标。5.1、五个评测维度这个项目二期里我设计了五个维度来评估每次生成结果的质量。维度权重评测方法参数提取准确率20%对比用户输入和解析结果检查流量、扬程、品牌等关键参数是否正确提取SKU 有效性20%检查生成的设备型号是否存在于 SKU 库业务约束合规性25%检查泵-变频器功率匹配、流量-管径匹配等业务规则过程可解释性25%检查召回的参考案例是否相关、BOM 清单是否完整响应时效性10%端到端响应时间目标 30 秒内关于权重的设定最初是根据经验先定了一版然后跟老板一起做了校准。方法是让老板用几个真实需求跑一遍系统他给每次结果打一个主观分“这个草稿我觉得能用/不能用”然后调整权重让自动评分和老板的主观感受尽量对齐。比如一开始 SKU 有效性的权重是 10%但发现老板对“型号不对”这件事特别敏感所以调高到了 20%。这样评测结果才能真正反映业务侧的实际感受而不是闭门造车。5.2、黄金测试用例基于前面的业务分析设计了 5 个黄金测试用例覆盖不同的场景和边界情况用例 1简单生成输入帮我做一套恒压供水系统3 台格兰富泵流量 50 吨扬程 80 米预期直接生成不追问 用例 2需要追问输入做个增压泵系统预期追问流量和扬程泵组场景的硬约束 用例 3品牌迁移输入参考上次的方案把泵换成国产南方泵业预期品牌切换后变频器和电气元件也应该联动切换 用例 4草稿修改输入上一轮生成后把泵的数量改成 4 台预期只改数量其他不变 用例 5档次切换输入做个循环水加药装置要经济型配置预期选择国产品牌价格在对应区间5.3、评测结果跑完 5 个用例的结果通过率100%所有用例都能正确完成平均得分87.2%SKU 有效性100%三道防线起作用了业务约束合规100%。5.4、评测驱动开发有了这套评测体系后每次改代码都可以先跑一遍测试量化地看效果变化。比如优化了追问逻辑后重新跑测试确认没有引入新问题。这个做法在 Agent 开发里特别重要因为 LLM 的输出本身就有随机性没有自动化测试兜底很容易改出问题都不知道。6、为什么不做价格预测在项目设计阶段我主动和老板讨论过一个问题就是二期的报价 Agent 要不要直接输出最终报价我的判断是不能至少现阶段不能。与老板讨论后他也认同这个判断。6.1、B2B 定价的本质是博弈工业设备的 B2B 报价最终成交价里包含了太多 LLM 无法感知的变量。经过和老板前后几次的沟通最终的签约价至少包含以下需要考虑的情形客户关系老客户可能有长期合作的默契价新客户要看竞争对手报价付款方式全款现结和 3 个月承兑汇票价格能差 5-10%项目背景有些项目是敲门砖亏本也要做有些是肥肉必须守住利润市场行情淡季和旺季的议价空间完全不同竞争态势知道对手报了多少自己才能定策略。这客户可能没说的一点是还有一个影响报价的因素是他报价时的心情。对于存在这种多的信息不对称让 LLM 预测最终成交价结果必然不靠谱。6.2、LLM 只提供参考价所以二期项目的定位很明确Agent 输出的是参考价不是成交价。具体做法是1、Agent 基于 20 份标准报价模式计算价格。注这 20 份报价模式的价格本身就来自历史成交数据的中位数。在前面的聚类分析阶段不仅对设备组合做了聚类价格也筛选了近三年的有效数据并取每个聚类中历史成交价的中位数。这意味着这些价格已经隐含了所有复杂的定价因素客户博弈、市场竞争、毛利平衡只是被平均掉了。用中位数而不是平均数是为了剔除极端值的干扰得到一个相对公允、有市场竞争力的基准价。2、前端显著标注此价格为参考价请根据实际情况调整3、老板在草稿基础上根据上面那些 LLM 不知道的因素做最终调整。6.3、未来的优化方向虽然现阶段不做价格预测但反馈数据会积累起来。每次老板把参考价改成最终报价时这个调整幅度就是有价值的信息。积累够了之后可以做一些事情。比如分析老板的调价习惯比如遇到央企客户老板习惯上浮 10%。再比如对于常见客户类型给出调价建议不是最终价而是调整方向。另外还有互联网获客场景给出价格区间而不是单一价格既能吸引客户又留有谈判空间这个演进路径的核心思想是向客户承认大模型的能力边界再通过数据积累逐渐让 LLM 学会如何更精准的给出参考报价。比起一上来就让 LLM 通过看似复杂算法预测价格然后被老板骂不靠谱这种渐进式的方式更容易获得信任。7、写在最后7.1、这个项目的后续规划目前二期项目已经在客户那边稳定运行老板反馈比以前快多了基本能用。但这只是起点后面还有几个阶段要做。当前阶段Phase 2内部辅助工具。Agent 生成报价草稿老板审核修改后发给客户。核心价值是省时间从以前的十几二十分钟缩短到几分钟。下一阶段Phase 3让 Agent 越来越懂老板。通过积累反馈数据分析老板的调价习惯和偏好逐步让生成的草稿更贴合老板的风格。这个阶段的关键是反馈数据的质量和数量。远期目标Phase 4/5互联网获客。把这套系统开放到网上潜在客户在抖音、百度看到广告后在线描述需求系统给出一个价格区间比如2.5w-3.0w吸引客户留资再由销售跟进。这个场景对价格的要求是区间而不是精确值正好规避了前面说的定价博弈问题。在对外版本中价格模型会做模糊化处理脱敏仅输出宽泛区间防止核心定价策略泄露。7.2、生成类场景的通用思路这个报价单生成项目只是我做过的众多生成类项目中的一种。之前在公众号里也分享过信贷尽调报告的生成、合同的生成也给一些企业做过标书生成、环控报告生成等场景的咨询。这些项目做下来有一个共同的认识大模型的核心能力其实在推理而不是生成。想让 LLM 在企业级生产环境里稳定输出可用的文档必须加上大量的工程技巧。格式模板、事实数据约束、SKU 库校验、业务规则配置这些是让生成结果能用的关键。具体来说生成类场景有几个绑定的要素格式约束保证输出结构稳定事实数据防止 LLM 编造经验模式让结果符合行业惯例代码校验兜底 Prompt 约束。这些要素缺一不可。只会调 Prompt 是没法做好生成类项目的必须把领域知识沉淀到系统里用代码强制约束再配合自动化评测持续迭代才能真正落地。7.3、一点感想做企业大模型应用落地项目最难的往往不是技术实现。现在 AI Coding 工具已经很强代实现层面的问题已经能够解决绝大部分。真正的难点在三个层面第一是领域知识的挖掘和沉淀业务专家的隐性经验很难显性化需要用数据分析的方式反向提炼第二是数据治理企业的历史数据往往是不完整且非标这部分工作量通常比写代码还大第三是项目管理本身如何与客户高效沟通、快速验证、小步迭代在资源有限的情况下找到投产比最高的路径。这些体感的积累远比掌握某个新框架更有价值也是我这两年做 2B 项目最深的体会。26 年会继续在有行业 Know-How 的场景里做深也会把沉淀下来的一些应用无论是数据治理脚本还是项目模板—逐渐开源出来供大家参考。如果有类似项目经验的盆友欢迎找我交流。另外这个案例我也会录制成视频明天发布在视频课程里作为5个进阶的agent案例的第一个。视频课程限时定价349目前已更新完10个主案例都有配套的源代码和测试文档。5个进阶的agent的案例预计1月下旬全部完更最后定价499。知识星球成员1元兑换。试看链接https://mp.weixin.qq.com/s/HidTZt2KtSch6alJRmIH4A