关于 elicit
elicit 是通过对话编写 Allium 规格的技能,帮助利益相关者界定范围、梳理工作流并澄清行为和边界情况,不规定具体实现方式。
主要功能
- 01通过对话起草规格
- 02定义范围与边界
- 03梳理流程和参与者
- 04引导探索主流程与失败路径
适用场景
- 01将模糊的产品构想整理为规格
- 02记录系统或功能的范围边界
- 03梳理参与者与生命周期转换
- 04识别边界情况和失败处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ juxt/ allium --skill elicit安装后在 Claude Code 中输入/elicit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/assumption-checking.mddetail-elicitation.mdlibrary-spec-signals.mdobstacle-elicitation.mdprocess-discovery.md详细介绍
需求引导
本技能将通过对话引导你构建 Allium 规范。目标是揭示歧义,并生成一份描述软件功能、但不规定实现方式的规范。
确定规范范围
在深入细节之前,先明确要规范的内容。并非所有内容都需要放在同一份规范中。
首先要问的问题
“这份规范的边界是什么?” 是一个完整的系统?一个独立的功能领域?更大系统中的一项服务?请明确说明哪些内容属于范围,哪些不属于。
“有没有我们应该有意排除的领域?” 第三方集成可能应归入库规范。旧版功能可能不值得规范。有些功能可能应放在单独的规范中。
“这是一个新系统,还是已经有现成代码?” 如果代码已经存在,你要做的是结合需求引导进行提炼。现有代码会限制规范中哪些内容切实可行。
记录范围决策
在每份规范的开头记录范围:
-- allium: 3
-- interview-scheduling.allium
-- Scope: Interview scheduling for the hiring pipeline
-- Includes: Candidacy, Interview, Slot management, Invitations, Feedback
-- Excludes:
-- - Authentication (use oauth library spec)
-- - Payments (not applicable)
-- - Reporting dashboards (separate spec)
-- Dependencies: User entity defined in core.allium
版本标记(-- allium: N)必须是每个 .allium 文件的第一行。请使用当前的语言版本号。
找到合适的抽象层级
过于具体,就会变成在规范实现方式;过于抽象,就会变得毫无用处。
“为什么”测试
对于每个细节,都要问:“利益相关者为什么在意这一点?”
| 细节 | 为什么? | 是否纳入? |
|---|---|---|
| “用户使用 Google OAuth 登录” | 他们需要进行身份验证 | 也许不需要,“用户进行身份验证”可能就足够了 |
| “我们支持 Google 和 Microsoft OAuth” | 用户可以选择自己的提供方 | 是,这是领域层面的选择 |
| “会话在 24 小时后过期” | 安全性/用户体验决策 | 是,会影响用户体验 |
| “会话存储在 Redis 中” | 性能 | 否,这是实现细节 |
| “密码必须至少包含 12 个字符” | 安全策略 | 是,会影响用户 |
| “密码使用 bcrypt 进行哈希处理” | 安全实现方式 | 否,这是如何实现的问题,而非实现什么 |
“可以换一种方式实现吗?”测试
问:“即使采用不同的实现方式,这仍然会是同一个系统吗?”
- 如果是,那它很可能是实现细节。将其抽象掉。
- 如果不是,那它很可能属于领域层面。将其纳入规范。
示例:
- “通过 Slack 发送通知”。也可以用电子邮件、短信等方式。将其抽象为
Notification.created(channel: ...)。 - “面试官必须在 3 小时内确认”。这个具体时限在领域层面很重要。请纳入时长。
- “我们使用 PostgreSQL”。也可以使用任何数据库。不要纳入。
- “出于合规要求,数据保留 7 年”。这是监管要求。请纳入。
“模板与实例”测试
这是一个事物类别,还是一个具体实例?
| 实例(实现方式) | 模板(领域层面) |
|---|---|
| Google OAuth | 身份验证提供方 |
| Slack | 通知渠道 |
| 15 分钟 | 链接过期时长(可配置) |
| Greenhouse ATS | 外部候选人来源 |
有时,实例本身就是领域关注点。“我们专门集成 Salesforce”可能是一项竞争优势。“我们恰好支持这三个 OAuth 提供方”可能属于设计范围。
如有疑问,可以询问利益相关者:“如果我们改变这一点,系统会变成另一个系统,还是只是换了一种实现方式?”
抽象层级
Too abstract: "Users can do things"
|
Product level: "Candidates can accept or decline interview invitations"
|
Too concrete: "Candidates click a button that POST to /api/invitations/:id/accept"
抽象得过头的迹象。 这份规范几乎可以描述任何系统。没有可测试的断言。产品负责人会说:“但这并没有体现……”
过于具体的迹象。 你提到了技术、框架或 API。你描述了 UI 元素(按钮、页面、表单)。实现团队会说:“为什么要规定我们该怎么构建?”
配置与硬编码
遇到特定值(3 小时、7 天等)时,请思考:
- 这是设计决策吗? 将其纳入规范。
- 它可能因部署或客户而异吗? 将其设为可配置项。
- 它是随意设定的吗? 考虑是否应该纳入。
-- Hardcoded design decision
rule InvitationExpires {
when: invitation: Invitation.created_at + 7.days <= now
...
}
-- Configurable
config {
invitation_expiry: Duration = 7.days
}
rule InvitationExpires {
when: invitation: Invitation.created_at + config.invitation_expiry <= now
...
}
黑盒
有些逻辑很重要,但属于不同的层级:
-- Black box: we know it exists and what it considers, but not how
ensures: Suggestion.created(
interviewers: InterviewerMatching.suggest(
considering: {
role.required_skills,
Interviewer.skills,
Interviewer.availability,
Interviewer.recent_load
}
)
)
规范说明存在一种匹配算法,会考虑这些输入,并生成面试官建议。规范没有说明匹配如何运作、使用哪些权重或采用哪种具体算法。
当算法复杂且不断演进、产品负责人关注输入和输出而非内部细节时,这就是合适的抽象层级;如有需要,也可以另行编写详细规范来说明。
阅读初始提示词
在选择方法之前,先评估用户带来了什么。初始提示词会告诉你从哪里着手。
用户描述了一个流程。“我们有一个招聘流程:候选人申请、经过筛选、参加面试,然后我们做出决定。”他们从流程层面思考。先进行流程探索——让他们描述流程,然后帮助他们将其整理为规范构造。请参阅流程探索。
用户提到了实体。“我需要为一个包含状态和转换的 Order 实体编写规范。”他们已经在构造层面思考。跳过流程探索,转到范围定义,然后补充细节。在梳理规则和表面时,请参阅细节 elicitation。
用户只有一个模糊的想法。“我们需要构建一个用于管理客户支持的东西。”在编写规范之前,他们需要帮助来理清想法。先通过开放式问题进行流程探索:“请告诉我客户寻求帮助时会发生什么。”请参阅流程探索。
用户已有代码。“我们有一个支付服务,我想记录它的功能。”这是提炼结合引导式访谈。引导他们使用 distill 技能,或结合两者:从代码中提炼结构,从利益相关者处引导出意图。
**用户已有规范。**先阅读规范。使用评估规范确定每个实体的开发阶段。跳过规范已经涵盖的阶段——对于已经包含范围注释的规范,不要重复询问范围问题;对于已经包含转换图的规范,不要重新探索流程。从每个实体所需的层级开始:对于有生命周期但没有规则的实体,进行细节引导;对于有规则但没有失败路径的实体,进行障碍引导。
引导方法
阶段 0:流程探索
目标: 在识别构造之前,了解系统支持的流程。
并非每次会话都需要此阶段。如果用户已经考虑好实体和生命周期,请跳到阶段 1。如果用户描述了一个流程或只有一个模糊的想法,请从这里开始。
在套用 Allium 结构之前,先让用户用自己的话描述系统。记录流程、参与者和结果,然后整理成构造。具体技巧请参阅流程探索。
输出: 流程名称和结果。大致的步骤顺序。已识别的参与者。足以编写粗略规范的信息(包含转换图和开放问题的实体)。
注意: 不要过早跳到实体定义。保持在流程层面,直到流程清晰为止。
阶段 1:范围定义
目标: 了解我们要规范什么,以及边界在哪里。
要问的问题:
- “这个系统本质上是做什么的?用一句话概括。”
- “这个系统从哪里开始,到哪里结束?哪些内容属于范围,哪些不属于?”
- “用户是谁?是否有不同的角色?”
- “它会与哪些现有系统集成?这些系统负责什么?”
如果跳过了阶段 0,还要询问:“这个系统支持哪些关键流程?每个流程怎样才算成功?”这能让实体识别以流程为依据,而不是孤立地罗列名词。此处也适用流程发现中的技巧——如果用户难以描述流程,可使用过去时回忆和以结果为先的提问方式。
输出: 参与者和角色列表。核心实体列表(如果执行了阶段 0,则根据流程推导)。边界决策(哪些内容属于外部)。一句话描述。
注意: 范围蔓延(“它还会做 X、Y、Z”,温和地引导回主题)。默认的假设知识(“它显然会处理身份验证”,要明确写出)。描述可能指向库规范,而非特定于应用的逻辑(例如 OAuth、支付处理、电子邮件发送)。
阶段 2:正常流程
目标: 从头到尾梳理主要流程。
如果阶段 0 产出了一个行走骨架(见流程发现),就以此为起点。否则,询问:“如果我们只能为这个流程构建一条路径,那会是什么?”将骨架写成粗略规范,并用领域术语向用户复述(见评估规范)。
然后进一步展开:“每个步骤由什么触发?涉及哪些人?会发生什么变化?”沿着一个实体的生命周期进行梳理,记录状态转换、参与者和触发条件。
Candidacy:
applied -> screening -> interviewing -> deciding -> hired | rejected
输出: 关键实体的转换图。主要触发条件及其结果。每个步骤的参与者分配。
注意: 过早转向边缘情况(“但如果……”,先记下来,继续关注正常流程)。实现细节开始渗入(“API 端点……”,引导讨论回到结果)。
编写完规范构造后,如果 CLI 可用,请运行 allium check。继续之前先修复结构问题——不要等到阶段 4 才验证。
建立骨架后,请参阅细节引导,了解补充规则、界面、字段和数据依赖关系的技巧。
阶段 3:边缘情况和失败路径
目标: 找出可能出错的情况,以及系统会如何处理。
请参阅障碍引导了解相关技巧。主要方法包括:
- 使用事前验尸法:“假设这个系统已经建成,但运行失败了。出了什么问题?”
- 对每个步骤询问:“如果这里没人采取行动会怎样?过一天呢?一周呢?”
- 对每次交接询问:“谁来接手?他们怎么知道轮到自己了?需要看到什么?”
- 对每次转换询问:“如果不满足前置条件会怎样?能否撤销?”
- 对于外部依赖,询问:“这些信息如何进入系统?如果外部服务不可用会怎样?”
输出: 异常转换。带有 requires 守卫条件的时间触发器。升级路径。终止错误状态。不变量。
注意: 无限循环(“然后重试,再重试……”,需要终止状态)。缺少升级机制,因为最终需要让某个人知情。
当利益相关者陈述系统范围的属性(“余额永远不会为负数”“同一候选人的两场面试不会重叠”)时,这些属性可能适合作为顶层不变量。请将其记录为 invariant Name { expression } 声明。
编写完规则和异常转换后,如果 CLI 可用,请运行 allium check。进入细化阶段之前先修复问题。
阶段 4:完善
目标: 验证并补全规范。
请参考假设检查中的技巧。用领域术语描述规范的内容,并根据用户的心智模型进行验证。沿着规范推演具体场景。检验顺序方面的假设。核实参与者的分配。
如果 Allium CLI 可用,请运行 allium check,并利用诊断结果找出结构性缺口。如果 allium analyse 可用,且规范包含规则和界面,请运行该命令,并利用结果找出流程层面的缺口。请参考处理分析结果,了解如何将分析结果转化为领域问题。
需要提出的问题:
- “看看[实体],这些状态完整吗?它还可能处于其他状态吗?”
- “还有什么内容尚未涵盖吗?”
- “这条规则引用了[X],我们需要对它进行定义,还是它属于外部内容?”
- “这个细节在这里必不可少吗,还是应该放在详细规范中?”
技巧: 选取一个具体场景,沿着规范进行推演。“假设 Alice 申请 Senior Engineer 职位。请带我逐步了解她的候选状态会发生什么变化。”
产出: 完整的实体定义。记录未解决的问题。确定延期编写的规范。确认外部边界。
当同一种义务模式(例如序列化约定、确定性评估要求)出现在多个界面时,建议将其提取为 contract 声明以供复用。
像明确逻辑一样明确结果
结果的形式是最容易丢失的决策——可能被删除、只记录一部分,甚至被排除在范围之外。应像深入探讨核心方法一样,深入确定结果形式。
- 逐字段直接询问结果的形式。“这必须准确返回或报告什么?每个字段分别是什么?”获取完整列表,而不是有代表性的示例。
- **任何派生、披露或报告依据与计算依据不同的数值,都应按核心方法的标准处理:**明确其精确定义、依据、精度以及在退化情况下的行为,并持续追问,直到没有任何歧义。此处一句话的回答往往藏着细微的要求——例如,按不同于计息日数的日计数方式披露的利率,或客户看到的数值与系统保存的数值不同。
- **默认将结果形式纳入范围。**切勿将系统必须返回或披露的内容排除在范围之外;排除某项输出是需要与操作人员确认的决策,而不是可以悄然省略的内容。
结束前:完整性检查
访谈通常会让人觉得早就结束了——只要主流程说得通,就会产生这种感觉,但这还远远不够。不要凭这种感觉结束访谈。在将对话视为完整之前,请根据以下决策类别逐项检查操作人员说过的所有内容。对于与本系统相关的每个类别,确认对方确实已经回答;如果还没有,就现在提出一个具体、可回答的问题,并继续追问,直到每个相关类别都已经问到并得到回答(或被明确排除在范围之外)。从未问过的类别不能算作“已假定”——它只是尚未问到;需求说明中的沉默意味着应该提问,而不是可以自行猜测。
- 核心方法或规则。 计算、推导或判定结果时,如果存在不止一种公认的方法或惯例,这个组织采用哪一种?不要默认采用最常见的做法。
- 单位和精度。 数量的计量单位、精度,以及如何处理更细粒度的数值。
- 舍入或截断。 将数值调整到其单位精度时,采用哪种方向,以及余数如何处理。
- 顺序和优先级。 应用或处理多项内容时,采用什么顺序,以及优先级相同时如何打破平局。
- 阈值和限额。 最小值、最大值、上限,以及低于这些值时会被豁免、忽略或视为零的容差。
- 时间和生效日期。 某事项何时生效、何时记录、是否允许追溯日期、是否按较早时间点重新计算,以及截止时间。
- 并发和顺序安排。 两件事同时或短时间内接连发生时,如何确定顺序,以及如何打破平局。
- 边界输入。 对于零值、负值、空值、缺失值、重复值、超出范围的值,分别是拒绝、忽略还是处理,以及是否仍保留记录。
- 盈余和短缺。 对任何多余或缺少的部分如何处理——留存、退回、结转、升级处理——以及接下来用于抵扣什么。
- 范围边界。 本系统负责什么,上游系统或外部系统提供什么。确认排除某项内容也是一项决策,不能悄然省略。
- 输出和报告形式。 结果必须包含的所有内容,逐字段列明;以及任何采用不同于计算或存储口径的方式报告或披露的数值。
- 完整生命周期,而不只是创建。 该对象在整个生命周期中会经历的所有事件,而不只是创建和正常运行——提前终止或取消、生命周期中的更改或更正、暂停或挂起、续订或延期、撤销、转移、到期,以及关闭或逐步停用——并且对每种事件,都要明确会重新计算什么,以及剩余部分如何处理。这是最容易被漏掉的类别;针对当前实体,逐项列出这些事件,并询问每一项是否适用。
只要上述任何相关类别仍是未提问的假设,就还没有结束。
需求收集原则
一次只问一个问题
不好的问法:“你有哪些实体,它们可以处于哪些状态,谁可以修改它们?”
好的问法:“这个系统主要管理哪些内容?” 接着问:“我们来看看 [候选资格]。它可以处于哪些状态?” 然后问:“谁可以更改候选资格的状态?”
深入探讨影响
出现选项时,不要只接受第一个答案。要探究其后果。
“你说邀请会在 48 小时后过期。之后会发生什么?” “如果重试后候选人仍未回复呢?” “如果他们一直不回复,这个候选资格会永远卡在那儿吗?”
这样可以发现他们尚未作出的决策。
区分产品与实现
听到实现层面的表述时,引导对方换个角度:
| 对方说 | 你可以这样引导 |
|---|---|
| “API 返回 404” | “所以用户会收到未找到的提示?” |
| “我们把它存储在 Postgres 中” | “会记录哪些信息?” |
| “前端显示一个模态框” | “系统会提示用户确认?” |
| “我们使用 cron 作业” | “这是定期执行的,频率是多少?” |
明确指出模糊之处
记录一个待解决问题,总比自行假设更好。
“我不确定拒绝后应该让候选人回到候选池,还是将其彻底移除。我先把这记为一个待解决问题。”
open question "When candidate declines, do they return to pool or exit?"
乐于迭代
修订先前的决定很正常。
“之前我们说所有管理员都能看到所有通知。但你现在提到了按角色划分的仪表盘。我们要重新讨论一下吗?”
深入优先于覆盖面
先完整梳理最重要的实体。其他实体先粗略处理,并留下待解决问题。用户可以在后续会话中继续完善。试图在一次对话中把所有实体都梳理到相同的细致程度,可能会耗尽上下文,却什么也没完成。
知道何时停止
并非所有内容都需要现在明确。
“这已经涉及匹配算法的工作方式了。我们要把它留到详细规格说明中吗?”
“我们已经讨论了主要流程。报告仪表盘听起来像是一个独立的规格说明。”
常见的需求引导陷阱
“显而易见”陷阱
当有人说“显而易见”或“当然”时,深入追问。“你说管理员显然会批准。有没有他们不需要批准的情况?以后能否将这一步自动化?”
“边缘情况螺旋”陷阱
有些人想立刻涵盖所有边缘情况。“我们先把它记为一个待解决问题,暂时聚焦主要流程。之后再回过头讨论边缘情况。”
“模糊同意”陷阱
不要接受没有具体细节的“是”。“你说是,候选人可以重新安排时间。可以改几次?有限制吗?之后会发生什么?”
“缺少执行者”陷阱
留意没有明确执行者的操作。“你说‘时段会被释放’。由谁或什么来释放?这是自动进行的,还是需要有人触发?”
“同义术语”陷阱
如果你听到不同利益相关者、现有代码或相关规格说明中,用不同术语表示同一概念,请停下来解决术语问题后再继续。
“你说的是‘购买’,但之前我们称它为‘订单’。应该使用哪个术语?”
仅仅备注两个术语含义相同,并不算解决问题。这只会确保两个术语都会出现在实现中。选定一个术语,交叉引用相关规格说明,并更新所有引用。不要在任何地方保留旧术语,连“另请参阅”备注中也不行。
需求引导会话结构
这些时间安排适用于由人工主持的会话。在 LLM 对话中,应根据各阶段的输出决定何时推进,而不是盯着时钟。
开场。 简要说明 Allium:“我们要记录的是软件做什么,而不是它如何构建。”商定本次会话的范围。
定义范围。 确定参与者、实体和边界。写出一句话描述。
主流程。 从头到尾梳理主要流程。记录状态、触发条件和结果。
边缘情况。 超时和截止时间、失败模式、升级路径。
总结。 回顾关键决定,列出待解决问题。指出哪些实体仍较粗略,以及下一步需要补充什么。必要时确定下次会话的范围。
需求引导后
对于已明确目标的定向修改,请使用 tend skill。对于需要结构化梳理的大型新增内容(新功能领域、复杂实体关系、需求不明确),即使已经存在规格说明,elicit 仍是合适的工具。检查规格说明与实现是否一致,属于 weed skill 的工作。