极点宏观|Financial Cloud Cloud · 构建文章
使用 Kiro 构建:他加禄语 学习 App 的深入开发流程工作坊
受众: 想要掌握端到端(End-to-End)AI 辅助工程流程的专业开发者
时长: 2 小时
核心 AWS AI 服务: Kiro
项目产出: 一个确定性(Deterministic)的静态网站产生器,能将句子库扩展为他加禄语学习卡、渲染 HTML、验证输出并打包发行成品(Artifact)。
仅限教育工程研讨会。这是一项软件架构练习,而非流程发布建议。
工作坊概述
本工作坊将引导开发者建立一个完整的「文件优先(File-first)」他加禄语学习网站构建流水线。参与者将使用 Kiro 来定义引导原则、建立文章数据模型、扩展句子库、生成辅助内容、渲染静态 HTML、验证输出并打包发行。核心重点在于可重复的工程流程,通过结构化数据与检查机制,确保生成的学习材料具备可预测性、可追溯性,并方便后续审查人员进行核对。
工作坊目标
本工作坊聚焦于开发流程。开发者将构建一个文件优先的内容流水线:包含文章中介数据(Metadata)、句子库、场景扩展、语法辅助、发音辅助、HTML 渲染、验证检查及发行打包。Kiro 将在流程中负责引导、规格制定、任务规划、实施支持、测试生成、钩子(Hooks)、文件编写以及发行审查。
2 小时议程
| 时间 | 模块 | 开发者成果 |
|---|---|---|
| 0–10 分钟 | Kiro 工作空间 | 准备好引导(Steering)与规格(Spec)文件夹 |
| 10–25 分钟 | 内容契约 | 设计文章与句子库的纲要(Schema) |
| 25–45 分钟 | 确定性扩展 | 将 10 个基础片语扩展为 40 张卡片 |
| 45–65 分钟 | 辅助函数 | 加入语法与发音辅助工具 |
| 65–90 分钟 | 静态渲染 | 从结构化数据生成 HTML 页面 |
| 90–105 分钟 | 验证机制 | 加入卡片数量、摘要与禁用字词参照检查 |
| 105–115 分钟 | 打包发行 | 建立 Zip 发行成品 |
| 115–120 分钟 | 成果审查 | 产出 Kiro 辅助的发行检查清单 |
步骤 1 — 建立文件优先产生器的 Kiro 引导原则
Kiro 提示词示例
建立檔案優先靜態網站產生器的引導文件。
此產品是為 AWS 馬尼拉社群日(AWS Manila Community Day)設計的他加祿語學習 App。
使用 Python 進行確定性生成、HTML 輸出、CSS 與 zip 打包。
信任來源為結構化資料,而非人工撰寫的頁面。
每個生成的頁面在發行前都必須經過驗證。
系统设计决策
● 文件优先架构: 此应用程序在工作坊期间不需要运行时(Runtime)后端。技术目标是展示如何将精简的原始数据转化为大型且一致的学习网站。文件优先的生成方式让每个构建步骤都清晰可见、易于检查且便于解说。
● 确定性生成优于随机输出: AI 可以协助草稿和代码编写,但发布的构建版本必须是确定性的。给定相同的句子库与场景,产生器每次都应产出完全相同的卡片与文件。这能让审查、测试与调试变得切实可行。
● 将验证视为产品功能: 生成的网站可能外表看起来很完整,却隐藏了遗漏卡片或行动版 CSS 跑版的问题。构建验证是系统设计的核心,而非事后补救。发行成品中必须包含已通过必要条件检查的辨识凭证。
代码示例 — .kiro/steering/static-generator.md
# 靜態產生器引導原則
本專案負責從結構化的 Python 資料生成他加祿語學習網站。
當渲染器可以自動生成頁面時,切勿手動撰寫最終頁面。
## 規則
- 文章中介資料(Metadata)控制導覽與檔案名稱。
- 句子庫為精簡的原始資料。
- 情境擴展以確定性的方式建立重複的學習卡片。
- HTML 輸出必須對生成的文字進行轉義(Escape)。
- 每篇文章必須恰好包含 40 張卡片。
- 發行檢查必須在生成後、打包前執行。
- 生成的語言內容在正式上線前,需要經過母語人士審查。
代码说明
● 商业逻辑: 引导文件定义了产生器的哲学:结构化数据、确定性输出与发行检查。
● 程序逻辑: Kiro 在生成 Python 函数、验证检查与发行任务时,会遵循这份 Markdown 指引。
● 预期结果: Kiro 的建议会倾向于使用可重复使用的渲染器与检查机制,而非手动复制 HTML。
步骤 2 — 生成流水线的 Kiro 规格书
Kiro 提示词示例
建立一個名為 tagalog-static-generator 的規格書。
功能:從中介資料與句子庫生成文章頁面。
包含需求、設計、資料流與實作任務。
驗收標準:每篇文章有 40 張卡片、HTML 內容需轉義、具備行動版 CSS,且發行 zip 包需包含生成的頁面。
系统设计决策
● 规格书描绘的是流水线,而非只有 UI: 核心的工程价值在于从原始数据到发行成品的转换流程。规格书应涵盖数据、扩展、渲染、验证与打包,好让开发者理解整个系统。
● 验收标准转化为发行检查: 「恰好 40 张卡片」与「具备行动版 CSS」是客观的指标。它们可以被计数与测试,这让发行过程透明且对工作坊友善。
● 可追溯的任务: Kiro 任务将流水线拆解为可管理的实施单元。专业开发者可以在实际团队中独立指派、审查或自动化每个任务。
代码示例 — .kiro/specs/tagalog-static-generator/design.md
# 設計
## 資料流
1. 文章中介資料定義頁面識別、Slug(網址別名)、標題、分類與摘要。
2. 句子庫定義精簡的文句元組(Tuple)。
3. 情境陣列將每個元組確定性地擴展為多張學習卡片。
4. 輔助函式附加語法說明、範例與發音。
5. 渲染器建立轉義後的靜態 HTML。
6. 驗證器檢查卡片數量、行動版 CSS 標記、摘要字數以及禁用的內部參照。
7. 打包器寫入 zip 發行成品。
## 核心模組
- `content.py`: 原始文章中介資料與句子庫
- `expand.py`: 確定性卡片擴展
- `helpers.py`: 語法、範例與發音輔助
- `render.py`: HTML 渲染
- `validate.py`: 發行檢查
- `build.py`: 編排生成與打包流程
代码说明
● 商业逻辑: 此设计展示了如何将文化学习目标转化为可重复的构建系统。
● 程序逻辑: 每个模块各司其职(单一职责原则),使 Kiro 生成的实施代码更易于审查。
● 预期结果: 开发者可以分步实施产生器,并在每个阶段进行验证。
步骤 3 — 建立文章中介数据与句子库模型
Kiro 提示词示例
建立靜態他加祿語學習網站的 Python 原始資料。
文章中介資料需包含 id、slug、title、category 以及 20 個字左右的英文摘要。
建立一個精簡的句子庫,包含英文、自然他加祿語、禮貌他加祿語、親切菲律賓英語(Taglish)、趣味菲律賓英語以及語氣。
系统设计决策
● 中介数据作为路由契约: 文章中介数据控制了文件名称、导览、页面标题与摘要。将中介数据视为契约可防止页面产生偏差,并让网站易于重新生成。
● 精简的元组原始数据: 句子库只存储经过审查的最小片语单元。产生器围绕稳定的基础句子扩展场景,而不是为每张卡片凭空捏造全新的输出。
● 原始数据中的语气变体: 每个基础片语都包含自然、礼貌、亲切与趣味版本。这确保了渲染器在显示侧边对比时,不需靠猜测来呈现语气差异。
代码示例 — content.py
ARTICLES = [
{
"id": 1,
"slug": "community-greetings-introductions",
"title": "Community Day: Greetings and Respectful Introductions",
"category": "Community Day",
"summary": "Practice polite greetings names beginner phrases and warm introductions for meeting speakers volunteers students and builders in Manila with confidence today"
}
]
SENTENCE_BANKS = {
1: [
(
"Hello, I am learning Tagalog.",
"Kumusta, nag-aaral ako ng Tagalog.",
"Kumusta po, nag-aaral po ako ng Tagalog.",
"Hello po, learning Tagalog ako.",
"Kumusta, learning Tagalog na ako, all right.",
"friendly beginner introduction"
),
(
"May I ask a question?",
"Puwede ba akong magtanong?",
"Puwede po ba akong magtanong?",
"Can I ask po?",
"Question time na ako, all right?",
"polite workshop request"
)
]
}
CONTEXTS = [
("at morning registration", "sa morning registration"),
("before a workshop starts", "bago magsimula ang workshop"),
("while meeting a volunteer", "habang may nakikilalang volunteer"),
("after a session", "pagkatapos ng session")
]
代码说明
● 商业逻辑: 这些数据定义了网站的教学内容以及页面的识别方式。
● 程序逻辑: ARTICLES 存储页面中介数据。SENTENCE_BANKS 将文章 ID 对应到片语元组。CONTEXTS 提供确定性扩展时所需的场景场景。
● 预期结果: 其他模块可以生成页面与卡片,而不需要在渲染函数中写死(Hardcode)内容。
步骤 4 — 将基础片语扩展为卡片
Kiro 提示词示例
建立一個 Python 函式,將每個基礎片語擴展為結合情境的卡片。
每張卡片必須包含:編號、背景說明、英文、自然、禮貌、親切、趣味、語氣與情境欄位。
藉由循環情境並裁切溢出欄位,確保每篇文章正好返回 40 張卡片。
系统设计决策
● 受控的扩展: 产生器扩展的是「场景」而非「含意」。保持基础句子的稳定有助于学习者在看到不同的活动场景时,能牢记核心句型。
● 精确的数量确保页面可预测: 文章契约规定每个页面有 40 张卡片。精确的数量让版面配置、审查工作量以及发行验证变得完全可预测。
● 构建版本中不包含随机性: 随机生成会使审查变得困难。确定性的循环确保了任何错误回报都能基于相同的输入数据完全重现。
代码示例 — expand.py
from itertools import cycle
from content import CONTEXTS
def expand_cards(base_sentences, article_id, target_count=40):
cards = []
context_cycle = cycle(CONTEXTS)
while len(cards) < target_count:
for sentence in base_sentences:
if len(cards) >= target_count:
break
english, natural, polite, friendly, playful, tone = sentence
context_en, context_tl = next(context_cycle)
cards.append({
"num": len(cards) + 1,
"background": f"Use this sentence {context_en}. It supports respectful event communication.",
"english": english,
"natural": natural,
"polite": polite,
"friendly": friendly,
"playful": playful,
"tone": tone,
"context_en": context_en,
"context_tl": context_tl,
"article_id": article_id
})
return cards
代码说明
● 商业逻辑: 将小型的句子库转化为完整的文章卡片集,同时完整保留经审查后的句型文字。
● 程序逻辑: 该函数会循环场景与基础句子,直到达到 target_count。每张卡片都会获得确定性的编号。
● 预期结果: 调用 expand_cards(SENTENCE_BANKS[1], 1) 将准确返回包含 40 个卡片字典的列表。
步骤 5 — 加入语法与发音辅助工具
Kiro 提示词示例
建立語法拆解與發音指引的輔助函式。
如果英文提到 thank,解釋 Salamat 與 po。
如果英文詢問 where,解釋 Saan 與 ang。
如果英文詢問 permission,解釋 Puwede、ba 與 magtanong。
返回適合初學者的簡短說明。
系统设计决策
● 基于规则的扩展(Augmentation): 语法备注应该保持一致且可审查。简单的规则比不透明的运行时生成更容易检查。
● 初学者优先的说明: 开发者应避免塞入过多学术性的语法。辅助工具应返回能融入行动版卡片的短小说明。
● 辅助工具将数据富集(Enrichment)与渲染分离: 渲染器只负责显示内容,不应决定语法逻辑。辅助函数能让富集逻辑保持可测试性与可重复使用性。
代码示例 — helpers.py
def grammar_breakdown(card):
english = card["english"].lower()
natural = card["natural"].lower()
if "thank" in english or "salamat" in natural:
return [
("Salamat", "thank you"),
("po", "politeness marker"),
("sa", "for, in, at, or to depending on context")
]
if "where" in english or natural.startswith("saan"):
return [
("Saan", "where"),
("po", "polite marker for respectful questions"),
("ang", "focus marker before the place or thing")
]
if "may i" in english or "question" in english:
return [
("Puwede", "may or can"),
("ba", "question marker"),
("magtanong", "to ask")
]
return [
("po", "polite marker used for respect"),
("ako", "I or me"),
("kayo", "polite or plural you")
]
def pronunciation_guide(tagalog):
if tagalog.startswith("Kumusta"):
return "koo-MOOS-tah. Keep the greeting warm and clear."
if tagalog.startswith("Puwede"):
return "PWEH-deh poh bah AH-kong mag-tah-NONG. Make the question gentle."
if tagalog.startswith("Saan"):
return "SAH-ahn poh. Keep the question short and polite."
return "Read vowels clearly: a as ah, e as eh, i as ee, o as oh, u as oo."
代码说明
● 商业逻辑: 辅助工具提供了超越单纯翻译的教学价值。
● 程序逻辑: grammar_breakdown 根据英文与他加禄语文本挑选初学者备注。pronunciation_guide 则返回特定片语的指引或默认方案。
● 预期结果: 每个生成的卡片都能显示语法与发音,而不必为每张卡片手动编写这些内容。
步骤 6 — 安全地渲染静态 HTML
Kiro 提示词示例
為文章頁面建立一個 Python HTML 渲染器。
對所有生成的文字進行轉義(Escape)。
渲染標題、摘要、分類、40 張句子卡片、語法拆解、發音指引以及行動版 CSS。
系统设计决策
● HTML 转义是强制性的: 生成的内容可能包含标点符号、单引号或非预期字元。转义能保护页面结构并防止意外的标记法注入(Markup Injection)。
● 每篇文章共用同一个渲染器: 单一渲染器可防止版面产生偏差。如果卡片设计需要变更,开发者只需更新一个函数并重新生成网站即可。
● 为了工作坊的简洁性而内嵌 CSS: 对大型网站来说,外部 CSS 较为乾净;但内嵌小型样式区块能让工作坊的产出物保持便携且易于检查。
代码示例 — render.py
import html
from helpers import grammar_breakdown, pronunciation_guide
def render_card(card):
grammar_items = "".join(
f"<li><strong>{html.escape(term)}:</strong> {html.escape(meaning)}</li>"
for term, meaning in grammar_breakdown(card)
)
return f"""
<article class="sentence-card">
<h2>Sentence {card['num']}</h2>
<p><strong>Background:</strong> {html.escape(card['background'])}</p>
<p><strong>English:</strong> {html.escape(card['english'])}</p>
<p><strong>Natural Tagalog:</strong> <span lang="tl">{html.escape(card['natural'])}</span></p>
<p><strong>Polite Tagalog:</strong> <span lang="tl">{html.escape(card['polite'])}</span></p>
<p><strong>Friendly Filipino-English:</strong> {html.escape(card['friendly'])}</p>
<p><strong>Playful Filipino-English:</strong> {html.escape(card['playful'])} <span class="badge">Informal</span></p>
<p><strong>Tone:</strong> {html.escape(card['tone'])}</p>
<h3>Grammar breakdown</h3>
<ul>{grammar_items}</ul>
<h3>Pronunciation</h3>
<p>{html.escape(pronunciation_guide(card['polite']))}</p>
</article>
"""
def render_article(article, cards):
body = "\n".join(render_card(card) for card in cards)
return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{html.escape(article['title'])}</title>
<style>
body {{ font-family: system-ui, sans-serif; margin: 0; padding: 1rem; line-height: 1.6; }}
main {{ max-width: 960px; margin: auto; }}
.sentence-card {{ border: 1px solid #d0d7de; border-radius: 16px; padding: 1rem; margin: 1rem 0; }}
.badge {{ background: #fff3cd; color: #7a4d00; padding: .15rem .5rem; border-radius: 999px; font-size: .75rem; }}
@media (max-width: 760px) {{ body {{ padding: .75rem; }} .sentence-card {{ padding: .85rem; }} }}
</style>
</head>
<body>
<main>
<p>{html.escape(article['category'])}</p>
<h1>{html.escape(article['title'])}</h1>
<p>{html.escape(article['summary'])}</p>
<p><strong>Review note:</strong> Generated language content requires native-speaker review before production use.</p>
{body}
</main>
</body>
</html>"""
代码说明
● 商业逻辑: 渲染器将结构化的学习卡片转换为可在浏览器中阅读的课程页面。
● 程序逻辑: html.escape 保护所有动态文字。render_card 处理卡片的标记,而 render_article 则将卡片包装进完整的 HTML 文件中。
● 预期结果: 生成的文章页面会包含精确传入渲染器的卡片,并在行动装置上保持良好的可读性。
步骤 7 — 验证并打包发行成品
Kiro 提示词示例
建立 Python 建置程式碼,用來生成文章 HTML、驗證每個頁面、列印 JSON 檢查結果並寫入 zip 壓縮包。
檢查項:每個頁面有 40 個 sentence-card 元素、行動版 CSS 存在、摘要剛好 20 個單字,且不含禁用的內部參照。
系统设计决策
● 以代码进行构建编排: 专业的工作坊应该以可重复执行的构建流程结束,而非手动复制示范档。build.py 以可执行代码的形式记录了发行流程。
● 在渲染后进行验证: 检查是针对生成的 HTML 进行,因为这才是学习者最终开启的文件。仅验证原始数据可能会漏掉渲染器本身的缺陷。
● 以 Zip 封装以便分享: Zip 文件在社区工作坊结束后非常便于发发。打包确切生成的文件,能避免对于哪些文件该被部署或审查产生模糊空间。
代码示例 — build.py
import json
import re
import zipfile
from pathlib import Path
from content import ARTICLES, SENTENCE_BANKS
from expand import expand_cards
from render import render_article
OUT_DIR = Path("dist")
ZIP_NAME = "tagalog-learning-static-site.zip"
def word_count(text):
return len(re.findall(r"\b\w+\b", text))
def validate_page(path, article):
text = path.read_text(encoding="utf-8")
return {
"file": path.name,
"sentence_cards": text.count('class="sentence-card"'),
"has_mobile_css": "@media (max-width: 760px)" in text,
"summary_words": word_count(article["summary"]),
"has_forbidden_internal_reference": bool(re.search(r"internal prompt|private instruction", text, re.I))
}
def build():
OUT_DIR.mkdir(exist_ok=True)
checks = []
for article in ARTICLES:
cards = expand_cards(SENTENCE_BANKS[article["id"]], article["id"])
html = render_article(article, cards)
output_path = OUT_DIR / f"article-{article['id']}-{article['slug']}.html"
output_path.write_text(html, encoding="utf-8")
checks.append(validate_page(output_path, article))
with zipfile.ZipFile(ZIP_NAME, "w", zipfile.ZIP_DEFLATED) as package:
for html_file in sorted(OUT_DIR.glob("*.html")):
package.write(html_file, arcname=html_file.name)
print(json.dumps(checks, indent=2))
failures = [
check for check in checks
if check["sentence_cards"] != 40
or not check["has_mobile_css"]
or check["summary_words"] != 20
or check["has_forbidden_internal_reference"]
]
if failures:
raise SystemExit("Release validation failed")
if __name__ == "__main__":
build()
代码说明
● 商业逻辑: 构建脚本建立了一个准备好发行的静态网站,并证明了该网站符合预期的学习架构契约。
● 程序逻辑: 该脚本扩展卡片、渲染 HTML、验证生成的页面、写入 zip 压缩包、输出 JSON 检查结果,并在违反任何规则时中断构建。
● 预期结果: 执行 python build.py 会建立 dist/*.html、生成 tagalog-learning-static-site.zip、输出检查报告,且只有在完全符合验证时才会成功退出。
步骤 8 — 加入 Kiro 发行钩子(Hook)与最终审查
Kiro 提示词示例
建立一個用於發行維護的 Kiro 勾點。
當 build.py 或 content.py 變更時,自動執行 python build.py,摘要驗證輸出,並針對失敗的卡片數量、行動版 CSS、摘要字數或禁用參照提供修復建議。
系统设计决策
● 让自动化贴近变更发生的核心: 构建验证应在原始内容或构建逻辑发生变更时立即触发,而非仅在发行前的最后一刻。Kiro 钩子能让开发者在第一时间获得反馈。
● 除了回报失败,更要解释原因: 当工具能解释发行检查失败的原因并给出具体的最小修复建议时,开发者能学得更快。这在参与者可能初次接触 Kiro 或此程序库的工作坊中尤为实用。
● 发行检查清单作为活文件: 钩子成为了「定义完成(Definition of Done)」的实时文件,为未来的项目贡献者留下了团队期望的标准。
Kiro 钩子示例 — .kiro/hooks/release-validation.md
# 勾點:發行驗證
觸發條件:當 `build.py`、`content.py`、`expand.py`、`helpers.py` 或 `render.py` 變更時。
執行動作:
1. 執行 `python build.py`。
2. 讀取 JSON 驗證輸出。
3. 若某個頁面的卡片數量不等於 40,指出是哪篇文章失敗。
4. 若遺漏行動版 CSS,提示在 `render.py` 中恢復媒體查詢(Media Query)。
5. 若摘要字數不等於 20,提示修正後的 20 字摘要。
6. 若出現禁用的內部參照,指出受影響的生成檔案並提示刪除該字詞。
钩子说明
● 商业逻辑: 该钩子能防止生成的学习网站偏离发行标准。
● 程序逻辑: 它会检测产生器文件的变更、执行构建、解析 JSON 检查结果并给出精确的修复指引。
● 预期结果: 开发者能在分享静态网站前,获得实时的发行质量反馈。
完成度检查清单
● [ ] Kiro 引导原则文件已详述文件优先产生器的规则。
● [ ] Kiro 规格书已描述数据流与各项任务。
● [ ] 文章中介数据与句子库已建立完成。
● [ ] 扩展函数能精确返回 40 张卡片。
● [ ] 语法与发音辅助工具正常运作。
● [ ] 渲染器已对生成文字进行转义。
● [ ] 构建脚本能正确验证生成的 HTML。
● [ ] Zip 发行压缩包已顺利建立。
● [ ] Kiro 发行钩子指引已就绪。
工作坊后的选配 AWS 延伸扩展
● 将 Zip 压缩包内容上传至 Amazon S3 静态网站代管。
● 在静态网站前方架设 Amazon CloudFront。
● 将原始 JSON 存储于已启用版本控制的 S3 存储桶中。
● 在 CI/CD 流水线中使用 Kiro CLI,以便在发出 Pull Request 时自动执行产生器检查。
额外实施开发者实验室(Labs)
这些实验室是 工作坊 3 — 深度开发流程 所独有的。它们延伸了 Python 静态网站产生器,加入了确定性构建作业、发行履历(Provenance)、发行信息清单(Manifest)、审查者软件包包(Reviewer Bundles)以及流水线测试。焦点在于产生器工程,而非前端 React 应用的行为。
实施实验室 A — 加入构建信息清单以实现发行履历
开发者行动
● 请求 Kiro 建立发行信息清单的格式。
● 在每次构建后生成 dist/manifest.json。
● 内容需包含文章总数、生成的文件、卡片计数、构建时间戳记与验证摘要。
● 加入一个验证器检查,若信息清单遗漏预期文件则判定构建失败。
Kiro 提示词示例
在 Python 靜態產生器中加入建置資訊清單。
在渲染頁面後,將生成的檔案名稱、文章 ID、卡片數量、驗證檢查結果以及建置時間戳記寫入 dist/manifest.json。
除了時間戳記外,資訊清單的內容必須是確定性的。
加入一項驗證,確保每個生成的 HTML 檔案都列在資訊清單中。
系统设计决策
● 发行履历至关重要: 当 Zip 成品中包含一份可供机器读取的生成内容描述时,审查者能更轻松地核对。
● 信息清单与验证相辅相成: 验证机制说明构建是否「通过」,而信息清单则记录了构建的「内容」。
● 审查者友善的成品: 审查人员不需逐一开启 HTML 页面,即可检查文件名称与数量。
代码示例 — manifest.py
from datetime import datetime, timezone
import json
from pathlib import Path
def write_manifest(output_dir: Path, articles: list[dict], checks: list[dict]) -> Path:
html_files = sorted(path.name for path in output_dir.glob("*.html"))
manifest = {
"generatedAt": datetime.now(timezone.utc).isoformat(),
"articleCount": len(articles),
"files": html_files,
"checks": checks,
"cardCounts": {
check["file"]: check["sentence_cards"]
for check in checks
}
}
manifest_path = output_dir / "manifest.json"
manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
return manifest_path
代码说明
● 商业逻辑: 信息清单为审查者与未来的维护人员提供了发行版本的完整记录。
● 程序逻辑: 该函数扫描生成的 HTML 文件,记录验证输出,并将 JSON 写入输出目录。
● 预期结果: 每次构建都会在生成页面的旁侧同步产出 dist/manifest.json。
实施实验室 B — 加入确定性卡片 ID 与 Slug 冲突检查
开发者行动
● 请求 Kiro 将纯数值的卡片识别改为「稳定 ID」。
● 结合文章 ID、原始句子索引与场景索引来生成卡片 ID。
● 针对文章中介数据加入 Slug 冲突检查。
● 当出现重复的卡片 ID 或文章 Slug 时,判定构建失败。
Kiro 提示词示例
為生成的卡片建立確定性 ID。
使用文章 id、原始句子索引、情境索引與序列號組合而成。
針對重複的卡片 id 與重複的文章 slug 加入發行驗證。
若發生衝突,返回能明確指出衝突紀錄的錯誤訊息。
系统设计决策
● 稳定的 ID 能支持审查意见: 当母语审查人员对特定卡片提出修改意见时,需要一个固定的参照基准。
● 冲突检查可防止页面遭到覆盖: 重复的 Slug 会导致某些文章在 dist 目录中被其他文章覆盖。
● 确定性有助于回归测试: 除非原始数据或扩展逻辑发生变更,否则 ID 绝不应改变。
代码示例 — identity.py
import re
def safe_slug(value: str) -> str:
text = value.lower().strip()
text = re.sub(r"[^a-z0-9]+", "-", text)
return text.strip("-")
def card_id(article_id: int, sentence_index: int, context_index: int, sequence: int) -> str:
return f"a{article_id:03d}-s{sentence_index:02d}-c{context_index:02d}-n{sequence:03d}"
def assert_unique(values: list[str], label: str) -> None:
seen: set[str] = set()
duplicates: set[str] = set()
for value in values:
if value in seen:
duplicates.add(value)
seen.add(value)
if duplicates:
joined = ", ".join(sorted(duplicates))
raise ValueError(f"Duplicate {label}: {joined}")
代码说明
● 商业逻辑: ID 与 Slug 成为了稳定的审查与路由契约。
● 程序逻辑: card_id 产生确定性的 ID;assert_unique 则在发现重复时立即引发错误(Fail-fast)。
● 预期结果: 审查者可以明确引用卡片 ID,且产生器会拒绝编译具歧义的文章输出。
实施实验室 C — 建立增量构建快取以加快反覆运算速度
开发者行动
● 请求 Kiro 为每篇文章的原始数据计算内容哈希值(Hash)。
● 将哈希值存储于 .build-cache.json。
● 除非带有 --force 参数,否则自动跳过未变更文章的渲染工作。
● 列印出哪些文章已构建、跳过或未通过验证。
Kiro 提示词示例
在靜態產生器中加入增量建置(Incremental build)功能。
為每篇文章的中介資料、句子庫與情境計算雜湊值。
將雜湊值儲存於 .build-cache.json。
除非使用了 --force,否則跳過未變更的文章。
在打包之前,務必對現有的輸出檔案重新執行驗證。
系统设计决策
● 为内容密集的构建提供快速反馈: 大型生成网站不应仅因为单一文章变更就重新渲染所有页面。
● 快取原始输入,而非输出文件的时间戳记: 对原始数据进行哈希运算,能让重新构建的决策具备确定性与可移植性。
● 验证机制依然执行: 跳过渲染并不代表能跳过发行检查,因为先前既存的输出文件仍可能无效或遗失。
代码示例 — cache.py
import hashlib
import json
from pathlib import Path
from typing import Any
CACHE_FILE = Path(".build-cache.json")
def stable_hash(value: Any) -> str:
payload = json.dumps(value, sort_keys=True, ensure_ascii=False)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
def read_cache() -> dict[str, str]:
if not CACHE_FILE.exists():
return {}
return json.loads(CACHE_FILE.read_text(encoding="utf-8"))
def write_cache(cache: dict[str, str]) -> None:
CACHE_FILE.write_text(json.dumps(cache, indent=2), encoding="utf-8")
def should_build(cache: dict[str, str], article_id: int, source_hash: str, force: bool = False) -> bool:
return force or cache.get(str(article_id)) != source_hash
代码说明
● 商业逻辑: 快取机制减少了等待时间,同时保有构建的可重现性。
● 程序逻辑: 通过排序键(Sorted keys)进行 JSON 序列化,为原始输入建立稳定的哈希值。
● 预期结果: 重新执行构建会跳过未变更的文章,但仍会对整体发行版本进行验证。
实施实验室 D — 生成用于母语人士审查的审查者软件包包
开发者行动
● 请求 Kiro 将生成的卡片导出为 CSV 格式以供审查人员使用。
● 字段包含:卡片 ID、文章标题、英文、自然他加禄语、礼貌他加禄语、趣味版本、语气与场景。
● 额外加入一栏留白的「审查者备注(reviewerNotes)」。
● 将此 CSV 文件与生成的 HTML 一同打包进发行的 zip 压缩包中。
Kiro 提示词示例
為產生的他加祿語卡片建立審查者 CSV 匯出功能。
每列應包含:穩定的卡片 id、文章標題、情境、英文、自然他加祿語、禮貌他加祿語、親切菲律賓英語、趣味菲律賓英語、語氣以及 reviewerNotes。
將檔案寫入 dist/reviewer-cards.csv 並將其包含在 zip 壓縮包中。
系统设计决策
● 审查者需要表格化的工作流: 相比于逐页点开 HTML,母语人士在试算表(Spreadsheet)中进行审查往往更有效率。
● 稳定的 ID 连结审查与输出: CSV 中的卡片 ID 必须与 HTML 页面中呈现的 ID 完全一致。
● 留白的审查者备注保留了人类权威: 产生器负责准备好审查结构,但不会代为捏造审查批准决策。
代码示例 — review_export.py
import csv
from pathlib import Path
def write_reviewer_csv(output_path: Path, article: dict, cards: list[dict]) -> None:
fieldnames = [
"cardId",
"articleTitle",
"context",
"english",
"naturalTagalog",
"politeTagalog",
"friendlyFilipinoEnglish",
"playfulFilipinoEnglish",
"tone",
"reviewerNotes"
]
with output_path.open("w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for card in cards:
writer.writerow({
"cardId": card["id"],
"articleTitle": article["title"],
"context": card["context_en"],
"english": card["english"],
"naturalTagalog": card["natural"],
"politeTagalog": card["polite"],
"friendlyFilipinoEnglish": card["friendly"],
"playfulFilipinoEnglish": card["playful"],
"tone": card["tone"],
"reviewerNotes": ""
})
代码说明
● 商业逻辑: 导出功能将生成的学习卡片转换为便于审查的工具包。
● 程序逻辑: csv.DictWriter 确保写入一致的字段,并妥善保存 UTF-8 编码的他加禄语文本。
● 预期结果: 审查人员收到一份能完美对应回 HTML 卡片的 CSV 文件。
实施实验室 E — 使用 pytest 加入流水线契约测试
开发者行动
● 请求 Kiro 为产生器的核心契约建立测试。
● 测试项目包含:精确的卡片数量、重复 ID 失败判定、HTML 转义、信息清单建立以及审查者 CSV 字段标头。
● 在修改生成逻辑后,于本地端执行 pytest。
● 请求 Kiro 解释失败的测试并给出最小的修正提案。
Kiro 提示词示例
為靜態網站產生器管線生成 pytest 測試。
測試擴展數量、確定性卡片 id、HTML 轉義、資訊清單檔案建立以及審查者 CSV 標頭。
儘可能使用小型的記憶體內(In-memory)範例資料。
除非必要,否則避免進行完整的 HTML 快照對比。
系统设计决策
● 流水线契约可保护发行行为: 产生器所面临的风险大于单一函数,因为数据会流经扩展、渲染、验证、信息清单记录与打包等复数流水线。
● 小型测试更容易诊断问题: 测试应验证核心契约,而非去匹配庞大的生成网页。
● 转义是不容妥协的: 渲染器测试必须包含不安全字元,以便在早期捕捉到转义退化(Regressions)的问题。
代码示例 — tests/test_pipeline_contracts.py
import json
from pathlib import Path
from identity import assert_unique, card_id
from manifest import write_manifest
from render import render_article
def test_card_id_is_deterministic():
assert card_id(1, 2, 3, 4) == "a001-s02-c03-n004"
def test_duplicate_detection_fails_fast():
try:
assert_unique(["intro", "intro"], "slug")
except ValueError as error:
assert "Duplicate slug" in str(error)
else:
raise AssertionError("Expected duplicate slug failure")
def test_render_article_escapes_title():
article = {"title": "Hello <Tagalog>", "category": "Test", "summary": "one two three"}
html = render_article(article, [])
assert "Hello <Tagalog>" in html
def test_manifest_records_generated_files(tmp_path: Path):
(tmp_path / "article-1-demo.html").write_text("<html></html>", encoding="utf-8")
manifest_path = write_manifest(tmp_path, [{"id": 1}], [{"file": "article-1-demo.html", "sentence_cards": 40}])
manifest = json.loads(manifest_path.write_text(encoding="utf-8"))
assert manifest["articleCount"] == 1
assert "article-1-demo.html" in manifest["files"]
代码说明
● 商业逻辑: 这些测试保护了稳定的标识符、安全渲染与发行文件的正确性。
● 程序逻辑: 每个测试案例都使用最精简的设具(Fixture)数据来验证特定契约。
● 预期结果: 流水线的任何变更都将更加安全,因为核心的产生器承诺均已涵盖在单元测试中。
实施实验室 F — 加入附带发行成品检查码的模拟执行模式
开发者行动
● 请求 Kiro 为构建脚本加入 --dry-run(模拟执行)选标。
● 在模拟执行模式下,产出验证输出,但跳过写入 zip 压缩包。
● 在常规模式下,计算 zip 压缩包与 manifest.json 的 SHA-256 检查码(Checksum)。
● 列印出一份提供给维护人员的最终发行摘要。
Kiro 提示词示例
在 Python 建置流程中加入模擬執行(Dry-run)模式與成品檢查碼。
--dry-run 應執行渲染與驗證,但跳過寫入 zip 包。
常規發行模式應為 zip 檔案與 manifest.json 寫入檢查碼。
列印一份簡潔的發行摘要,包含檔案、檢查項與檢查碼路徑。
系统设计决策
● 模拟执行能降低发行风险: 维护人员可以在不建立或覆盖任何产出物的情况下,验证候选的构建版本。
● 检查码保障成品完整性: 检查码让审查者能确认他们拿到的 zip 压缩包,与通过验证流水线的文件完全一致。
● 发行摘要改善交接流程: 构建输出应明确告诉维护人员发生了什么,以及能在哪里找到验证凭证。
代码示例 — checksums.py
import hashlib
from pathlib import Path
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as file:
for chunk in iter(lambda: file.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def write_checksum(path: Path) -> Path:
checksum = sha256_file(path)
checksum_path = path.with_suffix(path.suffix + ".sha256")
checksum_path.write_text(f"{checksum} {path.name}\n", encoding="utf-8")
return checksum_path
代码说明
● 商业逻辑: 检查码让生成的发行成品更易于验证与分享。
● 程序逻辑: 该函数以区块(Chunk)串流方式读取文件,并写入标准的 .sha256 附随文件(Sidecar file)。
● 预期结果: 常规发行会产出 zip 与信息清单的检查码,而模拟执行则只验证、不打包。
参考架构备注
● 本工作坊着重展现之 Kiro 能力:文件优先流水线规划、确定性 Python 生成、发行验证、发行成品打包、出处文件化、流水线测试以及构建审查自动化。
● 产品范畴:生成的他加禄语学习网页,用于审查及最终的静态代管。在经由他加禄语母语人士审查前,语言内容均视为草稿。
● 运行时范畴:优先生成静态 HTML。当发行成品通过验证后,后续的选配 AWS 部署可使用 Amazon S3 静态网站代管、Amazon CloudFront 或 AWS Amplify Hosting。