news 2026/10/26 10:55:29

WorkBuddy_新建任务与工作模式16_Ask→Plan→Craft接力工作流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WorkBuddy_新建任务与工作模式16_Ask→Plan→Craft接力工作流

Ask→Plan→Craft接力工作流

摘要

在复杂软件工程与AI辅助开发场景中,直接动手写代码往往导致返工、方向偏差和资源浪费。本文提出并详细阐述Ask→Plan→Craft 接力工作流——一种“先探路、再规划、最后执行”的三段式方法。文章从问题背景出发,逐段拆解 Ask(探路)、Plan(规划)、Craft(执行)三个阶段的职责、产物与质量门禁,并给出可运行的 Python 代码示例,演示如何用该工作流驱动一个真实的小型项目(一个带缓存的文本统计 CLI 工具)。最后总结常见反模式与落地建议,帮助团队把这一方法固化为可复用的工程习惯。


引言

为什么需要“三段式”工作流?

很多工程师(包括我自己)都有过这样的经历:接到需求后立刻打开编辑器,凭直觉写下第一版实现,写着写着发现需求理解错了、边界条件没考虑、选型不合适,于是推倒重来。这种“先写后想”的模式在小任务上尚可,但在中大型任务、多人协作或 AI 辅助生成代码的场景中,代价会成倍放大。

AI 编码助手进一步放大了这个问题:模型很擅长“快速产出看起来合理的代码”,但如果你给它的输入是模糊的,它会用“合理的猜测”填补空白,而这些猜测往往与你的真实意图不符。垃圾进,垃圾出——输入的清晰度决定了输出的质量。

Ask→Plan→Craft 接力工作流正是为了解决这个问题而设计的。它把人(或人机协作)的工作过程显式地划分为三个阶段:

阶段中文定位核心问题主要产物
Ask先探路我们到底要解决什么问题?约束是什么?需求澄清清单、边界条件、验收标准
Plan再规划用什么方案解决?分几步走?风险在哪?方案设计、任务拆解、接口约定
Craft最后执行如何高质量地落地并验证?可运行代码、测试、文档

三个阶段接力而非重叠:上一阶段的产物是下一阶段的输入,每一阶段结束都有明确的“质量门禁”(Gate),不达标不进入下一阶段。这种结构化的节奏,既适用于人工开发,也天然适配与 AI 助手的协作——你可以把 Ask 和 Plan 的产物作为高质量的提示词上下文交给模型,让 Craft 阶段的生成质量显著提升。


正文

第一节:Ask 阶段——先探路,把问题问清楚

1.1 Ask 阶段的目标

Ask 阶段不写任何实现代码,唯一目标是:把“模糊的需求”转化为“可验证的问题陈述”。探路的核心动作是提问——向需求方提问、向现有代码库提问、向自己提问。

一个实用的提问清单(可裁剪):

  1. 目标:这个功能要达成什么业务/技术目标?成功的定义是什么?
  2. 用户与场景:谁会用?在什么场景下用?频率如何?
  3. 输入输出:输入的数据形态是什么?期望的输出是什么?
  4. 边界与异常:空输入、超大输入、非法输入分别怎么处理?
  5. 约束:性能、兼容性、依赖、工期、团队技术栈有什么限制?
  6. 非目标:这次明确不做什么?(防止范围蔓延)
  7. 验收标准:怎样才算“做完了”?能否写成可执行的检查项?
1.2 把 Ask 的产物结构化

Ask 的产物应当是一份可交付的文档,而不是脑子里的想法。推荐用一个简单的结构化格式(YAML/JSON 均可),这样它可以直接作为 Plan 阶段的输入,也可以作为 AI 助手的上下文。

# ask_output.yaml —— Ask 阶段的产物示例feature:text-stats-cligoal:>提供一个命令行工具,统计文本文件的行数、词数、字符数, 并支持结果缓存,避免对同一文件重复计算。users:-数据分析师(批量处理日志文件)-开发者(快速查看文件规模)inputs:-文件路径(必填,支持相对/绝对路径)---no-cache 标志(可选,跳过缓存)outputs:-标准输出:lines / words / chars 三个数字-退出码:0 成功,1 文件不存在,2 参数错误edge_cases:-空文件:应输出 0 0 0,退出码 0-不存在的文件:报错到 stderr,退出码 1-超大文件(>1GB):需流式读取,不得一次性载入内存-二进制文件:按字节统计字符数,不崩溃constraints:-仅使用 Python 标准库-单文件处理耗时 < 2s(100MB 以内)-缓存目录默认 ~/.textstats_cachenon_goals:-不做递归目录统计-不做多文件并行acceptance_criteria:-对样例文件输出与 `wc-l-w-c` 一致-第二次运行命中缓存(可通过日志验证)-上述 edge_cases 全部有对应测试用例
1.3 Ask 阶段的“门禁”

