第 16 章 · 部署:CI 与托管平台
本章目标:学会在 GitHub Actions / GitLab CI 中运行 agent,把 Daytona、Render 作为运行目标,并用 @flue/sdk 消费已部署 agent 的会话。
16.1 CI 中运行 Agent 的场景
CI 里的 agent 不是常驻服务,而是"跑一次任务就退出":
text
① PR 打开 → agent 审查代码并评论
② 定时任务 → agent 巡检 issue 并打标签
③ 发布流水线 → agent 生成变更说明16.2 GitHub Actions
yaml
# .github/workflows/agent-triage.yml
name: agent-triage
on:
issues:
types: [opened] # 新 issue 触发
schedule:
- cron: "0 * * * *" # 每小时巡检一次
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm ci
- run: npx flue run agents/triage.ts "${{ github.event.issue.title }}"
env:
# 密钥来自仓库 Settings → Secrets,绝不硬编码
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}yaml
# 先做静态校验再真正执行(省 token 的好习惯)
- name: dry-run 校验 agent 定义
run: npx flue run agents/triage.ts --dry-run16.3 GitLab CI/CD
yaml
# .gitlab-ci.yml — 等价的 GitLab 流水线
agent-triage:
image: node:22
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" # 定时触发
script:
- npm ci
# 密钥来自 GitLab CI/CD Variables(masked)
- npx flue run agents/triage.ts "$ISSUE_TITLE"
variables:
ANTHROPIC_API_KEY: $ANTHROPIC_API_KEYCI 运行的注意点
- 每次 job 都是新环境:Durability 状态必须外接数据库,重试才能接力;
- job 有时限:长任务拆成多段或用
--output落盘中间结果; - 日志即观测:CI 里建议开启 verbose 输出便于排障。
16.4 托管平台:Daytona 与 Render
typescript
// Daytona:作为远程沙箱 provider 使用(见第 11 章)
// 这里展示在 Render 上托管 Node 版 Flue 服务| 平台 | 角色 | 要点 |
|---|---|---|
| Daytona | 远程沙箱 | 给 agent 提供隔离容器执行环境 |
| Render | Node 服务托管 | 常驻进程 + 环境变量注入 + Postgres 附加项 |
bash
# Render 用 render.yaml 声明服务
# services:
# - type: web
# name: flue-bot
# runtime: node
# buildCommand: npm ci && npm run build
# startCommand: node dist/main.js
# envVars:
# - key: ANTHROPIC_API_KEY
# sync: false # 在控制台填入,不入库
# - key: DATABASE_URL
# fromDatabase: { name: flue-db, property: connectionString }
# databases:
# - name: flue-db # 附带 Postgres 作持久化16.5 @flue/sdk:消费已部署的会话
部署完成后,前端或其他服务用 SDK 读取 agent 会话:
bash
npm install @flue/sdktypescript
// client.ts — 用 SDK 连接已部署的 agent
import { FlueClient } from '@flue/sdk';
const flue = new FlueClient({
baseUrl: 'https://flue-agent.example.com', // 你的部署地址
apiKey: process.env.FLUE_API_KEY!,
});
// 发起一次会话并等待结果
const session = await flue.sessions.create({
agent: 'Triage',
input: '分诊 issue #99',
});
console.log(session.id, session.status);
// 读取历史会话消息(构建聊天 UI 的数据源)
const messages = await flue.sessions.messages(session.id);
for (const m of messages) {
console.log(m.role, ':', m.content.slice(0, 80));
}typescript
// React 组件中消费(配合官方 Frontend React 指南)
import { useSession } from '@flue/sdk/react';
export function Chat({ sessionId }: { sessionId: string }) {
// 订阅该会话的消息流,UI 自动更新
const { messages, loading } = useSession(sessionId);
if (loading) return <p>加载中…</p>;
return (
<ul>
{messages.map((m) => (
<li key={m.id}><b>{m.role}</b>: {m.content}</li>
))}
</ul>
);
}16.6 本章小结
- CI 场景 = 单次执行 + 外接持久化 + Secrets 注入;先
--dry-run再真跑; - GitHub Actions 与 GitLab CI 配置同构,差异只在密钥变量语法;
- Daytona 提供远程沙箱,Render 托管常驻 Node 服务并附带 Postgres;
@flue/sdk是消费端:创建会话、读历史、React hooks 订阅消息流。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 在 CI 中运行 Flue agent 前,为什么建议先执行 --dry-run?
2. GitHub Actions 中注入 ANTHROPIC_API_KEY 的正确方式是?
3. Daytona 与 Render 在 Flue 生态中的角色分别是?
4. @flue/sdk 的主要用途是?
🛠️ 动手实践
- 把分诊 agent 接入 GitHub Actions,在新 issue 上真实跑一次并在 PR/issue 里留下评论。
- 为 GitLab 写一条带 schedule 规则的流水线,验证 masked variable 的密钥注入。
- 用 @flue/sdk 写一个 CLI 小工具:列出最近 10 个会话及其状态。