signal

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Skill: Signal — Smart Notification Engine

技能:Signal——智能通知引擎

Set up AI-powered notifications that decide IF, WHAT, WHEN, and HOW to notify each person. Signal uses Personize memory and governance to send the right message, to the right person, at the right time — or stay quiet when silence is better.
搭建由AI驱动的通知系统,该系统能够决定是否通知、通知内容、通知时间以及通知方式。Signal借助Personize的记忆与治理能力,在合适的时间给合适的人发送合适的消息——或者在更适合静默时保持不打扰。

What This Skill Solves

本技能解决的问题

Most notification systems are dumb rules: "if event X, send template Y." They don't know who the person is, what they care about, what was already sent, or whether this notification will help or annoy.
Signal replaces rules with intelligence:
  • IF — Should this person be notified at all? (AI scores 0-100 based on full entity context)
  • WHAT — What should the message say? (Uniquely personalized using everything known about the person)
  • WHEN — Now, or later in a digest? (AI decides priority: immediate / standard / digest)
  • HOW — Which channel? (Email, Slack, in-app, SMS — chosen based on context)
The intelligence comes from Personize SDK — memory, governance, and AI. Signal packages the orchestration into a reusable engine.

大多数通知系统都是呆板的规则:"如果发生事件X,就发送模板Y"。它们不了解用户是谁、关注什么、已经收到过哪些通知,也无法判断这条通知是有帮助还是会造成干扰。
Signal用智能决策替代了呆板规则:
  • 是否通知——是否应该通知该用户?(AI会基于完整的实体上下文给出0-100的评分)
  • 通知内容——消息应该包含什么?(基于用户的所有已知信息进行个性化定制)
  • 通知时间——立即发送,还是推迟到摘要中发送?(AI会判断优先级:即时/标准/摘要)
  • 通知方式——选择哪个渠道?(邮件、Slack、应用内、SMS——基于上下文进行选择)
这些智能能力来自Personize SDK的记忆、治理与AI功能。Signal将这些编排能力打包成可复用的引擎。

When This Skill is Activated

本技能的触发场景

If the developer mentions notifications, alerts, or messaging, start with ASSESS.
If the developer has a specific question (e.g., "how do I add a Slack channel to Signal"), jump to the relevant action.
If the developer wants to understand the architecture, walk them through the engine flow.

当开发者提及通知、告警或消息推送时,从**评估(ASSESS)**步骤开始。
当开发者有具体问题时(例如:"如何为Signal添加Slack渠道"),直接跳转到对应的操作步骤。
当开发者想要了解架构时,为他们讲解引擎的工作流程。

When NOT to Use This Skill

本技能的不适用场景

  • Need raw memory operations → use entity-memory
  • Need shared workspaces without notifications → use collaboration
  • Need governance rules without notifications → use governance
  • Need CRM data sync → use entity-memory (CRM sync section)

  • 需要原始记忆操作 → 使用entity-memory
  • 需要无通知功能的共享工作区 → 使用collaboration
  • 需要无通知功能的治理规则 → 使用governance
  • 需要CRM数据同步 → 使用entity-memory(CRM同步模块)

Actions

操作步骤

ActionWhen to Use
ASSESSUnderstand their stack, events, channels, and recipients
CONFIGUREGenerate Signal initialization code
CONNECTScaffold event hooks for their framework
GOVERNSet up governance rules for notification guidelines
TESTRun a dry-run evaluation and verify the decision

操作使用场景
ASSESS(评估)了解开发者的技术栈、事件、渠道与接收者
CONFIGURE(配置)生成Signal初始化代码
CONNECT(连接)为开发者的框架搭建事件钩子
GOVERN(治理)设置通知准则相关的治理规则
TEST(测试)运行空转评估并验证决策结果

Action: ASSESS

操作:ASSESS(评估)

Understand what the developer needs before writing code.
在编写代码前先了解开发者的需求。

Questions to Ask

