跳转至

术语表

这一页是 flower 文档的术语基准。同一个东西在全站只有一个叫法,中英对照在这里定死 —— 翻译版本也照这张表走。

每条给三样东西:这个词指什么它在代码里是什么它不是什么。第三样往往最有用, 因为多数误解来自把一个词当成了另一个。


框架与运行

长程

long-horizon

一次运行跨越数小时到数天、跨越多次会话、跨越进程重启,而不是一问一答。flower 的所有机制都是 为了让这种运行不半途散架。

实测参照:HT001 连续跑了 10.4 小时。

运行

run

一次 Runtime 从开始到结束的完整过程。一次运行内部可以有多个步骤、多个会话, 可以中断后接续。运行的记录落在 runs/manifest.jsonruns/sessions.db

不是:一次 API 调用,也不是一个会话。

会话

session

模型侧的一条上下文。有自己的 session_id,可以 resume、可以 fork。一次运行可能烧掉 好几条会话 —— 每次换代就换一条新的。

步骤

step · Step

流程里的一个可执行单元。拿到一个上下文字典,跑一个 agent,把结果写回字典。 Step 是一个类,见 Python API

流程

workflow · Workflow

按顺序串起来的一组步骤,外加步骤之间怎么传状态、什么时候提前退出。

框架不提供现成流程

flower 只提供机制。流程是你写的。见设计流程


角色

角色是 flower 对 agent 的分工。每个角色 = 一段注入的规则文本 + 一组工具 + 一组 hook。 五个角色都是工厂函数,见 Python API

协调者

coordinator · coordinator()

主线程上那个 agent。它拆解任务、派活、读报告、做决策,但不动手 —— 拿不到 Write / Edit。基础工具是 AgentTodoWriteRead (roles.py:27),但那不是最终清单,还会按参数往上加三种:glance=True(默认)加一个 受限的 Bash(只够 git status / ls 这类看一眼就完的命令,由 delegate_guard 把关); 给了提问通道加 inboxask;手下执行者带了 WebFetch / WebSearch 的话, 这两个也会被并进来 —— allowed_tools会话级的,不并的话 subagent 自己调用时 会卡在没人回应的权限审批上(roles.py:513-526)。

角色设定是"一个会用 Claude Code 的人",不是执行者。

不是:一个更聪明的 agent。它和执行者默认用同一个模型档次,省的是上下文,不是模型。

执行者

worker · worker()

真正干活的 subagent:写代码、跑测试、查资料。工具是 Read Write Edit Bash Glob Grep WebFetch WebSearch

回话格式被规则文本约束成四段 —— 结论 / 依据 / 产出 / 未验证,不超过 30 行, 禁止贴文件内容、命令输出、日志和 diff 原文。

确认者

clarifier · clarify()

动手之前把需求问清楚的角色。它不做事,只提问,问到清楚为止(没有轮数上限), 最后输出一份需求确认书。见前置确认

判定者

judge · judge()

判"做完了没有"的角色。它做两件事之一:开跑前设定目标(产出目标 + 判定清单), 或者每一轮结束后判定这一轮(产出判定)。见目标看守

关键:判定者判的是产出物,不是源码。

HT001 里栽过一次:验收标准写的是"在 macOS 终端直接运行",交付的产物 file 一跑是 ELF 64-bit LSB pie executable, ARM aarch64, GNU/Linux,判定却是通过。

两点要说清楚,否则这个例子会被误读:

  1. 那次判错的不是目标看守 —— HT001 还没有这个机制,判错的是协调者自发派的审计员。
  2. 默认配置的判定者大概率也会漏掉它。 judge() 默认 can_run=False,工具只有 Read/Glob/Grep —— 它跑不了 file,只会去读 Makefile 看到确实有 Darwin 分支, 然后判达成。

真正管用的是 HT002:判定者开了 judge_can_run,自己跑 filelsof 去看现场,明确避开了这个坑。所以"判产出物"这句话,要靠 can_run=True 才落得了地。

旁路顾问

oracle · oracle()

