了解我是如何管理上下文以保持 Copilot 的专注,使用 Plan 代理来明确模糊的需求,以及采用测试驱动开发实践来在用户之前发现错误。
在2025年最后一期“橡皮鸭星期四”直播节目中,我想制作一些庆祝性质的东西。一些能够体现“橡皮鸭星期四”精髓的东西:共同创作,从错误中学习,并向来自世界各地的每一位收看者致敬。
一路走来,我总结了一些使用人工智能的实用模式,你可以将它们应用到自己的项目中,无论你是开发倒计时应用还是其他完全不同的项目。从管理上下文窗口避免对话混乱,到使用Plan 代理进行需求发现,再到通过 Copilot 进行测试驱动开发来捕获极端情况。还有……为什么绘制世界地图比看起来要难得多。
从简单的开始:基本倒计时
倒计时器的概念很简单:天数倒计时到小时数,分钟倒计时到秒数。但有时候,正是这些简单的想法激发了我们最大的创造力。我想借此机会,以需求驱动的方式,运用 Copilot 开发一款倒计时应用,在新年来临之际,用绚丽的烟花营造出令人期待的氛围。
什么是规范驱动开发?
与先编写代码后编写文档的传统做法不同,规范驱动开发(Spec-drived Development)顾名思义,是从规范开始的。规范是代码行为的契约,也是工具和人工智能代理生成、测试和验证代码的权威依据。其结果是减少猜测、减少意外情况,并生成更高质量的代码。
幸运的是,软件开发是一个迭代过程,而这次直播也充分体现了这一点。虽然有些需求定义明确,但其他需求则是在直播过程中根据观众的建议实时演变而来的。像“计划”代理这样的自定义代理帮助我们弥合了这种差距,将模糊的想法转化为我可以执行的结构化计划。那么,让我们从头开始,从项目设置开始吧。
我使用 GitHub Copilot 创建了一个新的工作区,并根据一个非常具体的提示进行了设置。提示说明我们正在构建一个倒计时应用,并且我希望使用Vite、TypeScript和Tailwind CSS v4。它还列出了一些要求,包括深色主题、居中布局、带有微妙动画效果的大号粗体数字,默认目标时间为 2026 年 1 月午夜,并留有一些自定义空间。
#new
1. Create a new workspace for a New Year countdown app using Vite, TypeScript, and Tailwind CSS v4.
**Setup requirements:**
- Use the @tailwindcss/vite plugin (Tailwind v4 style)
- Dark theme by default (zinc-900 background)
- Centered layout with the countdown as the hero element
**Countdown functionality:**
Create a `countdown.ts` module with:
- A `CountdownTarget` type that has `{ name: string, date: Date }` so we can later customize what we're counting down to
- A `getTimeRemaining(target: Date)` function returning `{ days, hours, minutes, seconds, total }`
- A `formatTimeUnit(n: number)` helper that zero-pads to 2 digits
- Default target: midnight on January 1st of NEXT year (calculate dynamically from current date)
**Display:**
- Large, bold countdown digits (use tabular-nums for stable width)
- Labels under each unit (Days, Hours, Minutes, Seconds)
- Subtle animation when digits change (CSS transition)
- Below the countdown, show: "until [target.name]" (e.g., "until 2026")
**Architecture:**
- `src/countdown.ts` - pure logic, no DOM
- `src/main.ts` - sets up the interval and updates the DOM
- Use `requestAnimationFrame` or `setInterval` at 1 second intervals
- Export types so they're reusable
Keep it simple and clean—this is the foundation we'll build themes on top of.
我最喜欢“生成新工作区”功能的一点是,Copilot 会自动为我生成自定义指令文件,自动捕捉我的需求,包括倒计时应用、Vite、TypeScript 和深色主题。所有这些都在编写任何一行代码之前就记录下来了。