需要询问的问题

  1. What events matter? — What happens in your product that should potentially trigger a notification?
    • User lifecycle: signup, login, trial expiry, churn risk
    • Usage: milestones, drops, feature adoption
    • CRM: deal changes, meeting outcomes, support tickets
    • System: sync complete, errors, billing events
  2. Who are the recipients? — Who gets notified?
    • End users (in-app, email)
    • Internal team (Slack, email)
    • Both (different channels)
  3. What channels? — How should messages be delivered?
    • Email (SES, SendGrid)
    • Slack (webhook)
    • In-app (bridge to existing UI)
    • SMS (Twilio — community channel)
  4. What governance rules exist? — What constraints apply?
    • Max notifications per day per user
    • Quiet hours / time zones
    • Content policies (tone, compliance)
    • Opt-out preferences
  5. What framework? — Where do events originate?
    • Express.js / Fastify / Next.js / NestJS
    • Trigger.dev / n8n / cron jobs
    • Webhook-based (external system pushes events)
  1. 哪些事件需要关注?——你的产品中哪些事件可能触发通知?
    • 用户生命周期:注册、登录、试用到期、流失风险
    • 使用行为:里程碑、活跃度下降、功能采用情况
    • CRM:交易变更、会议结果、支持工单
    • 系统:同步完成、错误、计费事件
  2. 接收者是谁?——谁会收到通知?
    • 终端用户(应用内、邮件)
    • 内部团队(Slack、邮件)
    • 两者皆是(使用不同渠道)
  3. 使用哪些渠道?——消息应该通过什么方式投递?
    • 邮件(SES、SendGrid)
    • Slack(webhook)
    • 应用内(对接现有UI)
    • SMS(Twilio——社区渠道)
  4. 存在哪些治理规则?——有哪些约束条件?
    • 每位用户每日最多接收的通知数量
    • 安静时段/时区
    • 内容策略(语气、合规性)
    • 退订偏好
  5. 使用什么框架?——事件源自哪里?
    • Express.js / Fastify / Next.js / NestJS
    • Trigger.dev / n8n / 定时任务
    • 基于Webhook(外部系统推送事件)

Output

输出内容

After assessment, summarize:
  • Events to handle (with types like
    user.signup
    ,
    usage.drop
    )
  • Channels needed
  • Governance constraints
  • Framework/integration approach

