Quickstart
本页用一个 Email Assistant Agent 示例,说明如何从 Qoni SDK 调用开始,把 GenAuth、Web Agent 和 GUMem 组合成一个最小 Agent 服务。
Toward the agentic web
Qoni 的目标是让 Agent 成为 Web 的一等公民。Web Agent 不只是在后台模拟点击,也不只把页面渲染给人看;它把 Human、Agent 和 Web 放进同一个可审计的协作层,让 Agent 能在授权边界内读取、行动、产出结果,并在需要时把状态渲染回人类。
Task
这个 Agent 会在用户授权下打开邮箱,读取需要处理的邮件,使用用户的回复偏好生成草稿,并在需要时通过 Web Agent 查询公开网页上下文。它不会自动发送邮件。
What you will build
你会构建一个后端服务接口,例如 POST /agent/email-drafts。调用后它会完成下面的流程:
- 识别当前用户和 Email Assistant Agent。
- 让用户把有限权限委托给 Agent。
- 一次调用
doAnything.run(),传入完整任务描述和已召回的用户偏好。 - 以类型化事件和步骤截图的形式流式返回 Agent loop 的执行过程。
- 在需要时让用户在受控浏览器会话中完成邮箱登录。
- 在同一个 loop 内读取任务邮件、查询公开网页上下文并生成草稿;用户偏好由你的应用在任务前后读写 GUMem,长期偏好写入前必须经过应用确认。
- 返回来源、草稿、步骤截图、权限边界和 audit 信息。
Usage
从一个 SDK 接口开始。Qoni 当前提供官方 Node.js / TypeScript SDK(@qoniai/qoni),其他语言通过 HTTP API 调用;两种方式使用同一条概念流程。
1. Choose an SDK interface
import { Qoni } from '@qoniai/qoni'
const qoni = new Qoni({
accessKey: process.env.QONI_ACCESS_KEY!,
secretKey: process.env.QONI_SECRET_KEY!,
})export QONI_ACCESS_KEY="<access-key>"
export QONI_SECRET_KEY="<secret-key>"
export QONI_HOST="https://<your-qoni-console-or-sdk-gateway>"
# HTTP 直连需自行按 AK/SK 构造 Authorization 签名头官方统一 SDK 目前为 Node/TypeScript(@qoniai/qoni);其他语言直接调用 HTTP API。私有部署的 host 等构造器选项见 Qoni SDK。访问密钥只保存在服务端。
2. Identify the user and Agent
你的应用先完成用户登录,然后把用户身份、Agent 标识和任务输入传给后端服务。
const task = {
id: 'task-001',
userId: 'user_123',
agentKey: 'email-assistant',
instruction: '整理今天需要回复的客户邮件,并生成回复草稿'
}这里的 userId 来自你的登录系统或 GenAuth 会话。agentKey 是你在应用代码中给智能体定义的稳定标识,例如 email-assistant。
需要 agentKey 是因为委托权限不是发给任意后端代码,而是发给某个明确的智能体。同一个智能体每次运行都使用同一个 agentKey,这样 delegation 和 audit 记录就能知道是谁代表用户执行了任务。
在 GenAuth 中,Agent Profile 是用于描述智能体身份的托管配置,可以包含智能体的名称、用途、允许的 scope、拒绝的 scope 和审计归属。生产环境中,你可以把 agentKey 映射到 GenAuth 的 Agent Profile 或权限模板。
本快速开始主要说明智能体身份和委托授权。如果你还需要使用 GenAuth SDK 完成用户登录、注册或 token 校验,可以先阅读 使用 API & SDK 完成认证。
3. 委托有限权限
智能体需要代表用户访问资源,所以必须先获得本次任务的有限委托。委托只覆盖当前任务需要的 scope,并带明确的有效期。
import { Qoni, QoniScopes } from "@qoniai/qoni";
const qoni = new Qoni({
accessKey: process.env.QONI_ACCESS_KEY!,
secretKey: process.env.QONI_SECRET_KEY!,
});
// 用户级授权确认:先创建授权请求
const { data: authorization } = await qoni.delegateToken({
mode: "interactive",
agent: "email-assistant",
scopes: [
QoniScopes.DO_ANYTHING_READ,
QoniScopes.DO_ANYTHING_MANAGE,
QoniScopes.GUMEM_MEMORY_READ,
QoniScopes.GUMEM_MEMORY_WRITE,
],
redirectUri: "http://localhost:3000/qoni/callback",
state: "task-001",
user: { id: task.userId },
expiresIn: 900, // 秒(60–86400),按任务时长给
});
redirectUserTo(authorization.authorizationUrl);
// 下面的代码稍后在 GET /qoni/callback 服务端路由中执行
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")!,
});
// grant.token 只保存在服务端,供后续产品调用使用
return grant;
}curl -X POST "$QONI_HOST/api/v3/eak/delegations" \
-H "Authorization: <AK/SK 签名>" \
-H "Content-Type: application/json" \
-d '{
"mode": "interactive",
"agent": "email-assistant",
"scopes": [
"webagent.do_anything:read",
"webagent.do_anything:manage",
"gumem.memory:read",
"gumem.memory:write"
],
"redirectUri": "http://localhost:3000/qoni/callback",
"state": "task-001",
"userId": "user_123",
"expiresIn": 900
}'
# 首次响应包含 authorizationUrl 和 grantId,不包含委托令牌。
# 将用户带到 authorizationUrl;用户同意后,在服务端回调中完成兑换:
curl -X POST "$QONI_HOST/api/v3/eak/delegations/complete" \
-H "Authorization: <AK/SK 签名>" \
-H "Content-Type: application/json" \
-d '{
"grantId": "<callback-grant-id>",
"code": "<callback-code>",
"state": "task-001"
}'
# 完成接口响应中的委托令牌字段名是 delegationToken。完整流程与 scope 清单
上面是最短路径。授权确认的完整流程、真实 scope 常量、令牌兑换与吊销时效,见 GenAuth 的 第一次委托:30 分钟跑通。官方统一 SDK 目前为 Node/TypeScript(@qoniai/qoni),其他语言直接调用 HTTP API。
检查点:
- 委托权限有明确过期时间。
- Agent 不能发送、删除或修改邮箱设置。
- 服务端记录
userId、agentKey、scope、task id 和 audit id。
4. Run the Agent loop with doAnything
一次调用 doAnything.run(),把完整任务交给 Web Agent。这个调用内部会运行一个 Agent loop:打开邮箱、在需要时让用户完成登录、读取任务邮件、查询公开网页上下文,并生成回复草稿。用户偏好由你的应用在任务前通过 GUMem 召回并注入任务描述——Agent 不能绕过你的应用直接读写 Memory。不要把邮箱明文密码传给 Agent,也不要在应用服务中保存用户邮箱密码。
执行过程通过任务句柄实时返回:run.events() 提供类型化事件流(进度、消息、截图、交互、完成),wait() 的 onScreenshot 和 onInteraction 回调可以把步骤截图和需要用户操作的请求转发给前端。
// 任务前:召回用户已确认的回复偏好,由你的应用决定注入哪些上下文
// 首次使用时,先调用 qoni.gumem.createSession 创建这个 sessionId
const { data: recalled } = await qoni.gumem.recall({
token: grant.token,
sessionId: `user-${task.userId}`,
query: 'email reply tone, signature and standing preferences',
})
// 启动 Agent loop:打开邮箱、读取任务邮件、查询公开网页上下文、生成草稿
const run = await qoni.doAnything.run({
token: grant.token,
prompt: `
Open the user's mailbox.
Find customer emails from today that need a reply.
Apply the user's confirmed reply preferences listed below.
If an email mentions an unfamiliar company or link, research public web context.
Generate reply drafts only. Do not send email.
Confirmed reply preferences from Memory:
${JSON.stringify(recalled)}
`,
capture: { screenshots: true },
})
const draftTask = await run.wait({
// 步骤截图:流式转发给前端,展示执行过程
onScreenshot: (image, step) => broadcastStep(task.id, step, image.bytes),
// 需要用户参与的步骤:站点登录、MFA、二次确认等
onInteraction: (interaction) => notifyUserActionRequired(task.id, interaction),
})
// 任务后:用户在确认草稿时沉淀的长期偏好,由你的应用确认后再写入 GUMem
await qoni.gumem.addMessages({
token: grant.token,
sessionId: `user-${task.userId}`,
messages: [
{ role: 'user', content: 'Use a concise, warm tone for refund replies.' },
],
})如果邮箱登录需要 MFA、OAuth consent 或企业 SSO,任务会发出 interaction(例如 site_login 类型)。你的服务在 onInteraction 中把请求转发给用户;只有用户明确同意后,才根据 interaction 提供的动作调用 confirm()、openLogin() 或 confirmSignedIn()。不要因为 can('confirm') 为真就自动批准,confirmation 可能代表计划确认或破坏性操作。用户完成操作后,Web Agent 只能在委托令牌允许的范围内继续任务。
不使用 wait() 时,也可以直接迭代事件流。事件流断线时 SDK 会自动重连续传;事件类型常量见 QoniEventTypes,原始 wire 事件可通过 event.raw 读取:
for await (const event of run.events()) {
if (event.type === 'progress') appendTrace(event.data)
if (event.type === 'message') appendTrace(event.data.text)
if (event.type === 'screenshot') renderScreenshot(event.image)
if (event.type === 'interaction') handleInteraction(event.data)
if (event.type === 'done') return event.data.output
}面向用户展示的轨迹应该是可解释的摘要、动作和观察结果,而不是隐藏的内部 chain-of-thought。GUMem 的读写时机由你的应用控制:Agent 只能消费注入的上下文,长期偏好的写入必须经过你的应用确认。
检查点:
- 只读取用户可见、且本次任务需要的邮件。
- 不读取历史归档、设置页或无关文件夹。
- 只召回任务相关的 Memory,例如回复语气、签名规则和 Agent 不应自动承诺的事项。
- 只有在你的应用确认长期偏好后,才写回 Memory。
- 只在陌生公司或链接需要上下文时查询公开网页。
- 流式返回可解释的轨迹事件和步骤截图,让用户能检查执行过程。
5. Return result and audit data
doAnything.run() 返回 RunHandle;await run.wait() 完成后返回通用的 RunResult(runId、status、output、artifacts、terminalReason 等)。SDK 不会在顶层发明 drafts 这类业务字段;输出结构由任务描述约定,并在应用侧解析和校验。
return {
id: draftTask.runId,
status: draftTask.status,
output: draftTask.output,
artifacts: draftTask.artifacts,
audit: {
auditId: grant.auditId,
permissionBoundary: grant.grantedScopes
}
}不要把整封邮件、临时网页内容或敏感凭证写入长期 Memory。如果用户在确认草稿时形成了长期偏好,由你的应用决定是否调用 qoni.gumem.addMessages 写入。
Checkpoint
完成快速开始后,用户应该看到:
- 今天需要回复的邮件摘要。
- 每封邮件的回复草稿。
- 用于补充上下文的公开网页来源。
- 用于检查任务过程的轨迹事件和步骤截图。
- 本次任务的权限范围和 audit id。
- 需要用户确认的下一步动作。
智能体不应该自动发送邮件、删除邮件、修改邮箱设置或保存用户邮箱密码。
单独使用模块
快速开始展示的是 GenAuth、Web Agent 和 GUMem 的组合闭环。你不需要一次性接入全部模块。
- 只需要登录、委托授权或 permission boundary 时,可以单独使用 GenAuth。
- 只需要长期偏好、用户上下文或任务记忆时,可以单独使用 GUMem。
- 只需要网页搜索、抽取或受控浏览器操作时,可以单独使用 Web Agent。
当智能体同时需要身份、记忆和网页行动能力时,再把三个模块组合起来。
下一步
从左侧的应用场景中选择一个接近你业务的场景,或继续阅读 GenAuth、Web Agent 和 GUMem 的模块文档。