跳转到主要内容
指南

如何安全推出具备 actor 感知的策略

一种分阶段流程,用于将对人类、AI 辅助和 agent 流量的观察逐步推进到按 route 强制策略,并在每一步都进行测量和回滚。

Toshendra Sharma

作者 Toshendra Sharma创始人兼 CEO,AgentCredia · Updated November 13, 2026

中级 · 10 分钟阅读

推出 actor 感知策略时可能会出什么问题?

actor 感知策略会根据是谁或什么在执行操作来改变应用的响应。做得好时,它能让人获得顺畅体验,为合法 agent 提供结构化路径,并且只在真正重要的地方增加阻力。做得不好时,它会把真实用户挡在门外,破坏客户依赖的集成,并制造没人能解释的支持工单。

差异很少在于策略逻辑本身,而在于推出方式。(关于策略模型本身,参见 为人类和 AI agent 设计应用级策略。) 本指南描述了团队可以针对任何 route 采用的分阶段流程: 观察,预览,小范围强制,然后扩展。每个阶段都有明确的退出标准和快速回退方式。

三种模式

可以把每条策略理解为在每个 route 上以三种模式之一运行:

  • 观察。 分类结果会被记录下来。应用的响应与没有这些结果时完全一致。
  • 预览。 策略会被评估并记录其决策,但不会被应用。你可以看到本来会发生什么。
  • 强制执行。 决策会应用到真实请求上。

模式属于 route 和策略的组合,而不是整个应用。你的文档页面可以处于强制执行状态,而结账流程仍在观察。

第 1 步: 观察,直到你知道基线

从每个 route 都处于观察模式开始。至少收集:

  • route 和 method,
  • actor 类别(Human, Human + AI, Agent 或 Unknown)及其依据(verified, declared, inferred 或 unknown), 如 我们对应用流量的分类体系 所定义,
  • response 状态,以及该 route 的一个简单结果标记,例如 "account created" 或 "search returned results"。

运行足够长的时间,以覆盖正常的每周周期以及期间发布的任何版本。然后为你关心的每个 route 记录基线: 类别构成、Unknown 比例,以及各类别的结果率。后续所有数据都要与这些数值对比。

退出标准: 你可以针对每个候选 route 描述谁在使用它,以及结果如何随类别而变化。

第 2 步: 将策略写成数据

当策略以数据形式存在,而不是散落在各处的 if 语句中时,它们更容易审查、测试和回滚。

TS
type ActorClass = "human" | "human_ai" | "agent" | "unknown";
type Basis = "verified" | "declared" | "inferred" | "unknown";
type Action = "allow" | "adapt" | "challenge" | "review" | "limit";
type Mode = "observe" | "preview" | "enforce";

interface Rule {
  actor: ActorClass | "*";
  basis?: Basis[];
  action: Action;
}

interface RoutePolicy {
  route: string;
  mode: Mode;
  enforcePercent: number;
  rules: Rule[];
  fallback: Action;
}

export const policies: RoutePolicy[] = [
  {
    route: "GET /api/catalog",
    mode: "preview",
    enforcePercent: 0,
    rules: [
      { actor: "agent", basis: ["verified"], action: "allow" },
      { actor: "agent", basis: ["declared", "inferred"], action: "limit" },
    ],
    fallback: "allow",
  },
];

每条策略都有一个明确的 fallback。这是用于 Unknown 结果、超时以及任何规则未覆盖情况的 action。刻意选择它,是整个流程中最重要的安全决策。

第 3 步: 在一个地方进行评估

统一的 evaluation 函数可以让不同模式下的行为保持一致。

TS
export function decide(policy: RoutePolicy, actor: ActorClass, basis: Basis): Action {
  for (const rule of policy.rules) {
    const actorMatch = rule.actor === "*" || rule.actor === actor;
    const basisMatch = !rule.basis || rule.basis.includes(basis);
    if (actorMatch && basisMatch) return rule.action;
  }
  return policy.fallback;
}

然后应用模式:

TS
export function applyPolicy(policy: RoutePolicy, actor: ActorClass, basis: Basis, requestKey: string) {
  const decision = decide(policy, actor, basis);
  const inSlice = bucket(requestKey) < policy.enforcePercent;
  const enforced = policy.mode === "enforce" && inSlice;
  record({ route: policy.route, mode: policy.mode, actor, basis, decision, enforced });
  return enforced ? decision : "allow";
}

function bucket(key: string): number {
  let h = 0;
  for (const c of key) h = (h * 31 + c.charCodeAt(0)) >>> 0;
  return h % 100;
}

stable bucket 的含义是同一个 session 或 client 会始终留在强制切片内或切片外,这样用户不会看到请求之间的行为突然变化。

第 4 步: 在强制执行前如何测量误报?

将一个 route 切换到预览。此时每个请求都会记录它本来会得到的决策。你会在这里了解策略是否正确。

误报是指策略本来会限制,但实际上应该被允许的请求。你不能只通过分类结果来测量,因为分类本身正是你正在测试的内容。应当用独立信号来衡量合法使用:

  • 请求完成了正常的成功流程,例如已登录用户保存了工作,
  • client 是已知合作伙伴集成,或是你已经约定要服务的已验证 operator,
  • 支持工单或用户报告提到了受影响的 route,
  • 对一部分本应被限制的请求进行人工审查。

