《互联网产品的 AI 架构·平台化与治理》13.2 一次跨团队治理评审

治理基线只有进入评审桌才有意义。本节还原回声 2026 年 11 月 5 日的一次真实形态评审:内容团队要把摘要与审核能力开放给创作者工具,七个参会方带来七份证据,四个争议点分别涉及重复建设、额度借用、评测隐私与放量节奏。文章给出决议规则、待办跟踪与一次反例,并用确定性代码演示基线如何把结论限定为试点。

一场没有新技术的评审

2026 年 11 月 5 日,内容团队向治理评审会提交了一个方案:把 content.summarize 与 moderation.classify 开放给创作者工具,服务对象从主站编辑扩展到外部创作者,预计日调用量从 2,000 次升到 20,000 次。

方案里没有新技术。摘要与审核能力已经跑了两个季度,链路 A 的基线是 96.25%、每次成功交付 3,496 微元、P95 3,523ms。真正需要决定的是四件事:要不要为创作者工具新建一个回复能力、额度从哪里借、创作者草稿能不能进入跨团队评测集、首日放量到多少。

这四件事都不是模型问题,而是治理问题。评审会的作用不是重新讲一遍技术方案,而是让每个受影响的一方带着证据到场,用同一份基线做决定。

会前:七方各带什么

评审记录在会前 48 小时冻结。参会方不是「相关同学都来」,而是七个有明确职责的席位,每席必须带来指定证据。带不齐证据的席位可以发言,但不能投票。

席位角色必须带来的证据关心什么
内容团队提供方能力契约、试点计划能力是否按自己的场景运行
创作者工具调用方流量预测配额、延迟、失败时用户看到什么
交易团队出借方额度账本借出的份额能否按期归还
平台平台方基线报告目录、版本、成本口径是否一致
安全安全风险登记新入口是否扩大攻击面
合规合规留存策略创作者草稿的隐私与删除
SRE稳定性SLO 看板峰值是否超过平台容量

这份名单里没有「领导」席位。治理评审不是汇报会,决策权来自职责与证据,不来自职级。如果某一方缺席,相关议题只能记为待办,不能当场表决通过。

四个争议点

评审材料把分歧提前写成四条,每条都记录各方立场与最终决议。这样做的目的不是消灭分歧,而是让分歧可追踪。

编号争议主要立场决议
D1是否新建 reply.chat内容团队要按草稿场景调提示;平台要求同一功能键只有一个在役能力复用 chat.reply 2.1.0,只开放契约内的提示后缀与 economy 档位覆盖
D2借用交易团队 1,500 万微元内容团队大促需要扩容;交易团队要求明确归还日借用 1,500 万,单次上限不变,2026-12-15 前归还,逾期自动回收
D3创作者草稿进入评测集内容团队要真实样本;合规要求不跨租户共享跨团队只用合成与脱敏样本,租户样本留在租户域内
D4首日放量比例内容团队要 100%;SRE 要求先试点先 10% 试点两周,条件关闭后再评审全量

D1 的决议值得展开。内容团队的诉求是合理的:草稿场景的措辞确实与客服回复不同。但新建能力会让「回复」这个功能键出现两个在役实现,平台看板上的成功率被拆成两条曲线,复用收益也会消失。最终方案是把差异收进契约允许的覆盖范围,而不是复制一个能力。这既满足了场景,也保住了单一口径。

D2 的关键不是借多少,而是归还规则。没有归还日的借用就是永久占用;没有单次上限约束的借用会让借入方用低价请求把额度迅速耗光。决议把三个数字写死:1,500 万、2026-12-15、单次上限不变。

D3 是合规席位的否决点。创作者草稿包含未发布内容,跨租户共享会形成新的隐私面。评审没有讨论「能不能脱敏到足够安全」,而是直接限定样本类型:跨团队只使用合成样本与已脱敏样本。这条限制会降低评测覆盖率,但它是可执行的。

D4 是放量节奏。内容团队担心试点覆盖不到大促峰值,SRE 担心一次性放量会把平台 P95 推过 SLO。决议采用两阶段:先 10% 试点两周,用试点数据补足评测与容量证据,再开第二次评审判定全量。

基线怎么进入评审

评审的第一页不是方案,而是 13.1 的基线结论:12 条中 9 条通过、2 条部分、1 条失败,总体 conditional,没有 blocking 失败。

这张表改变了讨论方式。没有基线时,各方会从自己的立场出发争论「风险大不大」;有了基线,争论变成「哪条缺口必须在放量前关闭」。三个缺口自然对应到三个待办:评测语料缺隐私级别、合规缺删除演练、退役能力缺 owner 与迁移签字。