几分钟之内,我就做出了一个能用的倒计时。天、小时、分钟、秒,滴答滴答地倒数着,直到2026年。虽然它能用,但视觉效果却不怎么样。说实话,我最初的设计稿里并没有指定任何设计或主题偏好。所以,是时候迭代一下,让它更有趣了。
社区的建议指引了我们的方向
直播期间,观众来自印度、尼日利亚、意大利、美国(还有很多其他国家!);世界各地的开发者齐聚一堂,共同学习。聊天室里有人提出了一个建议,改变了我们接下来的计划:时区问题怎么办?
这并非我预期在直播期间需要完成的任务,所以我并没有清晰的方案。或许可以做一个可以旋转的地球仪来选择时区。或许可以做一个以时间旅行为主题的世界地图。总之,有很多不确定的选项。我的需求很模糊,所以我才求助于方案规划师。
计划代理人:我之前没想到要问的问题
最近我开始更有意识地使用计划代理,尤其是在我觉得需求不够明确的时候。计划代理不会根据我最初的提示直接生成计划,而是会提出一些澄清性的问题,这些问题可以帮助我发现一些可能没有考虑到的特殊情况。

我大致描述了一下我的想法:交互式时区选择器、时间旅行主题、时区切换动画,或许还可以加一张世界地图。项目专员提出的问题让我陷入了沉思:
| 问题 | 为什么这很重要 |
|---|---|
| 圆形表盘应该作为主表盘,世界地图作为副表盘,还是反过来? | 我还没决定视觉层级结构。 |
| 移动端会发生什么:下拉菜单还是触控友好的滚动条? | 我最初只考虑了桌面版的实现。移动端功能可能是未来的发展方向。 |
| 当某个时区过了午夜时,显示“已经开始庆祝”的彩带,或者显示一个计时器,表明从午夜至今已经过了多久? | 我想要的是庆祝活动,而不是倒计时。我之前没把要求说清楚。 |
| 旋转旋钮时会有轻微的音频反馈,还是只有视觉反馈? | 将音频功能引入应用程序属于范围扩大,但这可能是未来的需求。 |
这就是以这种方式与人工智能合作的妙处所在。规划代理会引导你思考,可能会提出澄清问题并提供选项 A 或 B。但当你仔细思考后,你会发现答案介于两者之间。
例如,在我第二次修改需求时,方案中询问烟花应该持续播放、只燃放一次还是循环播放。我回复说,这可能涉及到性能问题,我们应该选择折中的方案。我们还请直播观众投票决定组件应该以拨盘还是地图的形式呈现。地图胜出,所以我们最终决定采用世界地图作为主要选择器,并包含八个特色地点。
上下文窗口管理:只保留你需要的内容
在实施之前,我特意开启了一个新的聊天会话。
我们之前对话的背景信息(工作区创建、基本倒计时逻辑)已经不再需要了。任何可能有用的背景信息现在都已包含在我们的自定义指令文件中。在使用人工智能工具时,这个背景信息窗口至关重要。引入无关的历史信息会使对话变得混乱,分散注意力。因此,我清理了它,只保留了重要的内容:新的需求、计划代理的输出(我已要求 Copilot 将其写入单独的 Markdown 文件)以及关于时区的新关注点。
我还复用了另一个个人项目中的一些自定义指令文件、自定义代理和提示文件,以帮助 Copilot 朝着正确的方向发展,并集成用于相关任务的专用代理。这其中包括一个 UI 性能专家代理。
你知道吗? GitHub Copilot 的自定义代理功能允许你为不同的开发任务创建专门的角色。我在直播中构建的 UI 性能专家代理就是一个例子。你可以创建用于安全审查、架构规划或任何特定角色工作流程的代理。awesome -copilot 代码库中有很多示例。
实现方式:模块化、测试驱动,以及映射
计划代理完成工作后,我切换到 UI 性能专家代理,并要求它审查该计划,根据其专业知识提出更深入的实施细节。

上下文很重要,所以我没有另起炉灶,而是继续之前的对话。客服人员随后提供了一系列详细的考虑因素:
- 动画帧时间预算
- 地图SVG尺寸优化策略
- 庆典粒子限制(DOM元素问题)和清理注意事项
- 动画属性建议(仅限变换/不透明度)
- 减少运动支撑
看起来不错,但我又加了一些要求。我要求定制代理将实现模块化,先根据预期行为编写测试,等测试失败后再编写实现。
没错:使用 Copilot 进行测试驱动开发。
TDD周期
Copilot 为时区实用程序、城市状态管理和倒计时逻辑创建了测试文件。所有失败的测试都显示为红色。很好(这是我们少数希望看到测试失败的情况之一)!

