Flue 框架从入门到精通
Part 3 · 进阶实战

§08 工作流(Workflows):一次性的确定性任务

flue run 与一次性确定性任务,以及结构化结果

📑 本章目录

08.1 不是所有事都要长聊

上周我要批量总结 50 篇文档。开 50 个对话太傻。用工作流,一条命令全搞定。

有些活,干完就走。比如:收到一篇文档,总结它;收到一张工单,分类它。

你不想为它开一个"持续对话"。这就是工作流

08.2 Agent 还是工作流?

维度 交互式 Agent 工作流 Workflow 标叔的结论
对话 持续多轮 一次性,干完即走 多轮选 Agent
触发 HTTP/WS 实时 flue run 或 CI CI 选工作流
适合 客服、聊天 总结、分类、CI 任务 看是否长聊
有 runId 是,可查运行记录 要查账用工作流

重点看:最后一行"有 runId"。

工作流每次执行都有独立 runId,能查历史、查事件。Agent 的对话没有这个。

08.3 写一个工作流

// src/workflows/summarize.ts
import { createAgent, type FlueContext } from '@flue/runtime';

const summarizer = createAgent(() => ({
  model: 'anthropic/claude-haiku-4-5',
  instructions: 'Summarize the supplied document clearly.',
}));

// 文件名就是工作流名:summarize
export async function run({ init, payload }: FlueContext<{ text: string }>) {
  const harness = await init(summarizer);  // 这行初始化
  const session = await harness.session();
  const response = await session.prompt(payload.text);
  return { summary: response.text };        // 返回值就是结果
}

跑它:

pnpm exec flue run summarize --payload '{"text":"要总结的内容"}'

08.4 结构化结果,别让模型自由发挥

工作流常被别的代码调用。你要的是字段,不是散文。用 result 要结构化数据:

import * as v from 'valibot';
const response = await session.prompt(payload.ticket, {
  result: v.object({
    priority: v.picklist(['low', 'medium', 'high']),
    summary: v.string(),
  }),
});
// 注意:直接拿数据,不是 response.data
return response.data;

标叔的经验init() 只要 createAgent() 的结果。

我最常报的错就是 init() requires an agent created with createAgent(...)。把配置对象直接丢给 init,不行。必须 const agent = createAgent(...); init(agent)。这条记死。

工作流讲完。下一章,把 Agent 接到你自己的服务里,并加上鉴权。