Skip to content

第 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",但定位差异明显:

维度FlueMastrapi-agent-core
定位可编程 Agent Harness全栈 AI 应用框架极简 Agent 运行时
核心抽象'use agent' 函数 + harnessAgent / 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/resumeSQLite 会话后端(社区包)
部署形态Node/Workers/GitHub Actions/GitLab/RenderNode 服务 / 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 章的主线:

  1. 理念(01–02):Harness = 给模型一个能干活的完整工作环境;
  2. 核心四件套(03–06):'use agent' 函数、模型配置、Tools、Skills;
  3. 协作与入口(07–10):MCP 生态、Subagents 委派、Channels 入站;
  4. 生产三支柱(11–13):Sandboxes 安全边界、Durability 受理合约、Observability 事件观测;
  5. 交付(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 工程的长期维护,课程倡导的观点是?

🛠️ 动手实践

  1. 为 OnCall 助手增加"升级"逻辑:P0 故障 15 分钟未确认时自动 @ 管理员(提示:结合 Schedules)。
  2. 把同一个 weather agent 分别用三个框架实现并跑通,写一篇 300 字的体感对比笔记。
  3. 用决策树分析你当前工作中的真实需求,得出选型结论并列出三条理由。

下一章:实战三——代码审查 Agent