| 这个作业属于哪个课程 | 2026-01软件工程与软件工程实践 |
|---|---|
| 这个作业要求在哪里 | 第一次个人作业要求 |
| 这个作业的目标 | 完成 GitHub 与博客园账号准备、调用 HuggingFace Flux 模型生成写实图像并搭建交互前端、搭建 GitHub 个人主页、撰写技能自评与学习规划随笔 |
| 学号 | 102401623 |
软件工程第一次个人作业
目录
一、准备工作
1.1 GitHub 账号
已在 https://github.com/ 注册账号,用户名为 DUJIAWEI123374,完善了个人资料。

1.2 博客园账号
已在 https://www.cnblogs.com/ 注册账号,昵称 DUJIAWEI,设置了博客样式,并完成实名认证。
1.3 关注老师与助教博客
- 任课老师:(https://www.cnblogs.com/zhaowuhuang) ✅ 已关注
- 助教:(https://www.cnblogs.com/prcy) ✅ 已关注
- 助教:(https://www.cnblogs.com/jsm-blog) ✅ 已关注
1.4 加入班级博客
已通过班级邀请链接加入 福州大学 2026-01 软件工程与软件工程实践 班级博客,并完成实名制。
二、HuggingFace API 调用
2.1 账号注册与 API 获取
访问 https://huggingface.co/ 完成注册,登录后进入 Settings → Access Tokens,点击 Create new token 创建具有推理权限的 Token。本次创建的 Token 名称为 zpw。

Token 创建后需妥善保存,它是调用 HF Inference API 的身份凭证。
2.2 模型介绍
XLabs-AI/flux-RealismLora 是 XLabs AI 发布的、基于 Black Forest Labs FLUX.1-dev 训练的写实风格 LoRA 适配器,专门用于提升生成图像的照片级真实感。模型地址:https://huggingface.co/XLabs-AI/flux-RealismLora
该模型支持通过 Hugging Face Inference Providers(fal-ai、replicate 等服务商)调用,也可通过社区公共 Space 使用。
2.3 环境准备
- 操作系统:Windows 11
- Python 版本:3.14
- 依赖安装:
pip install flask huggingface_hub gradio_client Pillow requests
网络说明:国内网络直连 Hugging Face 的 inference 域名会被阻断,本机通过 SakuraCat 本地代理(127.0.0.1:12450)出网,因此在代码中设置
HTTP_PROXY/HTTPS_PROXY环境变量。
2.4 后端代码(app.py)
后端采用 Flask,提供 GET /(渲染前端页面)和 POST /generate(接收提示词并调用模型)两个接口。设计了双通道自动回退机制:优先使用 HF Inference API(fal-ai),额度耗尽时自动回退到公共 Space API。
import os
os.environ["HTTP_PROXY"] = "http://127.0.0.1:12450"
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:12450"
import io, base64, time
from datetime import datetime
from pathlib import Path
from flask import Flask, request, jsonify, render_template
from huggingface_hub import InferenceClient
from gradio_client import Client as GradioClient
from PIL import Image
HF_TOKEN = "hf_************************HSnl" # 替换为自己的 Token
MODEL_ID = "XLabs-AI/flux-RealismLora"
PROVIDER = "fal-ai"
SPACE_ID = "DamarJati/FLUX.1-RealismLora"
app = Flask(__name__)
hf_client = InferenceClient(provider=PROVIDER, api_key=HF_TOKEN)
DEFAULT_STYLE = ("photorealistic, natural lighting, detailed skin texture, "
"35mm photography, high detail, lifelike")
STYLE_MAP = {
"default": DEFAULT_STYLE,
"scene": "photorealistic landscape, golden hour lighting, ultra detailed, 35mm photography, high dynamic range",
"animal": "photorealistic animal portrait, sharp focus on eyes, detailed fur texture, natural lighting, 85mm lens",
"street": "photorealistic street photography, candid moment, cinematic lighting, 35mm lens, high detail",
"none": "",
}
BASE_DIR = Path(__file__).resolve().parent
GEN_DIR = BASE_DIR / "generated"
GEN_DIR.mkdir(exist_ok=True)
LOG_FILE = BASE_DIR / "api_call_log.txt"
def write_log(status, prompt, http_info, duration, channel):
line = (f"[{status}] {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | "
f"model={MODEL_ID} | channel={channel} | {http_info} | "
f"{duration:.2f}s | prompt={prompt}\n")
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(line)
def generate_via_hf(full_prompt):
image = hf_client.text_to_image(prompt=full_prompt, model=MODEL_ID)
return image, "hf-inference(fal-ai)"
def generate_via_space(full_prompt):
client = GradioClient(SPACE_ID, verbose=False, token=HF_TOKEN)
result = client.predict(prompt=full_prompt, cfg_scale=3.2, steps=32,
randomize_seed=True, seed=0, width=1024, height=768,
lora_scale=0.85, api_name="/run_lora")
img_path = result[0] if isinstance(result, tuple) else result
return Image.open(img_path).convert("RGB"), f"space({SPACE_ID})"
@app.route("/")
def index():
return render_template("index.html")
@app.route("/generate", methods=["POST"])
def generate_image():
data = request.get_json(silent=True) or {}
prompt = (data.get("prompt") or "").strip()
style_key = data.get("style", "default") or "default"
if not prompt:
return jsonify({"error": "请输入提示词"}), 400
style_words = STYLE_MAP.get(style_key, DEFAULT_STYLE)
full_prompt = f"{prompt}, {style_words}" if style_words else prompt
t0 = time.time()
image, channel, errors = None, "", []
try:
image, channel = generate_via_hf(full_prompt)
except Exception as e:
errors.append(f"主通道[{PROVIDER}]失败: {type(e).__name__}: {str(e)[:120]}")
try:
image, channel = generate_via_space(full_prompt)
except Exception as e2:
errors.append(f"回退通道[Space]失败: {type(e2).__name__}: {str(e2)[:120]}")
duration = time.time() - t0
if image is None:
write_log("FAIL", prompt, "HTTP 500", duration, "none")
return jsonify({"error": ";".join(errors)}), 500
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{ts}.png"
image.save(GEN_DIR / filename)
buf = io.BytesIO()
image.save(buf, format="PNG")
img_base64 = base64.b64encode(buf.getvalue()).decode("utf-8")
write_log("SUCCESS", prompt, "HTTP 200", duration, channel)
return jsonify({"success": True, "image": img_base64, "filename": filename,
"elapsed": round(duration, 2), "full_prompt": full_prompt,
"channel": channel, "fallback_note": errors[0] if errors else None})
if __name__ == "__main__":
app.run(debug=True, host="127.0.0.1", port=5000)
2.5 前端代码(index.html)
前端为单页应用,包含提示词输入框、风格预设下拉、生成按钮、图像展示区和生成历史记录,通过 fetch('/generate') 与后端通信。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Flux 写实图像生成器</title>
<style>
body { font-family: "Microsoft YaHei", Arial, sans-serif; background: linear-gradient(135deg, #0f172a, #1e293b); min-height: 100vh; color: #e2e8f0; display: flex; justify-content: center; padding: 40px 20px; }
.container { width: 100%; max-width: 900px; }
h1 { font-size: 28px; background: linear-gradient(90deg, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.card { background: rgba(30,41,59,.7); border: 1px solid rgba(148,163,184,.2); border-radius: 16px; padding: 24px; margin-bottom: 20px; }
textarea { width: 100%; min-height: 80px; padding: 12px 14px; background: #0f172a; border: 1px solid #334155; border-radius: 10px; color: #e2e8f0; font-size: 15px; }
button { padding: 11px 28px; font-size: 15px; font-weight: 600; background: linear-gradient(90deg, #38bdf8, #818cf8); color: #0f172a; border: none; border-radius: 10px; cursor: pointer; }
#imageContainer { display: none; margin-top: 8px; text-align: center; }
#imageContainer img { max-width: 100%; border-radius: 12px; border: 1px solid #334155; }
</style>
</head>
<body>
<div class="container">
<h1>Flux 写实图像生成器</h1>
<p>Hugging Face API · XLabs-AI/flux-RealismLora · 交互式生成</p>
<div class="card">
<label>提示词(Prompt,支持中英文)</label>
<textarea id="promptInput" placeholder="例如:an elderly Chinese fisherman with weathered skin"></textarea>
<div style="margin:14px 0">
<label>风格预设:</label>
<select id="styleSelect">
<option value="default">写实人像(默认)</option>
<option value="scene">户外风景</option>
<option value="animal">动物特写</option>
<option value="street">城市街拍</option>
<option value="none">不加增强词</option>
</select>
<button onclick="generate()">生成图像</button>
</div>
<div id="status">就绪</div>
<div id="imageContainer"><img id="resultImg"><div id="meta"></div></div>
</div>
<div class="card"><label>生成记录</label><ul id="history"><li>暂无记录</li></ul></div>
</div>
<script>
const STYLE_MAP = {
default: "photorealistic, natural lighting, detailed skin texture, 35mm photography, high detail, lifelike",
scene: "photorealistic landscape, golden hour lighting, ultra detailed, 35mm photography, high dynamic range",
animal: "photorealistic animal portrait, sharp focus on eyes, detailed fur texture, natural lighting, 85mm lens",
street: "photorealistic street photography, candid moment, cinematic lighting, 35mm lens, high detail",
none: ""
};
async function generate() {
const prompt = document.getElementById('promptInput').value.trim();
const style = document.getElementById('styleSelect').value;
const status = document.getElementById('status');
if (!prompt) { status.textContent = '请输入提示词'; return; }
status.textContent = '生成中...';
const res = await fetch('/generate', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({prompt, style}) });
const data = await res.json();
if (data.success) {
document.getElementById('resultImg').src = 'data:image/png;base64,' + data.image;
document.getElementById('imageContainer').style.display = 'block';
document.getElementById('meta').textContent = '✅ 生成成功 | 耗时 ' + data.elapsed + 's | ' + data.filename;
status.textContent = '✅ 生成成功!';
} else { status.textContent = '❌ ' + data.error; }
}
</script>
</body>
</html>
2.6 启动与调用
python app.py
# 浏览器访问 http://127.0.0.1:5000
2.7 API 调用成功记录
Flask 控制台日志:
* Running on http://127.0.0.1:5000
127.0.0.1 - - [10/Sep/2026 20:37:00] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [10/Sep/2026 20:37:14] "POST /generate HTTP/1.1" 200 -
api_call_log.txt 日志:
[SUCCESS] 2026-09-10 20:37:14 | model=XLabs-AI/flux-RealismLora | channel=hf-inference(fal-ai) | HTTP 200 | 8.31s | prompt=an elderly Chinese fisherman with a straw hat, weathered skin, portrait
[SUCCESS] 2026-09-10 20:50:50 | model=XLabs-AI/flux-RealismLora | channel=space(DamarJati/FLUX.1-RealismLora) | HTTP 200 | 26.68s | prompt=an old fisherman
[SUCCESS] 2026-09-10 20:56:17 | model=XLabs-AI/flux-RealismLora | channel=space(DamarJati/FLUX.1-RealismLora) | HTTP 200 | 26.69s | prompt=an old fisherman
HTTP 响应(JSON):
{
"success": true,
"channel": "space(DamarJati/FLUX.1-RealismLora)",
"elapsed": 26.68,
"filename": "20260910_205050.png",
"full_prompt": "an old fisherman, photorealistic, natural lighting, detailed skin texture, 35mm photography, high detail, lifelike",
"image": "<base64 图像数据>"
}
2.8 前端页面最终生成图像截图

前端显示:提示词「an old fisherman」+ 写实人像风格,耗时 26.69s,生成成功,完整提示词为
an old fisherman, photorealistic, natural lighting, detailed skin texture, 35mm photography, high detail, lifelike。
2.9 提示词设计思路与修改过程
写实图像生成的提示词遵循「主体 + 光线 + 镜头 + 细节 + 风格关键词」五维结构:
| 维度 | 作用 | 示例词 |
|---|---|---|
| 主体描述 | 明确画什么,越具体越好 | elderly Chinese fisherman, straw hat, weathered skin |
| 光线 | 决定画面氛围和真实感 | natural lighting, golden hour |
| 镜头 | 模拟真实相机参数 | 35mm photography, 85mm lens |
| 细节 | 提升纹理和清晰度 | detailed skin texture, high detail |
| 风格关键词 | 锁定写实风格 | photorealistic, lifelike |
修改过程:
- 第一版(朴素):
an old fisherman— 仅描述主体,缺少光线、镜头、细节约束,真实感不足 - 第二版(细化主体):
an elderly Chinese fisherman with a straw hat, weathered skin, portrait— 增加国籍、年龄、道具、皮肤特征和构图 - 第三版(追加写实增强词,最终版):在用户提示词后自动追加
photorealistic, natural lighting, detailed skin texture, 35mm photography, high detail, lifelike,从光线、镜头、皮肤纹理、画质四维度强化真实感
最终生成的老渔夫肖像皮肤纹理细腻、光影自然,达到照片级真实效果。
2.10 遇到的问题与解决
| 问题 | 现象 | 解决方案 |
|---|---|---|
| flask_cors 未安装 | 后端 import 报错,服务无法启动 | 同源部署无需 CORS,移除该依赖 |
| 模板路径错误 | index.html 不在 templates/ 目录,首页 500 | 将 index.html 移入 templates/ |
| img 标签笔误 | < img 多空格,图像不渲染 |
修正为 <img> |
| 国内网络阻断 | 直连 api-inference 域名连接被拒 | 设置代理环境变量走 127.0.0.1:12450 |
| 402 额度耗尽 | HF Inference Providers 免费额度用完 | 增加公共 Space(ZeroGPU)回退通道 |
2.11 API 调用体验与心得
- API 封装简洁:
InferenceClient.text_to_image()一行即可完成图像生成,无需手动处理 HTTP 请求和图像解码 - 免费额度有限:新账号约 1~2 次图像生成即耗尽,正式使用需充值或订阅 PRO
- 提示词是关键:写实风格的核心在于具体——具体的主体特征、光线条件、镜头参数,追加 photorealistic 等关键词后真实感有质的提升
- 工程健壮性:主通道 + 回退通道的自动降级机制保证了前端交互不中断,是必要的工程实践
- 网络环境:国内访问 HF 推理服务必须配置代理,开发时需显式设置代理环境变量
三、GitHub 个人主页搭建
采用方案一:创建与 GitHub ID 同名的特殊仓库 DUJIAWEI123374/DUJIAWEI123374,在根目录编写 README.md 作为个人资料自述文件。
3.1 README 内容结构
- 关于我:个人介绍、学校专业、兴趣爱好
- 个人成果 & 专业技能:已掌握能力、感兴趣方向、自我评估
- 未来三年发展规划:短期/中期/长期目标
- 代码量:当前与目标
- 联系方式
3.2 GitHub 主页截图

3.3 README 核心内容
https://github.com/DUJIAWEI123374/DUJIAWEI123374
四、技能树与自我评估
4.1 已具备的专业知识与能力
- 能力 A — 编程基础:掌握 Python、C/C++ 基本语法,能独立完成课程编程作业和小型算法题
- 能力 B — Web 前端:了解 HTML/CSS/JavaScript,能编写简单的静态网页和交互效果
- 能力 C — 开发工具:熟练使用 Git 进行版本控制,掌握 Markdown 文档编写,熟悉 VS Code 开发环境
- 能力 D — AI 应用:能调用 Hugging Face API 进行图像生成,会使用 AI 工具辅助开发和学习
- 能力 E — 文档能力:具备良好的文档编写习惯,能撰写需求说明和技术文档
4.2 感兴趣的技术方向
- 🌐 Web 全栈开发(前端交互 + 后端服务)
- 🤖 人工智能与大模型应用开发(API 调用、Prompt Engineering)
- ⚙️ 软件架构与系统设计
4.3 欠缺的能力
- 算法能力:数据结构与算法的熟练度不足,复杂问题的分析能力有待提升
- 工程经验:缺乏大型项目的架构设计和团队协作经验
- 测试能力:单元测试、集成测试的实践较少
- 后端技术:对数据库、服务器部署、微服务等后端技术了解较浅
- 英语阅读:阅读英文技术文档的速度和准确性需要提高
五、代码量
| 阶段 | 代码量 | 说明 |
|---|---|---|
| 截至目前 | 约 3000 行 | 课程作业 + 小项目 + 编程练习 |
| 本学期目标 | 8000+ 行 | 软件工程课程项目 + 个人项目 + AI 应用开发 |
| 毕业目标 | 30000+ 行 | 含实习项目、开源贡献与个人作品 |
代码量不是唯一目标,更重要的是代码质量、可维护性和工程规范。本学期将注重在项目中实践代码评审、模块化设计和文档编写。
六、课程期待
6.1 最期待学习的知识
- 软件需求分析与设计方法:学习如何从模糊的需求中提取出清晰、可验证的规格说明
- 软件架构与设计模式:理解如何设计可扩展、可维护的系统结构
- 团队协作与项目管理:学习敏捷开发、Git 分支策略、代码评审等工程实践
- 软件测试:掌握单元测试、集成测试和测试驱动开发
6.2 希望获得的收获
- 完成一个从需求到部署的完整软件项目
- 提升团队协作能力和沟通能力
- 养成良好的编码规范和文档习惯
- 建立软件工程思维,从"写代码"升级为"做软件"
七、AI 生成的软件工程学习指南与分析
7.1 AI 生成的学习指南
以下内容由 AI 工具(DeepSeek)生成。
软件工程课程学习指南
一、课程概述
软件工程是一门研究用工程化方法构建和维护有效、实用和高质量软件的学科。核心是掌握需求分析、设计、实现、测试、维护的完整生命周期。
二、学习路线
- 第一阶段(1-4 周):掌握 UML 用例图、类图、时序图;理解需求工程
- 第二阶段(5-8 周):学习面向对象设计原则(SOLID)和设计模式
- 第三阶段(9-12 周):代码规范、版本控制、单元测试、TDD
- 第四阶段(13-16 周):团队项目、敏捷开发、项目复盘
三、推荐资源
- 教材:《软件工程:实践者的研究方法》
- 工具:Git、VS Code、Draw.io、Jira
- 课程:中国大学 MOOC《软件工程》
四、学习建议
- 动手为主,每个阶段做小项目实践
- 文档先行,养成写需求和设计文档的习惯
- 积极参与团队项目,学习沟通协作
- 每个项目结束后做复盘总结
7.2 分析与评价
合理之处:
- 学习路线按四阶段划分,与课程进度基本吻合,逻辑清晰
- 强调"动手为主"和"文档先行",符合软件工程实践性强的特点
- 推荐的教材和工具都是行业标准,具有参考价值
- 提到了敏捷开发和代码评审,贴近工业界实际
不足之处:
- 内容较为通用,缺乏针对本校课程安排的具体建议
- 没有提到 AI 工具在软件工程中的应用(如 AI 辅助编码、测试生成),而这正是当前行业的重要趋势
- 对"代码量"和"项目实战"的量化目标不够明确
- 未涉及 DevOps、CI/CD 等现代工程实践
对自己的帮助:
- 四阶段学习路线可以作为本学期的学习节奏参考
- "文档先行"和"项目复盘"的建议值得采纳,将在课程项目中实践
- 推荐的教材《软件工程:实践者的研究方法》可以作为课外补充阅读
- 总体而言,这份指南提供了一个不错的框架,但需要结合本校课程实际和个人情况进行调整,特别是要补充 AI 辅助开发和 DevOps 相关内容
八、总结
本次作业完成了以下工作:
- 准备工作:注册并完善了 GitHub(DUJIAWEI123374)和博客园账号,关注了老师和助教博客,加入了班级博客
- HuggingFace API 调用:成功调用 Flux 写实模型生成了照片级真实图像,搭建了 Flask + 前端的交互生成系统,实现了双通道自动回退机制
- GitHub 个人主页:创建了同名仓库并编写了内容充实的 README,包含个人介绍、技能、自我评估和三年规划
- 技能自评与规划:梳理了当前技能树,明确了优势与不足,制定了代码量目标和课程期待
- AI 学习指南:使用 AI 生成了软件工程学习指南并进行了分析评价
通过本次作业,对软件工程课程的全貌有了初步认识,也在 AI 应用开发中积累了 API 调用、前后端联调和工程化的实践经验。

浙公网安备 33010602011771号