Google 发布了面向 TypeScript 和 Go 的 Genkit Agents API 预览版。它试图把智能体应用里反复出现的基础设施问题收进统一的 chat() 接口:消息历史、工具调用循环、流式输出和状态持久化都由框架协调。更值得关注的是两个执行能力:客户端断开后仍可继续工作的 detached turns,以及能暂停工具、等待人工决定再恢复的 interruptible tools。
chat() 统一的不只是对话调用
一个生产级智能体通常不是“发送提示词,等待模型文本”这么简单。模型可能先请求工具,工具结果又会触发下一轮推理;服务还要保存历史记录、持续发送流式事件,并在进程重启或请求中断后找回状态。
Genkit Agents API 将这些职责包装在单一的 chat() 入口后面,意味着业务代码可以围绕“会话”和“回合”组织,而不必在每个 HTTP handler 中重新拼接循环。根据发布摘要,这个抽象覆盖四类核心能力:
- 消息历史:维护用户、模型和工具之间的上下文。
- 工具循环:在模型提出工具调用后执行工具,并把结果送回模型。
- 流式处理:在完整回合结束前逐步返回事件或内容。
- 状态持久化:让会话状态脱离单个进程和单次连接。
统一接口并不等于状态消失了。应用仍要明确会话 ID 的生成方式、状态保存多久、哪些用户可以读取或恢复某个会话,以及工具调用是否具备幂等性。框架负责协调流程,业务系统仍然负责身份、授权和数据生命周期。
Detached turns:连接结束不代表任务结束
传统聊天接口经常把任务生命周期绑定到 HTTP 或 WebSocket 连接。一旦浏览器刷新、移动网络切换或反向代理超时,服务端工作可能被取消;即使任务继续运行,客户端也未必知道该去哪里查询结果。
Detached turn 把“启动回合”和“等待回合完成”拆开。客户端提交任务后可以拿到一个稳定的回合标识,随后断开连接;智能体继续执行工具链并持久化进度。客户端重新上线时,再根据会话或回合标识查询状态、恢复事件流或读取最终结果。
这种模式适合研究报告生成、多数据源检索、代码分析和批量文档处理等长任务。不过,后台执行也带来新的约束:
- 每个回合需要截止时间、取消入口和资源配额。
- 工具必须尽量幂等,重试不能重复扣款、发信或创建工单。
- 状态存储需要并发控制,避免两个 worker 同时推进同一回合。
- 日志和指标应使用
session_id、turn_id与tool_call_id串联。
可以把 HTTP 层设计成异步任务协议。下面是一个可直接改造的接口示例;具体 Genkit 预览版的方法名和返回类型应以项目安装版本为准:
POST /api/agent/turns HTTP/1.1
Content-Type: application/json
Authorization: Bearer <access-token>
Idempotency-Key: 8af5aa42-6fa2-4ac5-8656-59fc7674b2bd
{
"sessionId": "session-123",
"message": "分析本季度告警,并生成整改清单",
"detached": true
}
服务端可立即返回:
{
"turnId": "turn-456",
"status": "running",
"statusUrl": "/api/agent/turns/turn-456"
}
客户端重连后查询:
curl -sS \
-H 'Authorization: Bearer <access-token>' \
http://localhost:3000/api/agent/turns/turn-456
这里的重点不是 URL 命名,而是把回合 ID 设计成持久化资源,避免用某个内存 Promise 代表长任务。
Interruptible tools:在副作用发生前停下来
工具调用一旦能修改外部系统,风险就从“模型回答错误”升级为“模型执行了错误操作”。发送邮件、部署版本、删除数据、退款或调整权限,都不应该仅凭模型的一次判断自动完成。
Interruptible tools 允许智能体在工具边界暂停,把参数和上下文交给人工审核。审核者批准、修改或拒绝后,系统恢复原回合。发布摘要还特别提到恢复时的 anti-forgery validation,这一点很关键:恢复请求不能只携带一个可猜测的回合 ID,否则攻击者可能伪造“已批准”事件。
可以这样实践恢复令牌。下面的 TypeScript 示例不假定某个尚可能变化的 Genkit 预览 API 签名,而是演示可接在 interrupt/resume 端点外层的防伪校验。它只依赖 Node.js 标准库,可直接运行。
将代码保存为 resume-token.mjs,然后执行 node resume-token.mjs:
import { createHmac, timingSafeEqual } from 'node:crypto';
const secret = process.env.RESUME_SECRET ?? 'replace-in-production';
function sign(payload) {
const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
const signature = createHmac('sha256', secret).update(body).digest('base64url');
return `${body}.${signature}`;
}
function verify(token) {
const [body, supplied] = token.split('.');
if (!body || !supplied) throw new Error('Malformed token');
const expected = createHmac('sha256', secret).update(body).digest();
const actual = Buffer.from(supplied, 'base64url');
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
throw new Error('Invalid signature');
}
const payload = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
if (payload.expiresAt < Date.now()) throw new Error('Token expired');
return payload;
}
const token = sign({
sessionId: 'session-123',
turnId: 'turn-456',
toolCallId: 'tool-789',
action: 'approve',
expiresAt: Date.now() + 5 * 60_000
});
console.log('token:', token);
console.log('verified:', verify(token));
在真实系统中,还应把令牌绑定到当前用户和待恢复的精确工具调用,并将 nonce 标记为一次性使用。签名校验只证明令牌由可信服务签发;它不能替代登录认证、角色授权、审计日志和工具参数的二次校验。
恢复流程可以遵循以下顺序:验证用户身份,检查其是否有审批权限,验证签名与有效期,确认 turnId 和 toolCallId 仍处于等待状态,原子地消费 nonce,记录审批人及修改内容,然后调用与当前 Genkit 版本匹配的恢复接口。
采用时先划清执行边界
Genkit Agents API 仍处于预览阶段,TypeScript 和 Go 团队应锁定依赖版本,并把框架调用隔离在小型适配层中,以降低接口变化带来的迁移成本。比“接入哪个模型”更早需要确定的,是哪些工具可以自动执行、哪些必须人工批准、哪些操作根本不应暴露给智能体。
落地前可以检查这些项目:
- 会话、回合和工具调用都有稳定且不可枚举的标识。
- Detached turn 有超时、取消、重试上限和资源配额。
- 有副作用的工具接受幂等键,并保存执行结果。
- 恢复请求同时经过认证、授权、防伪校验和防重放处理。
- 审批界面显示完整参数、影响范围与调用原因,而不是只有“批准”按钮。
- 状态存储支持并发控制,日志能串联一次回合的全部模型与工具事件。
- 预览版依赖已锁定,并为升级准备了端到端回归测试。
Agents API 的价值不只是少写一段工具循环。Detached turns 和 interruptible tools 把长任务运行与人工控制变成框架中的一等概念,使智能体更接近可恢复、可审计的后台系统。不过,框架只能提供执行机制;权限模型、幂等策略和审批边界仍必须由应用团队明确设计。