基线也限定了决议的上限。conditional 不能被写成 approve,因为那等于宣称所有条件已满足;它只能写成 conditional,并附条件清单。如果基线是 blocked,决议只能是 reject,除非先补掉 blocking 项再重新开会。

决议规则

评审结论只有三种:approve、conditional、reject。它们与基线结论有硬约束:

基线结论允许的决议说明
passapprove / conditional / reject仍可因其他风险拒绝
conditionalconditional / reject不能直接 approve
blockedreject必须先补 blocking 项

待办也分两类。blocking 待办未关闭前,最多只能试点;非 blocking 待办可以并行推进,不影响试点,但会影响下一次全量评审。每条待办必须有 owner 与截止日期,日期统一写成 YYYY-MM-DD,便于按日比较。

评审记录的实现

下面的模块把参会方、争议、决议、待办与基线判定放在同一个记录里。它先校验记录自身是否一致,再给出放量级别与待办统计。

// file: src/gov/baseline-review.ts
// tier: L2
// verify: node --test test/gov/baseline.test.ts

import { ECHO_BASELINE, evaluateBaseline } from "./baseline.ts";
import type { BaselineItem } from "./baseline.ts";

export type ReviewOutcome = "approve" | "conditional" | "reject";
export type RolloutLevel = "full" | "pilot" | "blocked";
export type ReviewRole = "provider" | "caller" | "platform" | "security" | "compliance" | "sre";

export interface ReviewParticipant {
  team: string;
  role: ReviewRole;
  /** 该参会方必须带来的证据 id;空数组表示只需表决。 */
  brings: readonly string[];
}

export interface ReviewDispute {
  id: string;
  topic: string;
  positions: readonly { team: string; position: string }[];
  resolution: string;
}

export interface ReviewTodo {
  id: string;
  owner: string;
  /** 截止日期统一写 YYYY-MM-DD,便于按日比较而不是按时间戳比较。 */
  due: string;
  /** true 表示未关闭前只能试点,不能全量放量。 */
  blocking: boolean;
  done: boolean;
}

export interface ReviewRecord {
  id: string;
  subject: string;
  baseline: readonly BaselineItem[];
  participants: readonly ReviewParticipant[];
  disputes: readonly ReviewDispute[];
  outcome: ReviewOutcome;
  todos: readonly ReviewTodo[];
}

export type ReviewIssueCode =
  | "missing-role"
  | "duplicate-todo"
  | "invalid-todo-owner"
  | "invalid-todo-due"
  | "blocked-baseline-not-rejected"
  | "approve-with-conditional-baseline"
  | "approve-with-open-blocking-todo";

export interface ReviewIssue {
  code: ReviewIssueCode;
  subject: string;
  detail: string;
}

const DATE = /^\d{4}-\d{2}-\d{2}$/;
const REQUIRED_ROLES: readonly ReviewRole[] = ["provider", "caller", "platform"];

function openBlockingTodos(record: ReviewRecord): ReviewTodo[] {
  return record.todos.filter((todo) => todo.blocking && !todo.done);
}

/**
 * 评审记录先自证一致,再谈结论:角色齐、日期合法、决议与基线不矛盾。
 * 一次返回全部问题,避免修一个报一个。
 */
export function validateReview(record: ReviewRecord): ReviewIssue[] {
  const issues: ReviewIssue[] = [];
  const roles = new Set(record.participants.map((person) => person.role));
  for (const role of REQUIRED_ROLES) {
    if (!roles.has(role)) {
      issues.push({ code: "missing-role", subject: role, detail: "required participant is absent" });
    }
  }
  for (const item of record.baseline) {
    if (item.domain !== "compliance" && item.domain !== "security") continue;
    if (evaluateBaseline([item]).conclusion !== "pass" && !roles.has(item.domain)) {
      issues.push({
        code: "missing-role",
        subject: item.domain,
        detail: `${item.id} is not pass and no ${item.domain} participant`,
      });
    }
  }
  const seen = new Set<string>();
  for (const todo of record.todos) {
    if (seen.has(todo.id)) {
      issues.push({ code: "duplicate-todo", subject: todo.id, detail: "todo id appears twice" });
    }
    seen.add(todo.id);
    if (todo.owner.trim() === "") {
      issues.push({ code: "invalid-todo-owner", subject: todo.id, detail: "owner is empty" });
    }
    if (!DATE.test(todo.due)) {
      issues.push({ code: "invalid-todo-due", subject: todo.id, detail: `bad due ${todo.due}` });
    }
  }
  const baseline = evaluateBaseline(record.baseline);
  if (baseline.conclusion === "blocked" && record.outcome !== "reject") {
    issues.push({
      code: "blocked-baseline-not-rejected",
      subject: record.id,
      detail: `blocking failures: ${baseline.blockingFailures.join(",")}`,
    });
  }
  if (baseline.conclusion === "conditional" && record.outcome === "approve") {
    issues.push({
      code: "approve-with-conditional-baseline",
      subject: record.id,
      detail: `conditions: ${baseline.conditions.join(",")}`,
    });
  }
  if (record.outcome === "approve" && openBlockingTodos(record).length > 0) {
    issues.push({
      code: "approve-with-open-blocking-todo",
      subject: record.id,
      detail: openBlockingTodos(record).map((todo) => todo.id).join(","),
    });
  }
  return issues;
}

