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 接到你自己的服务里,并加上鉴权。