然后它实施了:
- 使用Intl.DateTimeFormat API 的时区实用程序
- 以纽约、伦敦、东京、悉尼等城市为特色的城市州
- 为选定的时区保留本地存储
- 应用状态管理
利用工具权限,自定义代理还在终端执行了测试。有两个测试用例失败:一个是判断跨年庆祝活动是否正确触发的逻辑。测试预期庆祝活动会在午夜处理,另一个是庆祝活动开始至今的持续时间。

由于 Copilot 可以访问输出,自定义代理捕获了测试失败,调整了时区实现,测试通过了。
思考:这正是测试驱动开发 (TDD) 和代码质量的重要性所在。就像我们开发者一样,人工智能辅助开发也会出错。测试可以帮助我们在用户发现问题之前就找到 bug。考虑到这是应用的核心功能,如果在 12 月 31 日才发现年份滚动这个特殊情况,那将非常尴尬!
但有些漏洞反而成了特色。我发现一个漏洞太有趣了,一时半会儿没忍住修复。咱们来聊聊世界地图吧。
或许是世界地图?
我打开应用后,倒计时功能正常,时区选择器也能用,计算结果也正确,从纽约切换到东京后,时差也显示正确。
但是世界地图呢?它的渲染效果不太理想。屏幕上显示的与其说是地理图像,不如说是抽象艺术。不过,这真的让我直播的时候笑出了声。

想法:我当时雄心勃勃地指定要添加一张世界地图,却没有提供足够的背景信息。既没有SVG素材,也没有引用任何现有的地图库。仅仅是“添加一张迷你世界地图”。这提醒我们,人工智能也会出错。
我本来可以修复它吗?当然可以。但当时直播已经进行了一个多小时,我们还有更多功能要开发。所以我放弃了。这张地图完美地诠释了迭代开发,事情不可能总是一次成功。(看得出来我们现在是在直播中开发东西吗?)
烟花:午夜将至,令人期待
倒计时本身就很实用,但烟花可以增添庆祝气氛,并带来一些视觉上的震撼(明白我的意思吗?)。
我切换回计划代理并创建了一个新的聊天线程(同样,通过上下文窗口管理,提示 Copilot 构建计划):
- 使用Fireworks.js实现特效。
- 根据剩余时间设置烟花表演行为
- 如果计时器剩余时间超过 24 小时,则不要燃放烟花,只显示环境星空。
- 如果计时器剩余时间在 24 到 12 小时之间,则每隔 30 秒燃放一次烟花。
- 在距离活动结束还有1小时10分钟时,烟花的强度应该会逐渐增强。
- 最后,在最后10秒钟,我们应该燃放连续不断的烟花,以达到最大的庆祝效果。
我还要求在屏幕底部添加城市天际线轮廓、深色夜空渐变效果以及主题控制器。此外,还有一个关键的测试要求:“添加一个查询参数,以便我可以指定距离午夜还有多少分钟,作为手动测试的覆盖设置。” 虽然我很喜欢和我们的社区一起直播,但我不太确定大家是否都愿意等到2026年才能看到结果!
方案代理人要求进一步说明星星的显示方式(是设置为 CSS 样式,还是设置为低强度烟花效果),以及一些性能方面的考虑。它还询问了切换按钮的位置,这让我措手不及。我不记得之前要求过添加切换按钮,可能是在方案的某个版本中遗漏了。
仔细审阅方案后,我发现我最初要求的辅助功能动画切换功能已经实现。这就是我喜欢这个辅助功能的原因。它就像一个人工智能橡皮鸭,能够理解你的对话上下文,并检查这些需求是否仍然合理。