一条只读旁路。运行还在跑的时候,你可以问它"现在到哪了",它看一眼最近事件和工作台 再回答。它说的话不会进入那次运行的上下文 —— 问了不影响运行,答完即弃。

subagent

Claude Agent SDK 的概念:主 agent 通过 Agent 工具派出去的子 agent。它有自己的一条 transcript,工具调用和试错都记在那条上,主线程只收到最终报告。

这是 flower 省上下文的第一层,也是省得最多的一层。见上下文经济学


四个机制

前置确认

clarify

动手之前先把需求问清楚,冻结成一份需求确认书,再开始执行。 挡的是"做出来不是想要的"。见前置确认

需求确认书

brief · Brief

确认者问完之后产出的文书,恰好四段。后续步骤读它,不再重新猜需求。

不要任务书混。需求确认书是"人想要什么",任务书是"这个 subagent 这次干什么"。

任务书

task brief

协调者派活时写给执行者的那段话。只写这次任务专属的东西 —— 对方已经知道的纪律不要复述。

实测:8/8 份任务书都在复述对方已知的纪律,最短一份 521 字符里只有约 120 字符是任务专属的, 一轮白占约 4.8k 永久上下文。

目标看守

goal guard

判定者在每轮结束后独立判定目标达成没有,没达成就打回去接着做。 挡的是"说做完了其实没做完"。见目标看守

判定

verdict · Verdict

判定者一轮判定的结果,恰好三段:结论 / 理由 / 未通过。

结论有三种,ACHIEVED(达成)、NOT_YET(没到)、UNREACHABLE(这个环境验不了)。 后两种是不同的结论 —— "这里没法验"绝对不判通过。

接续

continuity

同一个目录再跑一次,自动接上上一次的进度 —— 进程被杀、机器重启也一样。 挡的是"跑几小时崩了从头再来"。见接续

不要换代混:接续是跨进程接上一次运行;换代是同一次运行内部换一条新会话。

换代

handoff

上下文快满的时候,让当前会话写一份人能读、能改的交接书,然后开一条新会话接手。 挡的是"上下文满了被压成一段摘要"。见换代

不是 compact。见压缩

交接书

handoff document · Handoff

换代时写的文书,五段:doing(在做什么)、decided(定了什么)、deadends(走不通的路)、 next(下一步)、scene(现场)。

只有 doingnext 是必填的 —— 硬性要求"走不通的路"非空会逼模型编造。

压缩

compact

Claude Code 的原生做法:上下文满了,把前面的对话总结成一段摘要。

flower 不用它,用换代代替。区别在于:摘要是模型生成的、不可读不可改、丢什么你不知道; 交接书是结构化的、落盘的、你能打开改一行再让它接着跑。


上下文管理

主线程

main thread

协调者所在的那条会话上下文。它是唯一贯穿整次运行的上下文,所以最需要省。

代码里判定主线程的方式:hook 数据里没有 agent_id。subagent 的 hook 带 agent_id

工作台

workbench · Workbench

落盘的工作目录,三个子目录:

目录 放什么
scripts/ 要跑第二次的脚本,首行写 # desc: 一句话
artifacts/ 超过 2000 字符的长产出
notes/ 关键决策,一个决策一个文件

INDEX.md 是这三个目录的索引,注入到 system prompt 里,所以 agent 每轮都知道手上有什么。

两个入口,两个默认位置

工作台放在哪取决于怎么创建它,这一点容易踩:

创建方式 工作台根目录
Workbench(workspace) —— 也是 starter_flow() / wake_state() 走的路 <工作区>/.flower
Runtime(workbench=True) <run_dir>/workbench(默认 runs/workbench)

命令行走的是前者,所以 flower 跑出来的是 .flower/;但 Python 里直接 Runtime(workbench=True) 拿到的是 runs/workbench。要指定位置就传一个建好的 Workbench 实例,别依赖默认值。

索引 subagent 继承不到

索引走会话级的 system_prompt.append,subagent 拿不到。所以"长产出写 artifacts/" 这条规矩必须由协调者任务书里转述 —— 那是唯一通道。

