第 18 章 · 实战二:Slack 值班助手(含框架对比)
本章目标:综合运用 channels、durability、subagents 构建一个 Slack 值班助手,并通过选型对比表厘清 Flue、Mastra 与 pi-agent-core 的适用边界。
18.1 实战:Slack 值班助手
目标:值班群里 @机器人 上报故障 → agent 自动分级 → 严重故障委派子代理起草事故报告 → 结果回帖。
typescript
// agents/oncall.ts — 值班助手主 agent
'use agent';
import { useModel, useSandbox, useSkill, useTool, useSubagent } from '@flue/runtime';
import { virtual } from '@flue/runtime/sandbox';
import incident from '../skills/incident/SKILL.md';
import { postSlackMessage, queryMetrics } from '../tools/ops.ts';
// 声明子代理:专职撰写事故报告的"专家"
useSubagent({
name: 'report-writer',
description: '根据故障上下文撰写结构化事故报告(时间线/影响面/复盘项)',
prompt: `你是 SRE 报告专家。输入是故障的分级与关键指标,
输出 Markdown 格式报告,包含三节:【时间线】【影响面】【改进项】。`,
});
export function OnCall() {
useModel('anthropic/claude-sonnet-4-6');
// 值本助手只查指标、发消息,用虚拟沙箱即可(最小权限)
useSandbox(virtual());
useSkill(incident);
useTool(queryMetrics);
useTool(postSlackMessage);
return `
收到值班群消息后:
1. 按 SKILL.md 分级:P0(全站不可用)/ P1(核心功能受损)/ P2(一般问题);
2. P2 直接给出处理建议并回复;
3. P0/P1 先调 query_metrics 拉取最近 30 分钟指标佐证判断;
4. 委派 report-writer 子代理生成事故报告;
5. 用 post_slack_message 把结论与报告链接回帖。
`;
}typescript
// src/channels/slack.ts — Slack channel 接入
import { defineChannel } from '@flue/runtime';
export const slackChannel = defineChannel({
name: 'oncall-slack',
provider: 'slack', // Flue 负责验签 Slack 的签名头
});typescript
// src/main.ts — 组装:channel + durability + observability
import { createApp } from '@flue/runtime/node';
import { postgresAdapter } from '@flue/postgres';
import { observe } from '@flue/runtime';
import { Pool } from 'pg';
import { slackChannel } from './channels/slack.ts';
// 观测:错误告警 + 每日 token 成本(第 13 章)
observe((event) => {
if (event.type === 'error') console.error('[ALERT]', event);
});
createApp({
database: postgresAdapter({ // durability:崩溃后值班记录不丢
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
}),
})
.channel(slackChannel, { secret: process.env.SLACK_SIGNING_SECRET! })
.listen(3000);这个例子把前 17 章的主线全部串起:channel 入站 → 最小权限沙箱 → skill 方法论 → tool 执行 → subagent 委派 → durable 存储 → observer 监控。
18.2 框架对比:Flue vs Mastra vs pi-agent-core
三个框架都在 TypeScript 生态解决"构建 agent",但定位差异明显:
| 维度 | Flue | Mastra | pi-agent-core |
|---|---|---|---|
| 定位 | 可编程 Agent Harness | 全栈 AI 应用框架 | 极简 Agent 运行时 |
| 核心抽象 | 'use agent' 函数 + harness | Agent / Workflow / Memory 类 | Agent 类 + 事件流 |
| 独特能力 | Sandboxes、Channels、Durability 合约 | Workflow 图引擎、Evals、Studio | 统一多 Provider LLM API、极低心智负担 |
| 执行环境 | 内建三种沙箱(virtual/local/远程容器) | 依赖宿主环境,无内建沙箱概念 | 无内建沙箱(Pi 官方建议自行容器化) |
| 入站事件 | Channels(验证过的 HTTP ingress) | 集成进 Next.js/Node 应用自行接收 | 自行实现 |
| 持久化 | Durability 合约 + Postgres 适配器 | Storage 层 + suspend/resume | SQLite 会话后端(社区包) |
| 部署形态 | Node/Workers/GitHub Actions/GitLab/Render | Node 服务 / Vercel / standalone server | 作为库嵌入你的进程 |
| 适合谁 | 需要"自主干活"的生产 agent 团队 | 要做带 UI 的 AI 产品、重流程编排 | 想要最小依赖、完全掌控运行时 |
typescript
// 同一任务在三个框架中的"体感"对比:查天气工具调用
// Flue —— 声明式组装,harness 承担会话/沙箱
'use agent';
import { useModel, useTool } from '@flue/runtime';
import getWeather from '../tools/weather.ts';
export function Weather() {
useModel('anthropic/claude-sonnet-4-6');
useTool(getWeather);
return '回答天气问题时必须先调用工具获取实时数据。';
}typescript
// Mastra —— 面向对象注册,融入 mastra 实例
import { Agent } from '@mastra/core/agent';
import { weatherTool } from './tools/weather';
export const weatherAgent = new Agent({
name: 'weather',
instructions: '回答天气问题时必须先调用工具获取实时数据。',
model: 'anthropic/claude-sonnet-4-6',
tools: { weatherTool }, // 注册进全局 mastra 实例供 Studio 调试
});typescript
// pi-agent-core —— 手工装配,事件流自己消费
import { Agent } from '@earendil-works/pi-agent-core';
import { createModels } from '@earendil-works/pi-ai';
const models = createModels();
const agent = new Agent({
initialState: {
model: models.getModel('anthropic', 'claude-sonnet-4-6')!,
systemPrompt: '回答天气问题时必须先调用工具获取实时数据。',
tools: [weatherToolDef], // 工具定义即 JSON Schema
},
streamFn: models.streamSimple.bind(models),
});
agent.subscribe((e) => { if (e.type === 'message_update') process.stdout.write('.'); });
await agent.prompt('北京今天天气如何?');18.3 选型决策树
text
需要 agent 在隔离环境自主执行真实工作(改文件/跑命令)?
├─ 是 → 需要事件入站(Slack/GitHub)+ 崩溃恢复?
│ ├─ 是 → Flue(Harness 全家桶)
│ └─ 否 → Flue 或 pi-agent-core + 自行容器化
└─ 否 → 核心诉求是什么?
├─ 带 UI 的 AI 产品、复杂流程编排 → Mastra
├─ 嵌入现有 Node 进程的最小内核 → pi-agent-core
└─ 都不确定 → 从 pi-agent-core 起步,复杂了再迁移18.4 课程总结
回顾 18 章的主线:
- 理念(01–02):Harness = 给模型一个能干活的完整工作环境;
- 核心四件套(03–06):
'use agent'函数、模型配置、Tools、Skills; - 协作与入口(07–10):MCP 生态、Subagents 委派、Channels 入站;
- 生产三支柱(11–13):Sandboxes 安全边界、Durability 受理合约、Observability 事件观测;
- 交付(14–17):CLI 开发流、双目标部署、CI 运行、端到端实战。
下一步建议:把第 17–18 章的两个实战部署到真实环境,用第 13 章的监控看板持续迭代指令与技能——agent 工程的本质是持续运维而非一次性开发。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. OnCall 实战为什么给主 agent 配置 virtual() 虚拟沙箱而不是 local()?
2. 在 Flue vs Mastra vs pi-agent-core 的对比中,Flue 最独特的组合能力是?
3. 如果你的需求是"给现有 Node 服务加一个轻量 agent 能力、不想引入太多抽象",最适合的是?
4. 关于 agent 工程的长期维护,课程倡导的观点是?
🛠️ 动手实践
- 为 OnCall 助手增加"升级"逻辑:P0 故障 15 分钟未确认时自动 @ 管理员(提示:结合 Schedules)。
- 把同一个 weather agent 分别用三个框架实现并跑通,写一篇 300 字的体感对比笔记。
- 用决策树分析你当前工作中的真实需求,得出选型结论并列出三条理由。