2. 把需求交给 AI
一句请求要交给 AI 去实现,通常要包含三样东西:数据从哪里来、结果长什么样、遇到特殊情况怎么办。这三样东西不说清楚,AI 只能自己猜。猜对了看不出问题,猜错了要重新说一遍,说清楚一次就能省掉这一轮来回。
一句话请求里藏着什么空白
Section titled “一句话请求里藏着什么空白”「帮我整理报价」这句话读起来已经是一个完整的要求,拆开看却缺着好几处:
- 报价文本长什么样,字段之间用什么分隔?
- 「整理」之后,结果应该是什么形状:一张表、一行摘要,还是别的?
- 缺价格、缺单位这类情况该怎么处理:跳过,还是标出来?
这些空白没有唯一答案,答案在提出需求的人手里,不在 AI 手里。没写清楚时,AI 会按最常见的猜测往下走,猜测和实际情况不一致,结果就会跑偏。
先看模糊请求跑出什么
Section titled “先看模糊请求跑出什么”示范项目「供应商报价整理器」要处理的第一批输入是这样的,三个字段用英文逗号分隔,第三行故意缺一个单位:
M6 螺母,个,0.18M8 螺栓,个,0.55不锈钢垫片,,1.20把「帮我整理报价」原样交给 AI,不说明分隔符和缺失情况的处理方式,AI 只能挑一种常见格式去猜。拿到的实现类似这样:
// 只凭一句"帮我整理报价"猜出来的实现,猜的是 Tab 分隔function parseQuotesGuessed(text) { const lines = text.trim().split('\n').filter(Boolean); return lines.map((line) => { const [name, unit, price] = line.split('\t'); return { name, unit, price: Number(price) }; });}用上面那份 input.txt 的内容跑一遍这个函数:三行都没有 Tab 字符,split('\t') 切不出第二、第三段,结果是整行原文被塞进了 name,unit 完全丢失,price 变成 Number(undefined),也就是 NaN。三行输入,三行都不对。
这句请求确实没告诉它字段用逗号分隔,跟 AI「做得不够好」无关。换一句猜测(比如猜逗号、猜空格),结果会不一样,但换哪种猜测由 AI 决定,不由实际数据决定。
换一个模板,把空白填上
Section titled “换一个模板,把空白填上”北极星给出的最小模板只有三段,负责把上面那几处空白填上:
输入是……,输出是……,规则是……
把「帮我整理报价」换成这样一句话再交给 AI:
输入是:每行一条报价,字段用英文逗号分隔,顺序是品名、单位、含税价。输出是:一张表,每行对应一条报价。规则是:单位或价格缺失的行,对应字段标记为「缺失」,其余行照常显示数字。
这一次 AI 拿到的实现类似这样:
// 按模板说清楚分隔符和缺失规则之后得到的实现function parseQuotesWithTemplate(text) { const lines = text.trim().split('\n').filter(Boolean); return lines.map((line) => { const [name, unit, price] = line.split(',').map((cell) => cell.trim()); const missingUnit = !unit; const missingPrice = price === undefined || price === ''; return { name, unit: missingUnit ? '缺失' : unit, price: missingPrice ? '缺失' : Number(price), missing: missingUnit || missingPrice, }; });}同一份 input.txt 跑一遍:前两行品名、单位、价格全部对上;第三行「不锈钢垫片」缺单位,unit 字段被标成「缺失」,价格 1.2 正常显示。三行输入,这一次三行都对。
两版代码唯一的差别是分隔符和缺失处理这两处空白有没有被填上,跟哪一次 AI 更用心无关。
换一条规则再试一次
Section titled “换一条规则再试一次”模板里的「规则是」这一段可以继续加条件。价格是 0 该不该也算缺失?「不锈钢垫片,个,0」这一行按现在的规则会显示价格 0,不会被标成缺失,因为 price === '' 不成立。
把这条规则加进模板里再试一次:「价格等于 0 时,也标记为缺失」。把这句加进上面那段请求,重新交给 AI,看这一行的 missing 字段有没有变成 true。
代入自己的题目
Section titled “代入自己的题目”同一个模板可以套进第 0 章留下的那句问题陈述。报价整理器的版本是:
输入是:粘贴进网页的报价文本。输出是:一张统一格式的汇总表,标出缺失的行。规则是:品名、规格、单位不同写法要能对上同一列。
第 0 章「游戏灯光」那个念头例子,套进同一个模板会变成:
输入是:游戏发出的血量下降信号。输出是:桌面灯在收到信号后的颜色变化。规则是:信号在一秒内没有收到确认,灯自动恢复原色。
模板的三段位置不变,填进去的内容来自各自的问题陈述,不是套用同一个答案。
留下什么产物
Section titled “留下什么产物”把「换一个模板」那一节的实现存成一个能直接打开的页面,不需要先装运行环境:
文件夹quote-tool/
- index.html
<!doctype html><html lang="zh-CN"> <head> <meta charset="utf-8" /> <title>报价整理器</title> </head> <body> <!-- 粘贴报价文本:每行一条,品名、单位、含税价用英文逗号分隔 --> <textarea id="input" rows="6" cols="50"></textarea> <br /> <button id="run">整理</button> <table id="output"></table>
<script> // 按模板里说清楚的规则解析:逗号分隔,缺失字段标记出来 function parseQuotes(text) { const lines = text.trim().split('\n').filter(Boolean); return lines.map((line) => { const [name, unit, price] = line.split(',').map((cell) => cell.trim()); const missingUnit = !unit; const missingPrice = price === undefined || price === ''; return { name, unit: missingUnit ? '缺失' : unit, price: missingPrice ? '缺失' : Number(price), missing: missingUnit || missingPrice, }; }); }
// 点击「整理」后,把解析结果渲染成表格,缺失的行标红 document.getElementById('run').addEventListener('click', () => { const rows = parseQuotes(document.getElementById('input').value); document.getElementById('output').innerHTML = '<tr><th>品名</th><th>单位</th><th>含税价</th></tr>' + rows .map( (row) => `<tr style="color:${row.missing ? 'red' : 'black'}"><td>${row.name}</td><td>${row.unit}</td><td>${row.price}</td></tr>` ) .join(''); }); </script> </body></html>双击打开这个文件,粘贴 input.txt 里的三行,点击「整理」,能看到一张三行的表格,第三行标红。这个文件本身就是这一章的产物:不需要服务器,不需要提前装 Node.js,直接在浏览器里打开就能用。
另一份产物是两次请求的步骤对比,不靠秒表计时,靠往返次数:
| 请求方式 | 需要说明分隔符 | 需要说明缺失规则 | 跑通所需轮数 |
|---|---|---|---|
| 「帮我整理报价」 | 中途补充 | 中途补充 | 至少两轮 |
| 套用输入/输出/规则模板 | 一次说清 | 一次说清 | 一轮 |
这张表格记录的是刚才发生的事。模板不保证永远只需要一轮,规则本身写漏一处,一样要补第二轮,比如上一节故意漏掉的「价格等于 0」。
这一章可以对 AI 说的一句话
Section titled “这一章可以对 AI 说的一句话”我要一个程序。输入是……,输出是……,规则是……