/**
 * 放量级别只由三件事决定:基线是否 blocked、决议是否 reject、是否还有未关闭的
 * blocking 待办。三者都清才允许 full,否则最多 pilot。
 */
export function releaseLevel(record: ReviewRecord): RolloutLevel {
  const baseline = evaluateBaseline(record.baseline);
  if (record.outcome === "reject" || baseline.conclusion === "blocked") return "blocked";
  if (record.outcome === "approve" && baseline.conclusion === "pass" && openBlockingTodos(record).length === 0) {
    return "full";
  }
  return "pilot";
}

export type TodoState = "done" | "open" | "overdue";

export interface TodoReport {
  total: number;
  done: number;
  open: number;
  overdue: number;
  openBlocking: number;
  overdueBlocking: number;
}

export function trackTodos(record: ReviewRecord, asOf: string): TodoReport {
  if (!DATE.test(asOf)) throw new Error(`review: invalid as-of date ${asOf}`);
  const states = record.todos.map((todo): TodoState => {
    if (todo.done) return "done";
    return todo.due < asOf ? "overdue" : "open";
  });
  const count = (predicate: (todo: ReviewTodo, index: number) => boolean): number =>
    record.todos.filter((todo, index) => predicate(todo, index)).length;
  return {
    total: record.todos.length,
    done: count((_, index) => states[index] === "done"),
    open: count((_, index) => states[index] === "open"),
    overdue: count((_, index) => states[index] === "overdue"),
    openBlocking: count((todo) => todo.blocking && !todo.done),
    overdueBlocking: count((todo, index) => todo.blocking && states[index] === "overdue"),
  };
}

/** 2026-11-05 的内容团队放量评审,示例记录,不是生产会议纪要。 */
export const ECHO_REVIEW: ReviewRecord = {
  id: "gov-review-2026-11-05",
  subject: "内容团队把摘要与审核能力开放给创作者工具",
  baseline: ECHO_BASELINE,
  participants: [
    { team: "content", role: "provider", brings: ["capability-contract", "pilot-plan"] },
    { team: "creator-tools", role: "caller", brings: ["traffic-forecast"] },
    { team: "transaction", role: "caller", brings: ["quota-ledger"] },
    { team: "platform", role: "platform", brings: ["baseline-report"] },
    { team: "security", role: "security", brings: ["risk-register-2026-10"] },
    { team: "compliance", role: "compliance", brings: ["retention-policy"] },
    { team: "sre", role: "sre", brings: ["slo-dashboard"] },
  ],
  disputes: [
    {
      id: "D1",
      topic: "是否为创作者工具新建 reply.chat",
      positions: [
        { team: "content", position: "新建能力可以按草稿场景调提示与档位" },
        { team: "platform", position: "同一功能键不允许第二个在役能力" },
      ],
      resolution: "复用 chat.reply 2.1.0,只开放契约内的提示后缀与 economy 档位覆盖",
    },
    {
      id: "D2",
      topic: "内容团队借用交易团队 1,500 万微元额度",
      positions: [
        { team: "content", position: "大促期间需要临时扩容" },
        { team: "transaction", position: "借用必须在 12 月 15 日前归还" },
      ],
      resolution: "借用 1,500 万微元,单次上限不变,2026-12-15 前归还,逾期自动回收",
    },
    {
      id: "D3",
      topic: "创作者草稿能否进入跨团队评测集",
      positions: [
        { team: "content", position: "真实草稿最能覆盖边界" },
        { team: "compliance", position: "草稿含未发布内容,不能跨租户共享" },
      ],
      resolution: "跨团队只用合成样本与脱敏样本,租户样本留在租户域内",
    },
    {
      id: "D4",
      topic: "首日放量到 100% 还是先试点",
      positions: [
        { team: "content", position: "首日 100% 才能覆盖大促峰值" },
        { team: "sre", position: "基线与配额条件未关闭前只能试点" },
      ],
      resolution: "先 10% 试点两周,条件关闭后再评审全量",
    },
  ],
  outcome: "conditional",
  todos: [
    { id: "T1", owner: "eval-platform", due: "2026-11-12", blocking: true, done: false },
    { id: "T2", owner: "compliance", due: "2026-11-19", blocking: true, done: false },
    { id: "T3", owner: "platform-governance", due: "2026-11-30", blocking: false, done: false },
    { id: "T4", owner: "content", due: "2026-11-08", blocking: false, done: true },
  ],
};