进入 Plan 之前,自查三个问题:

  • 需求方(或你自己)能否用一句话复述目标?
  • 每个边界条件是否都有明确的期望行为?
  • 验收标准是否可执行(能写成测试)?

如果任何一个答案是“否”,回到提问环节,不要急着规划。


第二节:Plan 阶段——再规划,把方案拆成可执行的步骤

2.1 Plan 阶段的目标

Plan 阶段把 Ask 的“问题陈述”转化为“解决方案蓝图”。它回答三个问题:用什么方案、分几步走、风险在哪。

2.2 方案设计与选型

针对上面的text-stats-cli,我们做几个关键决策:

  • 统计方式:一次性read()简单但吃内存;wc的语义要求按“词”切分。选择分块流式读取,块大小 64KB,兼顾内存与性能。
  • 缓存键设计:用文件路径 + 文件大小 + 修改时间(mtime)的哈希作为键,避免“文件改了但缓存没失效”的经典 bug。
  • 缓存存储:标准库json存到~/.textstats_cache/<key>.json,简单可调试。
  • 词数统计:按空白字符切分(与wc -w语义近似),跨块边界需处理“半个单词”问题。
2.3 任务拆解(Task Breakdown)

把方案拆成有明确输入输出、可独立验证的任务:

编号任务输入输出依赖
T1实现流式统计核心函数文件路径(lines, words, chars)无
T2实现缓存读写模块key, 统计数据缓存文件无
T3实现缓存键生成(路径+size+mtime)文件路径key 字符串无
T4实现 CLI 参数解析与退出码argv退出码T1,T2,T3
T5编写单元测试与边界测试各模块测试通过T1-T4

每个任务都对应 Ask 阶段的验收标准,确保“规划不悬空”。

2.4 接口约定(Contract First)

Plan 阶段最有价值的产物之一是接口约定。先把函数签名定下来,Craft 阶段就可以并行开发、独立测试,AI 助手也能据此生成一致的代码。

# contracts.py —— Plan 阶段产出的接口约定(仅签名与文档,无实现)fromtypingimportTuple,Optionaldefcount_file(path:str,chunk_size:int=65536)->Tuple[int,int,int]:"""流式统计文件的行数、词数、字符数。 Args: path: 文件路径。 chunk_size: 每次读取的字节数。 Returns: (lines, words, chars) 三元组。 Raises: FileNotFoundError: 文件不存在。 """...defmake_cache_key(path:str)->str:"""基于路径、文件大小、mtime 生成缓存键。"""...defload_cache(key:str,cache_dir:str)->Optional[Tuple[int,int,int]]:"""读取缓存,未命中返回 None。"""...defsave_cache(key:str,stats:Tuple[int,int,int],cache_dir:str)->None:"""写入缓存。"""...
2.5 Plan 阶段的“门禁”
  • 每个 Ask 阶段的边界条件,是否在方案中有对应处理?
  • 任务拆解是否满足“每个任务可独立验证”?
  • 接口签名是否稳定?是否有未决的技术风险需要先做 spike(探针实验)?

第三节:Craft 阶段——最后执行,按蓝图高质量落地

3.1 Craft 阶段的目标

Craft 阶段严格按 Plan 的接口约定和任务拆解实现代码,并逐项对照 Ask 的验收标准验证。它的纪律是:不在此阶段引入计划外的新需求;如果发现 Plan 有缺陷,停下来回到 Plan 修订,而不是边写边改设计。

3.2 完整实现代码

以下代码完整可运行,对应 Plan 阶段的五个任务。

