
前一段时间在 Hacker News 上看到一个很有辨识度的 Show HN 项目作者说自己问了大语言模型一个近乎“终极”的问题——如果你能亲口对上帝说一番话你会说什么——然后把 LLM 的回答做成一个网站挂到了线上。这个项目本身很小没有复杂的后端也没有炫酷的交互但它在评论区引发了大量讨论。很多人讨论的不是“AI 是否真的有自我意识”而是另一个更实际的问题一个想法极其简单的 LLM 应用是怎么在短时间内变成可访问、可分享、可传播的网站的这篇文章不打算讨论神学也不打算吹捧 AI 的“人格”。我想把这类项目拆开来看它背后的技术路径是什么LLM 生成的内容如何被工程化处理为什么用静态站而不是上数据库移动端适配在传播中扮演了什么角色以及一个普通开发者要复刻类似项目时需要避开哪些坑。读完这篇文章你会得到一套可以直接套用的、从“问 LLM 一句话”到“上线一个网站”的完整工作流。1. 这类创意项目的技术含金量到底在哪如果只看表面这个项目似乎就是“调用一次大模型 API拿到一段文本然后贴到网页上”。但恰恰是这种“看似简单”的项目最容易让人低估它的工程细节。先说结论LLM 创意类网站的核心竞争力从来不是模型调用本身而是内容生成的质量控制、前端呈现的克制程度以及分享链路的顺畅程度。从技术角度看这个项目至少包含了三个关键环节第一提问即产品。同一个问题直接问和经过精心设计的 Prompt 约束后问得到的文本质量完全不同。LLM 生成的回答如果不加约束通常会又长又啰嗦、充满套话直接放上网页基本没人看得下去。第二输出即数据。要让 LLM 的回答能被网页消费不能直接把聊天窗口里的原始文本复制粘贴而是要把模型输出结构化。最小可行方案是约定 JSON 输出然后再做一次文本清洗和转义。第三页面即体验。这类项目通常只有一屏内容用户点开链接后能不能在 3 秒内感受到情绪冲击取决于排版、字体、留白、背景色这些看似无关紧要的细节。所以这个项目的技术含金量不是“用了多强的模型”而是**“如何用最少的工程成本把 LLM 的随机输出变成一个有情绪、有质感的页面”**。这对独立开发者、前端工程师甚至产品经理来说都是一个值得拆解的样板。2. 先判断LLM 生成的内容要不要后端配合很多第一次接触 LLM 开发的人会默认以为“做一个 AI 网站”就必须上后端、上数据库、上 WebSocket。其实大部分这类展示型项目完全不需要。这里要区分两种常见形态我做了个对比形态内容来源后端数据库典型成本适用场景动态生成式用户每次访问时实时调用 LLM API需要负责转发请求和管理密钥可选用于记录访问量每次访问都有 Token 消耗成本不可控需要千人千面、交互式对话的产品静态生成式提前用 LLM 生成内容构建时写入页面不需要不需要只在生成时消耗一次 Token托管几乎免费展示型、分享型、品牌型落地页这个 Show HN 项目属于典型的静态生成式。作者的核心诉求是让用户“看到一段回答”而不是“和 AI 聊一段天”。这种情况下动态调用大模型 API 反而是负担每次页面刷新都要计费接口超时会让整个页面白屏同时还要考虑并发、限流、密钥安全等一系列问题。静态生成式的做法是先用脚本调用一次 LLM API把输出保存为 JSON 文件再通过构建脚本把 JSON 合并进 HTML 模板最后把生成好的 HTML 部署到任意静态托管平台。更关键的是静态化之后分享链路最短。用户拿到的是一个普通链接点开直接看到内容不需要等待加载也不会被浏览器拦截或提示安全风险。这类项目本来就是为了在社区里被转发而生的静态化几乎是唯一合理的选择。3. 基础概念从 LLM API 到网页显示的完整链路在写代码之前先把这条链路里涉及的概念梳理清楚。LLM API大语言模型服务商提供的 HTTP 接口。开发者发送包含 Prompt 的请求模型返回生成的文本。现在主流服务商大多提供 OpenAI 兼容格式也就是说请求体和返回结构基本一致换服务商时只需要改 base_url、api_key 和 model 名称。Prompt你输入给模型的指令。它决定模型“以什么身份、用什么语气、回答什么内容”。同一个问题用不同 Prompt输出质量可能天差地别。结构化输出让 LLM 返回 JSON 而不是纯文本的能力。常见做法是在 System Prompt 里明确说明“只返回 JSON”或者要求模型在回答前后加特殊标记。现在部分服务商也支持 JSON Mode可以直接让模型保证输出合法 JSON。静态站点生成在本地把数据写入 HTML 模板生成一堆纯静态文件。用户访问时服务器只负责把这些文件原样返回不执行任何动态逻辑。移动端优先指设计页面时先从手机屏幕尺寸出发而不是先做桌面版再缩小。类似项目在 Hacker News 和社交媒体上传播时大部分流量来自手机浏览器。整条链路是编写 Prompt → 调用 LLM API → 得到 JSON 数据 → 合并 HTML 模板 → 静态文件 → 部署托管 → 分享链接其中前三步是“内容生产”后四步是“展示分发”。对创意类项目来说这两段缺一不可。4. 环境准备与前置条件动手实现前先准备好环境。这个项目的依赖很少核心是 Python 和前端基础。4.1 运行时环境建议使用 Python 3.10 或更高版本。LLM 的官方 SDK 普遍支持 3.8 以上但我建议直接装新版本省去兼容性麻烦。4.2 依赖安装创建项目目录并安装依赖mkdir llm-static-site cd llm-static-site python -m venv .venv source .venv/bin/activate pip install openai这里只需要一个有 SDK 的包。如果你使用的服务商没有提供 Python SDK也可以直接用requests库调用 HTTP 接口。为方便后续处理建议一并安装python-dotenv把 API Key 放在环境变量文件里pip install python-dotenv4.3 API Key 配置在项目根目录创建.env文件LLM_BASE_URLhttps://api.openai.com/v1 LLM_API_KEYyour_api_key_here LLM_MODELgpt-4o-mini注意model名称要以实际使用的服务商为准。不同服务商部署的模型不同命名差异很大这里只是给出一个通用示例。不要把 API Key 写进代码仓库尤其是如果你打算把项目传到公开平台务必用环境变量或 .env 文件管理密钥。4.4 前端工具这个项目只需要一个浏览器和一个文本编辑器。不需要 Node.js不需要打包工具。为了做移动端模拟调试建议装一个 Chrome 或 Edge用开发者工具的移动模式预览。5. 核心流程拆解四步把一个 LLM 回答变成网站接下来是文章的核心部分。我会把完整流程拆成四个步骤每一步都给出代码和解释。5.1 第一步设计 Prompt控制输出质量很多人写 Prompt 时喜欢写“请帮我写一段回答”然后抱怨 LLM 输出太水。这里真正的问题在于约束不够。在这个项目中目标是让 LLM 回答一个哲学性极强的开放问题。如果不加约束模型很容易输出几百字的排比句、心灵鸡汤、宗教引用结果就是毫无质感。下面这个 Prompt 做了四件事给模型一个明确的身份克制、有洞察力。给输出定下硬性限制不超过 300 字不引用经典原文。给表达定下风格基调平静、有对象感。切断模型“自我解释”的默认行为不要加前缀。# llm_generate.py import os import json from dotenv import load_dotenv from openai import OpenAI load_dotenv() client OpenAI( base_urlos.getenv(LLM_BASE_URL), api_keyos.getenv(LLM_API_KEY), ) PROMPT 你是一个善于用简洁、克制、有画面感的语言回答宏大问题的回答者。 请回答下面的问题要求如下 1. 不超过 300 字。 2. 不引用任何宗教经典原文不使用仪式化用语。 3. 表达要有对象感像在和一个你尊敬的人平静地对话。 4. 不要给回答加任何前缀解释不要以“如果……”开头。 问题如果你可以亲口对上帝说一番话你会说什么 response client.chat.completions.create( modelos.getenv(LLM_MODEL), messages[ { role: system, content: 你是一个克制而有洞察力的回答者。 }, { role: user, content: PROMPT }, ], temperature0.8, max_tokens500, ) answer response.choices[0].message.content.strip() print(生成内容如下) print(answer)代码里有一个容易忽略的细节max_tokens500。即使我们要求 300 字以内模型偶尔还是会写超所以从 Token 层面做一个硬性封顶是必要的。temperature0.8则给回答留了一点随机性让内容带有一定“个性”。如果你是第一次运行建议先跑通这一步多看几次输出再调整 Prompt。5.2 第二步把模型输出落成 JSON 数据拿到文本后下一步是把回答保存成 JSON 文件。这一步的好处是内容和展示分离。以后如果换模型重新生成不需要改前端模板只需要覆盖 JSON 文件再重新构建。# llm_generate.py续 import datetime data { question: What would you say to God if you could?, answer: answer, generated_at: datetime.date.today().isoformat(), } os.makedirs(data, exist_okTrue) with open(data/answer.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) print(内容已保存到 data/answer.json)保存后可以检查一下 JSON 文件的格式cat data/answer.json预期输出类似{ question: What would you say to God if you could?, answer: 我想说的不是忏悔也不是祈求。我想说谢谢你给我有限的生命让我终于学会珍惜每一次选择。, generated_at: 2025-11-16 }注意ensure_asciiFalse这个参数保证中文以明文写入文件而不是被转成\u开头的 Unicode 编码。如果丢了这个参数后续拼 HTML 时虽然也能显示正常但文件可读性会差很多。5.3 第三步写一个移动端优先的 HTML 模板这类项目通常只有一屏内容所以页面设计的重点是“让用户第一眼就看到核心文字”。我做模板时坚持三个原则字体要大行距要宽读起来像一段安静的独白。背景用深色渐变减少白色高亮带来的“工具感”。不做多余装饰不做弹窗不做分享按钮让内容自己说话。!-- template.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / title如果 LLM 可以向上帝说一句话/title style * { box-sizing: border-box; } body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(160deg, #0f0c29, #302b63, #24243e); color: #f0eef7; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Microsoft YaHei, sans-serif; } .card { max-width: 620px; padding: 48px 28px; text-align: center; } .question { font-size: 16px; opacity: 0.7; letter-spacing: 2px; margin-bottom: 28px; } .answer { font-size: 22px; line-height: 1.9; text-align: justify; text-align-last: center; margin: 0; } .meta { margin-top: 40px; font-size: 12px; opacity: 0.4; } /style /head body main classcard p classquestionQUESTION_TEXT/p p classanswerANSWER_TEXT/p p classmetagenerated by LLM/p /main /body /html模板中预留了QUESTION_TEXT和ANSWER_TEXT两个占位符下一步会通过脚本替换。这里有一个细节min-height: 100vh配合display: flex和align-items: center可以让内容在手机屏幕上垂直居中。这个效果虽然简单但对观感的影响非常大——内容偏上或偏下都会让人立刻觉得“这个页面不精致”。关于移动端优先多说一句。类似项目在设计时甚至会有意展示“此页面请在手机端浏览”这类提示。原因很直接传播链路中手机是主要入口。桌面端虽然也能打开但整屏居中的文字在宽屏显示器上会显得空旷真实故事带来的沉浸感会被稀释。如果你在做类似的创意站建议从设计阶段就默认“用户一定在用手机打开”。5.4 第四步写构建脚本把数据灌进模板有了 JSON 数据和 HTML 模板最后一步是写一个简单的 Python 脚本把内容合并成最终的dist/index.html。# build.py import json from pathlib import Path # 读取生成内容 data json.loads(Path(data/answer.json).read_text(encodingutf-8)) # 读取模板 template Path(template.html).read_text(encodingutf-8) # 替换占位符 html template.replace(QUESTION_TEXT, data[question]) html html.replace(ANSWER_TEXT, data[answer]) # 输出最终站点 out_dir Path(dist) out_dir.mkdir(exist_okTrue) Path(dist/index.html).write_text(html, encodingutf-8) print(构建完成dist/index.html)这里使用Path.read_text(encodingutf-8)显式指定编码避免不同操作系统默认编码不同导致的中文乱码。Windows 系统如果不指定编码读文件时默认可能是gbk直接读 UTF-8 的 JSON 会报错这也是新手很容易踩的坑。6. 运行、验证与上线代码写完后按顺序执行以下命令python llm_generate.py python build.py如果没有报错并且终端输出了“构建完成dist/index.html”说明本地构建成功。6.1 本地预览直接双击dist/index.html可以在浏览器里打开。但我更建议起一个本地静态服务器用真实 URL 访问方便后面做移动端模拟cd dist python -m http.server 8080然后在浏览器访问http://localhost:80806.2 移动端模拟验证在 Chrome 或 Edge 里按 F12 打开开发者工具点击设备切换图标将视口切成 iPhone 或 Pixel 尺寸检查以下三个点页面是否出现横向滚动条。文字是否完整显示没有被截断或溢出。字体大小在真实手机阅读时是否舒适。如果发现文字溢出最可能的修复方式是调整.answer的font-size或者给.card增加padding。如果发现背景渐变在部分 Android 手机上显示很生硬可以考虑在body上加一个纯色兜底背景例如background-color: #24243e这样即使渐变不支持也不会白屏。6.3 部署到线上静态站点可以部署到任何静态托管平台常见的有 GitHub Pages、Netlify、Vercel、Cloudflare Pages以及各大云厂商的对象存储加 CDN。以通用流程为例部署时做的事情基本一样把项目推送到 Git 仓库。在托管平台创建新站点关联仓库。指定构建目录为dist。保存平台会自动分配一个域名。如果你只是临时分享也可以直接把dist/index.html拖到一些支持静态托管的在线服务里几秒钟就能拿到链接。6.4 上线后检查清单上线后不要急着把链接发出去。建议先完成一次自检打开手机浏览器访问链接确认网络正常。打开页面确认回答内容没有被转义成乱码。检查页面标题、描述是否适合在社交平台分享。用无痕模式打开一次排除缓存干扰。6.5 运行失败的排错顺序如果某一步报错不要慌按这个顺序排查90% 的问题能解决第一看 API 返回错误。如果请求失败先打印response或查看异常信息。常见错误码 401 意味着 API Key 无效429 意味着触发了频率限制400 通常是请求参数格式有问题。第二检查 .env 是否被加载。在 Python 脚本开头加一行print(os.getenv(LLM_MODEL))确认环境变量真的读到了。很多时候文件路径不对或变量名拼错导致请求实际用的是空值。第三检查编码问题。如果 JSON 和 HTML 里出现乱码优先排查读写文件时是否都显式写了encodingutf-8。7. 常见问题与排查思路问题现象可能原因排查方式解决方案API 请求返回 401API Key 无效或 .env 未加载打印环境变量检查 Key 是否正确重新配置 API Key确认没有多余空格返回内容带 Markdown 符号模型没有严格遵循格式要求检查 Prompt 中的格式约束在 System Prompt 里明确要求“只输出纯文本”或增加response_format参数中文内容显示乱码读写文件时没有指定 UTF-8 编码检查代码中的open和read_text统一使用encodingutf-8手机访问时文字被截断固定高度或字体过大用开发者工具移动模式检查视口改用min-height适当缩小字体或增加 padding页面分享链接没有标题预览缺少基础 Meta 标签检查 HTML 的 head 区域补充title、description等标签构建时报错找不到模块Python 环境不对或依赖未安装检查激活的虚拟环境重新执行pip install openai python-dotenv数据文件被误提交到公开仓库.gitignore 未配置检查仓库文件列表添加.gitignore排除.env文件8. 最佳实践LLM 创意网站的通用模板这一节把上面的经验提炼成可以复用的方法论。以后再看到类似“让 AI 做一件有趣的事”的选题都可以按下面这套流程执行。8.1 先想清楚内容是一次性的还是持续生成的如果页面内容只需要生成一次就选静态化方案。如果内容需要每天更新或者用户要自己输入问题再考虑引入后端。不要为了“显得技术栈完整”而上数据库和服务器独立开发项目最大的优势就是轻。8.2 Prompt 设计时把“不要什么”写进去很多人只写“我要什么”不写“我不要什么”。结果是模型自由发挥生成一堆空话。实践中有三件事几乎每次都要声明不要解释你的回答。不要使用总结式结尾。不要使用固定句式或排比句模板。8.3 用 JSON 做中间层让 LLM 的原始输出先落成 JSON再做展示最大的好处是换肤方便。你可以同一份数据生成深色版、浅色版、中英文版。如果哪天觉得文案不好只需重新生成 JSON不需要改页面结构。8.4 页面越克制传播力越强这类创意项目的核心是文字本身传递的情绪。页面上任何多余的元素都会稀释情绪。在做过多个类似项目后我的判断是绝不要放显眼的“AI生成”标签也不要在页面底部摆一堆社交分享图标。用户自己会决定是否分享你只需要保证内容足够打动他。8.5 设计上多做一步移动端适配前面提到过传播场景里手机是主力入口。这里再补充一个容易忽略的点微信内置浏览器和系统自带浏览器对 CSS 的支持存在差异。写完样式后最好在 Android 和 iOS 的真实设备上都看一遍不要只依赖开发者工具模拟。8.6 想清楚边界不做有风险的内容LLM 生成的文本存在不可控性。即使是这种简单的创意页面也建议在生成后人工审核一遍确认内容没有冒犯特定群体没有恶意指代没有不适宜公开传播的信息。对敏感话题宁可直接换个选题也不要靠运气上线。8.7 从静态页面走向更复杂的应用当你熟练掌握了这套“单次生成 静态展示”的流程后下一步可以往两个方向扩展一是把单次生成扩展成多轮问答让用户输入自己的问题二是把生成好的内容整理成知识库配合检索和编排框架做成一个围绕特定主题的 LLM 问答应用。后一个方向已经触及 Agent 和 RAG 的范畴但起点仍然是“你如何设计 Prompt、如何结构化输出、如何呈现内容”。9. 关于这类型项目我的总结回到这个 Show HN 项目本身。它之所以吸引眼球不是因为技术上有多复杂而是因为它用一个极轻的技术方案完成了一次“提问—生成—呈现—传播”的完整闭环。作为开发者我们很容易陷入“一定要做重”的思路加数据库、加用户系统、加实时对话、加后台管理。但这类创意项目给了一个完全相反的样本一个 LLM 应用的最小闭环可以只有三样东西——一个精心设计的 Prompt、一次 API 调用、一个漂亮到让人愿意截图的页面。也正因为简单它才是学习 LLM 应用开发最好的起点。你可以从跑通上面的代码开始换一个问题换一组风格约束换一种页面设计看看最终效果会有什么不同。这种“改一个变量观察输出变化”的练习比看十篇框架教程更能建立对 LLM 的直觉。如果要给一个下一步行动建议我会说找一个你真正好奇的问题按这篇文章的流程把它做成一个页面发布出去然后观察有多少人愿意点开愿意转发愿意在评论区讨论。你会立刻理解LLM 应用的趣味和边界从来不只是模型能力而是你如何选择和呈现。