产品发布 Messaging Agent
本页说明产品发布 Messaging Agent 如何在交互式委托下受控读取内部资料库中授权可见的发布资料页面,结合公开竞品调研生成跨渠道一致的 launch messaging 草稿。读完本页,你能理解这个场景需要哪些模块、未发布信息如何被授权边界而不是 prompt 约束,以及定位与 claims 规则为什么应该放在规则库而不是 Memory。
适用场景
产品营销团队需要为新功能或新产品准备定位、发布公告、FAQ、sales snippets 和内部 enablement 材料。发布资料——release notes、产品文档、内部讨论——存放在公司内部资料库或文档系统里,只有部分页面对本次发布相关;发布涉及官网、博客、邮件、社媒和销售话术多个渠道,人工分头撰写时各渠道的说法容易互相偏离,发布日临近时信息还会频繁变更。
典型触发时机:
- 发布日期确定,需要在发布窗口前产出全套跨渠道 messaging 草稿。
- 竞品在相近时间发布类似能力,需要调整定位表述。
- 发布日前产品范围或定价发生变更,需要同步更新全部材料。
工程挑战
- 未发布信息的读取需要真实边界:常见做法是把内部资料明文粘进 prompt——这等于把机密内容交给任务描述自由处置,谁读过、读了多少、有没有外流都无从约束。正确做法是让 Agent 在授权边界内自行读取资料库中授权可见的页面,读取行为全程留痕。
- 跨渠道一致性靠人工核对不可靠:定位表述、claims 措辞在官网、邮件、销售话术之间逐渐漂移,发布日前的每次变更都要人工同步到全部材料,漏一处就是口径事故。
- 内部调研与公开调研必须隔离:竞品调研走公开网页,内部资料读取走授权页面;一旦两条通道混用,未发布细节就可能被带进公开查询。
模块组合
| 模块 | 角色 | 说明 |
|---|---|---|
| GenAuth | 核心 | 内部资料库授权可见页面的只读委托、撤销与审计链;委托清单之外的发布项目不可读。 |
| Web Agent | 核心 | 受控会话读取授权的内部发布资料页面,并通过 WebSearch 查询竞品发布和公开行业资料,两条通道严格隔离。 |
| GUMem | 不使用 | 产品定位与 claims 规则属于版本化规则,放在你的规则库(policy store)里按版本注入;发布草稿与口径决策属于业务状态,归档到你的发布资料库——都不属于 Memory。 |
权限与委托边界
Agent 本身不持有任何固有权限。每次发布任务的实际权限是三个集合的交集:用户真实权限 ∩ 本次显式委托范围 ∩ 企业批准边界。落到这个场景:
- 委托范围只覆盖"读取当前发布项目在内部资料库中授权可见的页面,查询公开市场页面",不包含读取其他产品线资料、修改产品文档或对外发布。
- 委托凭证短时效,单次发布任务建议分钟级有效期,过期后需重新委托。
- 用户或管理员可随时撤销授权;撤销后新的资料读取请求立即失败。
- 越权尝试(例如读取其他发布项目的机密资料)会被拒绝并留痕——审计链覆盖全部尝试,不只是成功行为。
注意:SDK 示例申请的是产品级 scope(如 webagent.do_anything:read)。域名清单、页面范围和动作白名单这类细粒度边界由 GenAuth 的 Agent Profile 或策略层配置强制执行,不由任务 prompt 承担;本页示例未展示该配置。完整语义见 Delegate Token 与缩权。
工作流程
用户选择本次发布项目,确定发布范围、目标渠道和授权可见的资料页面清单。
GenAuth 发起交互式委托;用户在 Qoni Console 确认后,服务端回调兑换出最小权限凭证。
应用从规则库读取当前版本的产品定位口径和 claims 规则,注入任务描述。
Web Agent 在受控会话中打开授权可见的发布资料页面,读取 release notes、文档和讨论要点。
检查点:未发布信息只在授权边界内读取和使用;后续公开网页查询的内容不应包含任何未发布细节。
Web Agent 通过 WebSearch 查询竞品发布和公开行业资料,每条外部事实保留来源 URL。
Agent 生成跨渠道 messaging 草稿:定位表述、发布公告、FAQ、sales snippets 和 enablement 材料,并逐渠道核对一致性。
Agent 输出全套草稿、来源清单和待确认差异,附 audit id;应用侧校验后交产品与法务确认。
检查点:各渠道草稿中的产品 claims 应互相一致且符合 claims 规则版本;与既有定位冲突的表述应标记为待确认,不静默采用。
示例代码
下面的示例使用官方 Qoni SDK(@qoniai/qoni)把这个场景接到你的服务端:交互式委托(完整 callback)→ 从规则库读取定位与 claims 规则 → 一次 doAnything.run() 受控读取授权资料页并完成公开调研与建稿 → 解析并校验草稿。注意 prompt 只指向授权可见的内部资料页面,不把内部资料明文塞进任务描述。
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 startLaunchTask(userId: string, taskId: string) {
// 1. 交互式委托:涉及内部资料库登录与机密资料读取,让用户在 Qoni Console 确认授权
const { data: authorization } = await qoni.delegateToken({
mode: 'interactive',
agent: 'product-launch-messaging',
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 loadLaunchTask(query.get('state')!) // 你的任务存储
return runLaunchTask(grant, task)
}
async function runLaunchTask(
grant: { token: string; auditId: string; grantedScopes: string[] },
task: { releaseId: string; releasePages: string[]; channels: string[] },
) {
// 2. 任务前:从你的规则库读取当前版本的定位与 claims 规则(不是 Memory)
const policy = await loadLaunchPolicy() // 例如 { version: '2026-08', positioning: ..., claimsRules: [...] }
// 3. 一次调用完成读取与建稿:prompt 指向授权可见的内部资料页,
// 不把未发布内容明文塞进任务描述
const run = await qoni.doAnything.run({
token: grant.token,
prompt: `
Open and read these internal release pages for release
"${task.releaseId}" (they are within your grant):
${task.releasePages.join(', ')}.
Research competitor launches on the public web, keeping a source
URL for every external fact. Draft cross-channel messaging for
${task.channels.join(', ')}: positioning, announcement, FAQs,
sales snippets and enablement. Return drafts as a JSON array of
{ channel, draft, claimIds, sourceUrls } objects, where sourceUrls
lists the public source URL for every external fact and the granted
internal release page URL for internal references, and flag any
cross-channel claim conflicts. Never include unreleased details
in any public query or page visit. Do not edit or publish anything.
Launch policy (version ${policy.version}):
${JSON.stringify(policy)}
`,
capture: { screenshots: true },
})
const result = await run.wait({
// 资料库登录墙 / 二次确认:转发给用户,由人处理
onInteraction: (interaction) => notifyUserActionRequired(interaction),
})
// 4. 应用侧解析并校验输出契约:缺少渠道、claims 标注或来源清单的草稿不进交付物
// 外部事实必须带公开来源 URL;引用内部资料的条目标注对应授权资料页 URL
const drafts = parseLaunchDrafts(result.output).filter(
(d) => d.channel && d.claimIds?.length && d.sourceUrls?.length > 0,
)
return {
drafts,
artifacts: result.artifacts, // 步骤截图,随草稿归档到你的发布资料库
policyVersion: policy.version,
audit: { auditId: grant.auditId, permissionBoundary: grant.grantedScopes },
}
}输出结构由任务描述约定:这里约定返回 { channel, draft, claimIds, sourceUrls } 数组,应用侧 parseLaunchDrafts 负责解析与校验,缺少渠道、claims 标注或来源清单(sourceUrls)的条目被直接丢弃——外部事实必须带公开来源 URL,引用内部资料的条目以授权资料页 URL 作为来源。SDK 顶层只返回通用的 RunResult(runId、status、output、artifacts 等)。
数据与记忆边界
这个场景涉及四类数据,本场景不使用 GUMem:
- 版本化规则:产品定位口径、claims 规则——放在规则库(policy store)里按版本管理,每份草稿引用规则版本号。
- 业务状态:发布资料、跨渠道草稿、口径决策——留在你的内部资料库和发布归档中,未发布信息不出授权边界。
- 审计记录:
grantId与auditId构成的委托与行为链,包括每次内部资料页的读取——由 GenAuth 维护。 - 用户 Memory(可选):只有用户明确确认的长期个人偏好才属于 GUMem;发布口径是团队级规则,不是个人记忆,本场景默认不召回也不写回。
失败处理
| 情况 | 推荐处理 |
|---|---|
| 授权资料页访问被拒 | 按边界拒绝处理并留痕,提示用户确认委托范围,不降级为猜测。 |
| 竞品或行业信息无可靠来源 | 标记为待确认,不把无来源结论写进对外材料。 |
| 委托范围外的资料请求 | 直接拒绝并记录,事后可在审计链中查到未遂访问。 |
| 输出草稿缺少渠道、claims 标注或来源清单 | 应用侧校验直接丢弃该条草稿,并标注丢弃数量和依据的规则版本。 |
生产注意点
未发布信息不能进入公开网页任务,也不应明文粘贴进任务描述——受控读取授权页面是这个场景的核心价值。对外文案必须经过产品、法务或负责人确认。发布日前信息变更频繁,交付物应标注生成时间、依据的资料页面和规则版本,避免旧版草稿被误用为最终口径。
下一步
- 阅读 快速开始 跑通 Agent 身份与委托的最短路径。
- 阅读 授权与浏览器沙盒 了解受控会话的安全边界。
- 继续查看 Campaign Brief Agent 了解相邻场景。