评估完成后,总结以下信息:
  • 需要处理的事件(类型如
    user.signup
    usage.drop
  • 需要的渠道
  • 治理约束
  • 框架/集成方案

Action: CONFIGURE

操作:CONFIGURE(配置)

Generate the Signal initialization code based on the assessment.
基于评估结果生成Signal初始化代码。

Minimal Setup

最小化配置

typescript
import { Personize } from '@personize/sdk';
import { Signal, ConsoleChannel, ManualSource } from '@personize/signal';

const client = new Personize({ secretKey: process.env.PERSONIZE_SECRET_KEY! });
const manual = new ManualSource();

const signal = new Signal({
    client,
    channels: [new ConsoleChannel()],
    sources: [manual],
});

await signal.start();
typescript
import { Personize } from '@personize/sdk';
import { Signal, ConsoleChannel, ManualSource } from '@personize/signal';

const client = new Personize({ secretKey: process.env.PERSONIZE_SECRET_KEY! });
const manual = new ManualSource();

const signal = new Signal({
    client,
    channels: [new ConsoleChannel()],
    sources: [manual],
});

await signal.start();

Production Setup

生产环境配置

typescript
import { Personize } from '@personize/sdk';
import {
    Signal,
    ManualSource,
    SesChannel,       // or SendGridChannel
    SlackChannel,
    InAppChannel,
} from '@personize/signal';

const client = new Personize({ secretKey: process.env.PERSONIZE_SECRET_KEY! });
const manual = new ManualSource();

const signal = new Signal({
    client,
    channels: [
        new SesChannel({ sourceEmail: 'notifications@yourapp.com' }),
        new SlackChannel({ webhookUrl: process.env.SLACK_WEBHOOK! }),
        new InAppChannel(async (recipient, payload) => {
            // Bridge to your existing notification UI
            await yourNotificationService.create({
                userId: recipient.userId,
                title: payload.subject,
                body: payload.body,
            });
            return { success: true, channel: 'in-app', timestamp: new Date().toISOString() };
        }),
    ],
    sources: [manual],
    engine: {
        dailyCap: 5,                              // max per user per day
        deduplicationWindowMs: 6 * 60 * 60 * 1000, // 6 hours
        memorize: true,                            // feedback loop
        workspaceUpdates: true,                    // track in workspace
    },
});

await signal.start();

// Schedule daily digests (weekdays 9 AM)
signal.schedule('daily-digest', '0 9 * * 1-5', async () => {
    const users = await getActiveUsers();
    await signal.digest.runBatch(users);
});
typescript
import { Personize } from '@personize/sdk';
import {
    Signal,
    ManualSource,
    SesChannel,       // or SendGridChannel
    SlackChannel,
    InAppChannel,
} from '@personize/signal';

const client = new Personize({ secretKey: process.env.PERSONIZE_SECRET_KEY! });
const manual = new ManualSource();

const signal = new Signal({
    client,
    channels: [
        new SesChannel({ sourceEmail: 'notifications@yourapp.com' }),
        new SlackChannel({ webhookUrl: process.env.SLACK_WEBHOOK! }),
        new InAppChannel(async (recipient, payload) => {
            // Bridge to your existing notification UI
            await yourNotificationService.create({
                userId: recipient.userId,
                title: payload.subject,
                body: payload.body,
            });
            return { success: true, channel: 'in-app', timestamp: new Date().toISOString() };
        }),
    ],
    sources: [manual],
    engine: {
        dailyCap: 5,                              // max per user per day
        deduplicationWindowMs: 6 * 60 * 60 * 1000, // 6 hours
        memorize: true,                            // feedback loop
        workspaceUpdates: true,                    // track in workspace
    },
});

await signal.start();

// Schedule daily digests (weekdays 9 AM)
signal.schedule('daily-digest', '0 9 * * 1-5', async () => {
    const users = await getActiveUsers();
    await signal.digest.runBatch(users);
});

Configuration Reference

配置参考

OptionDefaultDescription
engine.dailyCap
5Max notifications per email per day
engine.deduplicationWindowMs
6hSkip same event type within window
engine.memorize
trueRecord sent notifications in memory
engine.workspaceUpdates
falseCreate workspace entries on SEND/DEFER
engine.concurrency
5Max parallel evaluations
engine.maxEvaluationsPerMinute
20Rate limit for batch processing

选项默认值说明
engine.dailyCap
5每位用户每日最多接收的邮件通知数量
engine.deduplicationWindowMs
6小时在该时间窗口内跳过相同类型的事件
engine.memorize
true在记忆模块中记录已发送的通知
engine.workspaceUpdates
false在发送/推迟通知时创建工作区记录
engine.concurrency
5最大并行评估数
engine.maxEvaluationsPerMinute
20批量处理的速率限制

Action: CONNECT

操作:CONNECT(连接)

Scaffold event hooks that push product events into Signal.
搭建将产品事件推送到Signal的事件钩子。

Express.js Pattern

Express.js 模式

typescript
// event-hooks.ts — fire-and-forget, never throws, never blocks
import { ManualSource } from '@personize/signal';

const manual = new ManualSource(); // same instance used in Signal config

export const EventHooks = {
    async onUserSignup(email: string, data?: Record<string, unknown>) {
        manual.emit({
            id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
            type: 'user.signup',
            email,
            data: data || {},
            timestamp: new Date().toISOString(),
            metadata: { team: 'product' },
        });
    },

    async onUsageDrop(email: string, data: { metric: string; dropPercent: number }) {
        manual.emit({
            id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
            type: 'usage.drop',
            email,
            data,
            timestamp: new Date().toISOString(),
            metadata: { team: 'product' },
        });
    },
};
typescript
// event-hooks.ts — 即发即弃,永不抛出异常,永不阻塞
import { ManualSource } from '@personize/signal';

const manual = new ManualSource(); // 与Signal配置中使用的实例一致

export const EventHooks = {
    async onUserSignup(email: string, data?: Record<string, unknown>) {
        manual.emit({
            id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
            type: 'user.signup',
            email,
            data: data || {},
            timestamp: new Date().toISOString(),
            metadata: { team: 'product' },
        });
    },

    async onUsageDrop(email: string, data: { metric: string; dropPercent: number }) {
        manual.emit({
            id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
            type: 'usage.drop',
            email,
            data,
            timestamp: new Date().toISOString(),
            metadata: { team: 'product' },
        });
    },
};

Using in Controllers

在控制器中使用

typescript
// In your signup controller
router.post('/signup', async (req, res) => {
    const user = await createUser(req.body);
    EventHooks.onUserSignup(user.email, { plan: user.plan }).catch(() => {});
    res.json(user);
});
typescript
// 在注册控制器中
router.post('/signup', async (req, res) => {
    const user = await createUser(req.body);
    EventHooks.onUserSignup(user.email, { plan: user.plan }).catch(() => {});
    res.json(user);
});

Webhook Source (external events)

Webhook 事件源(外部事件)

typescript
import { WebhookSource } from '@personize/signal';

const webhookSource = new WebhookSource({
    path: '/webhooks/signal',
    secret: process.env.WEBHOOK_SECRET,
    parser: (body) => ({
        id: body.id || `wh_${Date.now()}`,
        type: body.event || 'webhook.received',
        email: body.email || body.user_email,
        data: body.data || body,
        timestamp: body.timestamp || new Date().toISOString(),
    }),
});

// Mount on Express
app.use(webhookSource.middleware());

typescript
import { WebhookSource } from '@personize/signal';

const webhookSource = new WebhookSource({
    path: '/webhooks/signal',
    secret: process.env.WEBHOOK_SECRET,
    parser: (body) => ({
        id: body.id || `wh_${Date.now()}`,
        type: body.event || 'webhook.received',
        email: body.email || body.user_email,
        data: body.data || body,
        timestamp: body.timestamp || new Date().toISOString(),
    }),
});

// 挂载到Express
app.use(webhookSource.middleware());

Action: GOVERN

操作:GOVERN(治理)

Set up governance rules that guide Signal's AI decisions.
设置指导Signal AI决策的治理规则。

Governance Variables to Create

需要创建的治理变量

Use the Personize web app or SDK to create these governance variables:
1. Notification Guidelines (tag:
notifications
)
Notification Policy:
- Never notify about events the user has already seen in the product
- Prioritize actionable insights over informational updates
- Match tone to the urgency: critical = direct, informational = friendly
- Always include a specific next step, not generic "check it out"
- Maximum 3 notifications per user per day (engine enforces 5 as hard cap)
2. Channel Routing (tag:
notifications
)
Channel Selection:
- Slack: For internal team alerts (sales, support, ops)
- Email: For user-facing notifications that need persistence
- In-app: For real-time product notifications when user is active
- Digest: For low-priority updates that can wait for weekly summary
3. Frequency Policy (tag:
communications
)
Communication Frequency:
- New users (< 30 days): Max 1 notification per day. Focus on onboarding milestones.
- Active users: Max 3 per week. Only high-signal events.
- At-risk users: Increase frequency for re-engagement. Max 1 per day.
- Churned users: Do not notify. Only send win-back campaigns via marketing.
使用Personize网页端或SDK创建以下治理变量:
1. 通知准则(标签:
notifications
通知政策:
- 绝不通知用户已经在产品中见过的事件
- 优先推送可执行的洞察,而非信息性更新
- 根据紧急程度匹配语气:紧急事件直接明了,信息性事件友好亲切
- 始终包含具体的下一步操作,而非笼统的"来看看"
- 每位用户每日最多接收3条通知(引擎强制上限为5条)
2. 渠道路由规则(标签:
notifications
渠道选择规则:
- Slack:用于内部团队告警(销售、支持、运营)
- 邮件:用于需要持久保存的用户端通知
- 应用内:用户活跃时的实时产品通知
- 摘要:用于可推迟的低优先级更新,汇总到每周摘要中发送
3. 发送频率政策(标签:
communications
沟通频率规则:
- 新用户(注册不足30天):每日最多1条通知,重点关注入职里程碑
- 活跃用户:每周最多3条通知,仅推送高信号事件
- 高风险用户:增加重触频率,每日最多1条通知
- 已流失用户:不发送通知,仅通过营销渠道发送赢回活动消息

How Governance Flows into Signal

治理规则如何作用于Signal

Signal's engine calls
smartGuidelines()
and
smartDigest()
in step 3 (context assembly). The AI sees these governance rules alongside the entity's full context and makes decisions accordingly. No code changes needed — update governance variables and Signal adapts.

Signal引擎在步骤3(上下文组装)中会调用
smartGuidelines()
smartDigest()
。AI会将这些治理规则与实体的完整上下文结合,进而做出决策。无需修改代码——更新治理变量后,Signal会自动适配。

Action: TEST

操作:TEST(测试)

Verify Signal is working with a dry-run evaluation.
通过空转评估验证Signal是否正常工作。

Quick Test

快速测试

typescript
const result = await signal.trigger({
    id: 'test_001',
    type: 'user.signup',
    email: 'test@example.com',
    data: { plan: 'trial', source: 'website' },
    timestamp: new Date().toISOString(),
});

console.log('Action:', result.action);    // SEND | DEFER | SKIP
console.log('Score:', result.score);      // 0-100
console.log('Reasoning:', result.reasoning);
console.log('SDK calls:', result.sdkCallsUsed);
console.log('Duration:', result.durationMs, 'ms');
typescript
const result = await signal.trigger({
    id: 'test_001',
    type: 'user.signup',
    email: 'test@example.com',
    data: { plan: 'trial', source: 'website' },
    timestamp: new Date().toISOString(),
});

console.log('操作:', result.action);    // SEND | DEFER | SKIP
console.log('评分:', result.score);      // 0-100
console.log('决策依据:', result.reasoning);
console.log('SDK调用:', result.sdkCallsUsed);
console.log('耗时:', result.durationMs, 'ms');

What to Verify

需要验证的内容

  1. Pre-check works — Trigger the same event twice within 6 hours. Second should SKIP instantly (0 SDK calls).
  2. Daily cap works — Trigger 6 events for the same email. The 6th should SKIP.
  3. AI decision varies — Trigger different event types. Scores should vary based on context relevance.
  4. Feedback loop — After a SEND, check that the notification was memorized:
    client.memory.recall({ query: 'signal:sent', email: '...' })
    .
  5. Dedup via memory — After step 4, trigger a similar event. The AI should reference the recently sent notification in its reasoning.
  1. 预检查功能正常——在6小时内两次触发同一事件,第二次应直接跳过(0次SDK调用)。
  2. 每日上限功能正常——为同一邮箱触发6次事件,第6次应被跳过。
  3. AI决策具有差异性——触发不同类型的事件,评分应根据上下文相关性有所不同。
  4. 反馈循环正常——发送通知后,检查该通知是否已被记录到记忆中:
    client.memory.recall({ query: 'signal:sent', email: '...' })
  5. 基于记忆的去重完成步骤4后,触发类似事件,AI应在决策依据中引用最近发送的通知。

Console Channel for Testing

用于测试的Console Channel

Use
ConsoleChannel
during development — it logs decisions without delivering:
typescript
const signal = new Signal({
    client,
    channels: [new ConsoleChannel()],  // logs to stdout
    sources: [manual],
});

在开发阶段使用
ConsoleChannel
——它会记录决策结果但不会实际发送通知:
typescript
const signal = new Signal({
    client,
    channels: [new ConsoleChannel()],  // 输出到标准输出
    sources: [manual],
});

Available Resources

可用资源

ResourceContents
../../signal/README.md
Full Signal documentation — architecture, channels, sources, workspace, digest, cost controls
../../signal/CLAUDE.md
AI tool instructions for Signal package
../../signal/examples/quickstart/
Minimal setup example
../../signal/examples/saas-onboarding/
Signup → nurture → convert sequence
../../signal/examples/sales-alerts/
Usage drop alerts to sales team
../../signal/examples/weekly-digest/
Deferred items → compiled digest
../../signal/examples/multi-team/
Product + Sales + Marketing on same records
../../signal/templates/CHANNEL_TEMPLATE.md
How to build a new channel
../../signal/templates/SOURCE_TEMPLATE.md
How to build a new source
recipes/quickstart.ts
Minimal Signal setup recipe
recipes/multi-team.ts
Multi-team configuration recipe
资源内容
../../signal/README.md
Signal完整文档——架构、渠道、事件源、工作区、摘要、成本控制
../../signal/CLAUDE.md
针对Signal包的AI工具使用说明
../../signal/examples/quickstart/
最小化搭建示例
../../signal/examples/saas-onboarding/
注册→培育→转化流程示例
../../signal/examples/sales-alerts/
面向销售团队的活跃度下降告警示例
../../signal/examples/weekly-digest/
推迟项→编译为摘要的示例
../../signal/examples/multi-team/
多团队(产品+销售+营销)共用记录的示例
../../signal/templates/CHANNEL_TEMPLATE.md
如何构建新渠道的模板
../../signal/templates/SOURCE_TEMPLATE.md
如何构建新事件源的模板
recipes/quickstart.ts
Signal最小化搭建方案
recipes/multi-team.ts
多团队配置方案