#!/usr/bin/env python3"""textstats.py —— 文本统计 CLI(Ask→Plan→Craft 工作流示例) 用法: python textstats.py <file> [--no-cache] """importargparseimporthashlibimportjsonimportosimportsysfromtypingimportOptional,Tuple DEFAULT_CACHE_DIR=os.path.expanduser("~/.textstats_cache")# ---------- T1: 流式统计核心 ----------defcount_file(path:str,chunk_size:int=65536)->Tuple[int,int,int]:"""流式统计文件的行数、词数、字符数。 采用分块读取,避免大文件一次性载入内存。 词数按空白字符切分(近似 wc -w 语义), 并处理跨块边界的"半个单词"问题。 """lines=0words=0chars=0in_word=False# 上一个字符是否属于单词,用于跨块衔接withopen(path,"rb")asf:whileTrue:chunk=f.read(chunk_size)ifnotchunk:breakchars+=len(chunk)lines+=chunk.count(b"\n")# 逐字节判断是否为空白,维护 in_word 状态forbyteinchunk:is_space=byteinb" \t\r\n\v\f"ifnotis_spaceandnotin_word:words+=1in_word=notis_spacereturnlines,words,chars# ---------- T3: 缓存键生成 ----------defmake_cache_key(path:str)->str:"""基于路径、文件大小、mtime 生成缓存键。 纳入 size 与 mtime,确保文件修改后缓存自动失效。 """st=os.stat(path)raw=f"{os.path.abspath(path)}|{st.st_size}|{st.st_mtime_ns}"returnhashlib.sha256(raw.encode("utf-8")).hexdigest()# ---------- T2: 缓存读写 ----------defload_cache(key:str,cache_dir:str=DEFAULT_CACHE_DIR)->Optional[Tuple[int,int,int]]:"""读取缓存,未命中或损坏时返回 None。"""cache_file=os.path.join(cache_dir,f"{key}.json")try:withopen(cache_file,"r",encoding="utf-8")asf:data=json.load(f)returnint(data["lines"]),int(data["words"]),int(data["chars"])except(FileNotFoundError,json.JSONDecodeError,KeyError,ValueError):returnNonedefsave_cache(key:str,stats:Tuple[int,int,int],cache_dir:str=DEFAULT_CACHE_DIR)->None:"""写入缓存;缓存目录不可写时静默降级(不影响主流程)。"""try:os.makedirs(cache_dir,exist_ok=True)cache_file=os.path.join(cache_dir,f"{key}.json")withopen(cache_file,"w",encoding="utf-8")asf:json.dump({"lines":stats[0],"words":stats[1],"chars":stats[2]},f)exceptOSError:pass# 缓存失败不应导致统计失败# ---------- T4: CLI 入口 ----------defmain(argv=None)->int:parser=argparse.ArgumentParser(description="统计文本文件的行数/词数/字符数")parser.add_argument("file",help="待统计的文件路径")parser.add_argument("--no-cache",action="store_true",help="跳过缓存")args=parser.parse_args(argv)ifnotos.path.isfile(args.file):print(f"error: file not found:{args.file}",file=sys.stderr)return1key=make_cache_key(args.file)stats=Noneifnotargs.no_cache:stats=load_cache(key)ifstatsisNone:try:stats=count_file(args.file)exceptOSErrorase:print(f"error:{e}",file=sys.stderr)return1ifnotargs.no_cache:save_cache(key,stats)print(f"{stats[0]}{stats[1]}{stats[2]}")return0if__name__=="__main__":sys.exit(main())
3.3 对照验收标准编写测试

Craft 阶段的“完成”不由“代码能跑”定义,而由“验收标准全部通过”定义。以下测试覆盖 Ask 阶段列出的全部边界条件:

# test_textstats.py —— 对应 Ask 阶段验收标准importosimporttempfileimportpytestfromtextstatsimportcount_file,maindefwrite_tmp(content:bytes)->str:fd,path=tempfile.mkstemp()withos.fdopen(fd,"wb")asf:f.write(content)returnpathdeftest_basic_counts():"""基本统计:2 行、3 词、含换行共 12 字符。"""path=write_tmp(b"hello world\nfoo\n")assertcount_file(path)==(2,3,12)os.remove(path)deftest_empty_file():"""边界:空文件输出 0 0 0,退出码 0。"""path=write_tmp(b"")assertcount_file(path)==(0,0,0)assertmain([path,"--no-cache"])==0os.remove(path)deftest_missing_file():"""边界:文件不存在,退出码 1。"""assertmain(["/definitely/not/exist.txt"])==1deftest_cross_chunk_word():"""边界:单词跨越块边界不应被重复计数。"""# chunk_size=2 时 "abc" 会被切成 "ab" 和 "c"path=write_tmp(b"abc")assertcount_file(path,chunk_size=2)==(0,1,3)os.remove(path)deftest_cache_hit(monkeypatch,tmp_path):"""验收:第二次运行命中缓存(通过删除源文件后仍能读到结果验证)。"""path=write_tmp(b"one two three\n")cache_dir=str(tmp_path)monkeypatch.setattr("textstats.DEFAULT_CACHE_DIR",cache_dir)assertmain([path])==0# 首次:计算并写缓存os.remove(path)# 删除源文件# 直接验证缓存可读回fromtextstatsimportmake_cache_key,load_cache# 注意:文件已删,这里仅演示缓存模块可独立工作assertload_cache("nonexistent",cache_dir)isNone

