ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

中秋「十五的月亮十七圆」?我用码道 Agent 写了个会算月亮的中秋赏月手册

中秋「十五的月亮十七圆」?我用码道 Agent 写了个会算月亮的中秋赏月手册 中秋「十五的月亮十七圆」我用码道 Agent 写了个会算月亮的中秋赏月手册今天是 2026 年 9 月 25 日中秋节。可你抬头看今晚的月亮其实不是最圆的——真正的满月在明天9 月 26 日夜里、北京时间 9 月 27 日凌晨才发生。天文上这叫「十五的月亮十七圆」是百年里数得着的稀罕年份。我没有靠查日历 App 知道这个冷知识而是让华为云码道 CodeArts 的代码智能体从一条提示词开始给我完整实现了一个能用天文算法把月亮算出来的小站——「中秋月相历」。它不靠任何接口、不联网、零依赖双击index.html就能用还能把 1900–2100 整整 201 年的中秋是哪一天、当晚月亮圆不圆、几点升起来全部本地算给你看。这篇文章记录我怎么用「人定架构与红线、AI 分块实现、本地验收为准」的方法把这个作品从零做到可交付。仓库全程公开、可运行、带测试。作品中秋月相历moonphase-almanac仓库https://atomgit.com/sdf56g99988/moonphase-almanac Public含 README MIT LICENSE运行克隆后node --test tests/domain.test.mjs跑 23 个领域测试双击index.html直接用本期完成人我与码道 Agent模型GLM-5.2一、为什么是「中秋月相历」而不是又一个抽奖/贪吃蛇中秋这个主题恰好有一个天然「硬核 有话题」的切入点月相是可以被天体力学精确计算的而「十五的月亮到底圆不圆」又是每年中秋大家都会争论的话题。把一个能当场算出「今晚月亮照亮 98.3%、其实是盈凸、明晚才满月」的工具做出来既有真实算法深度又戳中节日情绪。所以选题定为一个纯前端、零依赖、零联网的中秋赏月工具站核心是真实天文算法而非静态数据堆砌。二、作品是什么四个标签页把月亮算给你看「中秋月相历」是一个单页应用四个标签页今日月相选任意日期给出月龄、月相角、照亮比例、月相名朔/蛾眉/上弦/盈凸/望/亏凸/下弦/残月并用 Canvas逐像素渲染当晚的月亮——亮面朝东还是朝西、照到哪条晨昏线都是按真实黄经差算的不是贴图。百年中秋1900–2100 共 201 年每年中秋对应的公历日期、星期以及「十五圆 / 十六圆 / 十七圆」标注。赏月时刻输入观测纬度估算当晚月升 / 月上中天 / 月落时刻并给出「最佳赏月时段」及理由月升后约一小时月亮低、暖、大。中秋知多少科普「为什么十五的月亮有时十六圆、十七圆」。技术红线从一开始就写死零联网、零存储、零第三方依赖、零 CDN、零外链字体。整个作品就是index.html 一份 CSS 三个 JS领域层、数据表、主程序 一份测试拷走双击就能跑。先看今晚的实拍2026-09-25 中秋之夜如实判为「盈凸 · 照亮 98.3% · 月龄 13.65 天 · 月相角 165° · 亮面朝西」并明确提示满月还未到。三、需求澄清先把「算得准」的尺子立起来我没有一上来就让 Agent「写个中秋网站」而是先把验收标准立清楚尤其针对最容易翻车的「算不准」。关键约定月相一律以真实「日—月黄经差」为准采用 Meeus《Astronomical Algorithms》的日月黄经解析级数含主要摄动项精度约 0.1°–0.6° 级。线性平均模型月龄 天数 mod 朔望月在数十年跨度会漂移约 ±1 天只能用于教学演示不能用来判望。中秋历表用权威农历推排数据生成 1900–2100 共 201 条并以史实交叉验证如 2020-10-01 中秋国庆同日。所有读数统一锚定在「当日 12:00 UTC」避免本地时区跨日误差。月升时刻明确标注为「教学化简化估算」仅供科普。这一步的产出后来几乎原样成了js/domain.js顶部的算法注释。四、架构领域层纯函数 数据表 渲染层三层彻底分离这是本篇最想强调的架构决策。我把代码切成三层并让领域层完全不碰 DOM / Canvasjs/domain.js 领域层儒略日、Meeus 日月黄经、月相、月升估算、安全层纯函数可跑在 Node js/data-midautumn.js 数据层1900–2100 中秋历表201 条纯数据 js/main.js 表现层Tab 切换、主题、Canvas 月相盘像素渲染、输入绑定 tests/domain.test.mjs 测试node:test 跑领域层零依赖好处立竿见影可测试领域层是纯函数 UMD 封装浏览器挂window.MoonAlmanacNode 下require测试直接用 Node 内置node:test零测试框架依赖。可验收核心正确性用「天文硬尺子」在命令行就能验不必开浏览器。防退化渲染层随便改样式只要不碰领域层测试结果不变。js/domain.js的 UMD 头浏览器 / Node 双端通用(function(root,factory){if(typeofmoduleobjectmodule.exports){module.exportsfactory(require(./data-midautumn.js));}else{root.MoonAlmanacfactory(root.MID_AUTUMN);}})(typeofglobalThis!undefined?globalThis:this,function(MID_AUTUMN){use strict;// …… 全部纯函数最后用 deepFreeze 冻结导出});导出对象用deepFreeze递归冻结常量朔望月长度、黄赤交角、八个月相区间、参考新月锚点全部不可变——这是安全与确定性的第一道闸。五、核心算法用 Meeus 级数把月亮「算」出来整个作品的灵魂是日—月黄经差。月相角 月球视黄经 − 太阳视黄经0° 是朔、180° 是望。solarLongitude/lunarLongitude两个函数实现了 Meeus 的解析级数含主要摄动项/** 太阳视黄经度含光行差与主章动修正 */functionsolarLongitude(T){varL0280.4664636000.76983*T0.0003032*T*T;varM357.5291135999.05029*T-0.0001537*T*T;varC(1.914602-0.004817*T-0.000014*T*T)*sin(M)(0.019993-0.000101*T)*sin(2*M)0.000289*sin(3*M);varomega125.04-1934.136*T;returnnormalize(L0C-0.00569-0.00478*sin(omega));}/** 月球视黄经度含主摄动项精度约 0.1°–0.6° 级 */functionlunarLongitude(T){varLd218.3164477481267.88123421*T-0.0015786*T*T;// …… D / Ms / M / F / E 五个基本引数varlamLd6.288774*sin(M)// 主摄动项1.274027*sin(2*D-M)0.658314*sin(2*D)0.185116*E*sin(Ms)// …… 共 16 项returnnormalize(lam-0.00478*sin(omega));}/** 日月黄经差0–360°0°朔180°望 */functionphaseAngle(date){varTjulianCenturies(date);returnnormalize(lunarLongitude(T)-solarLongitude(T));}/** 照亮比例 (1 − cos(月相角)) / 2 */functionilluminatedFraction(date){varpaphaseAngle(date)*D2R;return(1-Math.cos(pa))/2;}有了真实黄经差「距满月还有几天」「是盈凸还是望」全部迎刃而解。比如判定「十五的月亮几圆」是把中秋日 12:00 UTC 的月相角换算成满月时刻再折算到北京时区UTC8的日界varphasephaseAngle(noon);// 中秋日 12:00 UTC 的月相角varoffDays(180-phase)*SYNODIC_MONTH/360;// 距满月还需几天varbjClock20offDays*24;// 中秋 12:00 UTC 北京 20:00vardayShiftMath.floor(bjClock/24);// 0十五 1十六 2十七正是这段代码把 2026 年如实判为「十七圆」——这也是文章开头那个冷知识的来源。六、最爽的部分Canvas 逐像素把月亮画出来月相盘不是贴图是逐像素按光照模型渲染的。核心思路把月面当成单位球对圆盘内每个像素求「太阳方向」与该点法向的点积得到明暗边缘做柔和过渡functiondrawMoonLayer(phaseDeg){varphiMath.PI-2*Math.PI*(phaseDeg/360);varspMath.sin(phi),cpMath.cos(phi);for(vary0;yMOON_SIZE;y){for(varx0;xMOON_SIZE;x){vardx(x-half)*inv,dy(y-half)*inv;varr2dx*dxdy*dy;if(r21)continue;varzMath.sqrt(1-r2);varlitsp*dxcp*z;// 半球面点积太阳方向→ 明暗varbr0.50.5*lit;varvbr*(1-Math.pow(r2,3));// 边缘微暗柔和过渡// 写入 RGBA暗面深蓝(8,10,30) → 亮面暖白(250,222,158)}}}再叠加两层径向渐变光晕、固定位置的环形山暗斑、暖色光泽月亮就有了立体感。因为明暗直接由phaseDeg驱动所以亮面朝东朝西、晨昏线弧度全都跟真实天文一致——今晚 165° 的盈凸亮面就是朝西向右张开。界面还内置了深色「夜」与浅色「笺」双主题一键切换。浅色下同一个月相盘依旧层次分明七、测试用「天文硬尺子」把正确性钉死这是我坚持的第二条红线Agent 说「全绿」不算数本地能复现的测试才算数。tests/domain.test.mjs共 23 个用例挑几个最能打的儒略日逐位对照精度 1e-9test(julianDay2000-01-01 12:00 UTC JD 2451545.0,(){constjdjulianDay(newDate(UTC(2000,0,1,12,0,0)));assert.ok(Math.abs(jd-2451545.0)1e-9);});中秋判望硬尺子2024、2025 中秋当晚应为「望」test(关键硬尺子2025-10-06中秋判望,(){constdtnoonUTC(2025,10,6);assert.ok(phaseName(dt).name.includes(望));assert.ok(illuminatedFraction(dt)0.98);});最狠的一条——2026 的特殊性规格初稿假设「中秋望」但权威数据显示 2026 中秋夜其实是盈凸。测试如实校准绝不放宽容差、不硬编码test(2026 真满月日 2026-09-26 判望中秋 09-25 之夜实为盈凸,(){constfullnoonUTC(2026,9,26);assert.ok(phaseName(full).name.includes(望));// 真满月日判望constmidnoonUTC(2026,9,25);assert.ok(phaseName(mid).name盈凸);// 中秋夜如实判盈凸assert.ok(illuminatedFraction(mid)0.97);});1 万次随机输入不崩安全/健壮性兜底test(随机 10000 次混合输入全部领域函数不抛错、输出有限,(){// phase∈[0,360)、fraction∈[0,1]NaN/Infinity/极区纬度都不抛错});本地实测结果ℹ tests 23 ℹ pass 23 ℹ fail 023/23 全绿且全部用 Node 内置node:test跑没有装任何一个第三方包。八、安全红线零联网、零存储、零依赖占评审 20% 的安全这个作品是「天然免疫」型——因为它根本没有攻击面零联网无任何fetch/XMLHttpRequest所有数据本地计算。零存储不用localStorage/cookie/indexedDB刷新即重置。零第三方依赖package.json里dependencies为空连测试都用 Node 内置 runner。零外链无 CDN、无外链字体、无外链图片断网可用。输入防护所有用户输入年份、纬度必经sanitizeNumber——NaN/Infinity/空值回落、越界 clampmidAutumnDate对非整数或超界年份直接throw RangeError。functionsanitizeNumber(x,min,max,fallback){if(x||xnull||xundefined)returnfallback;varn(typeofxnumber)?x:Number(x);if(!Number.isFinite(n))returnfallback;returnnmin?min:(nmax?max:n);}极端情形也处理了纬度 clamp 到 ±89.9 防tan(90°)除零极区「月亮不升不落」时moonriseApprox返回null而非抛错界面友好提示。九、运行效果「今日月相」与「双主题」的效果前面已经看过这里补另外三个标签页。百年中秋1900–2100 共 201 年历表每年标注公历日期、星期与「十五/十六/十七圆」。赏月时刻默认北京纬度 39.9°给出月升、月上中天、月落时刻与最佳赏月时段。中秋知多少科普「为什么十五的月亮有时十六圆、十七圆」。十、踩坑与收获最大的坑是「想当然」规格初稿写「中秋当晚判望」Agent 在自验时发现 2026 年根本不成立——满月在北京时间八月十七凌晨。如果当时偷懒用线性平均模型就会把今晚误判成「满月」闹笑话。用真实天文数据当尺子是这类作品的生命线。Agent 全绿 ≠ 本地验收Agent 报告「23/23、E2E 19/19」我仍在本地重新node --test跑了一遍确认并用浏览器逐页核对渲染数据月龄 13.65、照亮 98.3% 与算法自洽才算交卷。图片分析工具偶发失败Agent 想做最终视觉复核时AnalyzeImage工具超时失败了一次导致界面一度「看似卡住」。此时不能干等要用git ls-remote看远端提交、用页面快照看工具命令流判断真实进度——本次确认 commit 已推进后才继续。模型要选对人本轮我特意把码道对话模型切到GLM-5.2长链路的算法校准 编码 测试一气呵成没有来回返工。十一、建仓与复现整个开发过程在码道工作台完成建仓对话确立仓库归属与可见性开发对话绑定仓库后由 AgentGLM-5.2分块实现最终本地 HEAD 与远端 main 完全同步。仓库全程公开欢迎复现# 克隆gitclone https://atomgit.com/sdf56g99988/moonphase-almanac.gitcdmoonphase-almanac# 跑领域层测试零依赖Node ≥ 18 即可node--testtests/domain.test.mjs# 直接用浏览器打开无需任何构建/服务# 双击 index.html或python-mhttp.server8000# 然后访问 http://localhost:8000提交历史干净feat / test / docs 分离15b61f6 chore: init repository (README, MIT license, gitignore) e4a01eb feat: 领域层算法Meeus 摄动级数 中秋历表 201 条 bc95ce1 test: 领域层 23 个测试node:test零依赖 41bf396 feat: 四标签页界面 Canvas 月相盘像素渲染 53c29e2 docs: README功能/天文公式表/数据来源/运行测试十二、五维自检表维度权重本作品对应架构30%领域层纯函数 / 数据层 / 表现层三层分离UMD 双端通用deepFreeze 不可变导出代码30%Meeus 级数真实算法 逐像素 Canvas 光照渲染注释即文档feat/test/docs 提交分离安全20%零联网/零存储/零依赖/零外链sanitizeNumber 统一入口防护极区/非法输入不抛错UI/UX15%四标签页 夜/笺双主题 Canvas 月相盘 响应式赏月时段给出人话理由博文5%本篇提示词→架构→算法→代码→测试→踩坑→复现全链路结语中秋节最浪漫的事大概是「知道月亮什么时候最圆」。这个作品没有用一张月亮贴图而是把 400 多年前的开普勒式思考、浓缩进 Meeus 的级数里让浏览器现场把月亮算出来、画出来。而整个从提示词到交付的过程代码智能体GLM-5.2承担了繁重的实现与自验我只需要守住三件事定架构、立红线、做验收。这大概就是「AI 编程」最舒服的姿势——人是导演AI 是执行力最强的团队。今晚月亮 98.3% 圆明晚才是 100%。但没关系——十五的月亮十七圆也算是一种圆满。中秋快乐。
返回列表