适用场景
手里只有一句模糊的产品需求,比如"做一个活动报名页,填手机号就能提交",却没有前端资源,也不想从零手写 HTML/CSS/JS。这套方案用 Step 5 Preview 的对话接口,把一句需求直接生成单文件网页,再用多轮对话做迭代,最后落到静态托管上拿到一个可访问的链接。适合独立开发者、运营同学和需要快速出原型的 AI 从业者。
环境与前置条件
- 操作系统:macOS、Linux 或 Windows + WSL 均可,只要能跑命令行。
- 运行时:Python 3.9 及以上(只用标准库发请求,不装第三方包也能跑通)。
- 网络:能访问对应模型服务的 API 地址,企业内网需放行。
- 凭据:一个可用的 API Key,以及模型服务的基础地址(Base URL)和模型 ID。这三项的具体值以官方文档当前版本为准,不同账号、不同区域可能不一样。
- 磁盘:生成的是单文件网页,几 MB 足够;建议单独建一个空目录做工作区。
- 内存/显存:本地不跑推理,普通 8GB 内存的机器即可。
分步骤部署
步骤 1:建立工作目录并配置凭据
```bash
mkdir -p ~/step5-web && cd ~/step5-web
export API_BASE_URL="https://<你的服务地址>/v1" # 以官方文档当前版本为准
export API_KEY="<你的密钥>"
export MODEL_ID="<官方文档给出的模型 ID>"
```
这三条 export 只对当前终端窗口生效。想让新开的终端也能用,把它们写进 ~/.bashrc 或 ~/.zshrc,然后 source 一下。注意不要把 API_KEY 提交进 Git 仓库。
步骤 2:用一条请求确认链路是通的
先别急着生成网页,先用最小请求验证鉴权和地址没问题:
```bash
curl -sS "${API_BASE_URL}/chat/completions" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"model\":\"${MODEL_ID}\",\"messages\":[{\"role\":\"user\",\"content\":\"只回复两个字:正常\"}]}"
```
成功的标志是返回一段 JSON,其中 choices[0].message.content 里有"正常"两个字。如果返回 401、404 或一段 HTML(说明打到了错误的路径),先不要往下走,回到步骤 1 核对地址。
步骤 3:写生成脚本,把"一句话"变成完整 HTML
在工作目录新建 generate.py。核心思路是把提示词写成模板,对输出格式做硬约束:
```python
import json, os, sys, pathlib, urllib.request
API_BASE_URL = os.environ["API_BASE_URL"].rstrip("/")
API_KEY = os.environ["API_KEY"]
MODEL_ID = os.environ["MODEL_ID"]
PROMPT = """你是一名前端工程师。请把下面这句需求做成一个单文件网页。
要求:
1. 只输出一个完整的 HTML 文档,从 <!DOCTYPE html> 开始,到 </html> 结束。
2. CSS 写在 <style> 标签内,JS 写在 <script> 标签内,不引用任何外部文件和 CDN。
3. 在手机窄屏和桌面宽屏下都要能正常显示。
4. 不要输出任何解释文字,不要使用 Markdown 代码围栏。
需求:{req}
"""
def call_model(messages, max_tokens=8000):
body = json.dumps({
"model": MODEL_ID,
"messages": messages,
"max_tokens": max_tokens,
}).encode("utf-8")
req = urllib.request.Request(
f"{API_BASE_URL}/chat/completions",
data=body,
headers={"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=300) as resp:
data = json.loads(resp.read().decode("utf-8"))
return data["choices"][0]["message"]["content"]
def strip_fence(text):
text = text.strip()
if text.startswith("```"):
lines = text.splitlines()[1:]
if lines and lines[-1].strip().startswith("```"):
lines = lines[:-1]
text = "\n".join(lines)
return text
if __name__ == "__main__":
html = strip_fence(call_model([{"role": "user",
"content": PROMPT.format(req=sys.argv[1])}]))
pathlib.Path("index.html").write_text(html, encoding="utf-8")
print("已写入 index.html,长度:", len(html))
```
运行方式:
```bash
python3 generate.py "做一个活动报名页,标题、一段说明、手机号输入框和提交按钮,提交后显示一句感谢语"
```
出现 已写入 index.html,长度:xxxx 就算成功。长度低于几百字符通常说明模型只回了个开头,检查 max_tokens 是否给足。
步骤 4:本地预览第一版
```bash
python3 -m http.server 8080
```
浏览器打开 http://127.0.0.1:8080。用开发者工具的响应式模式切到手机宽度,看一看有没有横向滚动条、按钮点不点得动。
步骤 5:迭代,把"再改一点"说清楚
先把当前版本存一份,方便回退:
```bash
cp index.html index.v1.html
```
新建 iterate.py,把当前源码和修改意见一起发回去:
```python
import pathlib, sys
from generate import call_model, strip_fence
current = pathlib.Path("index.html").read_text(encoding="utf-8")
feedback = sys.argv[1]
prompt = f"""这是当前的网页源码:
{current}
请按下面的意见修改,保持原有结构与风格,不要推倒重来:
{feedback}
同样只输出完整 HTML,不要解释,不要 Markdown 围栏。"""
html = strip_fence(call_model([{"role": "user", "content": prompt}]))
pathlib.Path("index.html").write_text(html, encoding="utf-8")
print("已更新 index.html")
```
```bash
python3 iterate.py "主色调换成深蓝,按钮加圆角和悬停变色,标题字号再大一点"
```
一次只提一到两条具体意见,比一次性说"再好看一点"效果好得多。每轮迭代后刷新浏览器确认,满意就 cp index.html index.v2.html 留档。
步骤 6:部署成公网可访问的页面
静态页面有几种常见落法,按手头资源选一种。
方式一:托管平台拖拽或命令行上传。 GitHub Pages、Netlify、Cloudflare Pages 这类服务都支持上传一个目录,具体操作步骤以各自官方文档当前版本为准。
方式二:GitHub Pages 走 Git 流程。
```bash
git init
git add index.html
git commit -m "add landing page"
git branch -M main
git remote add origin git@github.com:<用户名>/<仓库名>.git
git push -u origin main
```
推送后在仓库的 Settings → Pages 里选择分支和目录保存,等构建完成会给出访问地址。
方式三:自己的服务器加 nginx。
```bash
scp index.html user@<服务器IP>:/var/www/html/
```
nginx 站点配置参考:
```nginx
server {
listen 80;
server_name your-domain.example;
root /var/www/html;
index index.html;
location / { try_files $uri $uri/ =404; }
}
```
重载:sudo nginx -t && sudo systemctl reload nginx。需要 HTTPS 时用 certbot 之类的工具签发证书,参数以官方文档当前版本为准。
这里有一条硬规矩:生成出来的页面是纯前端的,任何密钥、内部接口地址都不要写进 HTML,谁都看得到。
验证部署是否成功
1. 命令行确认状态码:
```bash
curl -s -o /dev/null -w "%{http_code}\n" https://你的访问地址
```
返回 200 表示页面可取到。
2. 确认返回的确实是刚生成的版本:
```bash
curl -s https://你的访问地址 | head -n 5
```
开头应当是 <!DOCTYPE html>,且标题和需求一致。
3. 浏览器打开,按 F12 看 Console 有没有红色报错,Network 里有没有 404 的资源请求。单文件页面正常情况下除了主文档不应该有其他请求。
4. 手机上扫码或直接用手机打开同一个地址,确认布局没散。
常见报错与解决
报错:HTTP Error 401: Unauthorized 或 invalid api key
原因:密钥写错、已失效,或者当前终端没有读到环境变量。
解决:
```bash
echo "$API_KEY" | wc -c # 输出应远大于 1,只有 1 说明变量是空的
export API_KEY="<重新填入>"
python3 generate.py "测试"
```
报错:HTTP Error 429: Too Many Requests
原因:短时间内请求过于密集,超出账号速率限制。
解决:串行执行、每次调用之间加等待,并在脚本里做退避重试。
```bash
python3 iterate.py "改标题颜色" && sleep 5 && python3 generate.py "再加一个页脚"
```
报错:页面打开是空白,或者源码开头就是 `html
原因:模型输出了 Markdown 代码围栏,浏览器把整段当成普通文本,或者 <script> 里报错导致渲染中断。
解决:脚本里的 strip_fence() 已经在处理围栏,若仍出现,手动检查并强化提示词里"不要使用 Markdown 代码围栏"这一条;同时打开 Console 看具体是哪一行 JS 出错。
报错:Address already in use,本地预览起不来
原因:8080 端口被别的进程占用。
解决:
```bash
python3 -m http.server 8081
```
或者查一下占用进程:lsof -i:8080。
报错:部署后访问返回 404(GitHub Pages 场景)
原因:Pages 选的分支或目录里没有 index.html,或者构建还没跑完。
解决:确认仓库根目录存在 index.html 且已推送,等构建状态变成成功后再刷。
后续维护
- 版本管理:把每次生成的
index.vN.html和对应的需求原文一起提交进 Git,方便回退和复盘哪句提示词产生了哪次改动。 - 提示词沉淀:把
PROMPT模板单独抽成prompt.txt,积累几套风格(活动页、落地页、表单页),下次换需求只改一句话。 - 密钥轮换:定期在服务商后台重置 API Key,本地同步更新;不要把 Key 写进任何会被托管的文件。
- 用量与日志:在
call_model()里打印返回体里的 usage 字段和请求 ID,记录到run.log,便于排查异常和估算消耗。 - 页面监控:交付出去的页面加一个 Uptime 类的可用性检测,或者至少定期
curl -I看状态码;页面里的表单提交如果是纯前端模拟,记得后续接上真实后端,否则用户提交的数据不会有任何落库。
