WorkBuddy_新建任务与工作模式20_新手推荐路径
新手推荐路径
摘要
在 AI 辅助开发工具快速普及的今天,新手面对最多的困惑往往不是"工具够不够强",而是"我该从哪一步开始用"。本文提出一条经过实践验证的三阶段学习路径:先从 Ask(问答式交互)起步熟悉工具的脾气,再用 Plan(计划模式)把控复杂任务的节奏,最后信任 Agent(智能体)去执行落地。文章会结合可运行的代码示例,逐层拆解每个阶段的目标、典型用法、常见坑点与过渡信号,帮助新手用最低的试错成本建立起对 AI 编程工具的正确认知。
引言
很多新手第一次打开 AI 编程工具时,会直接丢一句"帮我写一个完整的电商后端",然后对着生成的一大坨代码发愣——不知道对不对、不敢改、更不敢上线。问题不在工具,而在使用顺序。
AI 编程工具的能力大致可以拆成三层:
- Ask 层:你问它答。它像一个随时在线的资深同事,负责解释、答疑、给片段。
- Plan 层:你给目标,它拆步骤。它像一位项目经理,负责把模糊需求变成可执行的清单。
- Agent 层:你给任务,它自己读代码、改文件、跑测试。它像一个能独立干活的工程师。
这三层不是"越往后越好",而是有先后依赖的。跳过 Ask 直接上 Agent,你既看不懂它改了什么,也无法判断它对不对;跳过 Plan 直接上 Agent,复杂任务会跑偏且难以纠偏。本文按这条推荐路径展开,每一节都配有可直接运行的示例。
一、为什么新手必须从 Ask 起步
1.1 Ask 阶段的真正目标
Ask 阶段的核心不是"让 AI 帮我写代码",而是建立三个认知:
- 认知一:模型的能力边界。它擅长什么(样板代码、正则、SQL、解释报错),不擅长什么(涉及时效性的库版本、你公司内部约定)。
- 认知二:提问的质量决定回答的质量。模糊问题得到模糊答案,这是新手最容易踩的坑。
- 认知三:如何验证答案。AI 给的代码不一定对,你需要有能力跑一遍、读一遍、判断一遍。
1.2 一个对比示例:模糊提问 vs 结构化提问
假设你想实现"防抖"函数。新手常见的提问是:
帮我写个防抖
得到的答案可能是一个最简版本,没有取消功能、没有this绑定、没有立即执行选项。而结构化提问:
用 TypeScript 实现一个 debounce 函数,要求:1)支持立即执行选项
immediate;2)返回函数上挂一个cancel方法;3)正确处理this指向;4)给出使用示例和单元测试。
得到的代码质量完全不同。下面是一个符合上述要求的完整实现:
// debounce.tsexportinterfaceDebouncedFunction<Textends(...args:any[])=>any>{(this:ThisParameterType<T>,...args:Parameters<T>):void;cancel():void;}exportfunctiondebounce<Textends(...args:any[])=>any>(fn:T,wait:number,immediate=false):DebouncedFunction<T>{lettimer:ReturnType<typeofsetTimeout>|null=null;constdebounced=function(this:ThisParameterType<T>,...args:Parameters<T>){constcontext=this;constlater=()=>{timer=null;if(!immediate)fn.apply(context,args);};constcallNow=immediate&&timer===null;// 每次调用都重置计时器if(timer!==null)clearTimeout(timer);timer=setTimeout(later,wait);// immediate 模式下,首次调用立即执行if(callNow)fn.apply(context,args);}asDebouncedFunction<T>;debounced.cancel=()=>{if(timer!==null){clearTimeout(timer);timer=null;}};returndebounced;}配套的单元测试(使用 Vitest,可直接运行):
// debounce.test.tsimport{describe,it,expect,vi,beforeEach,afterEach}from'vitest';import{debounce}from'./debounce';describe('debounce',()=>{beforeEach(()=>vi.useFakeTimers());afterEach(()=>vi.useRealTimers());it('延迟执行,多次调用只执行最后一次',()=>{constfn=vi.fn();constd=debounce(fn,100);d();d();d();expect(fn).not.toHaveBeenCalled();vi.advanceTimersByTime(100);expect(fn).toHaveBeenCalledTimes(1);});it('immediate 模式下首次立即执行',()=>{constfn=vi.fn();constd=debounce(fn,100,true);d();expect(fn).toHaveBeenCalledTimes(1);d();expect(fn).toHaveBeenCalledTimes(1);// 窗口期内不重复执行});it('cancel 能取消待执行的调用',()=>{constfn=vi.fn();constd=debounce(fn,100);d();d.cancel();vi.advanceTimersByTime(200);expect(fn).not.toHaveBeenCalled();});});1.3 Ask 阶段的练习方法
建议每天做三件事:
- 拿真实报错去问,而不是自己猜。把完整堆栈贴进去,问"这个报错的原因是什么、怎么修、为什么"。
- 要求解释而非只给答案。追问"为什么这样写"、“有没有别的写法”,这是积累判断力的关键。
- 手动验证每个答案。跑一遍、改一个边界条件看它会不会挂。这一步不能省。
1.4 何时可以进入下一阶段
当你发现自己能预判 AI 大概会怎么答、并且能一眼看出答案里的问题时,Ask 阶段就基本毕业了。此时你会自然产生新需求:“这个功能涉及五六个文件,一句句问太累了”——这就是进入 Plan 阶段的信号。
二、Plan 阶段:用计划把控复杂任务
2.1 什么算"复杂任务"
一个经验判据:如果任务的完成需要改动超过 3 个文件,或者需要你先想清楚步骤才能动手,就属于复杂任务。典型场景包括:重构一个模块、给现有系统加一个横切功能(如日志、鉴权)、迁移一个库的版本。
这类任务的失败模式高度一致——方向错了,越努力越糟。Plan 阶段的价值就是在动手前把方向对齐。
2.2 Plan 的产出物:一份可执行的任务清单
好的计划不是一段散文,而是可勾选、可验证、有依赖顺序的清单。以"给 Express 应用加上请求级日志与耗时统计"为例,一份合格的计划长这样:
## 任务:为 Express 应用添加请求日志与耗时统计 ### 前置调研 - [ ] 确认现有中间件注册顺序(app.ts 第 20-40 行) - [ ] 确认日志库选型:项目已依赖 winston,复用而非新增 ### 实施步骤 - [ ] 1. 新建 src/middleware/requestLogger.ts - [ ] 2. 在 app.ts 中于路由注册之前挂载该中间件 - [ ] 3. 为每个请求生成 requestId(使用 crypto.randomUUID) - [ ] 4. 记录 method / path / status / duration - [ ] 5. 将 requestId 透传到响应头 X-Request-Id ### 验证 - [ ] 单元测试:中间件对 200 与 500 响应均能正确记录 - [ ] 手动验证:curl 一次接口,检查日志字段完整2.3 计划对应的实现代码
下面是与上述计划对应的完整可运行实现:
// src/middleware/requestLogger.tsimport{Request,Response,NextFunction}from'express';import{randomUUID}from'crypto';importwinstonfrom'winston';constlogger=winston.createLogger({level:'info',format:winston.format.json(),transports:[newwinston.transports.Console()],});exportfunctionrequestLogger(req:Request,res:Response,next:NextFunction){constrequestId=randomUUID();conststart=process.hrtime.bigint();// 透传到响应头,便于前端排查res.setHeader('X-Request-Id',requestId);// 响应结束时统一记录,保证能拿到最终 statusres.on('finish',()=>{constdurationMs=Number(process.hrtime.bigint()-start)/1e6;logger.info('request',{requestId,method:req.method,path:req.originalUrl,status:res.statusCode,durationMs:Number(durationMs.toFixed(2)),});});next();}// src/app.tsimportexpressfrom'express';import{requestLogger}from'./middleware/requestLogger';constapp=express();// 关键:日志中间件必须在路由之前注册,否则路由内的响应不会被统计app.use(requestLogger);app.get('/health',(_req,res)=>res.json({ok:true}));app.get('/boom',()=>{thrownewError('intentional error');});app.listen(3000,()=>console.log('listening on :3000'));2.4 Plan 阶段的三条纪律
- 先调研再列步骤。让 AI 先读相关文件、输出"现状理解",你确认无误后再让它出计划。
- 计划要能被人读懂。如果计划里出现"优化一下相关逻辑"这种模糊表述,打回去重写。
- 一个步骤一个验证点。没有验证方式的步骤等于没有步骤。
2.5 何时可以进入下一阶段
当你发现计划本身已经足够清晰、剩下的只是按部就班敲代码,并且你已经能信任 AI 在这个清晰边界内自主执行时,就可以进入 Agent 阶段了。注意:信任的是"边界清晰的执行",不是"无脑放手"。
三、Agent 阶段:在清晰边界内信任执行
3.1 Agent 与 Ask/Plan 的本质区别
Ask 是"你说一句它答一句",Plan 是"它给你清单你确认",Agent 是"它自己读文件、改文件、跑测试、根据结果调整"。Agent 的能力来自工具调用循环:读 → 想 → 改 → 跑 → 看结果 → 再改。
这也意味着 Agent 的风险点在于:它会真的修改你的文件。所以进入 Agent 阶段的前提是边界已经由 Plan 阶段界定清楚。
3.2 一个 Agent 任务的最小示例
假设 Plan 阶段已经确认:要在utils/下新增一个retry.ts,实现带指数退避的异步重试,并配套测试,不改动其他文件。给 Agent 的任务描述应该长这样:
任务边界:只允许在 src/utils/ 目录下新增文件,不得修改其他目录。 目标:实现 retry(fn, options) 异步重试工具。 要求: - 参数:fn 为返回 Promise 的函数;options 含 retries、baseDelay、maxDelay、shouldRetry - 指数退避:第 n 次重试等待 min(baseDelay * 2^n, maxDelay) - shouldRetry(err, attempt) 返回 false 时立即抛出 - 配套单元测试,覆盖成功、重试后成功、耗尽重试、shouldRetry 拦截四种情况 完成标准:npm test 全绿,且 git diff 只显示 src/utils/ 下的新增文件。Agent 产出的实现大致如下:
// src/utils/retry.tsexportinterfaceRetryOptions{retries?:number;baseDelay?:number;maxDelay?:number;shouldRetry?:(err:unknown,attempt:number)=>boolean;}constsleep=(ms:number)=>newPromise((r)=>setTimeout(r,ms));exportasyncfunctionretry<T>(fn:()=>Promise<T>,options:RetryOptions={}):Promise<T>{const{retries=3,baseDelay=100,maxDelay=5000,shouldRetry=()=>true,}=options;letlastErr:unknown;for(letattempt=0;attempt<=retries;attempt++){try{returnawaitfn();}catch(err){lastErr=err;// 最后一次尝试失败,或调用方明确不重试,直接抛出if(attempt===retries||!shouldRetry(err,attempt))throwerr;constdelay=Math.min(baseDelay*2**attempt,maxDelay);awaitsleep(delay);}}// 理论上不可达,仅为类型收窄throwlastErr;}配套测试:
// src/utils/retry.test.tsimport{describe,it,expect,vi}from'vitest';import{retry}from'./retry';describe('retry',()=>{it('首次成功直接返回',async()=>{constfn=vi.fn().mockResolvedValue('ok');awaitexpect(retry(fn,{baseDelay:1})).resolves.toBe('ok');expect(fn).toHaveBeenCalledTimes(1);});it('失败后重试成功',async()=>{constfn=vi.fn().mockRejectedValueOnce(newError('fail')).mockResolvedValue('ok');awaitexpect(retry(fn,{baseDelay:1})).resolves.toBe('ok');expect(fn).toHaveBeenCalledTimes(2);});it('耗尽重试后抛出最后的错误',async()=>{constfn=vi.fn().mockRejectedValue(newError('always'));awaitexpect(retry(fn,{retries:2,baseDelay:1})).rejects.toThrow('always');expect(fn).toHaveBeenCalledTimes(3);// 1 次初始 + 2 次重试});it('shouldRetry 返回 false 时立即抛出',async()=>{constfn=vi.fn().mockRejectedValue(newError('fatal'));awaitexpect(retry(fn,{baseDelay:1,shouldRetry:()=>false})).rejects.toThrow('fatal');expect(fn).toHaveBeenCalledTimes(1);});});3.3 如何"信任"而不"失控"
信任 Agent 的正确姿势是用可验证的完成标准兜底,而不是用人盯人的方式。具体做法:
- 给任务写死边界(如"只允许改 src/utils/"),Agent 越界时你能从 diff 里一眼看出。
- 给完成标准写死验证方式(如"npm test 全绿"),让 Agent 自己跑、自己看结果、自己修。
- 事后 review diff 而非 review 过程。过程交给 Agent,结果由你验收。
- 小步提交。每个 Agent 任务对应一个 commit,出问题能一键回滚。
3.4 Agent 阶段的典型坑
- 边界不清导致误改:没写"只改某目录",Agent 顺手重构了无关文件。→ 任务描述里显式写边界。
- 完成标准模糊:只写"实现功能",Agent 可能不写测试就宣布完成。→ 完成标准必须可执行、可验证。
- 一次给太大任务:把 Plan 阶段整份清单丢给 Agent。→ 拆成单个步骤,逐个交给 Agent。
四、三阶段如何平滑衔接
三个阶段不是割裂的,而是一条能力递进链。下面这张对照表帮你判断自己处在哪、下一步该练什么:
| 阶段 | 你的角色 | AI 的角色 | 核心产出 | 毕业信号 |
|---|---|---|---|---|
| Ask | 提问者 | 答疑同事 | 可验证的代码片段/解释 | 能预判并挑出答案的问题 |
| Plan | 决策者 | 项目经理 | 可勾选的任务清单 | 计划清晰到"只剩敲代码" |
| Agent | 验收者 | 执行工程师 | 通过验证的改动 | 能用边界+标准兜住自主执行 |
衔接的关键动作是每个阶段都留下可复用的资产:Ask 阶段留下的是一批"好问题模板",Plan 阶段留下的是一份份任务清单,Agent 阶段留下的是一串小步 commit。这些资产会让你在下一阶段起步时不是从零开始。
五、常见误区与纠正
误区一:跳过 Ask 直接上 Agent。
纠正:你无法验收你看不懂的东西。先花一周在 Ask 阶段把工具脾气摸熟。
误区二:Plan 阶段写"伪计划"。
纠正:计划里每一步都要有验证方式。"优化性能"不是步骤,"把列表接口的 N+1 查询改为批量查询,并用 EXPLAIN 确认走了索引"才是。
误区三:Agent 阶段全程盯着。
纠正:盯着过程说明你还不信任边界。把精力放在写清边界和完成标准上,过程放手。
误区四:三个阶段只用一种。
纠正:真实工作中三者是混用的。遇到陌生报错回到 Ask,接到大需求先 Plan,边界清晰后交 Agent。
总结
新手使用 AI 编程工具,最稳妥的路径是Ask → Plan → Agent的递进:
- Ask 起步,目的是建立对模型能力边界、提问质量、答案验证的三重认知,产出可验证的代码片段;
- Plan 居中,目的是把复杂任务的模糊需求转成可勾选、可验证、有依赖顺序的任务清单,在动手前对齐方向;
- Agent 收尾,目的是在清晰边界与可执行完成标准的兜底下,信任 AI 自主执行并只验收结果。