先说结论用AI写代码能不能稳定产出软件成品差别不在模型本身而在你有没有把“和模型聊天”升级成一件可控的生产流程。当我把DeepSeek接到自己搭建的这套Harness工作流里以后那种“生成代码一时爽调通基本靠扛”的体验才真正结束。这篇文章记录我如何用五个阶段从一句很模糊的原始需求把一个内部小工具做成可交付、可运行的成品。如果你也受够了AI生成代码跑不起来的随机性又不想依赖厚重的全家桶生成器这套思路可以直接抄作业。我所说的DeepSeek Harness并不是某个开箱即用的商业软件而是我围绕DeepSeek搭起来的一层轻量工作流它把提示词模板、上下文管理、文件写入、命令校验串成一条流水线让模型每一次输出都落在工程流程里。这套东西的核心价值是让AI从“偶尔给出一段能跑的代码”升级为“在一个有边界的流程里稳定地产出每个阶段的结果”。对个人开发者、小团队以及想把AI辅助开发规范化落地的技术管理者来说它比追逐最新提示词技巧要实用得多。1. 为什么需要一套Harness而不是直接让AI写整个项目很多刚上手AI辅助开发的同事都会问同一个问题既然模型这么强为什么不能把整个项目需求一次性丢进去让它输出全部代码我试过结论是大模型能力强不代表它适合在一段对话里构建完整产品。这里面的原因很实际也很有必要先讲清楚。1.1 Harness的定位把模型能力“缝”进工程流程里Harness这个词的本意是“线束”或“马具”放在开发场景里很形象模型本身是强劲的发动机但你需要一套约束来保证它的输出能传动到正确的位置。我的Harness只做四件事维护分阶段的提示词模板、管理会话上下文、解析模型输出并写入真实文件、执行基础校验。听起来不复杂但这四件事恰好解决了AI辅助开发最头痛的四个问题输出没边界、上下文越聊越散、生成内容和项目文件对不上、代码跑不跑得通没人知道。说得直白点Harness像一条流水线传送带把每个阶段的产物送到下一个工位。没有这条传送带模型输出只是碎片有了它每个阶段只有一个明确任务模型在约束范围内发挥所有产物都有固定形态可以被检查、被引用、被继续加工。1.2 五阶段如何划分各环节的输入输出我采用的五阶段并非原创本质是软件工程里再经典不过的生命周期但每个阶段的人机协作方式完全重新定义过。五个阶段分别是需求捕获、架构设计、功能实现、验证测试、部署交付。阶段主要输入主要输出人工关键动作需求捕获模糊想法、会议笔记、业务反馈需求规格说明书、验收标准清单逐条确认边界拍板“做/不做”架构设计需求规格说明书设计文档、模块任务卡、数据模型评审技术选型画边界功能实现设计文档、任务卡可编译的源代码文件抽查关键函数修正模型幻觉验证测试实现代码、需求条目测试用例、测试报告、修复记录确认业务规则补人工用例部署交付通过测试的代码部署脚本、Dockerfile、README、安装包跑通全流程核对文档每个阶段结束后我会把产物归档到项目的artifacts/目录下。下一阶段的对话只会引用上一个阶段的稳定产物而不是把上一阶段的聊天记录原封不动带过去。这避免了最常见的“上下文污染”模型对某个废案念念不忘反而干扰新任务。1.3 为什么不采用“一次对话生成整个项目”的极端做法原因有四个。第一上下文窗口再大塞进整份需求、全部设计、每个模块的代码约束之后模型能集中注意力处理的细节非常有限前后矛盾几乎是必然。第二一次性产物没有中间检查点出了问题你不知道问题出在需求理解、架构设计还是代码实现排查成本极高。第三软件开发的真正难点从来不是堆代码而是需求对齐和逻辑验证这两件事都必须有人在中间介入。第四后面维护阶段的增量开发无法靠“重发全量prompt”来完成你只能把新需求对应到某个具体模块这时候分阶段的产物就是唯一的可靠索引。这套结构让我想起一个很朴素的道理你不会请一个施工队不画图纸直接盖楼。AI写代码也一样图纸可以画得快一点、粗一点但必须画出来并且有人看过。2. 阶段一需求捕获把模糊想法变成可验收条目需求阶段最容易被人跳过。很多开发者的习惯是打开对话窗口直接说“帮我写一个工时统计工具”然后AI就开始输出代码。这种做法的后果通常会在测试阶段集中爆发业务方认为“周报”是按自然周汇总你实现的却是按项目维度汇总对不上了。需求捕获这个阶段的意义就是把所有“想当然”消灭在写代码之前。2.1 让模型扮演需求分析师用提问帮你澄清我准备的第一个提示词模板不是让模型直接生成需求文档而是让它先“审问”我。这个反直觉的设计是我踩过几次坑之后的修正模型在没有搞清楚场景之前就生成文档往往会把缺失的信息脑补成看似合理的假设而人很容易被顺畅的文档带偏。我使用的提问模板大致是你是需求分析师。我是开发者。现在有一个想法一句话描述。 请你通过提问帮我澄清需求。每次只问一个问题不要同时抛出一堆问题。 提问方向包括目标用户和使用场景、核心功能边界、输入输出的数据形态、 非功能需求性能/权限/兼容性、明确不做的事情。 直到你能够输出下面三项 1. 用户与场景 2. 功能清单编号列出 3. 每项功能的验收标准关键在于“每次只问一个问题”。模型一次问十个问题看起来效率高实际上人根本无法认真逐条作答最后又是一通敷衍问题被带进实现阶段。一次一个问题逼着你把每个细节想清楚。我试过对一个“项目工时统计工具”做完整提问整个过程大概十二轮对话但每一轮都有实际产出。2.2 生成需求规格说明书和验收标准当对话进行到模型认为信息足够时我再切换另一个模板让它把对话内容整理成结构化文档。我要求它输出这几节目标与背景、用户与场景、功能清单、验收标准、非目标。下面的片段是我实际跑出来的浓缩版格式可以直接复用### 目标 给团队内部使用。每个人登记每日工时按项目和人员维度汇总生成周报。 ### 用户与场景 - 团队成员每天花半分钟登记工时 - 团队负责人每周一看汇总周报查看未登记人员 ### 功能清单 1. REQ-01 登记工时记录人员、项目、日期、小时数、备注 2. REQ-02 修改或删除工时记录 3. REQ-03 按项目/人员/日期筛选并生成周报 4. REQ-04 输出未登记人员的提醒名单 ### 验收标准 - 能成功加一条“某日张三项目A8小时写周报”记录 - 周报输出包含总工时和每人平均工时 - 数据保存在本地数据库重启进程不丢失 - 未登记人员名单能根据当天日期正确计算 ### 非目标 - 不做工时审批流程 - 不做移动端 - 不做自动发送邮件需求文档里最重要的一栏是“验收标准”。标准必须可操作、可测试不能出现“体验良好”“响应快速”这种无法验证的描述。我还会专门追加一段“请列出你觉得业务上可能产生歧义的地方。”模型通常能给出不错的候选清单这正是人工确认的重点。2.3 需求捕获的实际注意点模型天然是“对话型”的很容易顺着你说的方向补全逻辑哪怕你的表述前后矛盾。所以在这个阶段人要做的事情是把需求文档里的每条功能逐字读一遍确认每一条都真实存在。我习惯把功能清单拆成“做”和“不做”两列“不做”清单尤其重要。比如“这个工具不需要登录权限控制”一旦明确写下来实现阶段就不会出现不必要的复杂度。另一个心得是验收标准要绑定到具体数据形式。光说“能生成周报”是不够的必须写“周报输出包含项目名、总工时、参与人数”。这样后面测试阶段才能直接拿同一句话去用例里验证。3. 阶段二架构设计先把模块边界和接口对齐拿到需求文档之后大多数人会迫不及待进入代码阶段。但这个阶段省下的时间会在实现阶段十倍还回去。架构设计的本质不是画几张大图而是把需求条目映射成模块边界和数据流让实现阶段每次只需要专注一个局部。3.1 让候选方案先出三套再对比选型我喂给模型的prompt会先给出约束再要求它输出多个方案。约束条件来自需求文档和我知道的技术前提比如“这是一个命令行工具数据量在几千条以内不需要网络服务团队希望安装越简单越好”。在这个前提下我让模型给出三套候选技术结构并分别说明优缺点。对工时统计工具来说模型给出的三套方案通常是纯脚本加CSV文件、Python内置sqlite3加命令行、FastAPI加Web界面。前两套都符合轻量前提第三套明显过度设计但模型会在对比里明确说“适用于多人同时访问或远程查看成本是部署复杂度上升”。这个对比过程很有价值因为你实际上是用模型的输出做了一轮结构化的头脑风暴而最终选哪个仍然由人拍板。我的选择是第二套sqlite3加命令行因为需求里明确写了“重启不丢失”这排除了CSV方案同时我们不需要远程访问这排除了Web方案。3.2 输出设计文档和任务卡选型确定后我用设计模板要求模型输出目录结构、数据库表设计、模块接口清单、每个模块的职责描述。下面是我得到并稍作修正的目录结构task-time-tracker/ ├─ app/ │ ├─ cli.py # 命令入口 │ ├─ models.py # 数据结构和校验 │ ├─ storage.py # sqlite3读写 │ ├─ reporter.py # 周报生成 │ └─ reminder.py # 未登记名单 ├─ tests/ │ ├─ test_storage.py │ ├─ test_reporter.py │ └─ test_reminder.py ├─ requirements.txt └─ README.md数据库表我要求模型直接给出建表语句级别的设计而不是概念描述。对工时记录表核心字段包括人员、项目、日期、小时数、备注。这里我专门要求日期统一存成YYYY-MM-DD字符串因为对这个量级的工具用字符串比用时间戳可读性好得多也不容易踩时区问题。然后我让模型把每个模块的职责和函数签名写进“任务卡”。任务卡是衔接设计和实现的关键产物它把一个大工程切成了一个个可以独立完成的单元。下面是任务卡模板的示例任务编号: IMPL-004 模块: reporter.py 相关需求: REQ-03 按项目/人员/日期筛选并生成周报 接口约定: - report_by_project(records: list[Record]) - str - export_csv(path: str, rows: list[dict]) - None 技术约束: - 只使用标准库的 csv 模块 - 日期过滤使用字符串比较 接受标准: - pytest tests/test_reporter.py 全部通过 - 生成周报前后调用不修改原始数据每一张任务卡就是下一阶段的一次模型调用输入。这个设计带来的好处是实现阶段不需要AI“理解”整个项目它只需要处理好眼前这张卡片的边界。3.3 设计阶段最容易犯的毛病过度设计让模型做设计时它经常会倾向于“更专业”的复杂方案。比如给一个单机命令行工具引入ORM框架或者给只有几个模块的项目设计一套插件体系。我定的规矩是任何增加依赖和复杂度的设计决策必须有对应的需求条目做支撑。没有需求支撑的设计哪怕看起来再先进也不要。另外设计文档里我会明确要求模型把“可能变动的地方”标出来。比如工时统计的周报周期是周一早上出还是周五下班前出这种业务规则随时可能变化我会要求模型把周起始日的计算逻辑收敛到reporter.py的一个常量或函数里方便后面改。这种“为变化留位置”的思路比盲目追求抽象设计更有实际价值。4. 阶段三实现用固定循环批量产出可靠代码架构设计的价值在这里兑现。实现阶段不再是一次性让AI生成整个项目而是按任务卡逐个模块生成每次调用都限定了范围和接口约定。这样生成的代码虽然仍需人工抽查但出大问题的概率显著下降。4.1 用任务卡喂代码生成模型实现阶段的prompt由三部分组成任务卡内容、相关需求条目原文、当前模块需要对接的接口签名。以reporter.py为例我传入的对话会让模型知道它只需要实现周报格式化输入是来自storage.py的records列表输出是一个字符串或CSV文件它不需要知道cli.py怎么解析命令。这种窄输入窄输出的方式比把整个需求文档丢给模型的单次效果稳定得多。模型不用猜上下文不需要自己编字段名职责卡得死死的。即使某个模块生成的代码有问题定位范围也清清楚楚。我给Harness写了一个最小循环核心逻辑类似这样def run_implementation(tasks, context): for task in tasks: prompt render_template(implement.md, tasktask, contextcontext) result call_llm(prompt) # 调用模型接口 files extract_code_files(result) # 解析返回的代码块 for path, code in files.items(): write_file(fsrc/{path}, code) run_basic_checks(files.keys()) # 跑语法检查和依赖检查 if failed_checks: fix_prompt render_template(fix_errors.md, errorsfailed_checks, codefiles) result call_llm(fix_prompt) apply_fix(result)这里有一个关键机制Harness并不仅仅是“让AI把代码生成出来写进文件”还会对生成结果执行基础检查比如语法检查、文件名是否和任务卡一致、import的模块是否真实存在。发现异常后把错误信息作为上下文回传给模型让它自己修复。这个“生成-检查-反馈”的循环是Harness和普通对话工具拉开差距的地方。4.2 上下文裁剪不要让模型看完整项目源码很多人用AI写代码时习惯把整个项目文件都贴进去觉得这样模型更“懂”。我实测下来这个做法弊大于利。项目一复杂贴进去的代码占了大量上下文空间模型反而无法聚焦当前任务。我的做法是构造一个精简的上下文切片需求文档的摘要、当前任务卡、当前模块依赖的接口签名、最近三至五条修复记录。再展开说一点每个阶段的上下文边界也应该是可配置的。需求阶段可以放开让模型自由发问设计阶段可以给更多背景实现阶段必须收窄到模块级测试阶段则把运行结果和需求条目作为主要上下文。这套粒度控制是我把温度参数一起配置的原因设计阶段我用0.2让输出更收敛代码生成阶段用0.4留一点灵活性生成测试样例时用0.5希望它能想出一些我自己没想到的边界条件。4.3 人工代码审查的四点重点我虽然让Harness做了不少自动检查但把安全和逻辑完全交给模型还不够。人工审查阶段我会优先看这几个最容易出幻觉的位置。第一依赖声明。模型经常生成某个很顺手的库但忘了写进requirements.txt。我要求每个任务卡生成的代码都必须在其所在模块里标注新增的外部依赖然后Harness自动对比requirements.txt发现不一致就报错。第二函数签名一致性。模型写storage.py时可能定义了get_records_by_user(user)写cli.py时却按load_records(user)去调用。因为它们是两次独立生成没人对时会断裂。我在设计阶段就把接口约定写死在任务卡里并在审查时逐个核对调用处。第三硬编码。模型很喜欢把数据库文件路径、配置项直接写死在代码里。我强制要求所有可变参数从环境变量或函数参数传入。第四隐藏的过度设计。模型偶尔会给简单模块加上不必要的异步、多线程或多继承。我会反问自己这段复杂度靠哪个需求条目支撑支撑不了就删。4.4 一个具体返工案例我在实现工时统计工具时就踩过一次典型的模型幻觉。模型生成的storage.py里读取函数返回的是以人员为key的嵌套字典但任务卡里约定的接口是统一的Record列表。reporter.py按任务卡要求处理列表结果运行时直接因为类型不符报错。问题定位很快因为两个文件分别和任务卡比对哪个不符合一眼就出来了。修复方式是让模型重新生成storage.py并把任务卡接口约定原封不动放进prompt里一次就改对了。这种事不是偶然。每当你让AI分模块生成代码接口被打破的概率就会增加。解决的办法不是彻底放弃分模块而是让接口约定成为“不可变输入”写进prompt模板同时靠测试阶段把问题兜住。5. 阶段四测试让模型生成用例并驱动修复测试阶段最容易变成走过场。很多人的习惯是代码能跑起来就算成功然后直接部署。以我自己的经验看哪怕是一个内部小工具不做测试也不会有安全感因为你不知道改一个查询条件会不会把另一个功能弄坏。5.1 让模型按需求条目生成测试用例我把实现代码和需求条目一起给模型让它生成pytest测试文件。但只给接口还不行我还会在模板里明确要求覆盖四类场景正常路径、空数据边界、非法输入、重复/并发型冲突。下面是我常用的测试生成模板基于以下需求条目和代码文件生成pytest测试用例。 要求 1. 每个测试用例用中文注释说明验证哪个验收标准 2. 覆盖正常路径、空数据、异常输入、重复登记 3. 不要修改被测模块的源代码 4. 测试数据使用固定值不要依赖随机数对工时统计工具来说模型通常能生成这样几条用例当日工时超过24小时会被拒同一人同一项目同一天重复登记时给出提示空数据库生成周报不报错。这些方向本身不算新颖但确实覆盖了主要的失败路径。真正有意思的是模型生成的测试用例有时候会暴露它自己代码里的问题因为它写测试时会设想一些实现阶段没处理的情况。5.2 把测试结果回传给模型形成修复循环Harness的测试循环和实现循环类似。每次运行pytest -q后如果退出码非零就把输出文本直接作为上下文传给模型让模型给出修复建议或代码补丁。这个机制有效但要遵守一条经验一次只喂一批错误信息不要把十条失败堆在一起让它改。模型处理多个问题时经常会修好一个、弄坏另一个。我建议Harness循环按文件分组失败用例。先针对test_storage.py的失败让模型修storage.py跑通了再处理test_reporter.py。每轮修复之后需要把旧用例重新跑一遍防止回归。下面的伪代码展示的就是这个闭环run: pytest -q tests/test_storage.py tests/test_reporter.py if exit_code ! 0: first_batch group_failures_by_file(output) for file_name, fail_messages in first_batch.items(): fix_prompt render_template( fix_test_failures.md, modulefile_name, outputfail_messages ) result call_llm(fix_prompt) apply_patch(result) run_subset_of_tests(file_name)5.3 测试阶段的人工职责业务规则不能交给模型模型能生成技术性测试用例但它没有业务判断力。工时统计工具里有个典型问题周报的“一周”从哪天开始算。模型可能默认从周一开始但团队实际约定是上周五到本周四或者财务按自然月汇总。这种业务规则差异AI不会主动提出因为需求文档里没写。所以我的测试清单里永远有一个人工补充的部分把需求文档里的每条验收标准手写成一条可运行的较真用例不依赖模型生成。这个动作比较笨但它是整个流程里确保“软件成品”真正符合业务预期的最关键一步。6. 阶段五部署交付从能跑的代码变成软件成品代码通过测试只代表“功能正确”不代表“可以交付”。交付意味着别人能在自己机器上把它跑起来文档能告诉使用者怎么操作部署过程可以重复执行。缺了这一步前面的工作就只是“能跑的代码”不是“软件成品”。6.1 生成部署配置和启动脚本部署阶段我让AI提供的产物包括依赖清单文件、Dockerfile如果有服务器部署需求、启动脚本、CLI帮助说明。对单机命令行工具我通常会生成一个简单的构建脚本把入口打包成可执行命令而不是让用户手动python main.py。我得到并调整过的Dockerfile思路是FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app ./app RUN mkdir -p /data VOLUME [/data] ENTRYPOINT [python, -m, app.cli]这里有一个细节数据目录通过卷挂载出来避免容器重启导致工时数据丢失。这个要求在需求文档里写的是“本地数据库重启不丢失”但没提容器。Harness生成Dockerfile时我特意把这一条作为约束写入prompt因为我知道容器方案下数据持久化是个常见坑。6.2 交付物检查清单交付阶段我手里始终有一张表格逐项打勾检查项说明源代码可干净拉取从版本库新clone后直接能跑测试依赖清单完整所有import都能在requirements里找到对应项README操作可复现按文档从零开始不额外百度数据持久化验证重启后数据仍在异常路径有反馈用户输错命令能得到提示不是报一堆traceback示例数据和示例输出方便用户快速理解6.3 用AI生成用户手册但必须亲自跑一遍让模型基于实际代码生成README效率很高但有一件事不能偷懒文档里的每条命令都必须亲手跑一遍。我吃过一次亏模型生成的README里写了一个--week参数但实际CLI代码里根本没有这个参数。原因很简单README是根据早期设计文档生成的实现时做了调整两者已经脱节。解决方式就是人工验证。我会把生成的README从零开始执行一遍遇到任何一处和实际不符就把修正后的内容回喂给模型让它重新对齐文档。7. 常见问题与避坑清单这套五阶段流程跑过几个项目之后我整理出了一份问题速查表。这些问题出现频率最高每条都来自实际踩坑不是理论推演。问题现象排查思路解决办法生成代码之间函数名对不上比对两个模块是否都严格按设计阶段的任务卡实现把接口签名原封不动写进实现prompt不开放发挥模型对老旧库API判断错误模型可能基于训练数据里的旧接口写代码把关键第三方库的版本号写进任务卡约束让它先查文档上下文聊深了就开始前言不搭后语一次对话塞了太多模块按阶段清空上下文只保留当前阶段产物摘要业务规则被模型“默认补齐”需求文档里的歧义没有人工确认需求阶段必须增加“业务歧义清单”逐条确认单元测试跑通但集成一跑就挂模块间数据格式没有真正对齐增加一层集成用例按主流程串联各模块模型“犟嘴”拒绝承认错误修复prompt没有提供足够错误细节把完整报错文本和失败用例代码原样贴给它从流程本身来看我最想强调的是三个习惯。第一阶段产物一定要落盘到文件不要只存在于对话记录里。模型上下文清掉以后需求文档、设计文档、任务卡、测试报告才是项目真正的地基。第二每个阶段结束前抽五分钟做一次“人工红队测试”故意给模型一个偏离要求的输入看它会不会犯错。第三不要把AI生成的代码当作免检产品尤其在涉及数据持久化、并发和文件操作的地方人工审查比“看起来能跑”重要得多。7.1 关于Harness状态的补充设计为了让五阶段之间能无缝衔接我还在Harness里维护了一个简单的状态文件记录当前阶段、已完成产物、待处理任务和关键决策。状态文件的内容类似{ current_stage: implementation, artifacts: { requirements: artifacts/requirements.md, design: artifacts/design_doc.md, task_cards: artifacts/task_cards/ }, completed_modules: [storage, models], pending_modules: [reporter, reminder, cli], known_decisions: [ 日期统一使用YYYY-MM-DD字符串, 周报以周一为一周起点 ] }有了这个文件即使隔了几天再回来开发也不需要重新把项目背景讲给AI听。直接读取状态文件把下一步任务和相关产物加载到上下文即可。这也让多人协作成为可能另一名开发者接手时打开状态文件和最近产物就能了解项目进度。7.2 几个值得坚持的小习惯最后分享几个我从实操中沉淀下来的习惯并不复杂但确实提升了整个流程的稳定性。一是每次调用模型之前在prompt里明确写明“如果信息不足直接说需要更多信息不要猜测”这个约束能大幅减少需求阶段的幻觉。二是在实现阶段每隔三到五个模块就运行一次测试不要等所有模块完成再统一跑否则错误堆在一起排查成本成倍增加。三是在交付前一晚故意把数据库文件删掉从零开始执行一遍部署脚本和用户手册这个过程能发现大量“我觉得没问题”的盲区。7.3 扩展方向这套流程不止用于新项目这套五阶段流程不只适合从零开发新项目。对存量代码改造我会在前两个阶段增加“现状梳理”环节先让模型阅读现有代码结构并输出模块间依赖关系然后再进入设计阶段。对需求频繁变动的业务场景我会把需求阶段缩小为每次迭代初期的“需求增量澄清”确保每次改动都有明确的验收标准。本质上Harness的可复用性来自它把流程变成了可裁剪的框架新增工具、扩展代码生成循环、调整验收钩子都只需要改配置不需要重写整个工作流。做了这么多项目之后我对AI辅助开发最大的感受是工具链越简单人的作用越突出。你不需要一本三百页的工程方法论只需要把最基础的五个阶段固定下来让AI在每个阶段都只做那个阶段该做的事然后认真检查它的输出。这套流程帮我从“和AI碰运气”变成了“和AI配合交付”中间少了很多返工也少了很多半夜调Redis连接、调路径引用的崩溃时刻。如果你也想从“AI生成代码碎片”过渡到“AI辅助交付软件”找一个一直想写但没动力做的事用这五个阶段完整跑一遍。跑完之后你会对手里的模型产生一个准确得多的信任边界。最后再分享一个我很受用的小技巧在每个阶段结束时花三分钟让AI把“这类任务我下次该注意什么”写进项目备注。因为模型上下文会被清空但这些经验沉淀下来之后你会惊讶地发现第二次跑同类型的项目整个流程顺手了不止一个档次。