ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

开发新Skill要注意什么?用状态机、独立盲审等机制解决AI跳步与虚假完成

开发新Skill要注意什么?用状态机、独立盲审等机制解决AI跳步与虚假完成 市面上已经有很多成熟的skill了但是我想自己拥有一个skill一来是可以熟悉软件开发的流程另一个是学习skill的创作但真正让我决定搞这个skill的是AI编程过程中AI总干出来的一些让人哭笑不得的事。一、遇到的问题1、起手就是干只说了一句帮我实现登录它已经开始写了第一轮测试我给的 prompt 很简单“帮我实现一个用户登录功能”。AI拿到需求后嘎嘎开始写 给我生成了一整个登录模块。数据库表设计没做。技术栈确认没问。异常处理策略不存在。AI也不会反问你或者提醒你还差什么这次开发计划是什么也不列出来总之挖了很多坑demo也是跑不起来的2、新任务等于又要描述一遍每个新对话都是一张白纸因为上下文过长导致一个窗口继续开发会效果不好所以有时候会新开任务但是新开任务就要和它重新描述甚至还要去老窗口把之前的问题复制过来很痛苦3、让它检查自己写的代码它说挺好的让 AI 审一下自己刚生成的代码。它看了一眼说代码质量不错结构清晰。这不是 AI 不够聪明是它没法把自己和产出物分开。就像让一个作家评价自己刚写完的小说很难客观。4、每天生成的文档长得都不一样今天的需求分析文档分了 5 个章节明天只有 3 个。有的写了数据字典有的没写。有的列了约束条件有的只列了两条。我一开始想内容完整就行了。后来发现不对——自动化解析这些文档的时候格式不一致意味着每个文档都得单独写解析器。5、AI说“搞定了”、“我已经修复了”实际没动手最离谱的一次AI 说需求分析文档已完成并写入文件。我跑去一看文件系统里什么都没多出来。它不是故意的。这是 LLM 的虚假完成问题——在语言层面上 AI 觉得自己完成了但实际上文件写入操作根本没执行这五个问题让我意识到一件事AI 的能力确实强但它不会自觉遵守流程。 就像你不能指望一个天才程序员在没有代码审查、没有设计评审、没有自动化测试的情况下交付可靠代码。流程和约束不是为了浪费时间是为了兜底。所以要开始想办法了。下面是我一步步填坑的过程按遇到的时间先后顺序来写不分优先级。二、开始填坑1、接力棒的应用以前我用 AI 批量处理数据的时候——Token 超限、网络闪断、浏览器崩溃什么情况都有。每次重新来AI 都是从零开始完全不知道之前处理到哪里了。后来我学了一招每处理完一个批次比如固定多少条往一个日志文件里写一条记录已处理到第 XXX 条。即使断掉新对话先读这个文件就能续上。这个经验被我照搬到了 ReqPlan-v3 里。在项目根目录塞了个文件 .agent/harness/_baton.md。结构很简单## 元信息 | 字段 | 值 | |------|-----| | 当前状态 | ANALYZE | | 模式 | NORMAL | | 重试计数 | 0 | ## 进度追踪 - [x] START - 启动 - [ ] ANALYZE - 分析 - [ ] CONFIRM - 确认 - [ ] DESIGN - 设计 - [ ] IMPLEMENT - 实现 - [ ] VERIFY - 验证 - [ ] JUDGE - 判断import json from datetime import datetime from pathlib import Path class Baton: AI Skill 接力棒 — 记录当前执行状态支持断点续跑 def __init__(self, project_root: str): self.baton_path Path(project_root) / .agent/harness/_baton.md def read(self) - dict: 读取接力棒获取当前状态 if not self.baton_path.exists(): return {status: START, mode: NORMAL} content self.baton_path.read_text(encodingutf-8) # 从 Markdown 表格中解析状态 return self._parse_status(content) def write(self, status: str, mode: str NORMAL, retry: int 0): 更新接力棒状态 content f## 元信息 | 字段 | 值 | |------|-----| | 当前状态 | {status} | | 模式 | {mode} | | 重试计数 | {retry} | | 更新时间 | {datetime.now().strftime(%Y-%m-%d %H:%M)} | ## 进度追踪 - [{x if status START else }] START - 启动 - [{x if status ANALYZE else }] ANALYZE - 分析 - [{x if status CONFIRM else }] CONFIRM - 确认 - [{x if status DESIGN else }] DESIGN - 设计 - [{x if status IMPLEMENT else }] IMPLEMENT - 实现 - [{x if status VERIFY else }] VERIFY - 验证 - [{x if status JUDGE else }] JUDGE - 判断 self.baton_path.parent.mkdir(parentsTrue, exist_okTrue) self.baton_path.write_text(content, encodingutf-8) def _parse_status(self, content: str) - dict: 解析 Markdown 中的状态字段 lines content.split(\n) for i, line in enumerate(lines): if 当前状态 in line and | in line: status line.split(|)[2].strip() return {status: status, content: content} return {status: UNKNOWN, content: content}每次 AI 做完一个阶段就更新这个文件。新对话启动后第一件事就是读它。叫它接力棒是因为它真的像接力赛里那个棒子——谁拿到了就知道现在跑到哪一步了。现在关掉对话多少次都无所谓重新打开后 AI 会主动说“当前处于 ANALYZE 阶段需求分析已完成等你确认。”顺便它还解决了一个我没想到的问题。AI 在修 Bug 的时候会陷入死循环——修一次没过再修一次还没过继续……我见过它连续修了七次到第八次我才反应过来不对。所以我在接力棒里加了个重试计数上限 2 次。同一个阶段修了 2 次还不过直接标记 FAILED通知我来处理。不允许 AI 自我感动式地无限修复。2、状态机的应用接力棒解决了失忆问题但没解决跳步问题。AI 还是动不动就跳阶段——我刚说分析一下它已经开始写代码了。我给它的自由裁量权太多了。于是设计了 7 个严格顺序的阶段START → ANALYZE → CONFIRM → DESIGN → IMPLEMENT → VERIFY → JUDGE → DONE其实就是项目启动 → 需求分析 → 方案确认 → 方案设计 → 开发实现 → 测试验证 → 评审判定 → 项目完成我一开始搞了 5 个后来发现 CONFIRM 这个环节得单独抽出来。因为它是整个流程里唯一需要人类拍板的节点——AI 可以帮你分析、设计、编码、测试但这事儿做不做的决策权不能在 AI 手里。class SkillStateMachine: AI Skill 7阶段状态机 — 防止跳步 # 合法转换表当前状态 → 允许的下一个状态 TRANSITIONS { START: [ANALYZE], ANALYZE: [CONFIRM], CONFIRM: [DESIGN, ANALYZE], # 确认不通过可以回 ANALYZE DESIGN: [IMPLEMENT], IMPLEMENT:[VERIFY], VERIFY: [JUDGE], JUDGE: [DONE, DESIGN, IMPLEMENT], # 不通过可以回修 DONE: [START], # 新任务重置 FAILED: [], # 终止态 } def __init__(self): self.baton Baton(./) def transition_to(self, target_state: str) - bool: 尝试转换到目标状态非法转换直接阻断 current self.baton.read()[status] allowed self.TRANSITIONS.get(current, []) if target_state not in allowed: print(f[阻断] 非法转换{current} → {target_state}) print(f 允许的下一状态{allowed}) return False print(f[状态机] {current} ✅ → {target_state}) self.baton.write(target_state) return True def run(self, task_description: str): 入口 — 自动识别当前状态并恢复执行 state self.baton.read()[status] print(f当前状态{state}任务{task_description[:30]}...) # 按状态路由自动进入对应阶段 handlers { START: self._do_analyze, ANALYZE: self._do_confirm, CONFIRM: lambda: print(等待用户确认...), DESIGN: self._do_implement, IMPLEMENT:self._do_verify, VERIFY: self._do_judge, } handler handlers.get(state) if handler: handler() # 模拟各阶段处理 def _do_analyze(self): print(执行需求分析...) self.transition_to(CONFIRM) def _do_confirm(self): print(展示分析报告等待确认...) def _do_implement(self): print(执行方案设计→编码...) self.transition_to(VERIFY) def _do_verify(self): print(运行验证检查...) self.transition_to(JUDGE) def _do_judge(self): print(综合判定...) self.transition_to(DONE) # 实际使用 sm SkillStateMachine() sm.run(帮我创建一个用户登录功能)规则其实很简单每阶段有明确的输入和输出没完成不准进入下一阶段阶段之间不能逆行只有 CONFIRM 阶段会停下来等我确认3、子agent独立审核的应用让 AI 自己检查自己这个思路我想放弃是花了一些时间的。让 AI 自我审查听起来很合理——它能写代码那应该也能审查代码对吧但几次测试下来AI 对着自己写的代码永远是一副挺好的的态度。换了个思路不自己审了找个独立的角色来审。具体做法是拉起一个全新的子 Agent跟主对话完全隔离。它不知道原始需求是什么不知道代码是谁写的甚至不允许它请求额外上下文。它只看产物文件本身按清单逐项检查。相当于是个蒙着眼睛的质检员。审核流程分了三个层次第1层阻断检查一票否决 发现严重问题 → 直接 FAILED不评分 第2层维度评分量化评估 按 N 个维度打分0-100分每项扣分必须有引用证据 第3层综合判定 A级(≥90分) / B级(75-89) / C级(60-74) / D级(60或阻断中断)这里我踩过一个坑。第一次测试审核机制的时候AI 说问题已修复。结果我检查发现它只是把文档里问题描述那段文字删掉了代码压根没改。所以加了修复验证闭环1、读取上次审核报告里的待修复问题清单2、逐条检查每个问题是否真的已修复3、标记修复状态已修复 ✅ 或 假修复 ❌4、有一条假修复 → 整体不通过后来我还加了个防评分偏移的设计如果连续 3 次审核都给出 90 分触发评分偏移告警标记该轮审核为可能不可靠让我手动确认。即使是 AI 自己在审长期固定标准也需要打个问号。4、分多个agent角色的应用在没分工的时候AI 在一个对话里什么角色都演分析需求、写代码、做测试、写文档。结果就是它经常串戏——分析需求的时候已经在想数据类型怎么定义了写代码的时候又想这功能要不要换个方案。我干脆把任务拆给了五个 Agent 角色每个角色有明确的职责和边界1、Analyzer解析需求、收集上下文、识别可复用资源 → 产出 _analysis.md。不写一行代码。2、Designer设计架构、定义模块、拆解实现任务 → 产出 _design.md。必须先读完分析报告才动笔。3、Implementer按设计文档写代码、更新进度 → 产出 _implementation.md 实际代码。读设计文档之前不能开始写。4、Verifier执行验证——静态检查、单元测试、构建集成、异常路径覆盖、流程合规 → 产出 _verification.md。只读不修改源码。5、Quality Auditor阻断检查、维度评分、等级判定 → 产出 _quality_audit_*.md。不允许额外上下文不修改任何产物。每个 Agent 有自己的产物文件不碰别人的。一个阶段的产物做得不好不会影响其他阶段——Analyzer 的分析报告如果搞砸了Designer 还没开始做所以不受牵连。我最开始觉得给 AI 分配不同角色这事有点玄学——反正底层是同一个 LLM角色扮演能有多大区别但实际跑起来我发现真的有区别。分析阶段的 AI 会像产品经理一样追问这功能给谁用设计阶段的 AI 像架构师一样画模块图实现阶段像开发者一样抠细节测试阶段像个 QA 一样专门挑毛病。同一个模型不同的上下文设定思考方式真的会不一样。5、渐进式披露的应用另一个头疼的问题是 Skill 文档本身越来越臃肿。ReqPlan-v3 的文章越写越长从几百字膨胀到几千行。但 AI 的上下文窗口有限把所有规则一次性塞进去那些当前任务不需要的知识就白白占了空间。而且信息太多反而影响判断——和人类一样注意力有限。我把文档拆成了 4 个块chunk-01意图引导教 AI 怎么理解用户要什么。每次都得加载。chunk-02三个流程的定义开发推动、分析评估、问题修复。高频场景才加载。chunk-03验证和审查的规则。做验证的时候加载。chunk-04存档归档的流程。低频只在最后阶段加载。分块加载不只是给 AI 用的。给用户看的信息我也做了分层——CONFIRM 阶段用户看到的只是一个精简摘要。而不是几十页的分析报告。你让人看完几十页再决定要不要继续他大概率会烦。6、文档的一致性的应用前面说到 AI 每次输出的文档格式不一样而且有时候说写完了文件系统里啥都没有。我对付这两个问题的方式挺粗暴的——给每个产物定了固定的模板结构。比如分析报告必须是这个结构_analysis.md:1、基本信息时间、分析者、场景类型2、需求理解核心功能表、角色、数据实体3、技术栈技术选型表、项目结构4、涉及文件需修改的文件、需新增的文件5、约束条件技术约束、业务约束6、风险评估已识别风险、风险等级from pathlib import Path from typing import List class VerificationChain: 验证链 — 防止 AI 口头完成 问题 三条铁律计数验证 / 列表验证 / 文件验证 staticmethod def verify_count(claimed: int, actual_items: List[str]) - bool: 计数验证声称 N 个就必须有 N 个 actual len(actual_items) if actual ! claimed: print(f❌ 计数验证失败声明的 {claimed} 个实际 {actual} 个) print(f 实际列表{actual_items}) return False print(f✅ 计数验证通过{claimed}/{actual}) return True staticmethod def verify_list(items: List[str], keyword: str ) - bool: 列表验证不能出现等、等字省略 for item in items: if 等 in item: print(f❌ 列表验证失败包含模糊词等 → {item}) return False if not item.strip(): print(❌ 列表验证失败存在空项) return False print(f✅ 列表验证通过{len(items)} 项均已明确列出) return True staticmethod def verify_file_written(file_path: str) - bool: 文件验证read 确认文件确实写入 path Path(file_path) if not path.exists(): print(f❌ 文件验证失败{file_path} 不存在) print(f AI 口中的已写入不成立属虚假完成) return False content path.read_text(encodingutf-8, errorsignore) if len(content.strip()) 0: print(f❌ 文件验证失败{file_path} 存在但为空文件) return False print(f✅文件验证通过{file_path}{len(content)} 字符) return True staticmethod def full_check(claimed_count: int, items: List[str], file_paths: List[str]) - dict: 全量验证返回通过/失败明细 results { count_check: VerificationChain.verify_count( claimed_count, items), list_check: VerificationChain.verify_list(items), file_check: all( VerificationChain.verify_file_write(f) for f in file_paths ) } results[passed] all(results.values()) return results # 使用示例 vc VerificationChain() result vc.full_check( claimed_count3, items[/src/auth.py, /src/db.py, /src/utils.py], file_paths[/src/auth.py, /src/db.py, /src/utils.py] ) # 输出✅ 计数验证通过3/3 # 输出✅ 列表验证通过3 项均已明确列出 # 输出✅ 文件验证通过...模板末尾附带强制检查清单AI 写完后必须逐项确认1、本产物已保存到文件系统2、产物头部包含版本号3、已确认产物包含所有必需章节4、已触发独立质量审核5、质量审核已通过6、已更新接力棒只要有一项没勾选不能进入下一阶段。这是硬阻断不是建议。然后为了解决对话里说写了实际没写的问题我加了三条验证规则1、计数验证说提取了 N 个功能→ 必须逐个列出来。声称 5 个但只写了 3 个 → 不通过回去补。2、列表验证说涉及 3 个文件→ 必须给出具体路径。main.py 和 utils.py 等→ 不通过把等去掉写清楚。3、文件验证说已写入产物文件→ 必须用 read 命令确认写入成功。对话里说写好了不算数。这三条看着挺死板的但加了之后口头完成的问题真的不再出现了。六个机制不是各自为政的。它们的分工大致是这样机制管理维度作用接力棒状态告诉 AI 现在该干什么状态机流程告诉 AI 能干什么、不能干什么角色分工执行不同阶段用不同角色去做模板产出每个阶段的产物该长什么样验证链质量产出之后怎么确认真的做完了分块加载效率只给当前阶段所需的知识不超载以前建议先分析再做设计→ 现在分析阶段没完成直接进不去设计阶段。以前建议检查文件是否写入→ 现在不去 read 一下检查清单过不了。以前建议请独立人员审核→ 现在强制拉起独立子 Agent不给上下文。所有的判定也从感觉变成了证据代码质量不错→ “代码质量评分 85扣分项缺少异常处理引用第 34 行”所有任务已完成→ “已完成 5 个中的 5 个任务1 ✅ 任务2 ✅ 任务3 ✅ 任务4 ✅ 任务5 ✅”涉及 3 个文件→ “涉及文件/src/a.py, /src/b.py, /src/c.py”写完这些机制后我回头看了下发现其实没有新发明的东西。接力棒就是一个 Markdown 文件状态机就是一张路由表审核就是一个子 Agent 调用分块加载就是拆文档。单个拎出来都很朴素。这些做法也不只对 AI 有用。接力棒配合状态机本质上就是一个轻量的工作流引擎CI/CD Pipeline、审批流程、ETL 任务都可以用。独立盲审不就是代码审查的最佳实践吗——审查者不看作者的上下文只检查产物本身。多角色分工更是任何一个成熟团队都在做的事。模板加检查清单就是格式控制的老办法。做这个项目最大的体感是AI 身上存在着和人类程序员一模一样的问题——跳步、遗忘、认知偏误、虚假完成。不同的是我们在软件工程里早就想好了怎么应对代码审查、设计评审、自动化测试、CI/CD。ReqPlan-v3 做的事就是把人类的这些流程翻译成了 AI 能理解和执行的版本。所以我不太同意AI 取代程序员的说法。更接近的说法是——有了流程约束的 AI 加上有流程意识的程序员这个组合比单打独斗的程序员和没有约束的 AI 都强。以上这些就是基于实际项目 ReqPlan-v3 的迭代经验整理感兴趣的朋友可以去下载这个skill使用项目地址Releases · songzhou666/ReqPlan-v3 · GitHub
返回列表