示例样本上的运行结果

用上面的示例评审记录运行 node --test test/gov/baseline.test.ts,结果是:

检查结果
validateReview返回空数组,记录自身一致
releaseLevelpilot,不是 full,也不是 blocked
trackTodos(2026-11-05)4 条待办:1 完成、3 开放、0 逾期、2 条开放 blocking
trackTodos(2026-11-20)2 条逾期,其中 2 条是 blocking

releaseLevel 返回 pilot 的原因不是基线 blocked,而是基线 conditional 且还有两条 blocking 待办未关闭。这正是评审想要的结果:内容团队可以开始 10% 试点,但不能把 20,000 次日调用一次性推上去。

到 11 月 20 日,T1 与 T2 都已逾期,且都是 blocking。这时试点本身也要降速:逾期 blocking 待办说明条件没有按计划关闭,继续扩大试点会把未验证的风险带给更多创作者。待办跟踪的意义就在这里,它把「会开完了」变成「条件关闭了没有」。

一次反例:为什么 approve 会被打回

把示例记录的决议改成 approve,再删掉合规席位,校验会返回三类问题:基线 conditional 却直接 approve、存在未关闭的 blocking 待办、合规条目未通过却没有合规参会方。

问题码触发条件后果
approve-with-conditional-baseline基线 conditional 且决议 approve决议无效,退回重审
approve-with-open-blocking-todo决议 approve 且存在未关闭 blocking 待办只能按 pilot 执行
missing-role必需席位缺席或相关条目未通过相关议题不得表决

反例说明一件事:评审记录不是会议纪要,而是可执行的约束。字段之间互相校验,才能防止「会上说得很严、纪要写成同意」。

评审之后

散会不是结束。决议要进入三个地方:能力目录记录本次放量的版本与覆盖范围,配额账本记录借用的 1,500 万与归还日,待办系统记录四条任务与 owner。三处都对得上,评审才算落地。

下一次全量评审的入口也很明确:两条 blocking 待办关闭、试点数据覆盖大促峰值、基线重新跑一遍。三项都满足,才把决议从 conditional 改成 approve。如果试点期间出现重复下单、越权或隐私事件,直接回到 blocked,不再讨论放量比例。

小结

这场评审没有产生新技术,只产生了四个决议和四条待办。基线把讨论从「风险大不大」变成「哪条缺口必须先关」,决议规则把 conditional 限定为试点,待办跟踪把会议结论变成可检查的日期与 owner。

跨团队评审的价值不在于人多,而在于每个席位都带证据、每条决议都受基线约束、每个条件都有负责人。下一节离开具体评审,回看四卷的分工与边界。

阅读导航:上一节:13.1 治理基线:回声的平台化清单 · 下一节:13.3 全书回顾与继续阅读 。

常见错误与边界

  • 把评审开成汇报会。没有争议点、没有证据、没有待办的会议不产生治理结论。
  • 参会方按职级而不是职责选择。决策需要的是 owner、调用方、平台、安全与合规。
  • 基线只放在附件里。评审第一页必须是基线结论与缺口清单。
  • 基线 conditional 却写 approve。字段校验应当直接把它打回。
  • 待办没有 owner 或截止日期。没有这两项,条件永远不会被关闭。
  • blocking 与普通待办混在一起。未关闭的 blocking 待办必须限制放量级别。
  • 借用额度没有归还日。借用会变成永久占用,季度结算必然争议。
  • 用真实租户数据填评测集。跨团队共享前必须限定样本类型与隐私级别。
  • 试点没有退出条件。只写「先试点」不写何时扩大、何时停止,试点会无限期停留。
  • 评审记录只存本地。目录、账本与待办系统三处对不上,决议就无法执行。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《互联网产品的 AI 架构·平台化与治理》附录 C 平台化评审清单
  2. 《互联网产品的 AI 架构·平台化与治理》附录 B 能力目录与配额字段速查
  3. 《互联网产品的 AI 架构·平台化与治理》附录 A 治理角色与职责速查