[{"data":1,"prerenderedAt":796},["ShallowReactive",2],{"tag:Go":3},[4,337],{"id":5,"title":6,"body":7,"date":316,"description":317,"draft":318,"extension":319,"featured":318,"lang":320,"launch":321,"license":322,"meta":323,"navigation":321,"order":309,"path":324,"seo":325,"series":326,"stem":327,"tags":328,"translationOf":335,"updated":335,"__hash__":336},"posts\u002Fposts\u002Fone-person-delivery-experiment-02-ai-engineering-stack.md","一人交付实验",{"type":8,"value":9,"toc":302},"minimark",[10,14,17,26,31,34,42,45,51,54,57,60,64,67,70,74,77,80,87,93,100,103,106,110,113,116,119,122,153,159,162,169,173,176,190,193,196,199,202,234,237,240,264,268,271,289],[11,12,13],"p",{},"很多 AI 项目的失败，不是模型选错了，而是只做了「模型」这一层。",[11,15,16],{},"一个能交付的 AI 应用，通常要同时解决三件事：模型怎么稳定地跑起来、LLM 怎么和业务工具接上、数据与算法怎么形成闭环。我把它们叫做 AI 工程落地的三层栈。这篇用我做过的金融级私有化项目拆开讲。",[11,18,19],{},[20,21],"img",{"alt":22,"src":23,"loading":24,"decoding":25},"AI 工程落地的三层栈","\u002Fimages\u002Fai-engineering-stack.svg","lazy","async",[27,28,30],"h2",{"id":29},"第一层模型服务不是写个-flask-调模型","第一层：模型服务，不是「写个 Flask 调模型」",[11,32,33],{},"模型服务层要回答的问题很具体：模型怎么加载、怎么批处理、怎么在 CPU\u002FGPU 之间切换、怎么预热、怎么观测、怎么升级。",[11,35,36,37,41],{},"在内容识别场景里，我用 YOLOv5 做检测、PaddleOCR 做文字识别（det + rec），模型导出 ONNX 后部署到 Triton Inference Server。Triton 里维护了 5 个模型仓库：检测和识别走 ONNX Runtime backend，前后处理用 Python backend 自己实现；",[38,39,40],"code",{},"max_batch_size"," 最高到 1000，其中 2 个模型配置了 model warmup。",[11,43,44],{},"为什么用 Triton，而不是直接在 Flask 里加载模型？因为交付环境要考虑的不只是「推理一次」：多个模型要共存、请求要批处理、CPU\u002FGPU 要有不同配置、模型升级要能回滚、进程重启后要能自动预热。Triton 提供的模型仓库和 backend 机制，把这些事收敛到了配置层；业务代码只负责调用，不负责管理模型生命周期。",[11,46,47],{},[20,48],{"alt":49,"src":50,"loading":24,"decoding":25},"Triton 模型仓库与 CPU\u002FGPU 双版本","\u002Fimages\u002Ftriton-model-repos.svg",[11,52,53],{},"对外接口是 Flask + waitress，部署在 Kubernetes 的 Deployment \u002F Service 上。客户环境不一定有 GPU，所以同一套服务提供 CPU \u002F GPU 双版本；在没有 GPU 的银行客户环境里，CPU 版本稳定吞吐大约 7–10 req\u002Fs，具体会随请求内容大小浮动。",[11,55,56],{},"这一层最容易被低估的是「前后处理」。检测框怎么裁、OCR 的 det\u002Frec 怎么串、图片怎么归一化、批量请求怎么拼，这些都不在模型文件里，却直接决定吞吐和准确率。把它们放进 Python backend，而不是散落在业务代码里，是我认为最值得的一个取舍。",[11,58,59],{},"模型服务还有几个必须提前想清楚的失败模式：图片超大导致显存\u002F内存暴涨；批量请求里混入损坏文件导致整批失败；模型版本升级后输出格式变化；CPU 环境下超时。这些不是「模型准不准」的问题，而是「服务能不能稳定交付」的问题。",[61,62,63],"h3",{"id":63},"模型服务的观测与回滚",[11,65,66],{},"模型服务的日志不能只记「成功\u002F失败」。我至少会记录：输入尺寸、批大小、推理耗时、CPU\u002FGPU、模型版本、错误类型。这样线上变慢时，才能判断是流量、模型还是环境的问题。",[11,68,69],{},"回滚也要提前设计：模型仓库按版本命名，配置里锁定版本；新版本先在一小部分请求上验证，再全量切换；切换失败时能回到上一版，而不是重新导出模型。对私有化客户来说，「能不能回滚」往往比「新模型准不准」更重要，因为客户环境里没有人能帮你现场调试。",[27,71,73],{"id":72},"第二层llm-应用重点是工具编排","第二层：LLM 应用，重点是工具编排",[11,75,76],{},"LLM 应用层不是「接一个聊天接口」就结束了。真正的价值在于：把自然语言变成对内部系统的可靠调用。",[11,78,79],{},"我做过两个方向：",[11,81,82,86],{},[83,84,85],"strong",{},"AI 智询","：用户用自然语言描述需求，系统做意图识别，然后路由到最合适的内部接口或工具，完成多步调用。它把过去需要在菜单里点很多次的流程，收敛成一次对话。难点不在 prompt，而在工具边界的定义：哪些工具可以暴露、参数怎么校验、失败怎么回滚、权限怎么继承。",[11,88,89,92],{},[83,90,91],{},"AI 鉴敏","：用语义分析识别用户行为与内容中的敏感信息，相比关键词匹配降低误判。它同样私有化部署在客户环境里，数据不出门。",[11,94,95,96,99],{},"这两个应用有一个共同约束：",[83,97,98],{},"私有化部署","。模型、向量、日志、工具调用都不能离开客户环境。这个约束会反过来影响架构：你不能依赖一个云端 LLM API 做所有事，要在本地做模型服务、缓存、降级和审计；你也不能把业务数据随便塞进第三方工具。",[11,101,102],{},"工具编排最容易犯的错误，是把「模型能调用」当成「模型可以随便调用」。我的做法是：每个工具都有明确的输入 schema、权限要求和失败语义；模型只负责选择工具和填参数，真正的执行、鉴权和审计由后端完成。这样即使模型选错工具，也不会越权。",[11,104,105],{},"权限模型也要按「用户身份」而不是「模型身份」设计。模型只是发起调用的一个环节，最终执行时仍然要带当前用户的身份、租户和角色；不能因为「是 AI 发起的」就绕过 RBAC。否则一次 prompt 注入，就可能变成一次越权操作。",[27,107,109],{"id":108},"第三层数据与算法决定系统是不是越用越准","第三层：数据与算法，决定系统是不是「越用越准」",[11,111,112],{},"第三层最容易被忽略，但它决定 AI 应用是不是只有 demo 价值。",[11,114,115],{},"在用户行为分析场景里，系统每天采集千万级的桌面操作元事件。我们用 Python + Pandas \u002F NumPy 按业务配置的异常规则做清洗、转换与特征工程，归纳到约 30 万条样本、4–10 维特征（分类 + 数值），在模型效率与检出效果之间取平衡；日均百万级预测。",[11,117,118],{},"这一层的关键不是「用哪个模型」，而是「数据怎么变成可解释的特征」。异常规则来自业务，特征工程要能回答「为什么这个用户被判定为异常」；如果模型只给一个分数，客户环境里没人敢用。",[11,120,121],{},"另一块是事件序列关联分析。我独立设计并实现了一个轻量的事件序列正则引擎：",[123,124,125,129,140,147,150],"ul",{},[126,127,128],"li",{},"词法 \u002F 语法分析 → AST → 后缀 IR → Thompson ε-NFA 构造与匹配；",[126,130,131,132,135,136,139],{},"支持并、连接、分组、有界重复 ",[38,133,134],{},"{n}"," 与无序组 ",[38,137,138],{},"\u003C...>","；",[126,141,142,143,146],{},"带 ",[38,144,145],{},"mustMatchPos"," 位置约束的反向匹配，保证刚到达的事件必须参与；",[126,148,149],{},"用字符集、长度 100、编译规模 3000、无序单元 5 四层约束规避灾难性回溯；",[126,151,152],{},"核心 pass 都有单元测试。",[11,154,155],{},[20,156],{"alt":157,"src":158,"loading":24,"decoding":25},"事件序列正则引擎","\u002Fimages\u002Fevent-sequence-regex.svg",[11,160,161],{},"为什么不用现成的规则引擎？因为需求要的是「事件之间的顺序关系」：先打开某个页面，再复制，再外发，才算一条风险序列；中间允许无关事件，但关键事件必须按顺序出现。现成引擎要么表达不了无序组，要么在长序列上回溯失控。自己实现一个小而可控的引擎，反而更容易测试和解释。",[11,163,164,165,168],{},"这段经历给我的最大提醒是：",[83,166,167],{},"AI 工程不是只有模型。"," 数据怎么采、特征怎么造、规则怎么表达、失败怎么解释，往往比换一个更大的模型更能决定系统能不能上线。",[27,170,172],{"id":171},"工程取舍为什么没有做-rag-和微调","工程取舍：为什么没有做 RAG 和微调",[11,174,175],{},"这套系统没有做 RAG，也没有做微调。不是这些技术不好，而是场景不匹配：",[123,177,178,181,184,187],{},[126,179,180],{},"客户环境要求私有化，数据不能出门；",[126,182,183],{},"业务问题更多是「识别 + 路由 + 规则」，不是开放域问答；",[126,185,186],{},"微调需要稳定的标注数据和持续评估，项目周期内不成立；",[126,188,189],{},"先把模型服务、工具编排、数据闭环做稳，收益比追新更大。",[11,191,192],{},"同理，Triton 也不是唯一选择。如果只是低频调用，一个 Flask 服务就够了；但当你要批处理、要多模型仓库、要在 CPU\u002FGPU 之间切换、要做 warmup 和观测，Triton 的价值才会显现。技术选型应该由交付约束驱动，而不是由技术名词驱动。",[11,194,195],{},"这也是为什么我先把模型服务做稳，再做 LLM 应用。模型服务是底座：它不稳定，上面的工具编排再优雅也没有意义。反过来，底座稳定之后，LLM 层可以快速试错，因为每次失败都能定位到是工具、提示词还是模型服务。对一人项目来说，顺序比速度重要：先做能复用的底座，再做能讲故事的场景。",[11,197,198],{},"还有一个取舍是观测。模型服务的日志不能只记「请求成功\u002F失败」，还要记输入尺寸、批大小、推理耗时、CPU\u002FGPU、模型版本。否则线上变慢时，你无法判断是流量、模型还是环境的问题。",[27,200,201],{"id":201},"常见失败模式",[123,203,204,210,216,222,228],{},[126,205,206,209],{},[83,207,208],{},"OCR 质量不稳定","：图片模糊、倾斜、遮挡会显著影响识别；需要在前后处理里加入质量判断和降级策略。",[126,211,212,215],{},[83,213,214],{},"CPU 吞吐不够","：没有 GPU 的客户环境，批处理和超时策略要重新设计，不能照搬 GPU 参数。",[126,217,218,221],{},[83,219,220],{},"工具越权","：模型选错工具或填错参数，必须在后端做权限校验和参数校验。",[126,223,224,227],{},[83,225,226],{},"数据漂移","：业务规则变化后，特征分布会变；需要定期回看样本和误判。",[126,229,230,233],{},[83,231,232],{},"模型升级破坏契约","：输出格式、类别、阈值变化要有版本和回滚。",[11,235,236],{},"这些失败模式有一个共同点：它们都不是模型能力问题，而是工程问题。模型服务层要把它们当成一等公民，提前设计检测、降级和回滚，而不是等上线后再补。",[27,238,239],{"id":239},"可复用清单",[241,242,243,246,249,252,255,258,261],"ol",{},[126,244,245],{},"先把模型服务做成独立层，不要让业务代码直接加载模型。",[126,247,248],{},"前后处理也属于模型服务，不要散落在业务里。",[126,250,251],{},"私有化场景先设计降级路径：没有 GPU、没有外网、模型升级失败时怎么办。",[126,253,254],{},"LLM 应用先定义工具边界和权限，再谈 prompt。",[126,256,257],{},"数据与算法层要能解释「为什么命中」，否则无法在客户环境里维护。",[126,259,260],{},"不要为了追新引入 RAG\u002F微调；先确认场景真的需要。",[126,262,263],{},"吞吐数字要写清测试条件，不要把实验室数字当成生产承诺。",[27,265,267],{"id":266},"回到一人交付","回到「一人交付」",[11,269,270],{},"这三层栈看起来是技术问题，其实也是交付问题：一个人要同时扮演模型工程师、后端工程师、数据工程师和运维，唯一的办法是把边界切清楚，让每一层都能独立验证。",[11,272,273,274,278,279,283,284,288],{},"这也是",[275,276,6],"a",{"href":277},"\u002Fseries\u002Fone-person-delivery-experiment","的核心：不是让一个人变成四个人，而是让一个人用流水线把四类工作组织起来。上一篇讲的是",[275,280,282],{"href":281},"\u002Fposts\u002Fone-person-delivery-experiment-01-multi-agent-saas","一个人如何交付一套多租户 SaaS","，这一篇讲的是 AI 应用的三层栈；如果你还没看开篇，可以从",[275,285,287],{"href":286},"\u002Fposts\u002Fwhy-i-started-one-person-delivery-notes","我为什么开始「一人交付笔记」","开始。",[11,290,291,292,296,297,301],{},"想继续聊模型服务、工具编排或事件序列引擎，欢迎通过 ",[275,293,295],{"href":294},"\u002Ffeed.xml","RSS","、知乎或公众号找到我；也可以先看看",[275,298,300],{"href":299},"\u002Fabout","关于我","。",{"title":303,"searchDepth":304,"depth":304,"links":305},"",2,[306,310,311,312,313,314,315],{"id":29,"depth":304,"text":30,"children":307},[308],{"id":63,"depth":309,"text":63},3,{"id":72,"depth":304,"text":73},{"id":108,"depth":304,"text":109},{"id":171,"depth":304,"text":172},{"id":201,"depth":304,"text":201},{"id":239,"depth":304,"text":239},{"id":266,"depth":304,"text":267},"2026-11-24","从模型服务、LLM 工具编排到数据与算法，拆解一套私有化 AI 应用真正落地时需要一起设计的三层栈。",false,"md","zh",true,"CC BY 4.0",{},"\u002Fposts\u002Fone-person-delivery-experiment-02-ai-engineering-stack",{"title":6,"description":317},"one-person-delivery-experiment","posts\u002Fone-person-delivery-experiment-02-ai-engineering-stack",[329,330,331,332,333,98,334],"AI 工程","Triton","YOLOv5","PaddleOCR","LLM","Go",null,"rNCMl3flRYGj61oYWnsfKq-GAzuM871gRhG9mfiFDjY",{"id":338,"title":6,"body":339,"date":785,"description":786,"draft":318,"extension":319,"featured":321,"lang":320,"launch":321,"license":322,"meta":787,"navigation":321,"order":304,"path":281,"seo":788,"series":326,"stem":789,"tags":790,"translationOf":335,"updated":335,"__hash__":795},"posts\u002Fposts\u002Fone-person-delivery-experiment-01-multi-agent-saas.md",{"type":8,"value":340,"toc":772},[341,344,347,407,410,413,416,430,436,439,443,449,456,459,462,468,471,474,477,481,496,499,502,505,509,515,518,554,557,560,566,569,575,581,587,593,596,600,603,633,636,639,642,645,671,674,677,680,706,709,711,714,741,744,747,750,766],[11,342,343],{},"先把结论放在前面：这是一套面向民办幼儿园的家园共育与园务管理 SaaS。产品方案、后端、两个微信小程序和一个管理后台，由我一个人完成；开发过程由一条自建的多 agent 流水线驱动。周期约 2.5 个月，代码合计约 14 万行。",[11,345,346],{},"这不是「AI 全自动写完」的故事。代码由 agent 生成、由人工评审与测试兜底；真正决定交付质量的，是切片方式、契约、评审隔离和证据留存。下面把过程完整拆开。",[348,349,350,363],"table",{},[351,352,353],"thead",{},[354,355,356,360],"tr",{},[357,358,359],"th",{},"维度",[357,361,362],{},"结果",[364,365,366,375,383,391,399],"tbody",{},[354,367,368,372],{},[369,370,371],"td",{},"周期",[369,373,374],{},"约 2.5 个月",[354,376,377,380],{},[369,378,379],{},"后端",[369,381,382],{},"Go，约 7 万行（含约 2.2 万行测试），约 300 个接口、70 张表",[354,384,385,388],{},[369,386,387],{},"前端",[369,389,390],{},"约 130 个 Vue 组件，约 7 万行",[354,392,393,396],{},[369,394,395],{},"端",[369,397,398],{},"老师端小程序、家长端小程序、Nuxt 3 管理后台",[354,400,401,404],{},[369,402,403],{},"合计",[369,405,406],{},"约 14 万行（AI 生成 + 人工评审）",[27,408,409],{"id":409},"问题与约束",[11,411,412],{},"民办幼儿园的家园沟通，长期依赖微信群和纸质记录：通知发了不知道谁看，请假靠口头汇总，接送靠人工辨认，成长记录散落在各个老师的手机里，出了纠纷很难追溯。",[11,414,415],{},"这类需求并不「高精尖」，但它有几个很现实的约束：",[123,417,418,421,424,427],{},[126,419,420],{},"幼儿园预算有限，不可能为一个内部系统养一支研发团队；",[126,422,423],{},"老师和管理员不是专业软件用户，界面必须足够简单；",[126,425,426],{},"儿童照片、接送人、健康信息都属于敏感数据，权限必须清楚；",[126,428,429],{},"需求会持续变化，系统必须能迭代，而不是一次性交付后锁死。",[11,431,432,433],{},"传统外包的问题不是做不出来，而是沟通成本和迭代成本太高；一个小团队的问题也不是技术，而是「为这个预算养团队」不成立。于是我想验证一件事：",[83,434,435],{},"如果一个人把交付流水线设计好，能不能独立完成一套企业级 SaaS？",[11,437,438],{},"我给自己定了三条边界：不接支付、不做开放注册、不碰与儿童隐私无关的扩张功能。范围越小，切片越清楚，评审越容易做。对一人项目来说，克制不是保守，而是交付的一部分。",[27,440,442],{"id":441},"架构先把边界画清楚","架构：先把边界画清楚",[11,444,445],{},[20,446],{"alt":447,"src":448,"loading":24,"decoding":25},"养成星球的三端与后端架构","\u002Fimages\u002Fnurture-planet-architecture.svg",[11,450,451,452,455],{},"后端使用 Go 1.25 + Gin + GORM + MySQL + Redis，权限用 Casbin，依赖注入用 Wire，分层是 ",[38,453,454],{},"handler → service → repository → domain","。这套分层不新，但它适合 agent 协作：每一层的职责固定，切片可以沿着层边界切，评审也更容易判断「有没有越层」。",[11,457,458],{},"为什么是 Go？不是因为性能神话，而是因为一人项目需要「部署简单、并发模型清楚、错误处理显式」。一个二进制、一套标准库、一个能看懂的错误链，比一堆框架魔法更适合长期维护。Gin 和 GORM 也不是唯一选择，但它们的生态足够成熟，agent 生成的代码更容易被评审。",[11,460,461],{},"多租户是这套系统的第一约束。所有业务表都带租户列，查询必须带租户条件，跨园所数据强制隔离；儿童照片只对授权家长可见。这个约束不是事后补的，而是从第一张表开始就写进 schema 和 repository 的。",[11,463,464],{},[20,465],{"alt":466,"src":467,"loading":24,"decoding":25},"多租户隔离：所有查询都必须带租户条件","\u002Fimages\u002Fmulti-tenant-isolation.svg",[11,469,470],{},"多租户有两种常见做法：每个租户一个 schema，或者共享表 + 租户列。前者隔离更彻底，但迁移、备份、跨租户统计都更复杂；后者实现简单，但要求每一层都记得带条件。对这套系统，我选了共享表 + 租户列，并把「忘记租户条件」当成最高级别的代码缺陷：repository 层强制注入，code review 里看到跨园所查询直接拒绝，测试里专门覆盖跨租户读取。",[11,472,473],{},"多租户的测试不能只测「正常查询」：要构造两个园所的数据，用 A 园所的账号去读 B 园所的学生、通知、照片、接送记录，确认全部返回空或 404；还要测管理员跨园所的统计接口，确认它只返回授权范围内的聚合。这类测试很枯燥，但它是多租户系统最值得投入的地方。",[11,475,476],{},"人脸识别抽象了一个 Provider 接口，接入腾讯云 IAI 做 1:N 检索：照片自动匹配学生，按人脸数量区分单人、小组和合影。后来供应商方案调整，我只替换了实现层，调用方零改动。对一个一人项目来说，这种「可替换边界」比「选对某个供应商」更重要：你没有团队去追每一个供应商的变化，只能把变化关在一个小盒子里。",[27,478,480],{"id":479},"三端把复杂度留在后端","三端：把复杂度留在后端",[123,482,483,486,489],{},[126,484,485],{},"老师端和家长端用 uni-app + Vue 3 + Vite，共用一套组件与设计 Token；",[126,487,488],{},"管理后台用 Nuxt 3 + Element Plus + Pinia + ECharts；",[126,490,491,492,495],{},"三端共享 ",[38,493,494],{},"tokens.json","，构建时生成产物，再用 lint 阻断硬编码色值和间距。",[11,497,498],{},"老师端的核心是「少点几下」：发通知、记成长、看待办、处理请假、接送核验。家长端的核心是「看得懂」：通知、成长档案、请假、接送授权、相册。管理后台的核心是「可管理」：园所、班级、学生、老师、权限、统计。",[11,500,501],{},"共享设计 Token 看起来是「设计系统」的事，实际上它是 agent 协作的护栏：当三个端由不同切片实现时，颜色、间距、圆角很容易各自发挥；Token + lint 把「审美」变成了可验证的约束。",[11,503,504],{},"另一个容易被忽略的点是错误提示。三端面向的是非技术用户，错误信息不能是「internal server error」。我在契约层定义了错误码和用户可读文案，前端只负责展示；这样 agent 在实现不同端时，不会各自发明一套错误语言。",[27,506,508],{"id":507},"多-agent-流水线主会话不读源码","多 agent 流水线：主会话不读源码",[11,510,511],{},[20,512],{"alt":513,"src":514,"loading":24,"decoding":25},"多 agent 交付流水线","\u002Fimages\u002Fmulti-agent-pipeline.svg",[11,516,517],{},"这是整个项目里最值得复盘的部分。流水线不是「开一个聊天窗口，让 AI 一直写」，而是把交付拆成几个角色：",[241,519,520,526,535,544],{},[126,521,522,525],{},[83,523,524],{},"主会话只做编排","。它不读源码，只读计划、切片状态和评审结论。这样可以避免主上下文被几十万行代码淹没，也能让「决策」和「实现」分开。",[126,527,528,534],{},[83,529,530,533],{},[38,531,532],{},"slice-worker"," 负责实现","。每个切片足够小，有明确的输入、输出和验收标准；worker 只在自己的切片里工作，失败可以回滚。",[126,536,537,543],{},[83,538,539,542],{},[38,540,541],{},"slice-supervisor"," 负责对抗式评审","。它是一个全新上下文的实例，不继承 worker 的自我判断；它读代码、跑测试、在微信开发者工具里做真机验证，然后给出 verdict。",[126,545,546,549,550,553],{},[83,547,548],{},"检查点与证据","。四个检查点、validator、密钥扫描，状态全部落在 ",[38,551,552],{},"PROGRESS.md","。主会话靠这些证据判断「能不能进入下一阶段」，而不是靠 agent 说「我完成了」。",[11,555,556],{},"四个检查点分别看四件事：规格是否写清楚、契约是否稳定、实现是否越层、真机行为是否符合验收。validator 负责机械检查：编译、测试、lint、密钥扫描、禁止的依赖。检查点不通过，worker 不进入下一个切片；supervisor 不通过，主会话不合并。",[11,558,559],{},"为什么 supervisor 要用全新实例？因为同一个上下文里，模型很容易为自己的实现找理由。换一个没有「沉没成本」的实例，评审会更接近一个真正的外部 reviewer。这一点在跨文件重构和权限边界上尤其明显。",[11,561,562,563],{},"这套流水线并不能让 agent 变成资深工程师。它做的是另一件事：",[83,564,565],{},"把「实现」变成可并行、可回滚、可复核的工作单元，把人的注意力留给架构、边界和取舍。",[61,567,568],{"id":568},"四个检查点具体看什么",[11,570,571,574],{},[83,572,573],{},"规格检查点","：需求有没有写成可验收的行为？边界条件、权限、失败路径有没有写清楚？如果规格里只有「实现通知已读」，没有「家长只能标记自己的记录」，这个切片就不该开始。",[11,576,577,580],{},[83,578,579],{},"契约检查点","：接口形状、错误码、字段类型有没有定下来？前端和后端是不是照着同一份契约写？契约没定就开工，最后一定会在联调时返工。",[11,582,583,586],{},[83,584,585],{},"实现检查点","：有没有越层？repository 里有没有业务逻辑？handler 里有没有直接拼 SQL？租户条件有没有漏？这些是机械检查能发现的部分，也是 supervisor 最先看的地方。",[11,588,589,592],{},[83,590,591],{},"真机检查点","：在微信开发者工具里跑一遍真实路径，不只看单元测试。登录、切换身份、打开通知、标记已读、返回列表，每一步都要在真机上确认。很多问题只在真机上出现，测试环境看不出来。",[11,594,595],{},"四个检查点都通过，切片才算完成；任何一个不通过，worker 回到实现，而不是把问题留给下一个人。",[27,597,599],{"id":598},"工程规范让-agent-有东西可以遵守","工程规范：让 agent 有东西可以遵守",[11,601,602],{},"agent 不会自动遵守你脑子里的规范，除非规范被写成可执行的约束。这个项目里，真正起作用的是四件事：",[123,604,605,615,621,627],{},[126,606,607,610,611,614],{},[83,608,609],{},"API 契约优先","：",[38,612,613],{},"backend\u002Fapi\u002F"," 是接口形状的唯一真相源。改接口先改契约，三端照着契约写；契约不一致，测试会先失败。",[126,616,617,620],{},[83,618,619],{},"OpenSpec 规格驱动","：跨三层的改动先写 proposal 和带 SHALL + Scenario 的验收标准，验收通过才归档。我试过不写规格直接改，结果就是三个月后没人记得当初为什么这么写。",[126,622,623,626],{},[83,624,625],{},"测试","：Go 端用 testify 做单元测试，前端用 Vitest。测试不是给 agent 看的，是给「未来的我」看的。",[126,628,629,632],{},[83,630,631],{},"检查点","：每个阶段结束必须留下可验证的产物；没有产物，不算完成。",[11,634,635],{},"这些规范听起来像「大团队才需要的东西」，但在一个人 + agent 的场景里反而更重要：agent 的速度越快，没有约束的返工就越贵。",[11,637,638],{},"我后来发现，规范的价值不在于「写得漂亮」，而在于「让 agent 可以被问责」。当验收标准是 SHALL + Scenario 时，supervisor 可以逐条对照；当接口有契约时，前端不需要猜后端；当测试覆盖真实行为时，agent 说「完成了」才有意义。",[27,640,641],{"id":641},"失败与取舍",[11,643,644],{},"这套流水线不是没有代价。几个真实的失败点：",[123,646,647,653,659,665],{},[126,648,649,652],{},[83,650,651],{},"跨文件重构仍然最容易失控","。切片一旦跨过太多文件，worker 很容易改出「局部正确、整体不一致」的结果；后来我把这类任务拆成「先改契约，再改实现，再改调用方」。",[126,654,655,658],{},[83,656,657],{},"UI 细节和真机兼容需要人盯着","。小程序的样式、键盘、滚动、真机表现，agent 可以写，但很难自己判断「看起来对不对」；supervisor 的真机验证是必要的，但仍然需要人做最终取舍。",[126,660,661,664],{},[83,662,663],{},"评审开销是真实成本","。全新上下文的 supervisor 会增加时间，但它减少的是更贵的返工；如果切片太大，评审开销会迅速失控。",[126,666,667,670],{},[83,668,669],{},"上下文管理比模型能力更关键","。主会话不读源码、切片足够小、状态落到文件，这些做法比换一个更强的模型更能提升稳定性。",[11,672,673],{},"如果重来一次，我会改三件事：切片再小一点；真机验证再早一点；契约测试再多一点。",[27,675,676],{"id":676},"时间花在哪里",[11,678,679],{},"2.5 个月里，真正写代码的时间没有想象中多。大致可以分成四块：",[123,681,682,688,694,700],{},[126,683,684,687],{},[83,685,686],{},"产品与范围","：把「什么都想要」压到「这一版必须有什么」，这部分只能人做；",[126,689,690,693],{},[83,691,692],{},"架构与契约","：定分层、租户模型、接口形状、设计 Token，这部分决定后面返工多少；",[126,695,696,699],{},[83,697,698],{},"实现与评审","：agent 写、supervisor 审、我处理冲突和边界；",[126,701,702,705],{},[83,703,704],{},"真机与收尾","：小程序兼容、权限边界、异常路径、部署与文档。",[11,707,708],{},"AI 明显加速的是第三块；第一、二、四块反而更需要人。这个分布也解释了为什么「一人 + agent」不是简单地把人替换掉：它把人的工作从「写」推向了「定义」和「判断」。",[27,710,239],{"id":239},[11,712,713],{},"如果你也想尝试「一个人 + agent」交付，下面几条可以直接抄走：",[241,715,716,723,726,729,732,735,738],{},[126,717,718,719,722],{},"按",[83,720,721],{},"可交付行为","切片，不要按文件切片。",[126,724,725],{},"每个切片先写验收标准，再让 agent 实现。",[126,727,728],{},"评审必须用没有参与实现的上下文。",[126,730,731],{},"主会话不要读全部源码，只读计划和 verdict。",[126,733,734],{},"契约优先，测试放在契约和真实行为的交界处。",[126,736,737],{},"每个阶段留下证据：测试、日志、检查点、回滚路径。",[126,739,740],{},"对外复盘前先脱敏：客户名称、儿童信息、内部域名、密钥，一个都不能留。",[11,742,743],{},"这套方法不适合所有项目。需求极不稳定、界面极重、强实时协作的场景，仍然需要更多人。但如果你在做一人公司、独立产品，或者想验证「一个人到底能交付到什么程度」，它值得一试。",[27,745,746],{"id":746},"下一篇",[11,748,749],{},"这篇讲的是「怎么把一套 SaaS 交付出来」；下一篇回到 AI 工程本身，拆解模型服务、LLM 工具编排、数据与算法三层怎么一起落地：",[123,751,752,757,761],{},[126,753,754],{},[275,755,756],{"href":324},"一人交付实验 #2：AI 工程落地的三层栈",[126,758,759],{},[275,760,287],{"href":286},[126,762,763],{},[275,764,765],{"href":277},"一人交付实验专题",[11,767,768,769,771],{},"如果你对某个环节想看得更细——切片怎么切、supervisor 怎么评审、契约怎么落地——欢迎通过 ",[275,770,295],{"href":294},"、知乎或公众号告诉我。",{"title":303,"searchDepth":304,"depth":304,"links":773},[774,775,776,777,780,781,782,783,784],{"id":409,"depth":304,"text":409},{"id":441,"depth":304,"text":442},{"id":479,"depth":304,"text":480},{"id":507,"depth":304,"text":508,"children":778},[779],{"id":568,"depth":309,"text":568},{"id":598,"depth":304,"text":599},{"id":641,"depth":304,"text":641},{"id":676,"depth":304,"text":676},{"id":239,"depth":304,"text":239},{"id":746,"depth":304,"text":746},"2026-11-17","一套多租户幼儿园家园共育 SaaS 的完整复盘：架构、三端、多 agent 流水线、工程规范、失败路径与可复用清单。",{},{"title":6,"description":786},"posts\u002Fone-person-delivery-experiment-01-multi-agent-saas",[791,792,793,334,794],"一人交付","AI Agent","多租户 SaaS","工程管理","d_npBYOCuvpBaAhqXmpANfEyUkRlE87GkMyqhqcdiUQ",1794018033908]