第 16 章 辅助编程实战:从零构建学习助手
🤖 本章特色:面向编程初学者,全程使用 OpenClaw 作为 AI 协作平台,配合大模型(LLM)从零生成代码。你会看到 OpenClaw 如何帮你完成需求分析、代码生成、文件读写、代码执行、测试部署等全流程操作,你负责提需求,OpenClaw 负责跑腿干活。
本章目录:
| 节 | 内容 | 你将学到 |
|---|---|---|
| 1 | 背景知识 | 项目需求、技术栈、数据结构与 API 规范 |
| 2 | index.html 实现 | 三步递进式提示词策略、OpenClaw 自动生成并保存代码 |
| 3 | upload.html 实现 | 接口规范前置、状态机、OpenClaw 一次生成完整页面 |
| 4 | 后端 Python 实现 | 架构约束前置、分层设计、OpenClaw 生成后端代码并运行 |
| 5 | 测试部署运行 | curl 测试、systemd 服务、Nginx 代理 |
| 6 | 总结 | 核心心法、提示词模板复用、课后练习 |
1. 背景知识:需求、技术与规范设计
1.1 背景故事:从家里的小白板,到一个班级的共享工具
痛点:家里的那块小白板
很多家长都有这样的经历:
孩子放学回家,家长问:"今天留了什么作业?"
孩子拿起粉笔,在家里的小白板上一条条写。写完发现漏了两科。
第二天,擦掉重写。第三天,又漏了。
家长想跟踪孩子哪些作业已完成、哪些还没做,只能靠眼睛看、脑子记。
小白板的局限:
- ❌ 记录靠手写,遗漏是常态:孩子凭记忆抄作业,记不全、抄错行、漏科目,几乎每天都会发生。
- ❌ 状态无法追踪:哪项已交、哪项待订正、哪项要签字,白板上只有一个勾或一个叉,看不出进度,也留不下历史记录。
- ❌ 无法班级共享:白板只在家里,其他家长看不到,信息无法同步。
- ❌ 擦掉即消失:白板是易失的。昨天的作业情况今天完全看不到,想回顾孩子这一周的完成情况,只能凭印象。
解决方案:用大模型构建"学习助手"Web 应用
我们要做的,是一个替代家里小白板的数字化工具:
- 跟踪老师布置的任务:拍张作业通知的照片,大模型自动识别并结构化
- 标注完成状态:每项作业可以打勾标记"已完成"或"未完成"
- 班级共享使用:一人录入,全班家长都能看到(部署后)
- 历史记录可追溯:每天作业按天归档,随时翻看之前的作业
- 个人进度独立跟踪:每个同学家长都能看到自己孩子的完成进度
1.2 OpenClaw 在开发中的角色
在整个开发过程中,OpenClaw 是你的全能协作伙伴:
| 开发阶段 | OpenClaw 的能力 | 具体操作 |
|---|---|---|
| 需求分析 | 对话式梳理需求 | 直接在聊天中描述想法,OpenClaw 帮你整理 |
| 代码生成 | 自动调用大模型生成代码 | 接收你的提示词,生成代码并自动写入本地文件 |
| 文件读写 | 创建、编辑项目文件 | 用 write/edit 工具自动写入 app.py、index.html 等 |
| 运行测试 | 执行 shell 命令 | 用 exec 工具启动 Flask 服务器、运行 curl 测试 |
| 部署运维 | 生成配置、重启服务 | 帮你写 systemd 配置、Nginx 配置并部署上线 |
| 记忆管理 | 记住你的项目上下文 | 下次打开会话,OpenClaw 还记得项目状态 |
🔑 关键理解:OpenClaw 不只是聊天工具
传统的大模型对话(如 ChatGPT 网页版)只能生成代码并显示在聊天窗口中,你需要手动复制、粘贴到编辑器、保存文件。而 OpenClaw 能直接操作你的本地文件系统:
- 大模型生成的代码,OpenClaw 用
write工具直接保存到你的项目目录 - 需要修改某几行?OpenClaw 用
edit工具精准替换,不用重写整个文件 - 想运行代码?OpenClaw 用
exec工具直接执行命令 - 出错了?OpenClaw 用
read工具读取日志,帮你定位问题
简单说:你在对话框里说需求,OpenClaw 自动完成"生成代码 → 保存文件 → 运行程序 → 检查结果"的完整闭环。
1.3 大模型编程基础知识
大模型(LLM)是什么?它能做什么?
大模型(Large Language Model)是经过海量文本训练的 AI 系统。在编程领域,它可以:
- 生成代码:你描述需求,它写出可用的代码
- 解释代码:你给它一段看不懂的代码,它逐行解释
- Debug 排错:你把报错信息丢给它,它告诉你原因和修复方案
- 重构优化:你说"让这段代码更简洁",它会重写
- 写文档:你说"给这个项目写个 README",它自动生成
🔑 初学者必读:大模型不是"自动编程机",而是你的高级结对编程伙伴。它拥有海量的代码知识库,但缺乏对你具体业务的理解。所以你负责架构设计和逻辑把控,它负责代码生成和细节补全。
提示词工程(Prompt Engineering)黄金公式
向大模型提问时,遵循以下结构可获得最佳结果:
角色设定 + 背景上下文 + 具体任务 + 约束条件 + 输出格式
❌ 糟糕的提示词:
"帮我写一个上传文件的网页。"
(结果:大模型会给你一个极其简陋、没有样式、不符合你业务逻辑的通用代码。)
✅ 优秀的提示词:
[角色] 你是一个资深前端工程师。
[背景] 我正在开发一个小学生作业录入系统。
[任务] 请帮我写一个 upload.html 页面,包含图片拖拽上传和 JSON 预览编辑功能。
[约束] 使用原生 HTML/CSS/JS,不使用任何框架;UI 风格要现代、简洁,主色调为蓝色。
[输出] 请输出完整的单文件 HTML 代码。
大模型的三种角色模式
| 模式 | 适用场景 | 提示词关键词 | 示例 |
|---|---|---|---|
| 生成模式 | 从零创建代码 | "请生成/编写" | 新建一个 Flask 路由 |
| 修改模式 | 已有代码上迭代 | "在现有基础上/请修订" | 给 upload.html 加每日限传功能 |
| Debug 模式 | 解决运行报错 | "报错信息如下/请排查" | 路由冲突导致 500 |
1.4 系统架构全景图
┌─────────────────────────────────────────────┐
│ 班级家长(手机/电脑浏览器) │
│ ┌──────────┐ ┌──────────┐ │
│ │ index │ │ upload │ ← 前端页面 │
│ │ .html │ │ .html │ │
│ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ Flask 后端(app.py + homework_ocr.py) │
│ ┌──────────────────────────────────────┐ │
│ │ /api/homework/status 检查是否已上传 │ │
│ │ /api/homework/ocr 图片 → 大模型 │ │
│ │ /api/homework/<date> 保存/更新数据 │ │
│ │ homework/<date>.json 读取 JSON 文件 │ │
│ └──────────────────────────────────────┘ │
└───────────────────┬─────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 文件系统(服务器本地) │
│ homework/2026-09-14.json ← 全班共享数据 │
│ homework/2026-09-15.json │
│ homework/2026-09-16.json │
│ logs/homework_app.log ← 运行日志 │
└─────────────────────────────────────────────┘
🔑 关键理解:
- 录入数据存在服务器端(JSON 文件),全班家长共享:"一人录入,全班可见"
- 完成状态存在客户端(localStorage),每人独立:"各记各的进度"
- 这种设计既保证了数据源唯一,又尊重了每个家庭的完成进度差异
1.5 技术栈选择依据
| 技术选择 | 替代方案 | 为什么选它 |
|---|---|---|
| Flask | Django/FastAPI | 轻量、路由直观、适合教学和初学者 |
| 纯原生 JS | Vue/React | 零构建步骤、零 CDN 依赖、内网可用 |
| JSON 文件存储 | SQLite/MySQL | 按天归档天然隔离、无需连接池、零运维 |
| 视觉大模型(Qwen-VL) | Tesseract OCR | Tesseract 对中文识别率低;多模态大模型理解语义结构 |
| localStorage | 后端用户系统 | 无需注册登录,开箱即用,每人独立进度 |
1.6 项目目录结构
homewk/
├── app.py # Flask 入口:路由 + 配置
├── homework_ocr.py # 纯业务逻辑:大模型调用 + JSON 保存
├── conf/
│ ├── homework.service # systemd 服务配置(生产部署用)
│ └── nginx.conf # Nginx 反向代理配置
├── homework/ # 数据存储:按天 JSON(全班共享)
│ └── 2026-09-14.json
├── logs/ # 日志自动轮转
├── static/
│ ├── index.html # ⭐ 每日作业列表页
│ └── upload.html # ⭐ 作业录入页
├── test/
├── test.jpg # 测试用图片
└── test.sh # curl 自动化测试脚本
1.7 规范先行:数据结构与 API 定义
💡 (规范先行):在让大模型写任何前后端代码之前,必须先定义好"数据长什么样"。这是前后端不扯皮、大模型不幻觉的基石。先定义数据结构和 API,再写代码
JSON 数据结构
这是2026年9月14日的真实项目数据:
{
"date": "2026-09-14",
"homework": [
{ "content": ["诊P18-21"], "subject": "物理" },
{ "content": ["课上学案"], "subject": "数学" },
{ "content": ["背单词", "诊P4-A"], "subject": "英语" },
{ "content": ["诊P5-6"], "subject": "化学" },
{ "content": ["完成第一课学探诊(自订自判)"], "subject": "历史" }
]
}
🔑 关键知识点:
1. homework 是数组而非字典,有多个科目的家庭作业。
2. content 也是数组:一科可能有多个任务。
3. 字段名 subject 和 content 必须在前后端、大模型 Prompt、JSON 存储中完全一致。
API 规范表
| 方法 | 路径 | 参数 | 成功响应 | 失败响应 | 说明 |
|---|---|---|---|---|---|
| GET | /api/homework/status |
date (query) |
{"uploaded_today": bool} |
— | 检查当日是否已存在 |
| POST | /api/homework/ocr |
image (file), date (form) |
200 + 结构化 JSON | 409 {"error": "..."} |
图片识别 |
| PUT | /api/homework/<date> |
JSON body | 200 {"success": true} |
409/400/500 | 保存/更新 |
| GET | homework/<date>.json |
无 | 200 + JSON | 404 | 直接读取文件 |
2. index.html 实现:用 OpenClaw 三步生成作业进度跟踪页
本节的重点是理解 OpenClaw 如何通过精心设计的提示词,分三步生成了 static/index.html 的完整实现。这是整个项目中前端代码量最大、算法逻辑最复杂的文件,但有了好的提示词策略,大模型可以一步步帮你搞定。
📄 参考提示词文件:每日作业打卡提示词-前端.txt(index.html 部分)
该文件包含三个递进式提示词,分别对应:① 基础功能与数据结构 → ② 双列布局与视觉优化 → ③ 布局算法精准调优。
2.1 提示词策略:三步递进式生成
在让 OpenClaw 生成 index.html 之前,我们先设计了一套三步递进式提示词。为什么不一次性把所有需求都塞进一个提示词?
- 需求迭代:我们并没有一次完全想清楚所有的事情。
- 大模型的注意力有限:一次性提出 10 个需求,它可能只做好其中 5 个
- 每步可验证:第一步生成基础页面后,你可以立即在浏览器中查看效果
- 迭代更精准:第二步说"在上一版基础上修订",大模型就知道不要重写已有功能
三步提示词概览:
| 步骤 | 核心目标 | 提示词关键词 |
|---|---|---|
| 第一步 | 基础功能与数据结构 | "数据源为 JSON"、"localStorage"、"进度条" |
| 第二步 | 双列布局与视觉优化 | "左右两栏"、"蓝色主色调"、"移动端单列" |
| 第三步 | 布局算法精准调优 | "贪心算法"、"降序排列"、"高度差最小化" |
🔑 提示词设计心法:每一步都以前一步的产物为基础,用"在上一版基础上"作为开头。这样大模型不会推翻已有代码,而是增量改进。
2.2 第一步:基础功能与数据结构
提示词原文
📄 摘自
每日作业打卡提示词-前端.txt第一步:请编写一个"每日作业打卡"单页应用(HTML+CSS+JS),要求如下:
1. 数据源为/homework/YYYY-MM-DD.json,格式为{ "date": "...", "homework": [{ "subject": "数学", "content": ["任务1", "任务2"] }] };
2. 顶部显示当前日期,支持"前一天/后一天"按钮切换;
3. 显示完成进度条和分数(如 5/7);
4. 点击任务可切换完成状态,状态按日期独立存储在 localStorage;
5. 若 JSON 文件返回 404,显示"当天未留作业"提示,并提供"录入作业"链接;
6. 作业卡片按任务数量从多到少排序显示。
提示词设计要点:数据结构前置(直接给出 JSON 格式)、行为描述而非实现描述、边界条件明确。
OpenClaw 执行过程
你将提示词复制到 OpenClaw 对话框后,OpenClaw 自动完成以下工作:
你(发送提示词)
↓
OpenClaw(调用大模型生成代码)
↓
OpenClaw(用 write 工具将代码直接保存为 static/index.html)
↓
OpenClaw(用 exec 工具启动 Flask 服务器)
↓
你(打开浏览器 http://localhost:5159 查看效果)
🔑 这就是 OpenClaw 的核心价值:大模型生成的代码不再需要你手动复制粘贴,OpenClaw 直接帮你保存到正确的位置,甚至可以帮你启动服务器让你立刻看到效果。
生成产物:ES6 Class 架构
大模型自动选择了 ES6 Class 模式组织代码:
class HomeworkApp {
constructor() { ... } // 初始化:日期、DOM 引用、事件绑定
loadHomework() { ... } // 加载数据:fetch JSON → 渲染
render() { ... } // 渲染引擎:贪心算法分配双列
createCard() { ... } // 卡片工厂:生成单个科目
toggleTask() { ... } // 交互处理:点击切换完成状态
updateProgress() { ... } // 进度条更新
}
🔑 为什么用 Class 而不是散乱的函数?
- 状态集中:
this.currentDate、this.homeworkData都在一个实例上,不会产生全局变量污染 - 方法复用:
changeDate和loadHomework互相调用,代码清晰 - 生命周期清晰:
constructor→bindEvents→loadHomework,一气呵成
生成产物:数据加载与 404 降级
async loadHomework() {
const dateStr = this.formatDate(this.currentDate);
try {
const resp = await fetch(`homework/${dateStr}.json`);
if (resp.status === 404) { this.renderEmpty(dateStr); return; }
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
this.homeworkData = data.homework || [];
// 按任务数量降序排列(贪心算法前置条件)
this.homeworkData.sort((a, b) => {
const lenA = Array.isArray(a.content) ? a.content.length : 1;
const lenB = Array.isArray(b.content) ? b.content.length : 1;
return lenB - lenA;
});
this.render();
} catch (err) {
// 错误状态渲染
}
}
🔑 关键知识点:
1. 直接 fetch JSON 文件:不走 API 路由,直接从 homework/YYYY-MM-DD.json 读取
2. 404 优雅降级:文件不存在时显示"当天未留作业"并提供"录入作业"链接
3. 降序排序:任务多的科目排在前面,这是贪心分配算法的前置条件
生成产物:localStorage 状态管理
getStorageKey() { return `hw_status_${this.formatDate(this.currentDate)}`; }
getLocalStatus() {
try { return JSON.parse(localStorage.getItem(this.getStorageKey())) || {}; }
catch { return {}; }
}
saveLocalStatus(map) { localStorage.setItem(this.getStorageKey(), JSON.stringify(map)); }
🔑 为什么用 localStorage?
| 家长 A | 家长 B |
|---|---|
| 看到同样的作业内容 | 看到同样的作业内容 |
| 记录自己孩子的进度 | 记录自己孩子的进度 |
| 无需注册登录 | 无需注册登录 |
| 换个浏览器会丢失 | 换个浏览器会丢失 |
这种设计让系统开箱即用:不需要搭建用户系统、不需要数据库,但每个家庭都能独立跟踪进度。
存储格式:{"物理_0": true, "英语_1": true}(科目名_任务索引 → 是否完成)
生成产物:完成状态切换
toggleTask(el) {
const key = el.dataset.key;
const statusMap = this.getLocalStatus();
statusMap[key] ? delete statusMap[key] : statusMap[key] = true;
el.classList.toggle('done');
this.saveLocalStatus(statusMap);
const total = document.querySelectorAll('.task-item').length;
const done = document.querySelectorAll('.task-item.done').length;
this.updateProgress(done, total);
}
- 点击任务项 → 切换
.done类(删除线 + 绿色对勾 + 半透明) - 更新 localStorage → 刷新进度条
- 无需刷新页面,状态即时反馈
2.3 第二步:双列布局与视觉优化
提示词原文
📄 摘自
每日作业打卡提示词-前端.txt第二步:在上一版基础上修订布局:
1. 将作业列表改为左右两栏显示;
2. 内容必须保持原始排序顺序(不能打乱科目先后);
3. 两栏视觉高度需尽量均衡,避免一侧过长另一侧过短;
4. 移动端自动切换为单列布局;
5. 整体风格简洁清爽,使用蓝色主色调,完成项显示绿色对勾。
提示词设计要点:"在上一版基础上"告诉大模型不要重写已有功能;视觉约束具体化(不说"好看一点",而是说"蓝色主色调"、"绿色对勾")。
OpenClaw 执行过程
你(发送第二步提示词)
↓
OpenClaw(读取现有 static/index.html 的内容)
↓
OpenClaw(将现有代码 + 修改提示词一起发给大模型)
↓
OpenClaw(用 edit 工具精准替换需要修改的部分)
🔑 OpenClaw 的 edit 工具 vs write 工具:
write是覆盖整个文件(适合第一次生成)edit是精准替换指定片段(适合迭代修改,不影响其他代码)- 这意味着第二步修改时,第一步已验证的功能不会被破坏
生成产物:CSS 变量主题系统
:root {
--primary-color: #4a90d9; /* 主色调(蓝色) */
--done-color: #52c41a; /* 完成色(绿色) */
--bg-color: #f5f7fa; /* 背景色(浅灰) */
--card-bg: #ffffff; /* 卡片背景(白色) */
--nav-height: 56px; /* 导航栏高度 */
--footer-height: 48px; /* 页脚高度 */
}
- 所有页面(
index.html+upload.html)共享同一套 CSS 变量 - 修改主题只需改
:root中的值,全站自动同步
生成产物:顶部导航栏
.top-nav {
position: sticky; top: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
}
position: sticky:页面滚动时导航栏固定在顶部(类似手机 App 的顶部栏)z-index: 100:确保导航栏在所有内容之上
生成产物:响应式设计
@media (max-width: 640px) {
.homework-columns { flex-direction: column; } /* 双列变单列 */
.top-nav { padding: 0 16px; }
.nav-links a { padding: 5px 10px; font-size: 0.8rem; }
}
- 640px 断点:手机端双列自动切换为单列(家长最常用手机查看)
2.4 第三步:布局算法精准调优
提示词原文
📄 摘自
每日作业打卡提示词-前端.txt第三步:当前双列布局在卡片任务量差异较大时仍不均衡,请修订分配逻辑:
- 采用贪心算法:先将科目按任务数降序排列,再依次将每个科目分配到当前已分配任务总数更少的一列;
- 确保两列高度差最小化,同时严格保持科目的原始相对顺序;
- 不要改变现有的样式结构和交互逻辑,仅修改 JS 中的渲染分配部分。
🔑 这是本项目中最精彩的提示词设计:先描述问题("不均衡"),再给出算法名称("贪心算法")和具体步骤("降序排列 → 分配到更短的列"),最后划定修改范围("仅修改 JS 渲染分配")。大模型对经典算法有精确理解,比模糊说"让两列看起来均衡"有效得多。
生成产物:贪心算法双列瀑布流
render() {
let leftWeight = 0;
let rightWeight = 0;
this.homeworkData.forEach((subject) => {
const taskCount = subject.content.length;
// 贪心:哪列更短就放哪列
if (leftWeight <= rightWeight) {
leftCol.appendChild(card);
leftWeight += taskCount;
} else {
rightCol.appendChild(card);
rightWeight += taskCount;
}
});
}
算法原理:
1. 科目已按任务数降序排列(任务多的先分配)
2. 每次将科目放入当前总任务数更少的那一列
3. 用任务数近似列高度(假设每个任务卡片高度相近)
为什么不用简单的"交替分配"(左右轮流放一个)?
| 分配策略 | 场景举例 | 两列差 |
|---|---|---|
| 交替分配 | 物理(1项) 数学(1项) 英语(2项) 化学(1项) 历史(1项) | 差2 |
| 贪心分配 | 任务多时差异显著缩小 | 最优 |
2.5 OpenClaw 三步生成的完整工作流
回顾整个过程,OpenClaw 在每一步中做了以下工作:
第1步:基础功能
你 → 发送提示词 → OpenClaw → 大模型生成代码
OpenClaw → write 工具保存 static/index.html
OpenClaw → exec 工具启动服务器
你 → 浏览器验证 ✅
第2步:双列布局
你 → 发送修改提示词 → OpenClaw → 读取现有代码
OpenClaw → 现有代码 + 修改提示词 → 大模型
OpenClaw → edit 工具精准替换代码片段
你 → 浏览器验证 ✅
第3步:贪心算法
你 → "两列不均衡,请用贪心算法优化"
OpenClaw → 修改提示词 + 现有代码 → 大模型
OpenClaw → edit 工具替换渲染逻辑
你 → 浏览器验证 ✅
💡 使用建议:
- 分步执行:严格按 1→2→3 的顺序发送,每步确认效果后再进入下一步
- 数据验证:在第1步完成后,建议手动创建一个测试 JSON 文件验证渲染效果
- 样式微调:若对配色、间距有特定要求,可在第2步中替换为具体参数(如"主色 #4a90d9,卡片圆角 12px")
3. upload.html 实现:用 OpenClaw 一次生成作业录入页
upload.html 比 index.html 交互更复杂,涉及文件上传、异步请求、JSON 校验、状态机管理。本节聚焦于提示词如何驱动 OpenClaw 一次性生成这些复杂功能。
📄 参考提示词文件:
每日作业打卡提示词-前端.txt(upload.html 部分)该提示词采用了"角色与目标 + 核心接口定义 + 功能与交互要求 + 代码规范 + 输出要求"的五段式结构。
3.1 提示词设计解析:五段式结构
upload.html 的提示词与 index.html 不同,采用了一次性完整描述的方式(而非分步迭代),因为 upload.html 的核心复杂度在于状态管理和接口对接,这些功能紧密耦合,拆分反而增加联调成本。
📄 摘自
每日作业打卡提示词-前端.txt(upload.html 部分):1. 角色与目标:你是一位资深前端工程师。请为 Flask 作业 OCR 系统编写一个单文件
upload.html页面。该页面需与后端POST /api/homework/ocr接口无缝对接,提供流畅的图片上传、OCR 识别结果展示及错误反馈体验。不依赖任何前端框架,仅使用原生 HTML/CSS/JS。2. 核心接口规范:
- 请求方式:POST /api/homework/ocr
- Content-Type:multipart/form-data
- 请求参数:字段名必须为image(与后端request.files['image']一致)
- 成功响应:HTTP 200,JSON 格式{"text": "识别出的文字内容"}
- 失败响应:HTTP 4xx/5xx,JSON 格式{"error": "具体错误描述"}3. 功能与交互要求:支持点击上传和拖拽上传;使用 fetch API 异步提交;成功后渲染到可编辑文本框;失败时展示错误信息。
4. 代码规范:单文件结构(HTML/CSS/JS 内联);零外部依赖(不引入 CDN);防御性编程。
5. 输出要求:提供完整可直接运行的文件;附带本地测试说明。
🔑 提示词设计的核心思路,"接口规范前置":
在提示词的第二段就给出了完整的接口规范,包括请求方式、字段名、响应格式。为什么这样做?
- 大模型不会猜测你的后端 API:如果不指定字段名,它可能生成
file而不是image,导致与后端不匹配 - 状态码明确化:指定 4xx/5xx 的错误格式,大模型就会写
res.json()的错误解析逻辑 - 前后端对齐:接口规范就是前后端协作的"唯一真理来源"
3.2 OpenClaw 执行过程
你(发送 upload.html 提示词)
↓
OpenClaw(调用大模型生成代码)
↓
OpenClaw(用 write 工具直接保存为 static/upload.html)
↓
你(打开浏览器 http://localhost:5159/upload 查看效果)
↓
你(拖入一张作业照片测试)
🔑 与 index.html 的区别:index.html 用了三步迭代,upload.html 一步到位。选择哪种策略取决于功能复杂度的性质,UI 布局适合迭代(每步可独立验证),状态管理适合一步到位(功能间紧密耦合)。
3.3 生成产物:页面状态机
upload.html 本质上是一个状态机,有四种状态:
[初始状态] ──选择图片──▶ [上传中] ──成功──▶ [预览编辑]
│ │ │
│ 已上传 │ 失败 │ 保存成功
▼ ▼ ▼
[已锁定] ◀──────409─────────┘ [已保存]
🔑 初学者必读:什么是状态机?
状态机由状态集合、事件、转移规则组成:在某个状态下,特定事件会触发转移到另一个状态。简单来说,状态机就是在不同状态下,同样的操作会有不同结果。比如:
- 初始状态下,点击上传区域 → 弹出文件选择器
- 已锁定状态下,点击上传区域 → 无反应(被拦截)
- 预览编辑状态下,点击保存 → 发送 PUT 请求
明确画出状态转换图,大模型就能写出正确的条件判断逻辑。
3.4 生成产物:当日上传限制逻辑
这是本项目最重要的业务规则:每天只能上传一次作业,防止重复录入。
let todayUploaded = false;
async function checkTodayStatus() {
try {
const res = await fetch(`/api/homework/status?date=${targetDate}`, { cache: 'no-store' });
if (res.ok) {
const data = await res.json();
todayUploaded = !!data.uploaded_today;
updateUploadUI();
}
} catch (e) {
console.warn('上传状态预检失败,由后端兜底', e);
}
}
🔑 关键知识点:
1. cache: 'no-store':强制不走浏览器缓存,每次获取最新状态
2. 预检失败静默降级:网络异常时不阻塞上传,由后端 409 兜底
3. !! 双重取反:将 undefined/null/false/true 统一转为布尔值
3.5 生成产物:UI 锁定与解锁
function updateUploadUI() {
if (todayUploaded) {
uploadArea.classList.add('disabled');
fileInput.disabled = true;
tip.style.display = 'block'; // 显示黄色警告条
} else {
uploadArea.classList.remove('disabled');
fileInput.disabled = false;
tip.style.display = 'none';
}
}
.disabled类:CSS 中设置opacity: 0.7; cursor: not-allowed- 黄色警告条(背景
#fffbe6,文字#ad8b00):提示"今日已存在作业文件,每日仅限录入一次"
3.6 生成产物:文件上传交互(拖拽 + 点击)
uploadArea.addEventListener('click', () => !todayUploaded && fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
if (todayUploaded) return;
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
if (todayUploaded) return;
if (e.dataTransfer.files.length) handleFile(e.dataTransfer.files[0]);
});
🔑 关键知识点:
1. e.preventDefault():浏览器默认行为是打开文件,必须阻止
2. 每个事件都检查 todayUploaded:确保上传被禁用时所有入口都被拦截
3. dragover + dragleave:实现拖拽悬停高亮效果
3.7 生成产物:OCR 请求与响应处理
async function handleFile(file) {
if (todayUploaded) {
showStatus('今日已上传过作业,请勿重复操作', 'error');
return;
}
if (!file.type.startsWith('image/')) return showStatus('请上传图片文件', 'error');
if (file.size > 10 * 1024 * 1024) return showStatus('图片大小不能超过10MB', 'error');
// 显示预览 + 加载状态
previewImg.src = URL.createObjectURL(file);
jsonEditor.value = '⏳ 正在识别中...';
saveBtn.disabled = true;
try {
const formData = new FormData();
formData.append('image', file);
formData.append('date', targetDate);
const res = await fetch('/api/homework/ocr', { method: 'POST', body: formData });
// 409 冲突处理
if (res.status === 409) {
const errData = await res.json().catch(() => ({}));
showStatus(errData.error || '今日已上传过作业', 'error');
todayUploaded = true;
updateUploadUI();
return;
}
if (!res.ok) throw new Error(`识别失败: ${res.status}`);
const data = await res.json();
jsonEditor.value = JSON.stringify(data, null, 2); // 格式化 JSON
saveBtn.disabled = false;
showStatus('✅ 识别完成,请核对内容后保存', 'success');
} catch (err) {
jsonEditor.value = '';
showStatus(err.message, 'error');
}
}
🔑 关键知识点:
1. FormData:浏览器自动构建 multipart/form-data 请求,字段名 image 必须与后端 request.files['image'] 一致
2. URL.createObjectURL(file):在浏览器本地生成图片预览 URL,无需先上传到服务器
3. JSON.stringify(data, null, 2):格式化 JSON(缩进 2 格),方便用户在 textarea 中阅读和编辑
4. 双重防御:前端先校验(文件类型、大小、今日限制),后端再校验(409 兜底)
3.8 生成产物:实时 JSON 校验
jsonEditor.addEventListener('input', () => {
try {
JSON.parse(jsonEditor.value);
jsonEditor.classList.remove('invalid');
saveBtn.disabled = false;
} catch {
jsonEditor.classList.add('invalid');
saveBtn.disabled = true;
}
});
- 每输入一个字符就校验一次,
input事件(不是change事件) - 非法时:边框变红,背景变浅红,保存按钮禁用
- 这是表单校验的最佳实践,实时反馈,而不是等到点击保存才报错
3.9 生成产物:保存作业与自动跳转
saveBtn.addEventListener('click', async () => {
const homeworkData = JSON.parse(jsonEditor.value);
const res = await fetch(`/api/homework/${targetDate}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(homeworkData)
});
if (!res.ok) throw new Error(`保存失败: ${res.status}`);
todayUploaded = true;
updateUploadUI();
setTimeout(() => window.location.href = `/homework?date=${targetDate}`, 1500);
});
- 保存成功后 1.5 秒自动跳转到列表页查看结果
- 同时更新
todayUploaded = true锁定上传区域
4. 后端 Python 实现:用 OpenClaw 生成 Flask 服务
本节聚焦于OpenClaw 如何通过后端提示词生成完整的 Flask 后端代码,并自动安装依赖、配置环境、启动服务。与前端提示词不同,后端提示词更强调架构约束、安全规范和异常处理。
📄 参考提示词文件:
每日作业提示词后端.md该提示词采用了"角色与目标 + 核心技术约束 + 文件结构与职责划分 + 具体功能需求 + 输出要求"的五段式结构。
4.1 提示词设计解析:架构约束前置
后端提示词的核心挑战是架构约束,如何让大模型生成符合专业软件工程规范的代码?答案是:把架构规则写进提示词。
📄 摘自
每日作业提示词后端.md:1. 角色与目标:你是一位资深 Python 后端工程师。请基于 Flask 框架开发一个"作业 OCR 识别与保存"模块。核心要求是:业务逻辑与 Web 框架解耦,且文件保存目录必须支持全局动态配置。
2. 核心技术约束:
- 配置优先级:保存目录需遵循环境变量 > .env文件 > 代码默认值三级策略。
- 依赖注入:homework_ocr.py作为纯业务模块,禁止直接导入app实例或使用current_app,所有外部配置必须通过函数参数显式传入。
- 安全性:文件名必须使用werkzeug.utils.secure_filename处理;启动时自动创建目录。
- 健壮性:OCR 接口需包含完整的异常捕获,区分内存错误、IO错误和业务错误,返回明确的 HTTP 状态码。3. 文件结构与职责划分:
-app.py:配置定义与路由入口
-homework_ocr.py:纯业务逻辑层(不依赖 Flask)
-.env:本地环境配置4. 具体功能需求:全局配置初始化、保存函数、API 接口、防御性编程。
5. 输出要求:完整可运行代码 +
.env.example+ Docker/生产环境配置说明。
🔑 提示词设计的核心思路,"架构约束前置":
在提示词的第二段就给出了四层技术约束(配置优先级、依赖注入、安全性、健壮性)。为什么这样做?
- 大模型不会主动分层:如果不指定"业务逻辑与 Web 框架解耦",它会把所有代码塞进
app.py - 安全规范显式化:如果不要求
secure_filename,它可能直接拼接用户输入的文件名,留下路径遍历漏洞 - 异常处理具体化:说"区分内存错误、IO错误和业务错误",比说"做好异常处理"有效 100 倍
4.2 OpenClaw 执行后端开发的完整过程
这是 OpenClaw 最强大的能力展示,从提示词到运行中的服务,全程自动化:
你(发送后端提示词)
↓
OpenClaw(调用大模型生成代码)
↓ 返回 app.py + homework_ocr.py + .env.example
OpenClaw(用 write 工具分别创建三个文件)
↓ write("app.py", ...)
↓ write("homework_ocr.py", ...)
↓ write(".env.example", ...)
OpenClaw(用 exec 工具安装依赖)
↓ exec("pip install flask flask-cors openai python-dotenv")
你(告诉 OpenClaw "帮我把 API Key 配置到 .env 文件")
↓
OpenClaw(用 write 工具写入 .env)
↓
你(告诉 OpenClaw "启动服务器")
↓
OpenClaw(用 exec 工具启动 Flask)
↓ exec("python app.py") → 服务运行在 localhost:5159
你(打开浏览器测试,或告诉 OpenClaw "用 curl 测试")
↓
OpenClaw(用 exec 工具执行 curl 测试命令)
↓
你(告诉 OpenClaw "看看日志")
↓
OpenClaw(用 read 工具读取 logs/homework_app.log)
🔑 关键理解:
- OpenClaw 不只是代码生成器:它能安装依赖、配置环境、启动服务、执行测试、查看日志,完整的开发闭环
- 你甚至不需要打开编辑器:整个后端开发过程,全部在 OpenClaw 的对话框中完成
4.3 生成产物:分层架构设计
| 文件 | 职责 | 关键约束 |
|---|---|---|
app.py |
路由定义 + Flask 配置 | 只处理 HTTP 请求/响应 |
homework_ocr.py |
纯业务逻辑 | 不导入 Flask,配置通过参数传入 |
🔑 为什么要把业务逻辑从 Flask 路由中剥离?
- 可测试性:homework_ocr.py 可以在不启动 Flask 的情况下独立测试
- 可复用性:同样的逻辑可以迁移到 FastAPI、CLI 工具或其他框架
- 职责清晰:修改路由不影响业务逻辑,修改业务逻辑不影响路由
4.4 大模型客户端初始化
此处初始化的是兼容 OpenAI 协议的多模态大模型客户端。
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY", "your-api-key-here"),
base_url=os.getenv("LLM_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
)
MODEL_NAME = os.getenv("LLM_MODEL", "qwen3.8-flash")
🔑 配置三级优先级:
- 环境变量(最高):生产环境通过 systemd
EnvironmentFile注入 .env文件:本地开发时使用python-dotenv加载- 代码默认值(最低):仅作为兜底,方便快速启动测试
本教程默认使用 qwen3.8-flash。qwen-vl-max 已于 2026 年 10 月 10 日下线,请勿使用。
qwen3.8-flash是一个可以识别图像,输出文本的大模型,并且费用比qwen3.8-max便宜很多,价格相差10倍左右。
4.5 生成产物:核心 Prompt 设计(大模型输出结构化 JSON)
prompt = f"""你是一个作业录入助手。请识别图片中的各科作业内容,并将其转换为严格的 JSON 格式。
要求:
1. 日期固定为:{target_date}
2. 提取所有科目(subject)和对应的具体任务列表(content)。
3. 如果某科没有具体任务,content 为空数组。
4. 仅输出合法的 JSON 字符串,不要包含任何 Markdown 标记(如 ```json)、解释性文字或多余字符。
期望的 JSON 结构如下:
{{
"date": "{target_date}",
"homework": [
{{
"subject": "数学",
"content": ["完成练习册第10页", "订正试卷"]
}}
]
}}
"""
🔑 视觉大模型 Prompt 设计技巧:
- 角色设定:"你是一个作业录入助手",限定模型的行为边界
- 强制 JSON 输出:"仅输出合法的 JSON 字符串",大模型默认喜欢加 Markdown 代码块,必须显式禁止
- 给出完整示例:用真实的 JSON 结构作为"期望输出",比文字描述精确 100 倍
- 边界条件说明:"如果某科没有任务,content 为空数组",防止模型输出
content: null或省略字段 - 日期注入:用 f-string 将
target_date直接写入 Prompt,确保模型不会编造日期 - temperature=0.1:降低随机性,保证输出格式稳定(结构化输出需要低温度)
4.6 生成产物:Base64 图片编码
def image_to_base64(image_path: str) -> str:
with open(image_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
为什么用 Base64? 用户上传的是临时文件,没有公网 URL。OpenAI 兼容协议支持 data:image/jpeg;base64,xxx 格式。对于作业照片(通常 < 5MB),Base64 增加约 33% 体积完全可接受。
4.7 生成产物:JSON 清理与异常分层
raw_text = response.choices[0].message.content.strip()
# 清理 Markdown 代码块标记
clean_text = re.sub(r'^```json\s*', '', raw_text, flags=re.IGNORECASE)
clean_text = re.sub(r'\s*```$', '', clean_text)
return json.loads(clean_text)
🔑 异常分层:
json.JSONDecodeError→ValueError(大模型返回了非 JSON 文本)Exception→RuntimeError(网络超时、API key 无效等)- 上层可根据异常类型给出不同的错误提示
4.8 生成产物:Flask 路由实现
OCR 上传路由(含临时文件处理)
@app.route('/api/homework/ocr', methods=['POST'])
def ocr_upload():
# 保存为临时文件
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp_file:
file.save(temp_file.name)
temp_path = temp_file.name
if homework_json_exist(target_date):
return jsonify({"error": "文件已存在"}), 409
try:
result_data = recognize_homework(temp_path, target_date)
return jsonify(result_data), 200
except Exception as e:
logger.error(f"OCR 处理失败 | 日期: {target_date} | 错误: {e}")
return jsonify({"error": str(e)}), 500
finally:
if os.path.exists(temp_path):
os.remove(temp_path)
🔑 finally 块的重要性:无论成功还是异常都清理临时文件,防止磁盘被临时图片撑满。
保存作业路由(纵深防御)
@app.route('/api/homework/<date>', methods=['PUT'])
def save_homework(date):
if not request.is_json:
return jsonify({"error": "请求体必须是 JSON 格式"}), 400
data = request.get_json()
if "homework" not in data or not isinstance(data["homework"], list):
return jsonify({"error": "数据缺少 homework 列表字段"}), 400
- 第一层(路由层):检查
request.is_json和homework字段 - 第二层(业务层):
save_homework_json内部检查文件是否存在 - 这是 纵深防御(Defense in Depth) 的安全实践
4.9 生成产物:数据一致性保障
def save_homework_json(data: dict, target_date: str) -> str:
# 强制覆写 date 字段,防止前端传入的 date 与 URL 中的 date 不一致
data["date"] = target_date
file_path = os.path.join(SAVE_DIR, f"{target_date}.json")
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
🔑 关键知识点:
data["date"] = target_date:强制覆写前端传来的 date 字段,确保文件内容与文件名一致ensure_ascii=False:保证中文正常显示,不被转义为\u4e2d\u6587indent=2:生成的 JSON 文件人类可读,方便手动检查和编辑
4.10 生成产物:生产级日志配置
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler(
"logs/homework_app.log", maxBytes=5*1024*1024, backupCount=3, encoding="utf-8"
)
- 双输出:同时写到控制台和文件
- 日志轮转:单文件最大 5MB,保留 3 个备份,防止日志撑爆磁盘
- 抑制第三方噪音:
openai和werkzeug设为 WARNING 级别
4.11 生成产物:路径遍历安全防护
if not all(c.isalnum() or c == '-' for c in date):
abort(400, description="非法的日期格式")
恶意用户可能传入 ../../../etc/passwd,通过白名单校验(只允许字母、数字和连字符)完全阻止此类攻击。这也是提示词中必须要求大模型实现的安全约束。
5. 测试、部署与运行
代码生成完毕后,OpenClaw 同样能帮你完成测试和生产部署。
5.1 用 OpenClaw 执行接口测试
你可以直接在 OpenClaw 对话框中说"帮我测试一下 OCR 接口",OpenClaw 会用 exec 工具执行以下命令:
# 测试 OCR 上传
curl -X POST -F "image=@test.jpg" http://127.0.0.1:5159/api/homework/ocr
# 测试 409 冲突(重复上传)
curl -s -w "\nHTTP Status: %{http_code}\n" \
-X POST -F "image=@test.jpg" -F "date=2026-09-16" \
http://127.0.0.1:5159/api/homework/ocr
# 测试保存作业
curl -X PUT -H "Content-Type: application/json" \
-d '{"date":"2026-09-16","homework":[{"subject":"测试","content":["test"]}]}' \
http://127.0.0.1:5159/api/homework/2026-09-16
🔑 OpenClaw 的测试能力:
- 执行 curl 命令并自动解析返回结果,告诉你测试是否通过
- 读取日志文件,帮你定位错误原因
- 如果测试失败,直接将报错信息发给大模型进行 Debug
5.2 人工验证清单
- [ ] 访问首页
index.html,页面正常加载,导航栏显示正确 - [ ] 访问
upload.html,拖拽图片,等待识别,返回 JSON 结构正确 - [ ] 再次上传同一天的图片,收到 409 错误提示
- [ ] 编辑 JSON 并保存,刷新 index.html 能看到更新
- [ ] 输入非法日期(如
2026/09/14或../../../etc/passwd),返回 400 错误 - [ ] JSON 编辑器中输入非法 JSON,保存按钮被禁用
- [ ] 查看
logs/homework_app.log,日志格式正确,无 Python 堆栈泄露
5.3 为什么不能只用 flask run?
| 维度 | flask run |
Gunicorn + Nginx |
|---|---|---|
| 并发 | 单线程,同时只能处理 1 个请求 | 多 worker,可处理数十并发 |
| 稳定性 | 崩溃后不自动重启 | systemd 自动重启(Restart=always) |
| 性能 | 纯 Python HTTP 服务器 | Gunicorn 用 C 优化,Nginx 处理静态文件 |
| 安全 | 直接暴露 Flask | Nginx 反向代理,隐藏内部端口 |
5.4 用 OpenClaw 生成部署配置
你可以直接告诉 OpenClaw"帮我生成 systemd 和 Nginx 配置",它会自动生成以下文件并用 write 工具保存:
systemd 服务配置
[Unit]
Description=homework Analysis API Service
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/srv/app/homework/
EnvironmentFile=/etc/homework/env
ExecStart=/usr/bin/gunicorn -w 2 -b 127.0.0.1:5159 app:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
🔑 关键配置:
- -w 2:2 个 worker,一个处理 OCR(耗时较长),另一个响应 API
- -b 127.0.0.1:5159:只监听回环地址,不暴露公网(由 Nginx 代理)
- Restart=always:崩溃后 5 秒自动重启
- EnvironmentFile:API Key 等敏感信息从独立文件注入
Nginx 反向代理
location ^~ /api/homework/ {
proxy_pass http://127.0.0.1:5159;
proxy_set_header Host $host;
proxy_read_timeout 90s; # LLM 识别耗时较长
proxy_connect_timeout 10s; # 后端挂了快速失败
}
5.5 班级共享部署方案
部署完成后,全班家长通过浏览器访问同一个 URL:
http://your-server-ip:5159 # 内网直接访问
http://edu.bjbook.net/xxl/homework/ #部署在公网服务器上,Nginx代理
- 作业数据:存储在服务器端 JSON 文件中,所有家长看到相同内容
- 完成进度:每个家长浏览器的 localStorage 独立存储,互不干扰
- 无需注册登录:打开网页即可使用
6. 总结
6.1 大模型编程的四个阶段
阶段 1:生成 —— "请帮我写一个 XXX"
↓
阶段 2:迭代 —— "在现有基础上,增加 XXX"
↓
阶段 3:调试 —— "运行报错了,错误信息如下..."
↓
阶段 4:重构 —— "请优化这段代码的结构"
一个完整项目通常会经历这四个阶段的多次循环。
6.2 提示词设计的核心心法
| 心法 | 一句话解释 | 本项目中的应用 |
|---|---|---|
| 接口规范优先 | 先定义数据结构,再写代码 | 先设计 JSON + API 规范表,再生成前后端 |
| 上下文为王 | 给大模型足够的背景 | 每次 Prompt 包含数据结构 + API 规范 |
| 循序渐进 | 拆分需求,逐步验证 | index.html 分 3 步迭代,每步可独立验证 |
| 架构约束前置 | 先说规则,再说功能 | 后端提示词第二段就给出四层技术约束 |
| 安全规范显式化 | 不依赖大模型的"常识" | 明确要求 secure_filename、白名单校验 |
| 纵深防御 | 多层校验 | 前端校验 + 路由校验 + 业务层校验 |
| 回传堆栈 | 完整堆栈给大模型 | 报错信息直接复制给 OpenClaw 让它 Debug |
6.3 提示词模板的复用与改编
| 模板文件 | 用途 | 改编方法 |
|---|---|---|
每日作业打卡提示词-前端.txt |
分三步生成 index.html + 一次生成 upload.html | 改数据结构、导航项 |
完整提示词.md |
一次性生成全项目 | 改业务规则、API 表 |
每日作业提示词后端.md |
生成 app.py + homework_ocr.py | 改存储方式、框架 |
改编原则:修改数据结构时前后端同步更新;修改业务规则时先改完整提示词再拆分。
6.4 OpenClaw 能力速查
| 你想做 | 对 OpenClaw 说什么 | OpenClaw 怎么做 |
|---|---|---|
| 生成代码 | 发送提示词 | 调用大模型生成,write 保存到本地文件 |
| 修改代码 | "把 XX 改成 YY" | edit 工具精准替换指定代码片段 |
| 安装依赖 | "安装 flask 和 openai" | exec 执行 pip install |
| 启动服务 | "启动服务器" | exec 执行 python app.py |
| 测试接口 | "用 curl 测试 OCR" | exec 执行 curl 命令并分析结果 |
| 查看日志 | "看看日志文件" | read 工具读取日志内容 |
| Debug | 复制报错信息 | 发给大模型分析并自动修复代码 |
| 部署上线 | "生成 systemd 配置" | 生成配置并保存,执行部署命令 |
| 记住上下文 | 自动 | 记忆系统保存项目状态,下次继续 |
6.5 课后练习
基础练习
- 使用本教程中的提示词,在 OpenClaw 中从零生成"学习助手"项目
- 替换 API Key 和 base_url,用真实的视觉大模型识别一张作业照片
- 修改
index.html,增加"按科目筛选作业"的筛选功能
进阶挑战
- 课程表功能:参照本项目流程,用 OpenClaw 从零实现课程表模块
- 部署实战:让 OpenClaw 帮你部署到 Linux 服务器,配置 systemd + Nginx
- 作业录入支持密码验证: 可在
upload.html增加密码输入框,前端将密码随FormData发送,后端在/api/homework/ocr中校验request.form.get('password')。 - 周末作业在周六周日均可以完整显示
附录 A:环境依赖与安装
pip install flask flask-cors openai python-dotenv gunicorn
# 启动开发服务器
python app.py # 访问 http://localhost:5159
# 生产部署
sudo cp conf/homework.service /etc/systemd/system/
sudo systemctl enable homework && sudo systemctl start homework
附录 B:常见错误速查表
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
ModuleNotFoundError: No module named 'flask_cors' |
未安装依赖 | pip install flask-cors |
AssertionError: View function mapping... |
路由 endpoint 冲突 | 检查重复函数名 |
OCR 返回 JSONDecodeError |
大模型返回了非 JSON | 检查 Prompt + 加正则清理 |
| 前端报 CORS 错误 | 前后端跨域 | 确认 CORS(app) 已调用 |
| Nginx 返回 504 | LLM 识别超时 | 增加 proxy_read_timeout 到 90s |
409 Conflict 但实际未上传 |
SAVE_DIR 路径错误 | 检查环境变量配置 |
💡 本章小结:你不需要成为编程专家才能构建一个实用的 Web 应用。有了大模型生成代码的能力,再加上 OpenClaw 的全流程协作,自动生成代码并保存到本地、自动安装依赖、自动启动服务、自动执行测试,任何人都可以从零做出一个全班共享的实用工具。关键是要学会"正确提问":这就是本章反复强调的 Prompt 工程。
本文为《OpenClaw 实战指南》系列第 16 篇。