落盘

spill

工具结果超过阈值(默认 4000 字符)时,PostToolUse hook 把它写到 <工作台根目录>/spill/,上下文里只留一行路径。

路径跟着工作台,不是写死的 —— 只有工作台在默认位置 <工作区>/.flower 时,它才正好是 .flower/spill/。开了隔离、工作台被 home= 指到仓库外时, spill 也跟着挪出去。

当场就剪,不是等上下文满了再回头压缩

一次性命令

ephemeral command

结果会过期、没有留存价值的命令 —— lsgit statusps 这类。它们的结果不进持久化的 会话记录。判断"能不能放行主线程跑一眼"和"结果会不会被裁掉",用的是同一个函数, 所以两个集合永远相等。

裁剪

trim · TrimmingSessionStore

resume 之前重写要喂回模型的那份消息(一次性命令的结果、超长工具输出)。

它只覆盖 load():SQLite 里的原文始终不动,裁掉的只是这次 resume 送进上下文的那一份。 所以裁剪是可逆的 —— 换个策略再 resume 一次,拿到的又是完整记录。

剪除

prune · PruningSessionStore

错误消息挡在上下文外面。断网重试期间产生的一堆报错不该占 resume 之后的上下文。

不要裁剪混:裁剪按体积和价值丢,剪除按"是不是错误"丢。


运行时

隔离

isolation

标记过的角色自动分到独立的 git worktree,由 hook 强制,不靠提示词。 并行改同一个仓库时不打架。

开隔离就要把工作台挪出仓库

开 worktree 隔离时,工作台必须用 home= 指到仓库外,否则被隔离的 agent 写不进共享 checkout。

韧性

resilience · Resilience

断网时挂着等而不是失败退出:DNS + TCP 探针盯着,网络恢复后 resume 续跑。 等待期间产生的错误消息由剪除挡在上下文外。

血缘

lineage · Lineage

跨进程记录"这次运行由哪条会话 fork 而来",落在 lineage.json接续靠它找到上次跑到哪。

不要运行清单混 —— 那是 runs/manifest.json,记的是每次运行的账目。

运行清单

run manifest · runs/manifest.json

每次运行的账目记录:花了多少钱、跑了多久、上下文多大。案例页里的数字都能在这里复算。

唤醒

wake · wake_state()

起跑之前的只读探测:看看这个工作区是不是已经有需求确认书和目标了, 从而决定这次是全新开始还是接续一个字节都不写。

wake_state() 是工作台位置的唯一定义处 —— 驱动程序想知道确认书在哪也得走它。 自己拼路径拼错了不会报错,只会静默失效。

事件

event · Event

SDK 的消息流被压平成的稳定结构。交互层只认 Event,不 import 任何 SDK 类型 —— 这是换 UI 不用改核心的边界。

交互层

interaction layer

人和运行之间的那层 UI。默认是终端,可以换成 Web、TUI、HTTP,或者全自动无人值守。 见换交互层

会话存储

session store · SessionStore

会话消息的持久化后端。默认 SqliteSessionStore 写到 runs/sessions.db, 可以套上裁剪剪除两层包装。

预算

budget · max_budget_usd

一次运行的花费上限,超了就停。长程运行没有这个会很贵 —— HT001 花了 $171.62。


可移植性

可移植

portable

换一台机器,行为一致。做法是 setting_sources=[] —— 不读宿主机的 ~/.claude/, 也不读项目的 .claude/。领域能力靠 plugin 随仓库走,凭证靠 .env 自带。

代价:凭证必须自带,不会自动继承宿主机配置。

叠加

append

领域指令追加在 Claude Code 原生系统提示词之后,而不是替换它:

system_prompt = {"type": "preset", "preset": "claude_code", "append": spec.instructions}

所以专门化不以损失通用能力为代价。

plugin

跟着仓库走的领域能力包。通过 plugins=[local] 加载,目录里可以放 skills/agents/hooks/.mcp.json。见部署