别把 userId 交给内容 Agent:聚合优先的数据契约

如果内容分析服务的第一版接口长这样,后面通常会越来越难收拾:

ts 复制代码
interface ReaderEvent {
  userId: string;
  contentId: string;
  channel: string;
  action: "view" | "like" | "collect";
  occurredAt: string;
  device?: string;
  region?: string;
}

它很灵活。Agent 可以按用户、设备、地区、时间随意切片,今天做选题复盘,明天做用户画像,后天再把原始轨迹塞进提示词。

问题也正在这里:一个服务原本只想回答"哪类文章值得继续写",却顺手获得了识别个人、拼接轨迹和跨用途使用数据的能力。对于人少、维护能力有限的小团队,这不是免费能力,而是一笔持续的安全与治理债务。

GitHub 在 2026 年 9 月 4 日发布了新的 Star history REST API endpoint。官方给出的能力是:返回带时间戳的历史 Star 数量,但不暴露具体 stargazer 身份。此前相关 stargazer 列表接口已收紧访问,用于保护用户隐私。

这里不讨论 GitHub 内部用了什么匿名化算法------官方资料没有这样说明。值得借鉴的是 API 边界:调用方要的是增长曲线,就给增长曲线,不必顺带给一份身份清单。

从输出开始设计,而不是从事件开始

先假设 Agent 最终只需要生成这种复盘:

json 复制代码
{
  "pillar": "agent-architecture",
  "window": "72h",
  "samples": 6,
  "medianViews": 128,
  "medianCollections": 9,
  "confidence": "low",
  "note": "样本较少,仅用于下一轮选题"
}

它要比较内容支柱,需要文章维度的窗口快照;它要表达不确定性,需要样本数;它不需要任何一个读者的身份。

因此数据链路可以反过来设计:

text 复制代码
Decision → Metric contract → Aggregate job → Ephemeral input

而不是:

text 复制代码
Collect everything → Store forever → Let Agent decide later

事件只是临时输入

如果平台或自有站点确实以事件形式提供数据,可以让事件只活在聚合窗口中:

ts 复制代码
type EphemeralEvent = {
  eventId: string;
  contentId: string;
  channel: string;
  action: "view" | "like" | "collect" | "comment";
  occurredAt: string;
  expiresAt: string;
};

type MetricSnapshot = {
  contentId: string;
  channel: string;
  window: "24h" | "72h";
  windowEndsAt: string;
  sampledAt: string;
  status: "available" | "not_due" | "unavailable";
  values?: Partial<Record<EphemeralEvent["action"], number>>;
  schemaVersion: 1;
};

EphemeralEvent 没有 userId,因为这项决策根本用不到它。若另一个业务确实需要身份,应该新建带独立目的、权限和保留期的契约,而不是污染内容复盘服务。

聚合任务也不应该在窗口未结束时抢跑:

ts 复制代码
function buildSnapshot(now: Date, endsAt: Date): MetricSnapshot["status"] {
  if (now < endsAt) return "not_due";
  return "available";
}

这行看似普通,却能挡住一种常见的数据污染:23 小时 48 分的当前总量,被自动化任务写成"24 小时数据"。

0 不是 unavailable

做跨平台内容复盘时,最危险的默认值可能就是 0。

一个平台公开显示 0 次收藏,是事实;页面没加载出来、指标暂不提供、登录过期导致取数失败,都不是 0。如果 schema 只允许数字,Agent 会把缺失当表现差,从而给出错误选题建议。

所以快照至少要保留状态和来源:

json 复制代码
{
  "contentId": "post_xxx",
  "window": "24h",
  "status": "unavailable",
  "sampledAt": "2026-09-06T20:45:30+08:00",
  "source": "public_page",
  "reason": "metric_not_exposed"
}

这里的 reason 应该是白名单枚举,不要把 Cookie、页面正文或异常堆栈直接带给 Agent。

小样本必须允许"不回答"

去掉 userId 以后,过细的维度仍可能把人重新找出来。一个只有一次互动的分钟级、地区级、设备级桶,匿名性非常有限。

可以在聚合出口增加抑制规则:

ts 复制代码
function releaseBucket<T extends { count: number }>(bucket: T, min = 10) {
  if (bucket.count < min) {
    return { status: "suppressed" as const, count: null };
  }
  return { status: "available" as const, count: bucket.count };
}

