AI生成PPT容易,但决策结论得用CI校验


在这里插入图片描述
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/HHX_01

前言

先说个扎心的事实:AI 现在连 PPT 都能给你整出来了,但它整出来的那页“结论”,你老板看完只会问一句:“所以呢?谁拍板?”

模型憋了半天,吐出一行字:

建议优先推进方案 B。

从渲染器眼里看:标题有了、要点齐了、这页能交差了。从决策角度看:这页连“希望谁批准什么”都没说清楚。就像你跟对象说“晚上吃火锅”,她追问“哪家、几点、谁买单”,你回一句“吃火锅”。方向绝对正确,等于啥也没说。

所以我的做法是:把决策页当成一个需要 CI 的构建输入。页面文案负责好看,判断链负责决定这页有没有资格进入渲染。好看是 PPT 的事,能不能拍板是人的事,中间这道闸,我交给 CI 来守。

1. 给决策页一个“旁路档案”

1.1 为什么不能塞进正文

把所有字段全怼进幻灯片正文?行,但过两周你自己都找不到证据在哪。强迫背景页也守同一套约束?背景页只想安安静静当个背景板,你非要它交代“撤回条件”,它比你还委屈。

所以规矩就一条:需要行动的页面标成 decision,再给它配一个同名 trace 文件。要行动的页面和纯展示的页面,各走各的道,谁也不耽误谁。

type Evidence = {
  id: string;
  kind: 'fact' | 'assumption';
  excerpt: string;
  locator: string;
};
type PassiveTrace = {
  slideId: string;
  kind: 'context' | 'status';
};
export type DecisionTrace = {
  slideId: string;
  kind: 'decision';
  claim: string;
  requestedDecision: string;
  evidence: Evidence[];
  rejectedOptions: string[];
  reconsiderWhen: string[];
  reviewOwner: string;
};
export type SlideTrace = PassiveTrace | DecisionTrace;

1.2 locator 不是用来装文艺的

locator 可以是文档锚点、数据集版本、查询保存位置,甚至是一张手写纸条的编号。它不是为了搞“引用格式”,而是让三个月后的倒霉蛋有地方可回。你不想当那个倒霉蛋,就老老实实填上。

2. 规则只拦一种错:结论页没有判断链

2.1 校验器长这样

const nonEmpty = (value: unknown): value is string =>
  typeof value === 'string' && value.trim().length > 0;
const nonEmptyList = (value: unknown): boolean =>
  Array.isArray(value) && value.some(nonEmpty);
const isRecord = (value: unknown): value is Record =>
  typeof value === 'object' && value !== null;
export function validateDecisionTrace(trace: unknown): string[] {
  const errors: string[] = [];
  if (!isRecord(trace)) return ['trace must be an object'];
  if (!nonEmpty(trace.slideId)) errors.push('missing slideId');
  if (trace.kind !== 'context' && trace.kind !== 'status' && trace.kind !== 'decision') {
    errors.push('kind must be context, status, or decision');
    return errors;
  }
  if (trace.kind !== 'decision') return errors;
  if (!nonEmpty(trace.claim)) errors.push('decision slide needs claim');
  if (!nonEmpty(trace.requestedDecision)) errors.push('decision slide needs requestedDecision');
  const evidence = Array.isArray(trace.evidence) ? trace.evidence : [];
  if (!Array.isArray(trace.evidence)) errors.push('decision slide needs evidence');
  evidence.forEach((item, index) => {
    if (!isRecord(item) || !nonEmpty(item.id) || !nonEmpty(item.excerpt) || !nonEmpty(item.locator)) {
      errors.push(`evidence[${index}] needs id, excerpt, and locator`);
    }
  });
  if (!evidence.some((item) => isRecord(item) && item.kind === 'fact' && nonEmpty(item.excerpt))) {
    errors.push('decision slide needs one fact evidence');
  }
  if (!nonEmptyList(trace.rejectedOptions)) errors.push('decision slide needs rejectedOptions');
  if (!nonEmptyList(trace.reconsiderWhen)) errors.push('decision slide needs reconsiderWhen');
  if (!nonEmpty(trace.reviewOwner)) errors.push('decision slide needs reviewOwner');
  return errors;
}
export function assertValidDecisionTrace(trace: unknown): asserts trace is SlideTrace {
  const errors = validateDecisionTrace(trace);
  if (errors.length > 0) throw new Error(errors.join('\n'));
}

2.2 允许假设,但不允许假装

这里允许模型写 assumption,但绝不允许它把假设伪装成唯一依据。也允许团队暂时不知道最终答案——不知道答案不丢人,丢人的是一页喊着“快给我批准”的页面,连个撤回条件都没有。那叫赌,不叫决策。

3. 失败的样子,要像 CI 一样难堪

3.1 先来个反面教材

下面是演示数据,不是你们业务,放心看:

// 模型或文件读取到的是未受信任输入,不该先假定它满足 DecisionTrace。
const draft: unknown = {
  slideId: 'strategy-03',
  kind: 'decision',
  claim: '建议优先推进方案 B。',
  evidence: [
    {
      id: 'model-summary',
      kind: 'assumption',
      excerpt: 'B 可能有更大的增长空间。',
      locator: 'generated-summary',
    },
  ],
};
console.log(validateDecisionTrace(draft));

输出:

[
  'decision slide needs requestedDecision',
  'decision slide needs one fact evidence',
  'decision slide needs rejectedOptions',
  'decision slide needs reconsiderWhen',
  'decision slide needs reviewOwner'
]

3.2 这份报错到底在说什么

注意,它一个字都没评价 B 对不对。它只是在说:在补齐这些信息之前,这页只能当讨论草稿,没有资格进入发布流程。

这个设计我很喜欢——它不替你做判断,它只负责让你没法假装自己做过判断。

4. 写个最小测试,防止规则被悄悄删掉

4.1 测试代码

const assert = require('node:assert/strict');
const { validateDecisionTrace } = require('./decision-trace.js');
const approved = {
  slideId: 'strategy-03',
  kind: 'decision',
  claim: 'B 值得先在目标人群中做限定范围验证。',
  requestedDecision: '批准限定范围验证,不批准全面切换。',
  evidence: [
    {
      id: 'cohort-report-v2',
      kind: 'fact',
      excerpt: '目标人群完成率高于当前基线。',
      locator: 'report.md#target-cohort',
    },
  ],
  rejectedOptions: [
    'A 缺少后续完成证据。',
    'C 缺少同口径对照。',
  ],
  reconsiderWhen: [
    '下一轮目标人群完成率不再高于当前基线。',
  ],
  reviewOwner: '业务负责人,在下一次复核决定是否扩大。',
};
assert.deepEqual(validateDecisionTrace(approved), []);
assert.match(
  validateDecisionTrace({ ...approved, reviewOwner: '' }).join('\n'),
  /reviewOwner/
);

4.2 成功的信号不是“PPT 生成了”

这里的成功信号是两件事:完整 trace 能通过;任何人敢删掉负责人、事实引用或撤回条件,构建测试当场翻脸。

规则这种东西,没人看着它就会“悄悄优化掉”。给规则上个锁,是规则的基本尊严。

5. 让它真的跑在 CI 里

5.1 先挂上脚本

上面的断言只有进入每次构建,标题里的 CI 才不是比喻。假设 TypeScript 已经由项目现有构建步骤编译到 dist/

{
  "scripts": {
    "test:decision-traces": "node --test dist/decision-trace.test.js",
    "check:decision-traces": "node dist/check-decision-traces.js",
    "build:ppt": "node dist/build-deck.js"
  }
}

5.2 构建步骤前加三道闸

- run: npm ci
- run: npm run test:decision-traces
- run: npm run check:decision-traces
- run: npm run build:ppt

5.3 检查脚本可以短到感人

const { readFileSync } = require('node:fs');
const { validateDecisionTrace } = require('./decision-trace.js');
const loaded = JSON.parse(
  readFileSync('slides/traces.json', 'utf8')
);
if (!Array.isArray(loaded)) {
  throw new Error('slides/traces.json must contain an array');
}
const failures = loaded.flatMap((trace, index) =>
  validateDecisionTrace(trace).map((error) => `trace[${index}]: ${error}`)
);
if (failures.length > 0) {
  console.error(failures.join('\n'));
  process.exit(1);
}

slides/traces.json 里每条记录跟待渲染页面按 slideId 对应。如果你们把 trace 分文件保存,就把读取部分换成目录遍历,校验逻辑和退出码一行都不用动。

这样,页面不会在评审结束、大家都准备下班的时候才暴露“没有人知道依据在哪”,而是压根进不了构建产物。加班?不存在的。

6. 在渲染前拦截,而不是在评审后补刀

6.1 接入点

type BuildInput = {
  trace: unknown;
  render: (trace: SlideTrace) => Promise;
};
export async function buildDeck(input: BuildInput[]): Promise {
  for (const slide of input) {
    assertValidDecisionTrace(slide.trace);
    await slide.render(slide.trace);
  }
}

运行环境是 Node.js 18+、项目现有的 TypeScript 编译步骤,加任意一个现成的渲染器。trace 可以让支持结构化输出的模型生成,也可以先由人写在同目录 JSON/YAML 里。如果你们连自动化 PPT 管线都没有——那就把同样的字段塞进 Markdown 备注,评审前手动跑一遍校验,也能拿到第一波收益。

6.2 边界说清楚,别让它背锅

这个校验器不验证证据是不是真的、引用全不全、替代方案公不公平,也不保证负责人真的会回来复核。它只是把一种最常见的流程漏洞变成可见的失败:

一页想推动行动的内容,不能只靠一句生成出来的结论就通过构建。

AI 把渲染页面变得便宜之后,CI 最该保护的就不只是格式和文件有没有生成成功,还应该包括:这页上面,人需要承担的那份判断,还在不在。

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/HHX_01

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东离与糖宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值