[{"data":1,"prerenderedAt":496},["ShallowReactive",2],{"tag:多租户 SaaS":3},[4],{"id":5,"title":6,"body":7,"date":476,"description":477,"draft":478,"extension":479,"featured":480,"lang":481,"launch":480,"license":482,"meta":483,"navigation":480,"order":462,"path":484,"seo":485,"series":486,"stem":487,"tags":488,"translationOf":494,"updated":494,"__hash__":495},"posts\u002Fposts\u002Fone-person-delivery-experiment-01-multi-agent-saas.md","一人交付实验",{"type":8,"value":9,"toc":460},"minimark",[10,14,17,77,81,84,87,103,110,113,117,126,134,137,140,146,149,152,155,159,174,177,180,183,187,193,196,233,236,239,245,249,255,261,267,273,276,280,283,313,316,319,322,325,351,354,357,360,386,389,392,395,422,425,428,431,452],[11,12,13],"p",{},"先把结论放在前面：这是一套面向民办幼儿园的家园共育与园务管理 SaaS。产品方案、后端、两个微信小程序和一个管理后台，由我一个人完成；开发过程由一条自建的多 agent 流水线驱动。周期约 2.5 个月，代码合计约 14 万行。",[11,15,16],{},"这不是「AI 全自动写完」的故事。代码由 agent 生成、由人工评审与测试兜底；真正决定交付质量的，是切片方式、契约、评审隔离和证据留存。下面把过程完整拆开。",[18,19,20,33],"table",{},[21,22,23],"thead",{},[24,25,26,30],"tr",{},[27,28,29],"th",{},"维度",[27,31,32],{},"结果",[34,35,36,45,53,61,69],"tbody",{},[24,37,38,42],{},[39,40,41],"td",{},"周期",[39,43,44],{},"约 2.5 个月",[24,46,47,50],{},[39,48,49],{},"后端",[39,51,52],{},"Go，约 7 万行（含约 2.2 万行测试），约 300 个接口、70 张表",[24,54,55,58],{},[39,56,57],{},"前端",[39,59,60],{},"约 130 个 Vue 组件，约 7 万行",[24,62,63,66],{},[39,64,65],{},"端",[39,67,68],{},"老师端小程序、家长端小程序、Nuxt 3 管理后台",[24,70,71,74],{},[39,72,73],{},"合计",[39,75,76],{},"约 14 万行（AI 生成 + 人工评审）",[78,79,80],"h2",{"id":80},"问题与约束",[11,82,83],{},"民办幼儿园的家园沟通，长期依赖微信群和纸质记录：通知发了不知道谁看，请假靠口头汇总，接送靠人工辨认，成长记录散落在各个老师的手机里，出了纠纷很难追溯。",[11,85,86],{},"这类需求并不「高精尖」，但它有几个很现实的约束：",[88,89,90,94,97,100],"ul",{},[91,92,93],"li",{},"幼儿园预算有限，不可能为一个内部系统养一支研发团队；",[91,95,96],{},"老师和管理员不是专业软件用户，界面必须足够简单；",[91,98,99],{},"儿童照片、接送人、健康信息都属于敏感数据，权限必须清楚；",[91,101,102],{},"需求会持续变化，系统必须能迭代，而不是一次性交付后锁死。",[11,104,105,106],{},"传统外包的问题不是做不出来，而是沟通成本和迭代成本太高；一个小团队的问题也不是技术，而是「为这个预算养团队」不成立。于是我想验证一件事：",[107,108,109],"strong",{},"如果一个人把交付流水线设计好，能不能独立完成一套企业级 SaaS？",[11,111,112],{},"我给自己定了三条边界：不接支付、不做开放注册、不碰与儿童隐私无关的扩张功能。范围越小，切片越清楚，评审越容易做。对一人项目来说，克制不是保守，而是交付的一部分。",[78,114,116],{"id":115},"架构先把边界画清楚","架构：先把边界画清楚",[11,118,119],{},[120,121],"img",{"alt":122,"src":123,"loading":124,"decoding":125},"养成星球的三端与后端架构","\u002Fimages\u002Fnurture-planet-architecture.svg","lazy","async",[11,127,128,129,133],{},"后端使用 Go 1.25 + Gin + GORM + MySQL + Redis，权限用 Casbin，依赖注入用 Wire，分层是 ",[130,131,132],"code",{},"handler → service → repository → domain","。这套分层不新，但它适合 agent 协作：每一层的职责固定，切片可以沿着层边界切，评审也更容易判断「有没有越层」。",[11,135,136],{},"为什么是 Go？不是因为性能神话，而是因为一人项目需要「部署简单、并发模型清楚、错误处理显式」。一个二进制、一套标准库、一个能看懂的错误链，比一堆框架魔法更适合长期维护。Gin 和 GORM 也不是唯一选择，但它们的生态足够成熟，agent 生成的代码更容易被评审。",[11,138,139],{},"多租户是这套系统的第一约束。所有业务表都带租户列，查询必须带租户条件，跨园所数据强制隔离；儿童照片只对授权家长可见。这个约束不是事后补的，而是从第一张表开始就写进 schema 和 repository 的。",[11,141,142],{},[120,143],{"alt":144,"src":145,"loading":124,"decoding":125},"多租户隔离：所有查询都必须带租户条件","\u002Fimages\u002Fmulti-tenant-isolation.svg",[11,147,148],{},"多租户有两种常见做法：每个租户一个 schema，或者共享表 + 租户列。前者隔离更彻底，但迁移、备份、跨租户统计都更复杂；后者实现简单，但要求每一层都记得带条件。对这套系统，我选了共享表 + 租户列，并把「忘记租户条件」当成最高级别的代码缺陷：repository 层强制注入，code review 里看到跨园所查询直接拒绝，测试里专门覆盖跨租户读取。",[11,150,151],{},"多租户的测试不能只测「正常查询」：要构造两个园所的数据，用 A 园所的账号去读 B 园所的学生、通知、照片、接送记录，确认全部返回空或 404；还要测管理员跨园所的统计接口，确认它只返回授权范围内的聚合。这类测试很枯燥，但它是多租户系统最值得投入的地方。",[11,153,154],{},"人脸识别抽象了一个 Provider 接口，接入腾讯云 IAI 做 1:N 检索：照片自动匹配学生，按人脸数量区分单人、小组和合影。后来供应商方案调整，我只替换了实现层，调用方零改动。对一个一人项目来说，这种「可替换边界」比「选对某个供应商」更重要：你没有团队去追每一个供应商的变化，只能把变化关在一个小盒子里。",[78,156,158],{"id":157},"三端把复杂度留在后端","三端：把复杂度留在后端",[88,160,161,164,167],{},[91,162,163],{},"老师端和家长端用 uni-app + Vue 3 + Vite，共用一套组件与设计 Token；",[91,165,166],{},"管理后台用 Nuxt 3 + Element Plus + Pinia + ECharts；",[91,168,169,170,173],{},"三端共享 ",[130,171,172],{},"tokens.json","，构建时生成产物，再用 lint 阻断硬编码色值和间距。",[11,175,176],{},"老师端的核心是「少点几下」：发通知、记成长、看待办、处理请假、接送核验。家长端的核心是「看得懂」：通知、成长档案、请假、接送授权、相册。管理后台的核心是「可管理」：园所、班级、学生、老师、权限、统计。",[11,178,179],{},"共享设计 Token 看起来是「设计系统」的事，实际上它是 agent 协作的护栏：当三个端由不同切片实现时，颜色、间距、圆角很容易各自发挥；Token + lint 把「审美」变成了可验证的约束。",[11,181,182],{},"另一个容易被忽略的点是错误提示。三端面向的是非技术用户，错误信息不能是「internal server error」。我在契约层定义了错误码和用户可读文案，前端只负责展示；这样 agent 在实现不同端时，不会各自发明一套错误语言。",[78,184,186],{"id":185},"多-agent-流水线主会话不读源码","多 agent 流水线：主会话不读源码",[11,188,189],{},[120,190],{"alt":191,"src":192,"loading":124,"decoding":125},"多 agent 交付流水线","\u002Fimages\u002Fmulti-agent-pipeline.svg",[11,194,195],{},"这是整个项目里最值得复盘的部分。流水线不是「开一个聊天窗口，让 AI 一直写」，而是把交付拆成几个角色：",[197,198,199,205,214,223],"ol",{},[91,200,201,204],{},[107,202,203],{},"主会话只做编排","。它不读源码，只读计划、切片状态和评审结论。这样可以避免主上下文被几十万行代码淹没，也能让「决策」和「实现」分开。",[91,206,207,213],{},[107,208,209,212],{},[130,210,211],{},"slice-worker"," 负责实现","。每个切片足够小，有明确的输入、输出和验收标准；worker 只在自己的切片里工作，失败可以回滚。",[91,215,216,222],{},[107,217,218,221],{},[130,219,220],{},"slice-supervisor"," 负责对抗式评审","。它是一个全新上下文的实例，不继承 worker 的自我判断；它读代码、跑测试、在微信开发者工具里做真机验证，然后给出 verdict。",[91,224,225,228,229,232],{},[107,226,227],{},"检查点与证据","。四个检查点、validator、密钥扫描，状态全部落在 ",[130,230,231],{},"PROGRESS.md","。主会话靠这些证据判断「能不能进入下一阶段」，而不是靠 agent 说「我完成了」。",[11,234,235],{},"四个检查点分别看四件事：规格是否写清楚、契约是否稳定、实现是否越层、真机行为是否符合验收。validator 负责机械检查：编译、测试、lint、密钥扫描、禁止的依赖。检查点不通过，worker 不进入下一个切片；supervisor 不通过，主会话不合并。",[11,237,238],{},"为什么 supervisor 要用全新实例？因为同一个上下文里，模型很容易为自己的实现找理由。换一个没有「沉没成本」的实例，评审会更接近一个真正的外部 reviewer。这一点在跨文件重构和权限边界上尤其明显。",[11,240,241,242],{},"这套流水线并不能让 agent 变成资深工程师。它做的是另一件事：",[107,243,244],{},"把「实现」变成可并行、可回滚、可复核的工作单元，把人的注意力留给架构、边界和取舍。",[246,247,248],"h3",{"id":248},"四个检查点具体看什么",[11,250,251,254],{},[107,252,253],{},"规格检查点","：需求有没有写成可验收的行为？边界条件、权限、失败路径有没有写清楚？如果规格里只有「实现通知已读」，没有「家长只能标记自己的记录」，这个切片就不该开始。",[11,256,257,260],{},[107,258,259],{},"契约检查点","：接口形状、错误码、字段类型有没有定下来？前端和后端是不是照着同一份契约写？契约没定就开工，最后一定会在联调时返工。",[11,262,263,266],{},[107,264,265],{},"实现检查点","：有没有越层？repository 里有没有业务逻辑？handler 里有没有直接拼 SQL？租户条件有没有漏？这些是机械检查能发现的部分，也是 supervisor 最先看的地方。",[11,268,269,272],{},[107,270,271],{},"真机检查点","：在微信开发者工具里跑一遍真实路径，不只看单元测试。登录、切换身份、打开通知、标记已读、返回列表，每一步都要在真机上确认。很多问题只在真机上出现，测试环境看不出来。",[11,274,275],{},"四个检查点都通过，切片才算完成；任何一个不通过，worker 回到实现，而不是把问题留给下一个人。",[78,277,279],{"id":278},"工程规范让-agent-有东西可以遵守","工程规范：让 agent 有东西可以遵守",[11,281,282],{},"agent 不会自动遵守你脑子里的规范，除非规范被写成可执行的约束。这个项目里，真正起作用的是四件事：",[88,284,285,295,301,307],{},[91,286,287,290,291,294],{},[107,288,289],{},"API 契约优先","：",[130,292,293],{},"backend\u002Fapi\u002F"," 是接口形状的唯一真相源。改接口先改契约，三端照着契约写；契约不一致，测试会先失败。",[91,296,297,300],{},[107,298,299],{},"OpenSpec 规格驱动","：跨三层的改动先写 proposal 和带 SHALL + Scenario 的验收标准，验收通过才归档。我试过不写规格直接改，结果就是三个月后没人记得当初为什么这么写。",[91,302,303,306],{},[107,304,305],{},"测试","：Go 端用 testify 做单元测试，前端用 Vitest。测试不是给 agent 看的，是给「未来的我」看的。",[91,308,309,312],{},[107,310,311],{},"检查点","：每个阶段结束必须留下可验证的产物；没有产物，不算完成。",[11,314,315],{},"这些规范听起来像「大团队才需要的东西」，但在一个人 + agent 的场景里反而更重要：agent 的速度越快，没有约束的返工就越贵。",[11,317,318],{},"我后来发现，规范的价值不在于「写得漂亮」，而在于「让 agent 可以被问责」。当验收标准是 SHALL + Scenario 时，supervisor 可以逐条对照；当接口有契约时，前端不需要猜后端；当测试覆盖真实行为时，agent 说「完成了」才有意义。",[78,320,321],{"id":321},"失败与取舍",[11,323,324],{},"这套流水线不是没有代价。几个真实的失败点：",[88,326,327,333,339,345],{},[91,328,329,332],{},[107,330,331],{},"跨文件重构仍然最容易失控","。切片一旦跨过太多文件，worker 很容易改出「局部正确、整体不一致」的结果；后来我把这类任务拆成「先改契约，再改实现，再改调用方」。",[91,334,335,338],{},[107,336,337],{},"UI 细节和真机兼容需要人盯着","。小程序的样式、键盘、滚动、真机表现，agent 可以写，但很难自己判断「看起来对不对」；supervisor 的真机验证是必要的，但仍然需要人做最终取舍。",[91,340,341,344],{},[107,342,343],{},"评审开销是真实成本","。全新上下文的 supervisor 会增加时间，但它减少的是更贵的返工；如果切片太大，评审开销会迅速失控。",[91,346,347,350],{},[107,348,349],{},"上下文管理比模型能力更关键","。主会话不读源码、切片足够小、状态落到文件，这些做法比换一个更强的模型更能提升稳定性。",[11,352,353],{},"如果重来一次，我会改三件事：切片再小一点；真机验证再早一点；契约测试再多一点。",[78,355,356],{"id":356},"时间花在哪里",[11,358,359],{},"2.5 个月里，真正写代码的时间没有想象中多。大致可以分成四块：",[88,361,362,368,374,380],{},[91,363,364,367],{},[107,365,366],{},"产品与范围","：把「什么都想要」压到「这一版必须有什么」，这部分只能人做；",[91,369,370,373],{},[107,371,372],{},"架构与契约","：定分层、租户模型、接口形状、设计 Token，这部分决定后面返工多少；",[91,375,376,379],{},[107,377,378],{},"实现与评审","：agent 写、supervisor 审、我处理冲突和边界；",[91,381,382,385],{},[107,383,384],{},"真机与收尾","：小程序兼容、权限边界、异常路径、部署与文档。",[11,387,388],{},"AI 明显加速的是第三块；第一、二、四块反而更需要人。这个分布也解释了为什么「一人 + agent」不是简单地把人替换掉：它把人的工作从「写」推向了「定义」和「判断」。",[78,390,391],{"id":391},"可复用清单",[11,393,394],{},"如果你也想尝试「一个人 + agent」交付，下面几条可以直接抄走：",[197,396,397,404,407,410,413,416,419],{},[91,398,399,400,403],{},"按",[107,401,402],{},"可交付行为","切片，不要按文件切片。",[91,405,406],{},"每个切片先写验收标准，再让 agent 实现。",[91,408,409],{},"评审必须用没有参与实现的上下文。",[91,411,412],{},"主会话不要读全部源码，只读计划和 verdict。",[91,414,415],{},"契约优先，测试放在契约和真实行为的交界处。",[91,417,418],{},"每个阶段留下证据：测试、日志、检查点、回滚路径。",[91,420,421],{},"对外复盘前先脱敏：客户名称、儿童信息、内部域名、密钥，一个都不能留。",[11,423,424],{},"这套方法不适合所有项目。需求极不稳定、界面极重、强实时协作的场景，仍然需要更多人。但如果你在做一人公司、独立产品，或者想验证「一个人到底能交付到什么程度」，它值得一试。",[78,426,427],{"id":427},"下一篇",[11,429,430],{},"这篇讲的是「怎么把一套 SaaS 交付出来」；下一篇回到 AI 工程本身，拆解模型服务、LLM 工具编排、数据与算法三层怎么一起落地：",[88,432,433,440,446],{},[91,434,435],{},[436,437,439],"a",{"href":438},"\u002Fposts\u002Fone-person-delivery-experiment-02-ai-engineering-stack","一人交付实验 #2：AI 工程落地的三层栈",[91,441,442],{},[436,443,445],{"href":444},"\u002Fposts\u002Fwhy-i-started-one-person-delivery-notes","我为什么开始「一人交付笔记」",[91,447,448],{},[436,449,451],{"href":450},"\u002Fseries\u002Fone-person-delivery-experiment","一人交付实验专题",[11,453,454,455,459],{},"如果你对某个环节想看得更细——切片怎么切、supervisor 怎么评审、契约怎么落地——欢迎通过 ",[436,456,458],{"href":457},"\u002Ffeed.xml","RSS","、知乎或公众号告诉我。",{"title":461,"searchDepth":462,"depth":462,"links":463},"",2,[464,465,466,467,471,472,473,474,475],{"id":80,"depth":462,"text":80},{"id":115,"depth":462,"text":116},{"id":157,"depth":462,"text":158},{"id":185,"depth":462,"text":186,"children":468},[469],{"id":248,"depth":470,"text":248},3,{"id":278,"depth":462,"text":279},{"id":321,"depth":462,"text":321},{"id":356,"depth":462,"text":356},{"id":391,"depth":462,"text":391},{"id":427,"depth":462,"text":427},"2026-11-17","一套多租户幼儿园家园共育 SaaS 的完整复盘：架构、三端、多 agent 流水线、工程规范、失败路径与可复用清单。",false,"md",true,"zh","CC BY 4.0",{},"\u002Fposts\u002Fone-person-delivery-experiment-01-multi-agent-saas",{"title":6,"description":477},"one-person-delivery-experiment","posts\u002Fone-person-delivery-experiment-01-multi-agent-saas",[489,490,491,492,493],"一人交付","AI Agent","多租户 SaaS","Go","工程管理",null,"d_npBYOCuvpBaAhqXmpANfEyUkRlE87GkMyqhqcdiUQ",1794018033979]