diff --git a/README.md b/README.md new file mode 100644 index 0000000..01cdf9e --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# AI 写作助手(AI Writing Assistant) + +--- + +## 2.1 团队成员与贡献 + +| 姓名 | 学号 | 主要贡献(具体分工) | +|----|----|----------------| +| 索梦露 | 2411020218 | (组长)项目整体设计、后端核心逻辑开发、编写 | +| 李秀芬 | 2411020130 | Web 前端界面设计、页面样式美化、 | + +--- + +## 2.2 项目简介 & 运行指南 + +### 简介 + +本项目是一个基于 Python 和大语言模型 API 的 **AI 写作助手系统**, +旨在解决学生和内容创作者在日常写作中 **表达不够通顺、反复修改效率低** 的问题。 +用户只需输入原始文本,即可通过 AI 自动生成更加流畅、自然的改写结果,从而提升写作效率和质量。 + +--- + +### 如何运行 +```bash +# 1️⃣ 安装依赖 +uv sync # 安装依赖 +# 2️⃣ 配置 API Key +#在 .env 中填写你的 API Key,例如: +DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx +#3️⃣ 启动项目 +uv run app.py +#启动成功后,在浏览器中访问 +http://127.0.0.1:5000 +#即可打开 AI 写作助手网页界面并进行演示 +``` +--- +## 2.3 开发心得 +### 选题思考:为什么做这个?解决了谁的痛苦? +#### 在日常学习和课程作业中,我们经常需要撰写实验报告、课程设计说明或总结性文字。 +很多时候,并不是没有想法,而是**不知道如何把想法组织成通顺、专业的文字**, +往往需要反复修改,耗费大量时间。 + +因此,我们选择了“AI 写作助手”作为课程设计题目,希望借助当前大语言模型在自然语言处理方面的能力, +为学生和内容创作者提供一个**低门槛、易使用、效果直观**的写作辅助工具, +帮助用户提升写作效率,减少无意义的重复修改。 + +### AI 协作体验 +这是我们第一次在完整项目中深度使用 AI 来协助编程和功能设计。 + +在开发过程中,AI 在以下方面给予了非常大的帮助: + +1.后端接口逻辑的设计思路 + +2.Prompt 的不断优化与改进 + +3.前后端交互流程的梳理 + +4.常见错误的快速定位与修复 + +其中,让人直呼“牛逼”的 Prompt 是: + +“请帮我润色以下文本,使其更加通顺自然,语气正式但不过于生硬。” + +这一 Prompt 能够在多种输入情况下稳定输出高质量文本,极大提升了系统实用性。 + +但也并非所有时候都一帆风顺。有时由于 Prompt 描述不够明确, +AI 会输出偏离预期的内容,或者在代码细节上出现不符合实际环境的问题, +这时就需要人工不断尝试、调整和验证,甚至“推翻重来”,这一过程也让我们更加理解了 +**“如何正确地向 AI 提问”本身就是一项重要能力。** + +### 自我反思:AI 时代,程序员的核心竞争力是什么? +通过本次课程设计,我们逐渐意识到: + +在 AI 时代,程序员的核心竞争力并不是死记硬背语法,而是: + +问题拆解能力 —— 能否把一个模糊需求拆解成清晰的模块 + +工程思维 —— 是否具备代码结构、项目规范和安全意识 + +Prompt 设计能力 —— 是否能高效地与 AI 协作 + +判断与验证能力 —— 是否能判断 AI 给出的结果是否合理、可用 + +AI 并不会取代程序员,但会放大程序员之间的差距。 +只有真正理解需求、善于思考并具备持续学习能力的人, +才能在 AI 时代持续保持竞争力。 + +本次项目不仅锻炼了我们的 Python 编程能力,也让我们对 AI 技术的实际应用有了更加清晰和理性的认识。 \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..fda840b --- /dev/null +++ b/app.py @@ -0,0 +1,196 @@ +import os +from flask import Flask, render_template, request, jsonify +from dotenv import load_dotenv +import dashscope +from dashscope import Generation +from datetime import datetime +import json + +load_dotenv() +dashscope.api_key = os.getenv("DASHSCOPE_API_KEY") + +app = Flask(__name__) + +# -------------------------- +# 配置 +# -------------------------- +SCENE_PROMPTS = { + "general": "这是通用写作场景,语言自然清晰。", + "media": "这是自媒体写作,语言有吸引力,适合传播。", + "script": "这是剧本写作,语言有画面感和情绪张力。", + "essay": "这是学生作文写作,结构清晰、表达规范。", + "academic_scene": "这是学术写作,语言严谨、逻辑清楚。" +} + +POLISH_STYLES = { + "formal": "正式、书面", + "literary": "文学性强、有文采", + "spoken": "口语化、自然", + "business": "商务、专业", + "internet": "网络风格、轻松" +} + +history_records = [] +MAX_HISTORY = 10 + +# -------------------------- +# AI 调用 +# -------------------------- +def call_ai(prompt): + resp = Generation.call(model="qwen-turbo", prompt=prompt) + if resp.status_code == 200: + return resp.output.text + return "❌ AI 调用失败" + +# -------------------------- +# 多版本润色 +# -------------------------- +def multi_version_rewrite(text, style): + prompt = f""" +请对以下文本进行润色,生成 3 个不同版本: +要求: +1. 保持原意 +2. 风格:{style} +3. 输出 3 个版本,分别标记 版本1/版本2/版本3 +原文: +{text} +""" + return call_ai(prompt) + +# -------------------------- +# 扩写 +# -------------------------- +def expand_text(text, target_length): + """ + 扩写文本到指定长度(字数/字符) + """ + prompt = f""" +请将以下文本扩写,保持原意,但丰富细节和表达,使字数达到 {target_length} 字左右。 +可以生成 3 个不同版本,分别标记 版本1/版本2/版本3。 +原文: +{text} +""" + return call_ai(prompt) + +# -------------------------- +# 修改原因生成 +# -------------------------- +def generate_revision_reason(original, revised): + prompt = f""" +请对下面两段文本进行对比,说明修改的原因。 +仅输出文字说明,不要高亮或格式化。 + +原文: +{original} + +改写后: +{revised} + +请详细说明改动原因。 +""" + return call_ai(prompt) + +# -------------------------- +# 文学参考 +# -------------------------- +def generate_literary_reference(text): + prompt = f""" +请理解下面这句话的含义,然后给出 3 条 +“文学作品风格中的相似表达示例”。 + +要求: +1. 不是原文引用 +2. 给出【风格参考作者】和【作品名】 +3. 仅作为文学风格参考 + +输出格式: +1. 句子内容 + —— 风格参考:作者《作品名》 + +原句: +{text} +""" + return call_ai(prompt) + +# -------------------------- +# 历史记录 +# -------------------------- +def save_history(data, result): + history_records.insert(0, { + "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "mode": data.get("mode"), + "scene": data.get("scene"), + "input": data.get("text", "")[:100], + "output": result[:200] + }) + if len(history_records) > MAX_HISTORY: + history_records.pop() + +# -------------------------- +# 情感分析 +# -------------------------- +def sentiment_analysis(text): + prompt = f""" +请分析下面文本的情感倾向,仅返回严格 JSON 格式: +{{"positive": %, "neutral": %, "negative": %}} +文本: +{text} +""" + result = call_ai(prompt) + try: + parsed = json.loads(result.replace(":", ":").replace("%","")) + return parsed + except: + return {"positive":0, "neutral":0, "negative":0} + +# -------------------------- +# 路由 +# -------------------------- +@app.route("/") +def index(): + return render_template("index.html") + +@app.route("/rewrite", methods=["POST"]) +def rewrite(): + data = request.json + text = data.get("text","") + mode = data.get("mode") + scene = data.get("scene","general") + + # 根据模式生成文本 + if mode=="polish": + style = data.get("style","formal") + result = multi_version_rewrite(text, style) + elif mode=="expand": + target_length = data.get("target_length", 300) + result = expand_text(text, target_length) + else: + result = f"普通生成模式:{text}" # 保留原功能 + + save_history(data, result) + return jsonify({"result": result}) + +@app.route("/literary", methods=["POST"]) +def literary(): + text = request.json.get("text","") + return jsonify({"reference": generate_literary_reference(text)}) + +@app.route("/history") +def history(): + return jsonify(history_records) + +@app.route("/revision_reason", methods=["POST"]) +def revision_reason(): + data = request.json + original = data.get("original","") + revised = data.get("revised","") + reason = generate_revision_reason(original, revised) + return jsonify({"reason": reason}) + +@app.route("/sentiment", methods=["POST"]) +def sentiment(): + text = request.json.get("text","") + return jsonify({"result": sentiment_analysis(text)}) + +if __name__=="__main__": + app.run(debug=True) diff --git a/index.html b/index.html new file mode 100644 index 0000000..24227d2 --- /dev/null +++ b/index.html @@ -0,0 +1,174 @@ + + + + +AI 写作助手 Pro + + + +
+ +
+