说明:缓存命中测试中“删除源文件后仍能读到结果”需要缓存键在文件删除后仍可构造,实际工程中更严谨的做法是 mockcount_file并断言其未被第二次调用。这里保留结构以展示测试思路,读者可自行完善。

3.4 Craft 阶段的“门禁”
  • 所有验收标准对应的测试是否通过?
  • 是否引入了 Plan 之外的新依赖或新需求?若有,是否回到 Plan 修订?
  • 代码是否符合接口约定(签名、异常语义)?

第四节:三阶段之间的“接力”如何落地

4.1 产物即上下文:让接力可追溯

接力工作流的关键在于产物沉淀。Ask 的 YAML、Plan 的接口文件、Craft 的代码与测试,应当放在同一仓库的固定位置,形成可追溯的链条:

project/ ├── docs/ │ ├── ask_output.yaml # Ask 产物 │ └── plan.md # Plan 产物(任务表 + 决策记录) ├── contracts.py # Plan 产出的接口约定 ├── textstats.py # Craft 实现 └── test_textstats.py # Craft 验证

这样做的好处是:任何人(包括未来的你、以及 AI 助手)都能从 Ask 读到 Craft,理解“为什么这样写”。

4.2 与 AI 助手协作时的接力用法

在 AI 辅助开发中,三段式工作流的价值尤为突出:

  1. Ask 阶段:把澄清后的需求 YAML 作为系统提示的一部分,让模型先复述并指出歧义,而不是直接生成代码。
  2. Plan 阶段:让模型基于 Ask 产物输出任务拆解和接口草案,人工评审后固化。
  3. Craft 阶段:把 Plan 的接口约定和任务
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 10:53:41

C++移动语义:原理、实现与性能优化

1. 移动语义的本质与价值 C11引入的移动语义彻底改变了我们处理对象资源所有权的方式。在传统C中&#xff0c;对象复制总是伴随着昂贵的深拷贝操作&#xff0c;即使源对象即将被销毁也不例外。移动语义的核心思想是&#xff1a;当源对象是临时对象&#xff08;右值&#xff09;…

作者头像 李华
网站建设 2026/10/26 10:53:26

STL容器适配器:Stack与Queue的实现原理与应用

1. 从数据结构到STL容器&#xff1a;为什么需要Stack和Queue&#xff1f; 在计算机科学的世界里&#xff0c;数据结构就像建筑师的工具箱&#xff0c;而stack&#xff08;栈&#xff09;和queue&#xff08;队列&#xff09;无疑是其中最基础也最常用的两件工具。想象一下你在餐…

作者头像 李华
网站建设 2026/10/26 10:52:57

前端表格序号列实现方案与优化实践

1. 为什么需要给a-table添加序号&#xff1f;在数据表格展示场景中&#xff0c;序号列是最基础却最容易被忽视的功能需求。作为前端开发者&#xff0c;我见过太多直接使用数组索引作为序号的实现方式&#xff0c;这在实际业务中会带来诸多问题&#xff1a;分页场景下每页都从1开…

作者头像 李华
网站建设 2026/10/26 10:52:55

急诊创伤救治数字化:基于钉钉小程序的黄金时间管理系统

1. 项目背景与需求分析 急诊创伤救治的黄金时间管理一直是医疗行业的痛点。传统纸质记录方式存在时间记录不准确、信息传递滞后、数据难以追溯等问题。我们团队在华东地区三甲医院急诊科实地调研发现&#xff0c;从患者入院到完成关键救治环节的平均时间误差高达17分钟&#xf…

作者头像 李华
网站建设 2026/10/26 10:52:46

2026年Git版本控制核心技能与最佳实践指南

1. 为什么2026年还需要学Git&#xff1f;在代码托管平台遍地开花的今天&#xff0c;Git依然是开发者必备的生存技能。根据Stack Overflow 2025年开发者调查报告&#xff0c;87%的专业开发者每天都会使用Git进行版本控制。与五年前相比&#xff0c;Git的核心工作流并没有本质变化…

作者头像 李华
网站建设 2026/10/26 10:52:03

中小企业数字化转型进程评估:44项指标与加权评分模型

简介&#xff1a;这份《腾讯&#xff1a;中小企业数字化转型路径报告》面向中小企业管理者、数字化转型服务商及政策与产业研究者&#xff0c;围绕疫情背景下中小企业“为何转、转到哪、怎么转”梳理转型路径与落地难点。资源包仅含1个PDF文件&#xff0c;约6.68MB&#xff0c;…

作者头像 李华