我和 Copilot 重新协商需求后,便采用了我们熟悉的测试驱动开发方法。最初,由于缺少 JSDOM 环境配置,一个测试失败了。Copilot 发现了这个问题,找到了配置错误的测试环境,并进行了修复。之后,所有测试都顺利通过了。
我们现在有了一个应用程序,它具有不同强度级别的烟花、使用 CSS 的动画星空、城市天际线、减少的运动支持以及查询参数覆盖。
测试强度级别
我?minutesToMidnight=1在网址中添加了内容。烟花以中等强度绽放,随着天空中色彩和粒子数量的增加,气氛也逐渐升温。午夜时分,新年快乐字样出现,庆祝活动更加热烈。强度曲线恰到好处,前奏营造了期待感,结尾更是精彩纷呈。

揭晓:我那天早上建造的东西
但我并没有就此止步。在整个直播过程中,我一直在暗示我当天早上还做了一个倒计时应用,主题非常契合。我们的观众猜测是另一个烟花倒计时应用、一个彩带计时器,甚至还有一个“诱导式井字棋”(公平地说,我们以前确实做过)。
但作为 GitHub 直播,我们只有一种方法可以完成它:那就是制作一个以贡献图为主题的倒计时!
倒计时位于屏幕中央,前方是一个动态的贡献图表。每个方格都闪烁着绿色的贡献值,如同波浪般在网格上出现和消失。正如烟花主题曲所描绘的那样,随着倒计时接近零,越来越多的方格被点亮,气氛也愈发热烈。

这场直播是一场庆祝活动。它让我们的社区跨越时区聚集在一起,让我们在世界各地的同一角落里共同筹备、倒计时,迎接同一时刻的到来。
直播期间,有人问到哪些编程语言最容易找到工作。我的回答和我做这个项目的理念一样:找到让你快乐的事情,合适的工具和语言自然就会到位。我开发这个 GitHub 倒计时主题,是因为它让我快乐。因为我想做一些“GitHub 出品”的东西,也因为我喜欢创造视觉体验。
自那次直播之后,我一直致力于将这两个项目整合到一个统一的开源倒计时应用 Timestamp 中。它拥有一个集中式的主题编排器,允许开发者接入通用架构并扩展新主题。每个倒计时都是一个 URL,因此可以轻松共享,并且有多种倒计时模式可供选择(本地时间、绝对时间点和计时器)。
您可以查看在线应用并查看代码库。欢迎您查看代码仓库、点赞、fork,甚至贡献新的主题。
我希望这能激励你去做那个一直搁置的项目,花些时间做些能给你带来快乐的事情。
我们学到了什么?
- 上下文窗口管理是一项技能。当不再需要旧的上下文时,开启新的聊天会话。保持对话聚焦。这关乎上下文工程,而不仅仅是提示工程。
- 计划代理会提出一些你可能已经忘记的问题。当需求模糊不清时,可以使用它。它可以通过澄清问题来揭示特殊情况。有时,A 或 B 的答案可能“介于两者之间”。
- 定制代理是专业的助手。我的 UI 性能专家精通帧预算、动画属性和可访问性。他负责提供实现细节,而计划代理则负责提出澄清问题,以确定范围。专业化至关重要。
- 使用 Copilot 进行 TDD 是行之有效的。先编写测试,让它们失败,然后再实现通过测试的功能。就像我们开发者一样,AI 辅助工具也会产生 bug。我们需要使用与以往相同的质量检查方法(构建、代码检查和测试),以便在用户发现问题之前将其捕获。
- 事情并非总能一次成功,这没关系。世界地图的渲染效果并不理想,我也没有急于求成,直到我对倒计时应用进行大规模重构和重建后才解决了这个问题。真正的开发过程意味着展现混乱的中间阶段,而不仅仅是最终的完美结果。我们从意料之外的结果中学习到的东西,与从成功中学习到的东西一样多。
- 目标要远大,实现要循序渐进。我们从基本的倒计时,到时区,再到绚丽的烟火效果,最终发展到以贡献图为主题的独立倒计时。罗马不是一天建成的,你也不需要第一天就把所有东西都做出来。
2026年你会建造什么?欢迎收看英国时间上午10:30和美国东部时间下午2:00的下一期“橡皮鸭星期四”直播,让我们一起建造一些能给我们带来快乐,但还没能排进“总有一天”愿望清单前列的东西!