<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Physics &amp; Agent Lab</title><description>记录 AI 开发、桌游实践与物理思考</description><link>https://physics-agent-lab.pages.dev/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.16.8</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年10月17日 19:15:33</lastBuildDate><item><title>用 AI 写项目，先把模块边界划清楚</title><link>https://physics-agent-lab.pages.dev/posts/module-boundaries/</link><guid isPermaLink="true">https://physics-agent-lab.pages.dev/posts/module-boundaries/</guid><description>从一个简化的棋类网页出发，谈模块职责、接口契约和多 Agent 协作：怎样让功能能跑，也让后续修改有迹可循。</description><pubDate>Sat, 17 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;从“加个悔棋”说起&lt;a href=&quot;#从加个悔棋说起&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;假设我们正在做一个棋类网页，落子、回合切换和 AI 对手都能运行。现在想加一个悔棋按钮，却发现点击事件里既有合法性判断，又有动画、历史记录和 AI 调用。撤回一步之后，该撤销哪些状态、已经开始思考的 AI 怎么处理，都得沿着代码重新找一遍。&lt;/p&gt;&lt;p&gt;下面就用这个简化场景讨论模块边界。它是说明性的例子，不对应某个已上线项目的实现。&lt;/p&gt;&lt;p&gt;用 AI 写功能时，很容易把注意力放在眼前的效果上：按钮能点，页面能打开，任务就像完成了。等到第二次、第三次修改，代码之间的依赖才逐渐显出来。为了少走到“哪里都不敢动”的地步，值得在开工前先写清每部分的职责。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;按职责和变化原因划分&lt;a href=&quot;#按职责和变化原因划分&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;给文件夹起名叫 &lt;code&gt;core&lt;/code&gt;、&lt;code&gt;ui&lt;/code&gt;、&lt;code&gt;utils&lt;/code&gt;，还不足以说明边界。更有用的问题是：它负责什么，谁管理状态，对外提供什么能力？&lt;/p&gt;&lt;p&gt;调整棋子的移动动画，主要应该影响界面；修改合法落子的条件，应能找到统一的规则入口；更换 AI 的选步方法，也应能定位到决策部分。如果每次都要在几个角落各改一遍同样的判断，就需要检查职责是否散开了。&lt;/p&gt;&lt;p&gt;这里借鉴了 Martin Fowler 对 &lt;a href=&quot;https://martinfowler.com/bliki/BoundedContext.html&quot; target=&quot;_blank&quot;&gt;Bounded Context（限界上下文）&lt;/a&gt; 的介绍：模型内部的概念要一致，不同上下文之间的关系要明确。它讨论的尺度比一个小组件大，不能直接等同于“多拆几个文件”。对个人项目而言，可以先在同一个应用里落实这些约定，不必一上来就拆微服务。&lt;/p&gt;&lt;p&gt;规则调整也可能影响 AI 或界面。合理的边界应让影响路径容易判断，并不要求每个模块永远互不影响。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;先写一张职责表&lt;a href=&quot;#先写一张职责表&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;对于这个棋类例子，可以先尝试下面的划分：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;模块&lt;/th&gt;&lt;th&gt;负责什么&lt;/th&gt;&lt;th&gt;如何与其他部分协作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;规则&lt;/td&gt;&lt;td&gt;检查动作，计算落子后的局面及结束条件&lt;/td&gt;&lt;td&gt;接收局面和动作，返回处理结果&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;对局流程&lt;/td&gt;&lt;td&gt;保存当前局面，安排回合操作，管理历史&lt;/td&gt;&lt;td&gt;调用规则，把新状态交给界面&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AI 决策&lt;/td&gt;&lt;td&gt;根据局面选择候选动作&lt;/td&gt;&lt;td&gt;提交动作，仍经过规则校验&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;界面&lt;/td&gt;&lt;td&gt;显示棋盘，播放动画，接收操作&lt;/td&gt;&lt;td&gt;发出操作意图，按状态更新显示&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;这张表需要随玩法调整。它先解决一个很实际的问题：当前局面由对局流程保存，规则负责计算新局面，界面不应在动画结束时顺手改掉轮到谁。&lt;/p&gt;&lt;p&gt;AI 也一样。它可以采用不同的选步算法，但不能因为是“程序自己走的棋”，就绕过普通玩家使用的合法性检查。以后换算法时，调用方仍可以沿用约定好的动作入口。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;接口要写到失败时怎么办&lt;a href=&quot;#接口要写到失败时怎么办&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我在整理开发流程时，会把契约先行作为一个约束：先说清模块之间怎么交接，再让 AI 实现各自的内部逻辑。这里的契约可以很短，例如：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;applyMove(state, move)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;合法：返回新局面 nextState&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;非法：返回拒绝原因 reason&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;两种情况都不修改传入的 state&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这只是接口示意。除了参数名字，还要约定“合法”由谁判断、回合变化在哪里计算、调用失败时旧局面是否保留。比如规定规则模块在 &lt;code&gt;nextState&lt;/code&gt; 中计算下一回合，对局流程保存结果，就能避免两边各自推进一次。&lt;/p&gt;&lt;p&gt;如果只写“返回一个局面”，实现者很可能作出不同理解：一个模块原地修改对象，另一个却还把它当作历史快照。历史记录也需要稳定的快照或不可变数据，不能只是把同一个可变对象反复塞进数组。&lt;/p&gt;&lt;p&gt;遇到悔棋这样的新功能，还要补充流程约定：恢复到哪一步，如何处理正在执行的 AI 任务，旧任务返回后是否允许落子。这些问题适合在接口讨论时暴露出来。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;给 Agent 一张范围清楚的任务卡&lt;a href=&quot;#给-agent-一张范围清楚的任务卡&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;多开几个 Agent，并不会自动得到低耦合的代码。大家如果各自理解一遍需求、各自发明一套数据结构，最后仍得重新拼接。&lt;/p&gt;&lt;p&gt;可以把“完善棋盘逻辑”改写成下面这样的任务：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;目标：补充非法落子的处理。允许修改规则模块及其测试；沿用现有局面和动作类型。暂不调整棋盘布局或 AI 策略。验收时检查越界、占用位置、非当前回合操作；被拒绝后，局面和回合都保持不变。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;任务卡让实现者和审查者对“完成”有共同理解。如果过程中发现共享类型不够用，应先列出需要调整的接口及调用方，再统一修改，避免在某个模块里悄悄追加一套兼容逻辑。&lt;/p&gt;&lt;p&gt;Google 的 &lt;a href=&quot;https://google.github.io/eng-practices/review/developer/small-cls.html&quot; target=&quot;_blank&quot;&gt;Small CLs&lt;/a&gt; 建议也适合放在这里：把修改组织成范围集中、能够独立理解的一次改动，并带上相关测试。小步修改不只是限制行数；新增接口时，合理的调用示例也应一起出现，让审查者知道它怎么用。&lt;/p&gt;&lt;p&gt;若需要大幅移动文件或重命名模块，尽量与新增功能分开处理，否则很难看出行为到底在哪里发生了变化。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;用行为检查边界是否有效&lt;a href=&quot;#用行为检查边界是否有效&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;职责表写完，还需要实际检查。对于这个例子，我会先关注这些行为：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;非法动作被拒绝后，棋盘和当前回合都不变。&lt;/li&gt;
&lt;li&gt;合法动作只应用一次，回合不会被重复推进。&lt;/li&gt;
&lt;li&gt;AI 提交的动作也要经过规则校验。&lt;/li&gt;
&lt;li&gt;纯界面改动不会影响规则计算的结果。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些检查不能证明架构已经完美，但能帮助发现哪些约定没有被遵守。规则可以单独测试，再用集成检查验证界面、流程与规则连在一起时的行为。只测某个函数返回正确，还不足以证明点击按钮后没有重复提交。&lt;/p&gt;&lt;p&gt;边界也需要调整。如果两个模块长期互相读取内部数据，或同一条业务规则总在几处同时修改，就值得重新看职责划分。反过来，一个简单动作若要经过许多只做转发的模块，继续细拆也可能只是在增加理解成本。&lt;/p&gt;&lt;p&gt;下一次让 AI 加功能前，我会先写下：这次改哪里，依赖哪份接口，怎样检查完成。先让这几项约定在一个小功能里跑通，再根据实际修改中遇到的问题调整结构。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;参考资料&lt;a href=&quot;#参考资料&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Martin Fowler：&lt;a href=&quot;https://martinfowler.com/bliki/BoundedContext.html&quot; target=&quot;_blank&quot;&gt;Bounded Context&lt;/a&gt;。用于理解概念边界与上下文之间的关系。&lt;/li&gt;
&lt;li&gt;Google Engineering Practices：&lt;a href=&quot;https://google.github.io/eng-practices/review/developer/small-cls.html&quot; target=&quot;_blank&quot;&gt;Small CLs&lt;/a&gt;。用于理解改动范围、审查与相关测试的组织方式。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;</content:encoded></item></channel></rss>