手动审查一个样本。阅读二十个真实的、策略本来会 challenge 的请求,是发现规则过宽的最快方法。

退出标准: 本应被限制的集合主要由你确实打算限制的流量构成,并且你能为发现的每一类误报给出解释。

第 5 步: 对一小部分流量执行强制

将该 route 切换为带有一个很小 enforcePercent 的强制执行。切片要足够小,以便错误只影响少量请求,同时也要足够大,能在一两天内产生可测信号。

关注与基线相同的指标:

  • 该 route 上各类别的结果率,
  • error 率和 challenge 完成率,
  • 提到该 route 的支持量,
  • 延迟,因为 evaluation 位于请求路径上。

如果强制切片中的 Human 流量结果相较于其余流量下降,就要在扩展前停止并调查。

第 6 步: 逐步扩展

按步骤增加切片,例如从一小部分到四分之一,再到一半,最后到全部流量,并在每一步停留足够长时间以观察一个正常周期。若前面的阶段做得好,扩展过程会很平稳。这正是目标。

选择回退和故障行为

提前决定当证据缺失或 evaluation 无法运行时会发生什么。

  • Fail open 表示请求会按没有 actor 上下文时的方式继续执行。适用于低风险 route,例如公开页面、文档和只读目录 endpoint。
  • Fail closed 表示会应用你现有的更强检查,例如重新认证或 review 队列。适用于你已经具备此类检查的高影响操作。

不要让 timeout 或 error 产生 Human 结果。缺失证据属于 Unknown,而 Unknown 采用该 route 的 fallback。我们关于 为什么在分类 AI 流量时 Unknown 是一个有效结果 的文章解释了这一规则为何重要。

TS
async function actorWithTimeout(evaluate: () => Promise<{ actor: ActorClass; basis: Basis }>) {
  const timeout = new Promise<{ actor: ActorClass; basis: Basis }>((resolve) =>
    setTimeout(() => resolve({ actor: "unknown", basis: "unknown" }), 150),
  );
  return Promise.race([evaluate(), timeout]);
}

如何快速回滚策略?

回滚应该是一次配置变更,而不是一次 deploy。将策略保存在团队可以快速编辑的存储中,并让以下操作立即生效:

  1. 将某个 route 恢复为预览,这样会停止强制执行,同时保留测量。
  2. 将 enforcePercent 设为零。
  3. 禁用单条规则,而不影响其他规则。

记录每次策略变更,以及变更者和时间。当结果发生变化时,第一个问题永远是: 什么变了。

避免对个人做出具有后果的决策

actor 分类描述的是关于一次交互的证据。它们不是对某个人的判断。不要把推断出的类别作为影响某人就业、信贷、教育、住房或基本服务获取的唯一依据。对于这些流程,应使用 actor 上下文来选择一个流程,例如路由到人工审查,而不是据此直接决定结果。

在强制执行前先沟通

会影响自动化 client 的策略,也会影响运行这些 client 的人和公司。适当沟通可以减少大量支持工作:

  1. 公布自动化访问规则。 说明哪些 route 开放、适用哪些限制,以及 client 如何使用带签名的请求表明身份,方法是 HTTP Message Signatures (RFC 9421) 和 IETF Web Bot Auth protocol draft。
  2. 提前告知合作伙伴和集成方。 如果某个已知集成在预览期间落入受限切片,请在强制执行前联系其负责人。
  3. 返回有用的响应。 当策略限制或 challenge 某个请求时,请返回清晰的 status code,例如 429 Too Many Requests 配合 Retry-After,并提供指向文档的链接。使用 Problem Details 格式的机器可读 body 可以为自动化 client 提供结构化原因。设计良好的 agent 在理解原因后会进行调整。
  4. 把证据交给支持团队。 当收到关于被阻止请求的工单时,支持团队应能够在不询问工程的情况下查到 route、类别、依据和策略决策。

会提前宣布变更并进行解释的团队,遇到的意外通常远少于悄悄切换规则的团队,而能够适应变化的 client 会随着时间更容易被识别。

每个 route 的检查清单

  • 已记录类别构成、Unknown 比例和结果的基线。
  • 策略已写成数据,并带有明确 fallback。
  • 已运行预览,已审查样本,已解释误报类别。
  • 已在小切片上开始强制执行,并监控指标。
  • 已选择故障行为: fail open 或 fail closed。
  • 在扩展前已测试回滚。

Proof of Actor 在这里的作用

Proof of Actor 正是围绕这个流程设计的。观察是默认状态,每个结果都携带其依据,而 Route 策略由你来定义,并在你自己的代码中按 route 进行强制执行。Detect, Understand, Route: 按这个顺序。

如果你想和我们的团队一起在自己的应用上完成一次推出,请 申请提前访问。若要了解端到端的决策流程,请阅读 Proof of Actor 如何工作。

来源

与我们一起构建。

加入 Proof of Actor founding developer program。

想先聊聊? 联系创始人