Skip to main content
Sonzai Docs
教程·约 15 分钟

自定义状态与工作流事件

自定义状态允许您在智能体的记忆旁存储任意结构化数据—— 例如绩效指标、任务完成情况、里程碑标记或任何特定于应用的上下文。 工作流事件允许智能体对对话之外发生的事情做出反应 (达成里程碑、完成任务、实现目标)。 完成本教程后,您将了解如何读取、写入和监听这两者。

您将构建什么

  • → 一个追踪用户进度分数和等级的自定义状态,每次会话后更新
  • → 一个工作流事件触发器,当用户达到里程碑时触发,使智能体做出反应
  • → 批量读取用户仪表板的所有自定义状态
  • → 一个用于从后端进行幂等状态更新的 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. 幂等更新状态

当用户状态发生变化时——会话结束后、购买后或按计划—— 从后端使用 upsertupsert 会在状态不存在时创建,存在时替换。

// 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?"

事件传递

工作流事件会被排队,并在下一次对话轮次中传递。它们不会中断活跃会话。 智能体在下一次 chatchatStream 调用开始时消费待处理事件, 并将其融入开场消息或首次响应中。

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 天打卡。准备好继续了吗?"

下一步

  • → 阅读 自定义状态与工具 参考了解完整 API
  • → 添加 库存 追踪,用资产组合丰富状态
  • → 设置 Webhook 以在智能体触发特定事件时通知您的后端
  • → 探索 人格 了解事件如何影响智能体的情感演化