10 只是示例,不是通用合规阈值。真正的 min 应由数据敏感度、业务规模和可关联性决定。对于数据量本来就小的一人公司,更好的办法常常是直接砍掉地区、设备等低价值维度。

Agent 看到 suppressed 后应停止下钻,而不是换一种分组继续逼近同一批人。

给 Agent 的是任务视图,不是数据库权限

数据权限按"人类 / AI"二分不够用。写作 Agent、复盘 Agent、发布 Agent 的工作不同,能看到的数据也应不同。

yaml 复制代码
views:
  review_agent:
    allow:
      - content_metrics_24h
      - content_metrics_72h
      - topic_pillar
      - cover_style
    deny:
      - raw_events
      - viewer_identity
      - cross_account_join

  publishing_agent:
    allow:
      - current_draft
      - current_assets
      - publish_checklist
    deny:
      - analytics_raw_store
      - unrelated_projects

每次调用还应记录 viewVersion、用途、读取条数和输出结论。这样口径变化后,才能解释为什么两次复盘结果不一致。

一个最小的可观测记录可以是:

ts 复制代码
type AgentDataAccessLog = {
  jobId: string;
  role: "review_agent" | "publishing_agent";
  purpose: string;
  viewVersion: string;
  rowCount: number;
  accessedAt: string;
};

它记录"哪项工作读了哪个聚合视图",而不是再复制一份原始用户数据。

四个容易忽略的失败分支

第一,聚合任务失败。不要删除原始输入后才发现快照没写成功;可以采用"快照提交成功 → 标记输入可清理"的两阶段状态。

第二,平台指标延迟。窗口结束不代表数据立刻稳定,允许设置小幅采样延迟,并保留实际 sampledAt。

第三,口径升级。新增指标或修改去重规则时提升 schemaVersion,不要把不同版本直接放在一条趋势线上。

第四,样本太少。系统要能输出"证据不足",而不是为了完成任务强行给标题或封面风格排出胜负。

这对一人公司有什么意义

小团队最缺的通常不是更多原始数据,而是口径稳定、能重复运行的判断流程。少存一个身份字段,意味着少一类权限、少一个泄露面、少一段需要解释的生命周期。

这也是我们做 Tipkay 时坚持岗位拆分的原因。Tipkay 面向一人公司、小微企业和小团队,不同垂类 AI 员工分别处理内容复盘、写作、配图和发布准备。一个岗位能完成工作所需的数据,未必应该自动共享给另一个岗位。按任务给最小视图,比把整套数据塞进一个万能 Agent 更容易长期维护。

一个人的生意,也能有一支专业团队。但专业团队的标志之一,就是有人负责什么、能看什么、什么时候必须停下来,都有清楚边界。

最后

数据最小化不是拒绝分析,而是逼迫系统把问题说清楚。

如果要判断选题,就保存可比的选题快照;如果要判断标题,就记录同窗口的标题实验;如果一个决策不需要知道读者是谁,就别让 userId 进入它的默认契约。

能用趋势回答的问题,不要靠追踪个人来解决。

资料:

相关推荐
QuZhengRong6 分钟前
【News】2026.10.09–10.15 AI 科技周报
人工智能·科技·openai·苹果·新闻
console.log('npc')15 分钟前
[特殊字符] AI Agent 工作台前端实战教程 · 新手入门指南
前端·人工智能
温温top16 分钟前
AnythingLLM+deepseek+RAG知识库实现AI客服
人工智能
果霸大叔25 分钟前
AI 网关第 10 篇:协议无关层——一个请求如何被认出、翻译、透传
人工智能
adinnet202625 分钟前
企业碳资产管理:从碳盘查到碳交易,把排放理清
人工智能·深度学习·自动化
林墨聊AIGC25 分钟前
AI分镜模板:一键生成高效短剧视觉流程
人工智能·ai作画·aigc·音视频·ai写作
Pocker_Spades_A26 分钟前
论文精读(十):面向具身人工智能的物体目标导航综述:从“找东西“到启元机器人的导航基础能力
人工智能·机器人
whyutianict_vv27 分钟前
AI 大模型技术架构解析:从推理链路到训练流程
人工智能·ai·架构·开发
一根会思考芦苇28 分钟前
王阳明五百年前的知行合一,能解决今天的 AI 对齐难题吗?
人工智能
方方洛28 分钟前
ai-agent教程-12-生产部署
人工智能·llm·agent