diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2761.png" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2761.png" new file mode 100644 index 0000000000000000000000000000000000000000..3ee7a5a398dccd533ff68ffaed6bebd2025beb04 Binary files /dev/null and "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2761.png" differ diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2762.png" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2762.png" new file mode 100644 index 0000000000000000000000000000000000000000..d5a722440703d2825eb44a07e6ccc80b0b8434f6 Binary files /dev/null and "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2762.png" differ diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2763.png" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2763.png" new file mode 100644 index 0000000000000000000000000000000000000000..018845c1f5fca00d4163cf57544121eb81551ca9 Binary files /dev/null and "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2763.png" differ diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2764.png" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2764.png" new file mode 100644 index 0000000000000000000000000000000000000000..05731e0a5ced0c513893b3719a046c63a4dfb201 Binary files /dev/null and "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\350\213\261\350\257\255\346\225\231\346\241\210\346\225\210\346\236\234\345\233\2764.png" differ diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/inspectionProfiles/Project_Default.xml" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/inspectionProfiles/Project_Default.xml" new file mode 100644 index 0000000000000000000000000000000000000000..bde24fcd47b9c9debd34d41e5d3593c37619a626 --- /dev/null +++ "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/inspectionProfiles/Project_Default.xml" @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/inspectionProfiles/profiles_settings.xml" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/inspectionProfiles/profiles_settings.xml" new file mode 100644 index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99 --- /dev/null +++ "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/inspectionProfiles/profiles_settings.xml" @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/vcs.xml" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/vcs.xml" new file mode 100644 index 0000000000000000000000000000000000000000..2e3f6920d05c75d1fd79409538252175c9b9cf14 --- /dev/null +++ "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/vcs.xml" @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/workspace.xml" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/workspace.xml" new file mode 100644 index 0000000000000000000000000000000000000000..ae642f32f2b32b9039fd9791a2dc6b9162b62cc8 --- /dev/null +++ "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/.idea/workspace.xml" @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 8 +} + + + + { + "keyToString": { + "ModuleVcsDetector.initialDetectionPerformed": "true", + "Python.main.executor": "Run", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", + "RunOnceActivity.git.unshallow": "true", + "git-widget-placeholder": "master", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + + + + 1765188655578 + + + + + + + + + \ No newline at end of file diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/README.md" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..76c7fb6feb2f79210f7334663c08fe358f4e7712 --- /dev/null +++ "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/README.md" @@ -0,0 +1,92 @@ +# 📚 基于 LazyLLM 开发的智能教案生成Agent + +## 📝 项目简介 + +**项目名称:** 智能教案生成Agent + +**项目背景:** 面对教育工作者**备课、教案设计**等耗时费力的教学准备任务,本项目旨在利用大模型(LLM)技术,构建一个**智能教案生成Agent**。该Agent能够接收教师的 **教学目标** 和 **课程信息** 作为输入,并自动生成结构清晰、内容专业的完整教案。 + +**核心价值:** + +- **效率提升:** 将繁琐的教案撰写过程交给机器,大幅减少教师的备课时间。 +- **质量保障:** 确保教案内容符合教学大纲,结构完整,教学环节设计合理。 +- **低代码实现:** 采用 **LazyLLM** 低代码框架进行快速开发和部署。 + +## ⚙️ 技术栈 + +| **类别** | **技术/工具** | **描述** | +| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| **核心框架** | **LazyLLM (v0.x)** | 低代码 LLM 应用开发框架,用于快速构建和编排 Agent Pipeline。 | +| **大模型(LLM)** | **SenseNova** | 提供核心语言生成能力的 API 接口。 | +| **编程语言** | Python 3.12 | 项目主要实现语言。 | +| **API/工具** | `os`, `lazyllm.OnlineChatModule`, `pipeline`, `warp`, `bind`, `JsonFormatter` | 用于环境变量配置、模块定义、流程编排和数据处理。 | +| **部署环境** | `lazyllm.WebModule` | 提供内置的 Web Server,用于启动和暴露 Agent 服务。 | + +## 🏗️ 架构设计 + +本项目采用 **Agent Pipeline** 架构,基于 LazyLLM 框架实现**多步骤任务拆解与协同**。 + +#### **Agent 流程(Pipeline)** + +1. **Outline Generator (`outline_writer`):** + - **职责:** 根据用户输入(教学目标 + 课程信息),调用 LLM 生成**结构化、分层级**的教案大纲(TOC)。 + - **核心组件:** `OnlineChatModule` + **`JsonFormatter`** + `toc_prompt`。`JsonFormatter` 确保大纲输出为合法的 JSON 格式,便于后续处理。 +2. **Story Generator (`story_generater`):** + - **职责:** **并行/逐个**处理 Outline Generator 输出的每个章节标题和写作指导。 + - **核心组件:** **`warp`** + `OnlineChatModule` + `writer_prompt`。`warp` 函数是 LazyLLM 中实现**循环迭代**的关键,它使得 Pipeline 可以将大纲列表中的每个元素作为输入,并行调用 LLM 生成对应的教案内容。 +3. **Synthesizer (`synthesizer`):** + - **职责:** 接收 Story Generator 输出的所有教案段落,以及 Outline Generator 产生的原始大纲结构,将**标题**和**正文**进行合并,生成最终的 Markdown 格式教案。 + - **核心组件:** `lambda` 函数 + **`bind`**。`bind` 用于将 `outline_writer` 的输出作为命名参数传递给 `lambda` 函数,实现跨步骤的数据引用。 + +#### 整体流程图 + +![整体流程图](整体流程图.png) + +## 📦 部署说明 + +#### 1. 前置准备 + +1. **安装 LazyLLM:** + + ```bash + pip install lazyllm + ``` + +2. **获取 SenseNova API 凭据:** 在 SenseCore 控制台获取 `API_KEY` 和 `SECRET_ID`。 + +#### 2. 代码配置 + +将获取到的 API 凭据替换代码中的占位符(已完成): + +```Python +API_KEY = "..." # 替换为您的 API Key +SECRET_ID = "..." # 替换为您的 Secret ID + +os.environ["LAZYLLM_SENSENOVA_API_KEY"] = API_KEY +os.environ["LAZYLLM_SENSENOVA_SECRET_ID"] = SECRET_ID +``` + +#### 3. 启动 Web 服务 + +在终端运行该 Python 文件: + +```Bash +python main.py +``` + +Agent 将在 **`http://localhost:23466`** 上启动 Web 服务,并自动提供一个简易的 Web 界面供用户输入测试。 + +**服务访问:** + +- **URL:** `http://localhost:23466` +- **接口名称:** `ppl` (对应代码中的 `with pipeline() as ppl:`) + +![启动](启动.png) + +## 效果展示 + +**Agent生成教案效果图:** + +> 输入:\# 教案生成 { "subject": "高中数学", "topic": "二次函数", "grade": "高一", "duration": "45分钟", "objectives": "理解二次函数的概念和性质,掌握二次函数的图像特征", "students": "普通班学生,数学基础中等" } + +![教案生成效果图](教案生成效果图.png) \ No newline at end of file diff --git "a/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/main.py" "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/main.py" new file mode 100644 index 0000000000000000000000000000000000000000..00324471a446db743b03e253e15118ad2ae62d85 --- /dev/null +++ "b/\351\200\240\346\265\252AIAgent\346\225\231\346\241\210\347\224\237\346\210\220\345\212\251\346\211\213/\351\241\271\347\233\256\346\272\220\344\273\243\347\240\201/main.py" @@ -0,0 +1,129 @@ +import os + +# 设置 SenseNova API 凭据(获取地址:https://console.sensecore.cn/iam/Security/access-key) +API_KEY = "019B2B0A124E736082E255C6E99817E1" +SECRET_ID = "019B2B0A124E7350B10A17CB460F8DA5" + +os.environ["LAZYLLM_SENSENOVA_API_KEY"] = API_KEY +os.environ["LAZYLLM_SENSENOVA_SECRET_ID"] = SECRET_ID + +import lazyllm +from lazyllm import pipeline, warp, bind +from lazyllm.components.formatter import JsonFormatter + +# === Prompt 定义 === + +toc_prompt = """ +你现在是一个专业的教学设计助手。你的任务是根据教师提供的教学目标和课程信息,生成一份完整教案的大纲。请将大纲以列表嵌套字典的形式输出。每个字典包含一个 `title` 和 `describe`,其中 `title` 中需要用Markdown格式标清层级,`describe` 是对该部分的写作指导,说明应包含哪些内容、如何组织语言。 + +请根据以下用户输入,生成教案的列表嵌套字典: + +--- +输出示例: +[ + { + "title": "# 教学目标", + "describe": "明确列出本节课的知识与技能目标、过程与方法目标、情感态度与价值观目标,目标要具体可测量。" + }, + { + "title": "# 教学重难点", + "describe": "分析本节课的教学重点和难点,说明确定依据,并提出突破难点的策略。" + }, + { + "title": "# 教学准备", + "describe": "列出教学所需的教具、学具、多媒体设备等,以及教师和学生的课前准备工作。" + }, + { + "title": "# 教学过程", + "describe": "详细设计课堂教学的各个环节,包括导入、新授、练习、总结等,每个环节要说明时间分配和具体活动。" + }, + { + "title": "## 导入环节", + "describe": "设计生动有趣的课堂导入,激发学生学习兴趣,建立新旧知识联系,时间控制在5分钟以内。" + }, + { + "title": "## 新课讲授", + "describe": "系统讲解新知识点,采用启发式教学,注重师生互动,突出重点,突破难点。" + }, + { + "title": "## 练习巩固", + "describe": "设计层次分明的练习题,包括基础题、提高题和拓展题,确保学生掌握所学知识。" + }, + { + "title": "## 课堂小结", + "describe": "总结本节课的主要内容,强化重点知识,布置作业,为下节课做铺垫。" + }, + { + "title": "# 板书设计", + "describe": "设计清晰、美观的板书布局,突出重点知识,便于学生理解和记录。" + }, + { + "title": "# 教学反思", + "describe": "预设教学中可能遇到的问题及应对策略,体现教师的教学智慧和专业素养。" + } +] + +注意: +- 专注于教案设计,体现教学的专业性和系统性; +- 根据不同学科和学段特点调整内容; +- 注重学生主体性和教师主导性的统一; +- 强调教学目标的可操作性和可测量性; +- 输出必须为合法 JSON 格式。 +""" + +completion_prompt = """ +你现在是一名经验丰富的教师,正在撰写专业的教案材料。你的语言风格应严谨、实用、具有可操作性,符合教育教学规律。 + +你的任务是:根据给定的标题和写作指导,结合提供的教学目标和课程信息,生成一段高质量的教案内容。 + +要求: +- 内容紧扣教学目标,体现教学的专业性和科学性 +- 注重教学过程的可操作性和实用性 +- 语言准确、条理清晰,符合教案写作规范 +- 不要添加额外标题或格式 +- 专注于教案设计,体现以学生为中心的教学理念 +- 根据学科特点和学生实际情况设计教学活动 +- 注重教学评价和反馈机制 +- 体现教师的专业素养和教学智慧 + +输入格式为 JSON:{"title": "...", "describe": "..."} +""" + +# 构造 writer 的 prompt 模板 +writer_prompt = { + "system": completion_prompt, + "user": '{"title": {title}, "describe": {describe}}' +} + +# === 构建 Pipeline === +with pipeline() as ppl: + # Step 1: 生成大纲(使用 JsonFormatter 确保输出为 JSON) + ppl.outline_writer = lazyllm.OnlineChatModule( + source="sensenova", + api_key=API_KEY, + secret_key=SECRET_ID, + model="Qwen3-Coder", + stream=False + ).formatter(JsonFormatter()).prompt(toc_prompt) + + # Step 2: 逐段生成正文(warp 用于循环处理多个 outline 条目) + ppl.story_generater = warp( + lazyllm.OnlineChatModule( + source="sensenova", + api_key=API_KEY, + secret_key=SECRET_ID, + model="Qwen3-Coder", + stream=False + ).prompt(writer_prompt) + ) + + # Step 3: 合并大纲与正文 + ppl.synthesizer = ( + lambda *storys, outlines: "\n".join([ + f"{o['title']}\n{s}" for s, o in zip(storys, outlines) + ]) + ) | bind(outlines=ppl.output('outline_writer')) + +# 启动 Web 服务 +if __name__ == "__main__": + lazyllm.WebModule(ppl, port=23467).start().wait() \ No newline at end of file