Files
dsProject/dsLightRag/TeacherHelper/Kit/TeacherHelper.py
2025-09-04 16:09:11 +08:00

227 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import logging
import os
import aiofiles
from openai import AsyncOpenAI
from Config.Config import ALY_LLM_API_KEY, ALY_LLM_BASE_URL, ALY_LLM_MODEL_NAME
# 配置日志
logger = logging.getLogger(__name__)
class LLMClient:
"""
大语言模型客户端封装类提供与LLM的交互功能
"""
def __init__(self, api_key=None, base_url=None, model_name=None, system_prompt=""):
self.api_key = api_key or ALY_LLM_API_KEY
self.base_url = base_url or ALY_LLM_BASE_URL
self.model_name = model_name or ALY_LLM_MODEL_NAME
self.system_prompt = system_prompt
async def get_response(self, query_text: str, knowledge_content: str = "", stream: bool = True):
"""异步获取大模型响应"""
try:
client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
full_query = query_text
if knowledge_content:
full_query = f"选择作答的相应知识内容:{knowledge_content}\n下面是用户提的问题:{query_text}"
completion = await client.chat.completions.create(
model=self.model_name,
messages=[
{'role': 'system', 'content': self.system_prompt},
{'role': 'user', 'content': full_query}
],
stream=stream
)
if stream:
async for chunk in completion:
if chunk and chunk.choices and len(chunk.choices) > 0:
delta = chunk.choices[0].delta
if delta and delta.content and delta.content.strip():
yield delta.content
else:
if completion and completion.choices and len(completion.choices) > 0:
message = completion.choices[0].message
if message and message.content and message.content.strip():
yield message.content
except Exception as e:
logger.error(f"大模型请求异常: {str(e)}")
yield f"处理请求时发生异常: {str(e)}"
async def save_lesson_plan(content_chunks, output_file, stream_print=False):
"""异步保存教案内容到文件,支持实时打印"""
try:
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
logger.info(f"创建输出目录: {output_dir}")
async with aiofiles.open(output_file, 'w', encoding='utf-8') as f:
full_content = []
async for chunk in content_chunks:
full_content.append(chunk)
await f.write(chunk)
await f.flush()
# 实时打印输出内容
if stream_print:
print(chunk, end='', flush=True)
logger.info(f"教案已保存到: {os.path.abspath(output_file)}")
return ''.join(full_content), True
except Exception as e:
logger.error(f"保存教案失败: {str(e)}")
return None, False
async def generate_lesson_plan(system_prompt=None, user_prompt=None):
"""生成教案的异步函数,支持动态传入提示词"""
# 默认系统提示词
default_system_prompt = """你是市级骨干教师,撰写教案时严格对标"核心素养四维度(物理观念、科学思维、探究能力、态度责任)",并采用"目标—评价—活动"逆向设计模板UbD。活动设计需包含"教师行为+期望的学生行为+评价要点"三列表格。"""
# 使用传入的系统提示词或默认值
system_prompt = system_prompt or default_system_prompt
# 默认用户提示词
default_user_prompt = """请输出"万有引力"第1课时的详案要求
1. 教学重难点用"行为动词+知识内容"表述;
2. 至少1个"演示实验"+1个"小组探究"+1个"即时反馈"技术如Padlet
3. 时间轴精确到分钟;
4. 课后"教学反思提示"留3条空白供教师手写。
【格式要求】
### 1. 教材与学情分析
### 2. 四维目标(对应核心素养)
### 3. 重难点
### 4. 教学准备(器材+数字资源)
### 5. 教学过程(时间轴表格)
### 6. 板书设计ASCII示意图
### 7. 作业布置(与导学案分层衔接)
### 8. 教学反思提示(空白)
【变量】
教材版本:{人教版八年级下第十章第2节}
学生基础:{已学过重力、匀速圆周运动}
课时长度:{45分钟}"""
# 使用传入的用户提示词或默认值
user_prompt = user_prompt or default_user_prompt
llm_client = LLMClient(system_prompt=system_prompt)
async def get_lesson_plan():
async for chunk in llm_client.get_response(user_prompt, stream=True):
yield chunk
return get_lesson_plan()
async def generate_courseware(system_prompt=None, user_prompt=None):
"""生成课件大纲的异步函数,支持动态传入提示词"""
# 默认系统提示词
default_system_prompt = """你是PPT视觉设计教练遵循"6×6原则"每页≤6行每行≤6词字体≥28pt主色调#005BAC教育蓝强调色#FFB703暖黄。所有动画≤0.5s,禁止花哨。需要给出演示者备注栏(<备注>)。"""
# 使用传入的系统提示词或默认值
system_prompt = system_prompt or default_system_prompt
# 默认用户提示词
default_user_prompt = """"万有引力"生成可直接导入PowerPoint的Markdown大纲共12页
1. 封面(课程名+章节+教师姓名留白)
2. 情境导入1个30s短视频建议+2张图片提示
3. 概念建构(苹果落地+月亮绕地对比图)
4. 规律探究卡文迪许实验GIF占位
5. 公式推导F=G·m₁m₂/r²分三步行
6. 例题精讲2道step-by-step动画
7. 当堂检测Padlet二维码占位
8. 小结思维导图可一键转SmartArt
9. 作业二维码(链接到在线表单)
10. 结束页("思考:如果没有万有引力?"留白)
【格式要求】
每页用三级标题###表示,下方用<ul>列要点;如需图片,用! `https://via.placeholder.com/800x450?text=Image` 占位并给出版权提示;在要点后另起一行写<备注>演示者话术。"""
# 使用传入的用户提示词或默认值
user_prompt = user_prompt or default_user_prompt
llm_client = LLMClient(system_prompt=system_prompt)
async def get_courseware():
async for chunk in llm_client.get_response(user_prompt, stream=True):
yield chunk
return get_courseware()
async def generate_homework(system_prompt=None, user_prompt=None, material_path=None):
"""生成课后作业的异步函数,支持动态传入提示词和材料路径"""
# 默认系统提示词
default_system_prompt = "你是命题专家,熟悉布鲁姆认知分类和'双减'政策。你需要根据用户提供的题目材料来生成作业,而不是自己创造题目。客观题采用'四选一'单选,难度比例易:中:难=6:3:1主观题设置2小问第1问'解释现象'对应'理解'第2问'方案设计'对应'创新'。题量控制为20分钟完成。"
# 使用传入的系统提示词或默认值
system_prompt = system_prompt or default_system_prompt
# 默认材料路径
default_material_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static", "YunXiao.txt")
material_path = material_path or default_material_path
# 读取题目材料
try:
with open(material_path, "r", encoding="utf-8") as f:
question_material = f.read()
except Exception as e:
logger.error(f"读取题目材料失败: {str(e)}")
question_material = ""
# 默认用户提示词
default_user_prompt = f"""请根据以下'万有引力'相关题目材料输出课后作业满分100分
A. 客观题8题×5分=40分
- 从提供的材料中选择题目,并按要求重新组织
- 前3题考'史实&概念'识记
- 中间3题考'公式变形&比例'理解
- 后2题考'情境估算'应用
B. 主观题2题30+30分
- 题1结合'天问一号'发射新闻,解释地球与火星之间的引力如何变化
- 题2设计一个实验用智能手机+免费APP估算地球质量写出步骤与所需测量量
C. 评分标准主观题分点给分每点10分
D. 参考答案与解析(客观题给出选项+一句话解析;主观题给出关键公式与评分关键词)
以下是题目材料:
{question_material}
【格式要求】
### A. 客观题
<ul>
<li>1. ……</li>
……
</ul>
### B. 主观题
#### 1. ……
#### 2. ………
### C. 评分标准
……
### D. 参考答案与解析
……"""
# 使用传入的用户提示词或默认值
user_prompt = user_prompt or default_user_prompt
llm_client = LLMClient(system_prompt=system_prompt)
async def get_homework():
async for chunk in llm_client.get_response(user_prompt, stream=True):
yield chunk
return get_homework()
# 保持向后兼容性的函数别名
async def generate_gravitation_lesson_plan():
"""生成万有引力教案的异步函数(向后兼容)"""
return await generate_lesson_plan()