diff --git a/README.md b/README.md index 184252f..7b67820 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,136 @@ -# AI 作文批改小助手 (´。• ᵕ •。`) ♡ +# AI 作文批改助手 ✨ -(^▽^)ノ゙ 哈罗!欢迎来到AI作文批改小助手的世界! +(^▽^)ノ゙ 欢迎使用 AI 作文批改助手!这是一款专为教育工作者和学生设计的智能桌面工具,能够像经验丰富的英语老师一样,自动批改手写英文作文图片,并生成专业详细的批改报告。 -这是一款用爱发电的桌面小工具,它能像一位经验丰富的英语老师一样,帮你自动批改手写的英文作文图片,并生成超级详细的批改报告哟~ - -### ☆ 应用界面预览 ☆ -![alt text](photo/1.png) -![alt text](photo/2.png) -![alt text](photo/3.png) -![alt text](photo/4.png) ---- - -## ☆ 亮点闪闪 (Features) ☆ - -* **双AI引擎驱动 (Dual AI Engine)** - * 首先,由专业的 **视觉模型(VLM)** 来进行高精度的手写文字识别(OCR),并像老师看卷面一样,给出一个专业的“书写质量分”!(๑•̀ㅂ•́)و✧ - * 然后,强大的 **大语言模型(LLM)** 会接棒,结合识别出的文本、作文题目和书写分,给你一份内容详实、有深度、有温度的批改报告! - -* **超级自由的可配置性 (Super Flexible)** - * **API随便换**:VLM和LLM的服务地址、API Key、模型名称都可以分开设置,不管你用哪家的AI服务(只要兼容OpenAI格式),都能轻松适配! - * **逻辑你来定**:觉得书写分太严或者太松?在设置里调一下“敏感度因子”就好啦~ - * **模板全开放**:核心的批改指令(Prompt)模板完全开放给你!你可以在设置里直接修改它,调整评分标准、总分、或者让AI老师的语气更温柔(或者更严格!),打造你专属的批改风格!ദ്ദി(•ω•´) - -* **闪电般的并发处理 (Lightning-Fast Concurrency)** - * 内置了超高效的多线程引擎!不管你一次选一张还是一百张图片,小助手都会派出多位AI老师**同时开始批改**,再也不用漫长地等待啦,大幅节省你的宝贵时间!O(∩_∩)O - -* **企业级的安全感 (Enterprise-Grade Security)** - * 你的API Key是非常重要的秘密!我们使用强大的 `cryptography` 库对所有密钥都进行了**加密处理**,才会保存在本地的配置文件里。绝对不会明文泄露,让你的账户安全感满满!(。・ω・。)ノ♡ - -* **开箱即用的小可爱 (Ready to Use)** - * 项目已经为 `PyInstaller` 打包做好了万全准备,可以轻松编译成一个独立的 `.exe` 或 `.app` 文件,方便地分享给不太懂技术的朋友们使用~ (^ω^) +## 🎯 应用界面预览 +![应用界面](photo/1.png) +![设置界面](photo/2.png) +![批改过程](photo/3.png) +![批改报告](photo/4.png) --- -## 使用教程 (ゝ∀・) +## ✨ 核心特色功能 -1. **下载程序**: 前往本仓库的 [Releases 页面](https://github.com/Eric-Terminal/Pro_llm_correct/releases) 下载适用于你操作系统的最新版本。 -2. **首次配置**: - * 运行程序,它会发现你还没填API Key,然后自动弹出“设置”窗口。 - * 在这里填上你的 VLM 和 LLM 服务提供商的 **URL**、**API Key** 和 **模型名称**。 - * 点击“确定”保存,你的小秘密就被安全地加密保存好啦! -3. **开始批改**: - * 在主界面的大框框里,输入本次作文的“**题目**”。 - * 点击“**选择图片**”,可以一次选中所有学生的作文图片哟。 - * 点击“**开始批改**”,然后就可以泡杯茶,看日志区和进度条实时更新啦! -4. **获取报告**: 任务完成后,每一张图片对应的 `.md` 格式批改报告,都会自动出现在原图片所在的文件夹里,是不是很方便呀? +### 🤖 双AI引擎智能处理 +- **视觉语言模型(VLM)**: 专业的手写文字识别(OCR)和书写质量评估,给出精准的卷面分数 +- **大语言模型(LLM)**: 深度内容分析,提供专业的语法纠错和写作建议 +- **智能作文类型识别**: 自动识别应用文(15分制)和读后续写(25分制)两种高考作文类型 + +### ⚙️ 极致灵活配置 +- **服务独立配置**: VLM和LLM支持完全独立的API服务、密钥和模型配置 +- **评分标准可调**: 书写质量"敏感度因子"自由调节,适应不同评分要求 +- **Prompt模板开放**: 核心批改指令完全可自定义,打造个性化批改风格 + +### 🚀 高效并发处理 +- 多线程并发引擎,支持批量处理任意数量的作文图片 +- 智能任务调度,大幅提升批改效率,节省宝贵时间 +- 实时进度显示和详细日志输出,随时掌握处理状态 + +### 🔒 企业级安全保障 +- 军事级加密算法保护API密钥,防止敏感信息泄露 +- 本地配置文件加密存储,确保账户安全无忧 +- 透明的Token使用统计,方便成本控制 + +### 📊 专业输出格式 +- **Markdown源文件**: 完整的批改报告,支持进一步编辑和定制 +- **HTML可视化报告**: 美观易读的网页格式,方便分享和查看 +- **详细错误分析**: 语法错误、表达问题、修改建议一应俱全 +- **精准分数评估**: 专业的评分体系,符合高考评分标准 --- -## 给开发者小伙伴 (づ。◕‿‿◕。)づ +## 📖 使用指南 -想从源码运行或者一起把它变得更好吗?欢迎欢迎! +### 快速开始 +1. **下载程序**: 前往 [Releases页面](https://github.com/Eric-Terminal/Pro_llm_correct/releases) 下载最新版本 +2. **首次配置**: + - 运行程序,自动弹出设置窗口 + - 配置VLM和LLM服务的URL、API密钥和模型名称 + - 点击确定保存,密钥自动加密存储 +3. **开始批改**: + - 在主界面输入作文题目 + - 点击"选择图片",多选需要批改的作文图片 + - 点击"开始批改",程序自动进行并发处理 +4. **查看报告**: 处理完成后,Markdown和HTML格式报告自动保存在原图片目录 +### 输出文件说明 +- `原文件名_report.md`: Markdown格式详细批改报告 +- `原文件名_report.html`: HTML可视化批改报告 +- 包含: 作文内容、综合评价、亮点优点、问题建议、分数评估 + +--- + +## 🛠️ 开发者指南 + +### 环境搭建 ```bash -# 1. 把这个可爱的仓库克隆到本地 +# 1. 克隆仓库 git clone https://github.com/Eric-Terminal/Pro_llm_correct.git cd Pro_llm_correct -# 2. (推荐) 创建一个专属的虚拟环境 +# 2. 创建虚拟环境(推荐) python3 -m venv venv -source venv/bin/activate # Windows上是 `venv\Scripts\activate` 哦 +source venv/bin/activate # Linux/Mac +# venv\Scripts\activate # Windows -# 3. 安装所有需要的魔法咒语 (依赖) +# 3. 安装依赖 pip install -r requirements.txt -# 4. 运行! +# 4. 运行程序 python main.py ``` -### 打包成魔法小盒子 (o´ω`o)ノ - -想把它打包成一个独立的程序吗?用 `PyInstaller` 就行啦! - +### 项目打包 ```bash -# --noconsole: 这样运行时就不会弹出黑乎乎的命令行窗口啦 -# --onefile: 把所有东西都打包成一个文件,干净! +# 打包为独立可执行文件 pyinstaller --noconsole --onefile main.py + +# 打包好的程序在 dist/ 目录 ``` -打包好的魔法小盒子会出现在 `dist` 文件夹里。 + +### 技术架构 +- **前端**: Tkinter GUI界面 +- **核心**: 双AI引擎架构 (VLM + LLM) +- **安全**: cryptography加密库 +- **并发**: threading + concurrent.futures +- **输出**: Markdown + HTML渲染 --- -## 开源许可证 (License) ( ´ ▽ ` )ノ +## 📝 配置说明 -本项目采用 [MIT License](LICENSE) 开源。简单来说,就是你可以自由地使用、修改和分享它,只要保留原始的版权声明就好啦~ +### 必需配置项 +- `VlmUrl`: VLM服务地址 +- `VlmApiKey`: VLM服务密钥(自动加密) +- `VlmModel`: VLM模型名称 +- `LlmUrl`: LLM服务地址 +- `LlmApiKey`: LLM服务密钥(自动加密) +- `LlmModel`: LLM模型名称 + +### 可选配置项 +- `SensitivityFactor`: 书写评分敏感度因子(默认1.5) +- `MaxWorkers`: 最大并发数(默认4) +- `MaxRetries`: 最大重试次数(默认3) +- `RetryDelay`: 重试延迟秒数(默认5) +- `SaveMarkdown`: 是否保存Markdown文件(默认True) +- `RenderMarkdown`: 是否渲染HTML报告(默认True) --- -*由 Eric-Terminal 精心创造。如果你喜欢这个项目,请不要吝啬你的 Star ☆ 哦!(⁄ ⁄•⁄ω⁄•⁄ ⁄)* +## 📄 开源协议 + +本项目采用 [MIT License](LICENSE) 开源协议。您可以自由地使用、修改和分发本软件,只需保留原始的版权声明即可。 + +--- + +## 🤝 贡献与支持 + +如果您在使用过程中遇到问题或有改进建议,欢迎: +- 提交 [Issue](https://github.com/Eric-Terminal/Pro_llm_correct/issues) +- 发起 [Pull Request](https://github.com/Eric-Terminal/Pro_llm_correct/pulls) +- 给项目点个 ⭐ Star 支持一下! + +--- + +*由 Eric-Terminal 精心开发。希望这个工具能够帮助更多的教育工作者和学生!(。・ω・。)ノ♡* diff --git a/api_services.py b/api_services.py index 44385fb..5f06f62 100644 --- a/api_services.py +++ b/api_services.py @@ -53,6 +53,7 @@ Based on the identified essay type, apply the corresponding grading logic. The H Analyze the text, identify the essay type, calculate the scores, and present your complete feedback in **Simplified Chinese** using the precise Markdown format specified in the "OUTPUT SPECIFICATION" section. Ensure the final score correctly reflects the total points possible (15 or 25). #--- End of English Instructions --- # OUTPUT SPECIFICATION (MUST BE IN SIMPLIFIED CHINESE) +#你应该综合考量书写和内容的评分,内容是主要的,字体是次要的,例如对于写的内容和作文毫无关系但是字很好看的,不应该给3分而是直接给0分 # 请使用以下Markdown格式,并用简体中文填充所有内容,优点找不到不要硬找,问题建议要把全部问题找出来并且解析,都要遵循类似格式。对于分数的总分则必须由你选择是15分还是25分(不一定是下面的15分)。 diff --git a/app_ui.py b/app_ui.py index 0fc4d7c..b80d2b7 100644 --- a/app_ui.py +++ b/app_ui.py @@ -42,39 +42,47 @@ class AboutDialog(tk.Toplevel): llm_out = config_manager.get('UsageLlmOutput', 0) about_text = f""" -欢迎使用 AI 作文批改助手!这是一款专为教育者和学生设计的智能工具,旨在利用前沿的人工智能技术,提供高效、精准、个性化的英文作文批改体验。 +欢迎使用 AI 作文批改助手!这是一款专为教育工作者和学生设计的智能工具,利用前沿的人工智能技术,提供高效、精准、个性化的英文作文批改体验。 -核心亮点: -- **双核AI引擎:** 采用先进的两步式处理流程。首先由专用的视觉语言模型(VLM)对作文图片进行高精度手写识别(OCR)与专业的书写质量评估;随后,强大的大语言模型(LLM)会结合识别出的文本、作文题目以及书写评分,进行深度、全面的分析与批改。 +✨ 核心特色: -- **极致的灵活性与可配置性:** - * **服务分离:** 您可以为VLM和LLM设置完全独立的API服务地址、密钥和模型名称,轻松适配不同的AI提供商(需要兼容OpenAI格式)或自建服务。 - * **逻辑定制:** 书写评分的“敏感度因子”可在设置中调整,以适应不同年级或要求的评分标准。 - * **模板开放:** 核心的LLM批改指令模板(Prompt)完全开放给用户。您可以在设置中自由修改,调整评分维度、总分、反馈风格等,实现高度个性化的批改要求。 +- **双AI引擎架构:** 采用创新的两步式处理流程。首先由专业的视觉语言模型(VLM)进行高精度手写文字识别(OCR)和专业的书写质量评估;然后由强大的大语言模型(LLM)结合识别文本、作文题目和书写评分,进行深度内容分析和专业批改。 -- **闪电般的并发处理:** 内置高效的多线程并发引擎,无论您选择一张还是上百张图片,程序都能同时处理,大幅缩短批量批改的等待时间。最大并发任务数亦可在设置中自由调整。 +- **极致灵活性:** + * **服务独立配置:** VLM和LLM支持完全独立的API服务地址、密钥和模型名称,轻松适配各种AI服务提供商(兼容OpenAI格式) + * **智能评分调节:** 书写质量"敏感度因子"可自由调整,适应不同年级和评分标准要求 + * **Prompt完全开放:** 核心批改指令模板完全可自定义,支持调整评分标准、总分设置和反馈风格 -- **企业级的安全保障:** 我们深知API密钥的敏感性。所有密钥信息在保存到本地配置文件时,均经过强大的加密算法处理,有效防止明文泄露,保障您的账户安全。 +- **高效并发处理:** 内置多线程并发引擎,支持批量处理任意数量的图片,大幅提升批改效率,最大并发数可配置 -- **人性化的评分策略:** 卷面书写分采用更符合教学直觉的“向上取整至0.5分”规则,确保评分结果既精确又公平。 +- **企业级安全保障:** 所有API密钥均采用军事级加密算法存储,确保您的账户信息安全 -使用说明: -1. **初次配置:** 点击“设置”,分别填入您的VLM和LLM服务提供商的URL、API密钥和模型名称。 -2. **输入题目:** 在主界面上方的文本框中,输入本次批改的“作文题目”。 -3. **选择文件:** 点击“选择图片”,一次性选择所有需要批改的学生作文图片。 -4. **开始批改:** 点击“开始批改”,程序将自动在后台进行并发处理,您可以在日志区看到实时进度。 -5. **获取报告:** 任务完成后,每一张图片对应的Markdown格式详细批改报告,都会自动生成在原图片所在的目录下。 +- **专业评分体系:** 针对高考英语作文场景设计,支持应用文(15分制)和读后续写(25分制)两种评分标准 + +📋 使用指南: +1. **首次设置:** 点击"设置",配置VLM和LLM服务的URL、API密钥和模型 +2. **输入题目:** 在主界面文本框中输入本次批改的作文题目 +3. **选择图片:** 点击"选择图片",可多选需要批改的作文图片 +4. **开始批改:** 点击"开始批改",程序自动进行并发处理 +5. **查看报告:** 处理完成后,Markdown和HTML格式的详细批改报告将保存在原图片目录 + +🎯 输出格式: +- Markdown源文件(可编辑) +- HTML可视化报告(美观易读) +- 详细的语法错误分析 +- 专业的写作建议 +- 精准的分数评估 作者: Eric_Terminal -https://github.com/Eric-Terminal/Pro_llm_correct -版本: 3.0 +项目地址: https://github.com/Eric-Terminal/Pro_llm_correct +版本: 3.1 --- -历史Token使用量 (仅供参考): -- VLM 输入: {vlm_in:,} -- VLM 输出: {vlm_out:,} -- LLM 输入: {llm_in:,} -- LLM 输出: {llm_out:,} +历史Token使用统计: +- VLM 输入Token: {vlm_in:,} +- VLM 输出Token: {vlm_out:,} +- LLM 输入Token: {llm_in:,} +- LLM 输出Token: {llm_out:,} """ text_widget.insert("1.0", about_text) @@ -93,28 +101,31 @@ https://github.com/Eric-Terminal/Pro_llm_correct class SettingsDialog(tk.Toplevel): - """“设置”对话框,允许用户配置VLM、LLM服务及其他应用参数。""" - def __init__(self, parent, current_config: dict): + """"设置"对话框,允许用户配置VLM、LLM服务及其他应用参数。""" + def __init__(self, parent, config_manager: ConfigManager): super().__init__(parent) self.transient(parent) self.title("设置") self.result = None + self.config_manager = config_manager # 为VLM和LLM服务分别创建Tkinter字符串变量 - self.vlm_url = tk.StringVar(value=current_config.get("VlmUrl", "https://api.siliconflow.cn/v1/")) - self.vlm_api_key = tk.StringVar(value=current_config.get("VlmApiKey", "")) - self.vlm_model = tk.StringVar(value=current_config.get("VlmModel", "Pro/THUDM/GLM-4.1V-9B-Thinking")) - self.llm_url = tk.StringVar(value=current_config.get("LlmUrl", "https://api.siliconflow.cn/v1/")) - self.llm_api_key = tk.StringVar(value=current_config.get("LlmApiKey", "")) - self.llm_model = tk.StringVar(value=current_config.get("LlmModel", "moonshotai/Kimi-K2-Instruct")) - self.sensitivity_factor = tk.StringVar(value=current_config.get("SensitivityFactor", "1.5")) - self.max_workers = tk.StringVar(value=current_config.get("MaxWorkers", "4")) - self.max_retries = tk.StringVar(value=current_config.get("MaxRetries", "3")) - self.retry_delay = tk.StringVar(value=current_config.get("RetryDelay", "5")) - self.render_markdown = tk.BooleanVar(value=current_config.get("RenderMarkdown", True)) + # 使用config_manager.get()方法获取解密后的值用于显示 + self.vlm_url = tk.StringVar(value=config_manager.get("VlmUrl", "https://api.siliconflow.cn/v1")) + self.vlm_api_key = tk.StringVar(value=config_manager.get("VlmApiKey", "")) + self.vlm_model = tk.StringVar(value=config_manager.get("VlmModel", "Pro/THUDM/GLM-4.1V-9B-Thinking")) + self.llm_url = tk.StringVar(value=config_manager.get("LlmUrl", "https://api.siliconflow.cn/v1")) + self.llm_api_key = tk.StringVar(value=config_manager.get("LlmApiKey", "")) + self.llm_model = tk.StringVar(value=config_manager.get("LlmModel", "moonshotai/Kimi-K2-Instruct")) + self.sensitivity_factor = tk.StringVar(value=config_manager.get("SensitivityFactor", "1.5")) + self.max_workers = tk.StringVar(value=config_manager.get("MaxWorkers", "4")) + self.max_retries = tk.StringVar(value=config_manager.get("MaxRetries", "3")) + self.retry_delay = tk.StringVar(value=config_manager.get("RetryDelay", "5")) + self.save_markdown = tk.BooleanVar(value=config_manager.get("SaveMarkdown", True)) + self.render_markdown = tk.BooleanVar(value=config_manager.get("RenderMarkdown", True)) # 智能加载Prompt模板:优先使用用户自定义模板,否则使用默认模板 - user_template = current_config.get("LlmPromptTemplate") + user_template = config_manager.get("LlmPromptTemplate") self.llm_prompt_template_str = user_template if user_template else DEFAULT_LLM_PROMPT_TEMPLATE @@ -156,8 +167,10 @@ class SettingsDialog(tk.Toplevel): ttk.Entry(other_frame, textvariable=self.max_retries, width=40).grid(column=1, row=2, sticky=(tk.W, tk.E)) ttk.Label(other_frame, text="重试延迟(秒):").grid(column=0, row=3, sticky=tk.W, pady=2) ttk.Entry(other_frame, textvariable=self.retry_delay, width=40).grid(column=1, row=3, sticky=(tk.W, tk.E)) - ttk.Label(other_frame, text="渲染Markdown报告:").grid(column=0, row=4, sticky=tk.W, pady=2) - ttk.Checkbutton(other_frame, variable=self.render_markdown).grid(column=1, row=4, sticky=tk.W) + ttk.Label(other_frame, text="保存Markdown文件:").grid(column=0, row=4, sticky=tk.W, pady=2) + ttk.Checkbutton(other_frame, variable=self.save_markdown).grid(column=1, row=4, sticky=tk.W) + ttk.Label(other_frame, text="渲染HTML报告:").grid(column=0, row=5, sticky=tk.W, pady=2) + ttk.Checkbutton(other_frame, variable=self.render_markdown).grid(column=1, row=5, sticky=tk.W) # LLM Prompt模板编辑区域 prompt_frame = ttk.LabelFrame(frame, text="LLM Prompt 模板 (可在此修改,请勿修改{}占位符内容导致程序参数无法正常传递,通常情况下修改总分即可)", padding="10") @@ -191,6 +204,9 @@ class SettingsDialog(tk.Toplevel): "LlmModel": self.llm_model.get(), "SensitivityFactor": self.sensitivity_factor.get(), "MaxWorkers": self.max_workers.get(), + "MaxRetries": self.max_retries.get(), + "RetryDelay": self.retry_delay.get(), + "SaveMarkdown": self.save_markdown.get(), "RenderMarkdown": self.render_markdown.get(), "LlmPromptTemplate": self.llm_prompt_text.get("1.0", "end-1c") } @@ -318,7 +334,7 @@ class MainApp: def _open_settings_dialog(self): """打开设置对话框,并根据返回结果更新和保存配置。""" - dialog = SettingsDialog(self.root, self.config_manager.config) + dialog = SettingsDialog(self.root, self.config_manager) if dialog.result: # 清理旧的、统一的AI配置和OCR配置,以兼容新版分离的配置 self.config_manager.config.pop("AiUrl", None) @@ -413,10 +429,14 @@ class MainApp: try: final_report, vlm_usage, llm_usage, html_path = self.api_service.process_essay_image(file_path, topic) - # 保存Markdown源文件 + # 检查是否保存Markdown文件 + save_markdown = self.config_manager.get("SaveMarkdown", True) report_filename_md = os.path.splitext(file_path)[0] + "_report.md" - with open(report_filename_md, 'w', encoding='utf-8') as f: - f.write(final_report) + + # 保存Markdown源文件(如果配置开启) + if save_markdown: + with open(report_filename_md, 'w', encoding='utf-8') as f: + f.write(final_report) vlm_in = vlm_usage.get("prompt_tokens", 0) vlm_out = vlm_usage.get("completion_tokens", 0) @@ -426,10 +446,20 @@ class MainApp: usage_log = f"Token用量: VLM(in:{vlm_in}, out:{vlm_out}), LLM(in:{llm_in}, out:{llm_out})" # 记录所有生成的文件 - output_files = [os.path.basename(report_filename_md)] + output_files = [] + if save_markdown: + output_files.append(os.path.basename(report_filename_md)) + + # 检查是否只勾选了HTML,如果是则删除Markdown文件 + render_html = self.config_manager.get("RenderMarkdown", True) if html_path and os.path.exists(html_path): output_files.append(os.path.basename(html_path)) self.ui_queue.put(("log", f"已生成HTML报告: {os.path.basename(html_path)}")) + + # 如果只勾选HTML,不勾选Markdown,则删除Markdown文件 + if not save_markdown and render_html and os.path.exists(report_filename_md): + os.remove(report_filename_md) + self.ui_queue.put(("log", f"已删除Markdown文件(仅保留HTML)")) self.ui_queue.put(("log", f"完成批改: {base_name} -> {', '.join(output_files)}")) self.ui_queue.put(("log", usage_log)) diff --git a/config_manager.py b/config_manager.py index 982866e..bd38e6d 100644 --- a/config_manager.py +++ b/config_manager.py @@ -38,14 +38,10 @@ class ConfigManager: def _ensure_default_render_settings(self): """确保渲染相关的默认设置存在""" - if self.get("RenderMarkdownToImage") is None: - self.set("RenderMarkdownToImage", True) # 默认开启渲染功能 - if self.get("RenderImageFormat") is None: - self.set("RenderImageFormat", "png") # 默认PNG格式 - if self.get("RenderImageWidth") is None: - self.set("RenderImageWidth", 800) # 默认图片宽度 - if self.get("RenderImageQuality") is None: - self.set("RenderImageQuality", 90) # 默认图片质量 + if self.get("SaveMarkdown") is None: + self.set("SaveMarkdown", True) # 默认保存Markdown文件 + if self.get("RenderMarkdown") is None: + self.set("RenderMarkdown", True) # 默认开启HTML渲染功能 def _decrypt(self, encrypted_value: str) -> str: """ diff --git a/markdown_renderer.py b/markdown_renderer.py index 8acb7c9..64cf96c 100644 --- a/markdown_renderer.py +++ b/markdown_renderer.py @@ -119,7 +119,6 @@ class MarkdownRenderer: margin: 10px 0; } - """ return f"""