自定义状态与工作流事件
自定义状态允许您在智能体的记忆旁存储任意结构化数据—— 例如绩效指标、任务完成情况、里程碑标记或任何特定于应用的上下文。 工作流事件允许智能体对对话之外发生的事情做出反应 (达成里程碑、完成任务、实现目标)。 完成本教程后,您将了解如何读取、写入和监听这两者。
您将构建什么
- → 一个追踪用户进度分数和等级的自定义状态,每次会话后更新
- → 一个工作流事件触发器,当用户达到里程碑时触发,使智能体做出反应
- → 批量读取用户仪表板的所有自定义状态
- → 一个用于从后端进行幂等状态更新的 upsert 模式
什么是自定义状态?
自定义状态是一个键值记录,作用域为 智能体 + 用户(或仅智能体)。 值可以是任何 JSON 可序列化类型:字符串、数字、布尔值、数组或嵌套对象。
与记忆(从对话中提取的非结构化文本)不同, 自定义状态是您从后端显式写入的结构化数据。 智能体可以在对话中通过 get_custom_state 工具读取它们, 因此它始终知道用户当前的等级、连续天数、余额等。
自定义状态(您写入)
- · 结构化 JSON 数据
- · 您的后端控制
- · 任务进度、分数、里程碑
- · 通过 SDK 或 REST 更新
记忆(自动提取)
- · 自由格式文本事实
- · 平台从对话中提取
- · 偏好、事件、目标
- · 每条消息后自动更新
1. 创建自定义状态
首次为用户设置状态时调用 create。 后续写入应使用 upsert(见步骤 3)以实现幂等更新。
import { Sonzai } from "@sonzai-labs/agents";
const client = new Sonzai({ apiKey: process.env.SONZAI_API_KEY! });
const AGENT_ID = "agent_abc";
const USER_ID = "user_123";
const state = await client.agents.customStates.create(AGENT_ID, {
userId: USER_ID,
key: "user_progress",
value: {
tier: "silver",
score: 2340,
score_to_next: 3000,
streak_days: 12,
milestones: ["first_chat", "50_tasks", "7_day_streak"],
},
});
console.log("Created:", state.state_id, state.key);2. 在对话中读取状态
当智能体可以访问 get_custom_state 工具时(当存在自定义状态时自动启用), 它会在对话开始时获取当前状态。您也可以随时从后端读取。
// Read by key from your backend
const state = await client.agents.customStates.getByKey(AGENT_ID, {
userId: USER_ID,
key: "user_progress",
});
const progress = state.value as {
tier: string; score: number; score_to_next: number; streak_days: number;
};
console.log(`${progress.tier} tier · ${progress.score}/${progress.score_to_next} pts · ${progress.streak_days}-day streak`);在对话中,智能体调用 get_custom_state("user_progress") 并将进度数据自然地融入其响应——无需提示注入。
3. 幂等更新状态
当用户状态发生变化时——会话结束后、购买后或按计划—— 从后端使用 upsert。upsert 会在状态不存在时创建,存在时替换。
// Called after each work session ends
async function onSessionEnd(userId: string, sessionScore: number) {
const current = await client.agents.customStates.getByKey(AGENT_ID, {
userId,
key: "user_progress",
}).catch(() => null);
const tiers = ["bronze", "silver", "gold", "platinum"];
const prev = (current?.value ?? { tier: "bronze", score: 0, score_to_next: 1000, streak_days: 0 }) as {
tier: string; score: number; score_to_next: number; streak_days: number; milestones: string[];
};
const newScore = prev.score + sessionScore;
const promoted = newScore >= prev.score_to_next;
const tierIndex = tiers.indexOf(prev.tier);
const newTier = promoted ? (tiers[tierIndex + 1] ?? prev.tier) : prev.tier;
await client.agents.customStates.upsert(AGENT_ID, {
userId,
key: "user_progress",
value: {
tier: newTier,
score: promoted ? newScore - prev.score_to_next : newScore,
score_to_next: promoted ? prev.score_to_next * 1.5 : prev.score_to_next,
streak_days: prev.streak_days + 1,
milestones: prev.milestones,
},
});
if (promoted) {
// Notify the agent so it can congratulate the user next session
await client.agents.triggerGameEvent(AGENT_ID, {
userId,
eventType: "tier_promotion",
payload: { new_tier: newTier, previous_tier: prev.tier },
});
}
}4. 触发工作流事件
工作流事件允许您的后端告知智能体对话之外发生的事情。 下次用户聊天时,智能体会看到待处理的事件并自然地做出反应。
// Trigger from your backend when something notable happens
await client.agents.triggerGameEvent(AGENT_ID, {
userId: USER_ID,
eventType: "task_complete",
payload: {
task_name: "Q1 Revenue Analysis",
deliverable: "Revenue Report",
category: "Analytics",
time_taken: "3h 42m",
},
});
// Next time the user opens a conversation:
// Agent: "I see you finished the Q1 Revenue Analysis! That report is a key
// deliverable. Want to discuss the findings or start the next task?"事件传递
工作流事件会被排队,并在下一次对话轮次中传递。它们不会中断活跃会话。 智能体在下一次 chat 或 chatStream 调用开始时消费待处理事件, 并将其融入开场消息或首次响应中。
5. 列出用户的所有状态
适用于构建管理仪表板、用户资料页面或调试。 返回智能体 + 用户对的所有自定义状态。
const { states } = await client.agents.customStates.list(AGENT_ID, {
userId: USER_ID,
});
for (const state of states) {
console.log(`[${state.key}]`, JSON.stringify(state.value, null, 2));
}
// [user_progress] { tier: "silver", score: 340, ... }
// [preferences] { theme: "dark", notifications: true }
// [daily_summary] { last_active: "2025-03-20", sessions_today: 2 }6. 更新特定字段
当您想通过 state_id 更改状态时使用 update。 与 upsert 不同,update 执行部分合并—— 您只需传递要更改的字段。
// Add a milestone without overwriting the whole state
const state = await client.agents.customStates.getByKey(AGENT_ID, {
userId: USER_ID,
key: "user_progress",
});
const progress = state.value as { milestones: string[]; [k: string]: unknown };
await client.agents.customStates.update(AGENT_ID, state.state_id, {
value: {
...progress,
milestones: [...progress.milestones, "100_tasks"],
},
});7. 删除状态
通过 ID 或键删除状态。下次对话时,智能体将无法访问该状态。
// Delete by key (finds and removes the state)
await client.agents.customStates.deleteByKey(AGENT_ID, {
userId: USER_ID,
key: "user_progress",
});
// Or delete by state_id if you already have it
await client.agents.customStates.delete(AGENT_ID, stateId);常见模式
入职状态
在注册时创建一个 onboarding 状态,值为 { step: 0, completed: false }。智能体会在早期对话开始时检查它, 并自然地引导用户完成设置。
订阅上下文
存储 { plan: 'pro', expires_at: '...' },这样智能体就知道 应该提供或推销哪些功能,无需在每次聊天请求中传递。
每日摘要缓存
在每天结束时写入一个包含关键指标的 daily_summary 状态。 智能体在第二天对话开始时引用用户的活动—— "昨天您完成了 3 项任务,连续 12 天打卡。准备好继续了吗?"