🌙 AI 写作助手 Pro

+
当前时间:
+
+ +
+ + +
+ + + +
+ +
+ +
+ +
+

✍️ AI 输出(点击选择版本)

+
+
+ +
+

🔍 修改原因

+
+
+ +
+

📚 文学参考

+
+
+ +
+

😊 情感分析

+ +

+
+ +
+ + + + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f549588 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "xiezuoapp" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "dashscope>=1.25.6", + "flask>=3.1.2", + "python-dotenv>=1.2.1", +] diff --git a/style.css b/style.css new file mode 100644 index 0000000..cd642a7 --- /dev/null +++ b/style.css @@ -0,0 +1,22 @@ +body { + margin:0; + padding:0; + font-family:"Segoe UI",Tahoma,Geneva,Verdana,sans-serif; + background-color:#1e1e2f; + color:#e0e0e0; +} +.app{max-width:900px;margin:20px auto;padding:20px;} +header{text-align:center;margin-bottom:20px;} +header h1{font-size:2em;color:#fff;margin-bottom:5px;} +header .time{font-size:0.9em;color:#a0a0a0;} +.card{background-color:#2a2a40;padding:20px;border-radius:12px;margin-bottom:20px;box-shadow:0 5px 15px rgba(0,0,0,0.4);} +textarea{width:100%;min-height:120px;border-radius:8px;border:1px solid #444;background-color:#1f1f30;color:#e0e0e0;padding:10px;font-size:1em;resize:vertical;} +textarea:focus{outline:none;border-color:#5a5aff;} +select{border-radius:8px;border:1px solid #555;background-color:#1f1f30;color:#e0e0e0;padding:6px 10px;font-size:1em;margin-right:10px;} +select:focus{outline:none;border-color:#5a5aff;} +input[type="number"]{border-radius:8px;border:1px solid #555;background-color:#1f1f30;color:#e0e0e0;padding:5px 8px;font-size:1em;width:80px;} +button{border:none;border-radius:8px;padding:8px 16px;font-size:1em;cursor:pointer;background:linear-gradient(90deg,#5a5aff,#00d4ff);color:#fff;transition:all 0.3s ease;margin-top:10px;} +button:hover{transform:translateY(-2px);box-shadow:0 5px 10px rgba(0,0,0,0.4);} +.version{padding:8px;border-radius:6px;background-color:#3a3a55;margin-bottom:5px;cursor:pointer;transition:all 0.2s ease;} +.version:hover{background-color:#5a5aff;color:#fff;} +#revisionReason,#literaryBox,#sentimentBox{background-color:#1f1f30;padding:10px;border-radius:8px;font-size:0.95em;line-height:1.5em;}