社媒草稿 Agent
本页说明社媒草稿 Agent 如何在交互式委托下登录社媒后台,读取 campaign 上下文和历史帖子,按平台生成待确认的内容草稿。读完本页,你能理解这个场景需要哪些模块、发布动作为什么必须留在委托范围之外,以及品牌规则这类数据为什么应该放在规则库而不是 Memory。
适用场景
Marketing 团队需要把一次 campaign 拆成 X、LinkedIn、公众号、邮件或社区渠道的草稿,但不希望 Agent 自动发布。各平台的长度、语气和格式差异大,人工逐平台改写既慢又容易偏离品牌规则;直接把社媒账号交给脚本,则会把发布、评论和私信权限一并交出去。
典型触发时机:
- 新 campaign 上线,需要在同一个工作日内产出多平台首发草稿。
- 一条产品更新公告需要按平台改写成不同长度和语气的版本。
- 品牌规则或平台偏好调整后,需要按新版本规则重写下一批草稿。
工程挑战
- 平台约束多且互相冲突:X 的长度限制、LinkedIn 的行业语气、公众号的图文结构各不相同,同一条 campaign 信息在逐平台改写中很容易漂离品牌规则,人工逐条核对成本高。
- 草稿与发布只隔一个按钮:多数社媒后台"保存草稿"和"发布"入口相邻,一次误触就是线上事故;"只写草稿"的意图需要机制保障,不能只靠 prompt 叮嘱。
- 借用运营账号权限过宽:运营登录态天然带发布、评论和私信能力,而建稿任务只需要"读取上下文、写入草稿箱"这一件事。
模块组合
| 模块 | 角色 | 说明 |
|---|---|---|
| GenAuth | 核心 | 社媒后台登录的交互式委托、撤销与审计链;发布与互动动作留在委托范围之外。 |
| Web Agent | 核心 | 受控会话登录社媒后台或 CMS(Profiles 可复用登录态),读取 campaign 上下文与历史帖子,把草稿写入草稿箱。 |
| GUMem | 可选 | 仅保存用户明确确认的长期语气偏好(例如惯用的开场方式)。品牌 voice、平台规则、禁用表达属于版本化规则,放在你的规则库(policy store)里按版本注入;历史表现数据属于业务状态,放在你的 analytics 存储——都不属于 Memory。 |
权限与委托边界
Agent 本身不持有任何固有权限。每次草稿任务的实际权限是三个集合的交集:用户真实权限 ∩ 本次显式委托范围 ∩ 企业批准边界。落到这个场景:
- 委托范围只覆盖"读取历史帖子与 campaign 资料、创建并更新草稿",不包含发布、评论、私信或批量互动。
- 委托凭证短时效,单次草稿任务建议分钟级有效期,过期后需重新委托。
- 用户或管理员可随时撤销授权;撤销后新的读取或建稿请求立即失败。
- 越权尝试(例如触发发布动作)会被拒绝并留痕——审计链覆盖全部尝试,不只是成功行为。
注意:SDK 示例申请的是产品级 scope(如 webagent.do_anything:read)。域名清单、页面范围和动作白名单这类细粒度边界由 GenAuth 的 Agent Profile 或策略层配置强制执行,不由任务 prompt 承担;本页示例未展示该配置。完整语义见 Delegate Token 与缩权。
工作流程
用户选择 campaign 和目标渠道(X、LinkedIn、公众号、邮件等)。
GenAuth 发起交互式委托;用户在 Qoni Console 确认后,服务端回调兑换出最小权限凭证。
应用从规则库读取当前版本的品牌 voice、平台规则和禁用表达,注入任务描述。
Web Agent 打开社媒后台或 CMS;首次访问时用户在受控会话中完成登录,后续任务可通过 Profiles 复用登录态。
检查点:登录墙、验证码或风控页出现时,Web Agent 应升级给人处理,而不是静默绕过。
Web Agent 读取 campaign 资料和历史帖子,按平台生成草稿,标注各平台在长度、语气和格式上的差异。
Web Agent 把草稿写入平台草稿箱,或返回给应用界面等待确认;输出附 audit id。
检查点:任何草稿都不应进入发布状态;后台界面上"保存草稿"与"发布"入口相邻时,该步骤应按风险动作过类型化关卡。
应用侧解析草稿输出并校验渠道标注;用户的采纳、修改和否决结果归档到你的内容库。
示例代码
下面的示例使用官方 Qoni SDK(@qoniai/qoni)把这个场景接到你的服务端:交互式委托(完整 callback)→ 从规则库读取品牌规范 → 一次 doAnything.run() 完成建稿 → 解析并校验草稿。
import { Qoni, QoniScopes } from '@qoniai/qoni'
const qoni = new Qoni({
accessKey: process.env.QONI_ACCESS_KEY!,
secretKey: process.env.QONI_SECRET_KEY!,
})
export async function startDraftTask(userId: string, taskId: string) {
// 1. 交互式委托:涉及社媒后台登录,让用户在 Qoni Console 确认授权
const { data: authorization } = await qoni.delegateToken({
mode: 'interactive',
agent: 'social-media-draft',
scopes: [QoniScopes.DO_ANYTHING_READ, QoniScopes.DO_ANYTHING_MANAGE],
redirectUri: 'https://app.example.com/qoni/callback',
state: taskId,
user: { id: userId },
expiresIn: 900, // 单次草稿任务给分钟级有效期
})
redirectUserTo(authorization.authorizationUrl)
}
// GET /qoni/callback —— 用户同意后在服务端兑换 grant,继续执行任务
export async function handleQoniCallback(request: Request) {
const query = new URL(request.url).searchParams
const { data: grant } = await qoni.completeDelegateToken({
grantId: query.get('grantId')!,
code: query.get('code')!,
state: query.get('state')!,
})
const task = await loadDraftTask(query.get('state')!) // 你的任务存储
return runDraftTask(grant, task)
}
async function runDraftTask(
grant: { token: string; auditId: string; grantedScopes: string[] },
task: { campaignName: string; channels: string[] },
) {
// 2. 任务前:从你的规则库读取当前版本的品牌规范(不是 Memory)
const policy = await loadBrandPolicy() // 例如 { version: '2026-08', voice: ..., platformRules: [...], forbiddenPhrases: [...] }
// 3. 一次调用完成建稿:登录后台、读取上下文、按平台写入草稿箱
const run = await qoni.doAnything.run({
token: grant.token,
prompt: `
Open our social media tool and read the campaign "${task.campaignName}"
plus recent posts for these channels: ${task.channels.join(', ')}.
Draft one post per channel, adapting length, tone and format.
Return drafts as a JSON array of { channel, draft, notes } objects.
Save every draft to the draft box only. Do not publish, comment,
send direct messages, or perform any bulk engagement.
Brand policy (version ${policy.version}):
${JSON.stringify(policy)}
`,
capture: { screenshots: true },
})
const result = await run.wait({
// 登录墙 / 验证码 / 发布确认弹窗:转发给用户,由人处理
onInteraction: (interaction) => notifyUserActionRequired(interaction),
})
// 4. 应用侧解析并校验输出契约:缺少渠道标注的条目不进交付物
const drafts = parseDrafts(result.output).filter((d) => d.channel && d.draft)
return {
drafts,
artifacts: result.artifacts, // 步骤截图,随草稿归档到你的内容库
policyVersion: policy.version,
audit: { auditId: grant.auditId, permissionBoundary: grant.grantedScopes },
}
}输出结构由任务描述约定:这里约定返回 { channel, draft, notes } 数组,应用侧 parseDrafts 负责解析与校验,缺少 channel 或 draft 的条目被直接丢弃。SDK 顶层只返回通用的 RunResult(runId、status、output、artifacts 等)。
数据与记忆边界
这个场景涉及四类数据,只有最后一类属于 GUMem:
- 版本化规则:品牌 voice、平台规则、禁用表达——放在规则库(policy store)里按版本管理,草稿输出引用规则版本号。
- 业务状态:草稿、采纳与否决结果、历史帖子表现数据——归档到你的内容库和 analytics 存储,供复盘与追溯。
- 审计记录:
grantId与auditId构成的委托与行为链——由 GenAuth 维护。 - 用户 Memory(可选):仅保存用户明确确认的采纳、修改和否决结果沉淀出的长期语气偏好——这才是 GUMem 的位置;单次建稿默认不召回也不写回。
失败处理
| 情况 | 推荐处理 |
|---|---|
| 登录态失效 | 任务挂起,通知用户重新登录,从断点继续。 |
| 后台改版导致建稿失败 | 按失败处理并回放会话记录,没有证据证明写入成功就不算成功。 |
| 委托范围外的发布或互动请求 | 直接拒绝并记录,事后可在审计链中查到未遂动作。 |
| 输出草稿缺少渠道标注或含禁用表达 | 应用侧校验直接剔除该条草稿,并在交付物中说明剔除原因和依据的规则版本。 |
生产注意点
默认禁止自动发布、自动评论、自动私信或批量互动。草稿必须经过用户确认。对同一平台的建稿和读取频率应设置上限,避免触发平台风控;遇到风控或异常验证时升级给人处理,不尝试绕过。品牌规则更新后应在规则库中发布新版本,避免旧规则继续约束新草稿。
下一步
- 阅读 快速开始 跑通 Agent 身份与委托的最短路径。
- 阅读 授权与浏览器沙盒 了解受控会话的安全边界。
- 继续查看 Newsletter Curation Agent 了解相邻场景。