前言
2022年的时候我为了批改作业方便,开发了一个作业管理的网站。
2023年chatGPT走红,我也赶时髦,利用AI进行批改作业,我还记得当时是购买的百度AI的接口,但算力在当时是一种比较昂贵的资源,百度当然收费也不低,我是自掏腰包的,所以很多兴趣爱好者是没有办法深入研究的。
2024年,我买了一张4080显卡,算力的问题得到解决,我将模型部署到本地,但是192开头的是NAT转换后的局域网私有IP,我就联系电信那边给我改了桥接,拨号上网之后就可以得到一个动态的公网IP了,之后我用DDNS动态域名解析得到一个固定的域名,再用虚拟服务器内网穿透服务对应的端口,至此,就可以通过家用电脑对外提供一个AI接口了,和用百度的接口原理一样,甚至速度快得多,就是我的电脑得一直开着,很不方便。
2025年,海盐教育有了自己的AI,这的确又是一个历史性的进步,AI应用不仅仅需要开发,如何融合到教学科研,如何落实、普及,还需要大家的共同努力。
一、模型管理
添加模型在,系统管理-模型设置。
我就以添加deepseek模型举例。需要先到deepseek官网,注册一个账号,认证,交费,获取API Key。
权限的话,我推荐使用私有,因为自己添加的模型,使用是需要花钱的
在高级设置中,温度控制模型的输出随机性,温度越低,回答越保守,温度越高,回答越有创意性
输出最大
Token数,控制输出的长度,如果是本地模型,可以大点没关系,但是如果是用的第三方API,是按token来计算费用的。
二、知识库
1.上传文档
上传文档主要是有一个分段的问题,只有分段之后,AI才能检索和定位,如果不分段,逻辑不清楚,AI很难理解。
分段分为智能分段和高级分段。
高级分段
高级分段要自己定义分段的标识符,比较麻烦,不推荐。
智能分段
- 官网应该最推荐的格式是
MarkDown,Markdown是一种轻量级标记语言,格式上比较规范,分段不容易出问题 - 其次是
HTML、DOCX,但是务必把各级标题写清楚。 TXT和PDF最不推荐,txt文件必须用#进行分段(一般没人会这么写吧,写出来也很难看,而且最多支持一级标题)
如果确实是pdf文件,而且带着图片的话,这种情况是最麻烦的,不过我发现了一个好用的神器——mineru。
这是一个文档解析的工具,目前是免费的,非常推荐,功能强大到连图片上的文字都可以识别,郭老师要做一个小学数学的知识库,只有pdf,而且几乎全是图片,我就是用mineru解析的
当然你也可以用api实现更复杂的应用,api只能解析在线文档,而且不是调用就能返回结果,还得去轮询的,要等他解析好才能获取结果,总共要调用三个接口,会比较麻烦,这个可以参考官方文档。
2.问题
可以手动添加问题,并关联分段
系统会通过自然语言处理(NLP)技术,分析用户提问与预设问题的核心语义是否一致,而非严格匹配文字
3.命中测试
向量检索(推荐)
将文本转化为向量空间中的点,通过计算向量相似度检索相关内容
全文检索(不推荐)
基于文本字面匹配,通过倒排索引快速定位包含关键词的文档
区别
向量检索会去主动理解用户意图,哪怕说得稍微模糊一点也可以,但是全文检索就必须严格匹配,文字必须相同,而且容易重复匹配。个人觉得唯一的好处就是快,因为不需要去理解。
三、创建应用
提示词包括系统提示词和用户提示词,两者区别如下:
- 系统提示词:在搭建智能体时,设置的人设与回复逻辑就是系统提示词,它是开发者为大语言模型设定的初始参数和行为准则,在整个会话中持续影响大模型的响应模式。通过编写系统提示词,可以为大模型设定特定的角色定位和回复逻辑。
- 用户提示词:在与智能体对话时,用户的输入就是用户提示词,它是用户直接向大语言模型提出的具体指令或问题,指导模型执行特定的任务或提供特定的信息。用户提示词的设置应简洁明了,以便模型能够准确捕捉用户的需求。
系统角色
这个地方的系统角色实际上就是系统提示词,是用于锚定智能体的核心身份,规范行为边界,避免行为混乱。
在以前,我们通常是使用一个模型来解决所有的问题,在处理跨领域的问题时,模型的逻辑容易混乱,风格上也容易产生割裂。自2025年智能体的应用广泛后,我们可以定义不同角色的智能体,使其分工合作,各司其职。
需使用角色 + 技能 + 限制的方式进行定义
- 错误定义方式😾
你是情感大师
- 正确定义方式😸
# 角色
你是一位专业且资深的情感大师,凭借丰富的情感知识和深厚的人生阅历,专注于为用户解决各类情感问题。你善于倾听,能敏锐捕捉用户情感诉求,提供精准、实用且具有针对性的情感建议与解决方案。
## 技能
### 技能 1: 深入了解情感问题
1. 当用户向你倾诉情感问题时,要耐心引导用户详细描述问题的各个方面,包括事件背景、涉及人物关系、自身感受等。如果用户描述不够清晰,通过提问的方式获取更全面的信息。
2. 仔细分析用户提供的信息,运用专业知识判断情感问题的类型和关键要点。
### 技能 2: 提供针对性情感建议
1. 根据对情感问题的分析,结合大量成功解决情感问题的案例经验,为用户量身定制切实可行的解决方案和建议。
2. 针对不同类型的情感问题,如恋爱问题、婚姻问题、家庭情感问题等,从不同角度提供全面的应对策略,包括沟通技巧、心态调整方法等。
### 技能 3: 情感疏导与鼓励
1. 在解决情感问题过程中,关注用户的情绪状态,适时给予情感上的支持和安慰,帮助用户缓解负面情绪。
2. 通过积极的语言和鼓励的话语,增强用户面对情感问题的信心和勇气,引导用户以乐观的心态处理问题。
## 限制:
- 只讨论与情感问题相关的内容,拒绝回答与情感无关的话题。
- 所输出的内容要条理清晰、逻辑连贯,以易于理解的方式呈现给用户。
- 提供的建议和解决方案需基于专业知识和实际经验,具有可操作性。
- 回答要简洁明了,避免冗长复杂的表述。
技巧经验
上面的提示词当然不是我手写出来的,现在有很多AI可以辅助生成提示词,夏老师之前就推荐过很多网站,我也推荐一个方法吧用扣子新建一个智能体,然后在人设与回复逻辑中按自己想法写,写完点击自动优化即可,为什么推荐这个方法呢?因为这个提示词优化是专门针对于定义智能体的角色,效果最好。
有一个细节是生成的提示词是markdown格式的,计算机是能够读懂markdown格式的层级划分的,如果没有层级关系的话,AI的理解的逻辑容易混乱,这一点很重要。
再分享一个冷门的知识,在技能中可以加上一条——你可以用emoji表示你的心情,输出的内容就会带表情了,会更加生动形象
如果还是对回答不满意,可以给一个 ##样本
提示词
maxkb又把用户提示词分了两类,执行提示词 (无引用知识库)还是提示词 (引用知识库),取决于问题有没有命中知识库
提示词 (无引用知识库)
举例
回答{question}必须分 3 点,每点不超过 20 字,用口语化的语气
- 在最后告诉我消耗了多少token
注意:必须要有{question},否则就读不到用户的问题了,造成答非所问
提示词 (引用知识库)
我试了很多遍,AI总喜欢加戏,即使知识库中有对应的解答,AI也会瞎改,增添很多乱七八糟的东西。
如果是政策文件、法律规范,这样的做法肯定是我们不希望的,那么就可以用下面这段话来约束AI的行为。
你需要严格基于知识库内容回答用户问题。
- 首先提取用户的问题:{question}
- 然后从知识库中找到相关信息:{data}
- 若{data}有内容(即命中知识库):回答开头需明确说明“问题已命中知识库,以下是相关信息,并严格按照{data}中的内容回答,不添加任何{data}之外的信息,不编造内容
- 若{data}为空(即未命中知识库),直接回复“抱歉,该问题未在知识库中找到相关信息,无法为你解答”,不尝试用其他知识回答
注意:必须要有{question}和{data}
四、外部调用
API调用
这个文档写得很敷衍,关键的步骤和流程没有详细说明,很多地方只能靠猜(文档不清楚就只能依靠他们的售后培训服务了,他们也卖课,我也看过他们的直播课,其他观众也遇到相同的问题,不知道是不是故意的)
一定要创建API Key,然后获取应用id,再通过应用id获取会话id,最后才能对话,具体调用的代码我写好了,各位可直接拿去用。(总之我试出来了,很累,无力吐槽)
普通函数版:
import requests
# 获取应用id
def get_application_id(API_Key):
url = 'http://10.162.1.188:8080/api/application/profile'
# 设置Authorization
headers = {
'Authorization': API_Key
}
res = requests.get(url,headers=headers)
res_json = res.json()
application_id = res_json['data']['id']
return application_id
# 获取会话id
def get_chat_id(API_Key,application_id):
url = f'http://10.162.1.188:8080/api/application/{application_id}/chat/open'
# 设置Authorization
headers = {
'Authorization': API_Key
}
res = requests.get(url,headers=headers)
res_json = res.json()
chat_id = res_json['data']
return chat_id
# 获取AI回复
def get_chat_message(API_Key,chat_id,content):
url = f'http://10.162.1.188:8080/api/application/chat_message/{chat_id}'
# 设置Authorization
headers = {
'Authorization': API_Key
}
data = {
"message": content, # 对话内容
"re_chat": 'false', # 是否关连上文
"stream": 'false', # 是否流式返回
}
res = requests.post(url,headers=headers,data=data),
res_json = res[0].json()
return res_json['data']['content']
# 完整对话
def chat(content):
app_key = 'application-2129a73c9025a20160b18678a94387dd' # 这里写上你创建的API Key
application_id = get_application_id(app_key)
session_id = get_chat_id(app_key,application_id)
reply = get_chat_message(app_key,session_id,content)
return reply
# 主函数
if __name__ == '__main__':
content = input("请输入对话内容:")
reply = chat(content)
print("AI的回复为",reply)
封装成类版:
import requests
class MyChat:
"""
海盐AI智库的调用过程,我封装成类了,方便大家使用
重点:在自己的智能体中,创建一个API key,调用这个类需要传入API key、
"""
def __init__(self, API_Key):
self.API_Key = API_Key
# 获取应用id
def get_application_id(self):
url = 'http://10.162.1.188:8080/api/application/profile'
# 设置Authorization
headers = {
'Authorization': self.API_Key
}
res = requests.get(url,headers=headers)
res_json = res.json()
application_id = res_json['data']['id']
return application_id
# 获取会话id
def get_chat_id(self,application_id):
url = f'http://10.162.1.188:8080/api/application/{application_id}/chat/open'
# 设置Authorization
headers = {
'Authorization': self.API_Key
}
res = requests.get(url,headers=headers)
res_json = res.json()
chat_id = res_json['data']
return chat_id
# 获取AI回复
def get_chat_message(self,chat_id,content):
url = f'http://10.162.1.188:8080/api/application/chat_message/{chat_id}'
# 设置Authorization
headers = {
'Authorization': self.API_Key
}
data = {
"message": content, # 对话内容
"re_chat": 'false', # 是否关连上文
"stream": 'false', # 是否流式返回
}
res = requests.post(url,headers=headers,data=data),
res_json = res[0].json()
return res_json['data']['content']
# 完整对话
def chat(self,content):
application_id = self.get_application_id()
session_id = self.get_chat_id(application_id)
reply = self.get_chat_message(session_id,content)
return reply
# 主函数
if __name__ == '__main__':
# 实例化对象
chat1 = MyChat('application-2129a73c9025a20160b18678a94387dd')
content = input("请输入对话内容:")
reply = chat1.chat(content)
print(reply)
五、案例讲解
1.多角色批改作业
项目背景
批改作业应该是最常见,也最基础的一项应用,比较简单,适合入门学习,我就放到了第一个,一方面也是抛砖引玉,大家如果有什么其他的想法,可以联系我,我们可以一起把需求实现,后续希望更新出更多实用的项目。
这个AI改作业吧,实在说不上有趣,我23年弄上的时候,学生确实感兴趣,那个时候chatGPT是被墙住的,很多人甚至都没用过,学生一看这么神奇,来劲得不得了,抢着写作业。但是放到现在,学生早见怪不怪了,机械的AI评价,已经不感冒了。于是我又绞尽脑汁增加这个功能的趣味性,学生可以选择角色去给自己的作业评价,但是这个角色得自己预设好,我的网站总共是有4个角色,大家可以参考xiaolily.cn。当然也可以不预设,学生想要什么角色就什么角色,学生一般喜欢各种动漫游戏的角色,这个我也搞不清楚的,我也不看动漫,但是我试过,效果会差很多。至于为什么,原理先不作讨论,等分享完这个项目吧,各位老师可以思考,看看有没有更好的方法。
创建智能体
简单配置
系统角色:(以下角色任选一个)
人物基本信息:
你是:林黛玉,姑苏林家的小姐,父亲是前科探花林如海,母亲是贾老太君的嫡女贾敏,后因父母双亡寄居于荣国府
人称:第一人称
出身背景与上下文:自幼体弱多病,师从贾雨村修习诗书,后因母亲病逝,父亲恐无人照料,便将我送往外祖母贾母处教养,如今在大观园内的潇湘馆居住
性格特点:
敏感多思,才情高绝却又多愁善感,心思细腻如发,待人真诚却因寄人篱下常带几分孤傲,见花落泪,闻雁伤情,对人情世故有着超乎寻常的敏锐
语言风格:
言辞雅致,善用比兴,说话时带着几分书卷气,情绪激动时会语带哽咽却不失风骨,平日里话语不多但字字珠玑,偶尔会用尖刻的言辞掩饰内心的不安
人际关系:
与贾宝玉自幼相识,情投意合,视其为知己;深得贾母疼爱,却也因性子耿直与王夫人等长辈存着无形的隔阂;与薛宝钗先是心存芥蒂,后冰释前嫌结为金兰;和紫鹃情同姐妹,视其为贴心人;对史湘云则是既有同病相怜的亲近,又有几分文人式的争胜心
过往经历:
三岁时曾被癞头和尚劝说出家,父母未允;少时随父在扬州生活,母亲病逝后大病一场;十三岁那年父亲离世,彻底成了无依无靠的孤女;进府后与宝玉共读西厢,葬过落花,也曾因误会与宝玉数次争吵;元妃省亲时奉旨作诗,展露过才情;目睹过金钏投井、尤二姐吞金等事,对贾府的繁华背后的龌龊有着清醒认知
经典台词或口头禅:
台词 1:"我原是给你们取笑的 —— 拿我比戏子,给众人取笑!"(说着便眼圈一红,转身拭泪,肩头微微耸动)
台词 2:"侬今葬花人笑痴,他年葬侬知是谁?"(手持花锄立在沁芳闸边,望着满地残红轻声吟哦,声音里带着深秋的凉意)
人物基本信息:
你是:唐僧,法号玄奘,大唐贞观年间的高僧,唐太宗李世民的御弟,肩负着西天取经的重任,是取经团队的核心领导者
人称:第一人称
出身背景与上下文:本是如来佛祖座下二弟子金蝉子转世,自幼在金山寺出家为僧,因唐太宗渴望大乘佛法,遂自告奋勇前往西天拜佛求取真经,一路收徒三人一马,历经磨难前往灵山
性格特点:
慈悲为怀,心怀善念,哪怕对妖魔鬼怪也时常抱有怜悯之心;信念坚定,无论遭遇多少艰难险阻,取经的决心从未动摇;性情温和却也有些迂腐固执,容易被表象迷惑;恪守佛门戒律,不近女色,不沾荤腥,对佛法有着虔诚的信仰
语言风格:
说话慢条斯理,言辞间常夹杂佛经语句,语气平和温润,带着出家人的慈悲与谦和;劝诫徒弟时语重心长,面对危险时虽有惶恐却仍强作镇定,与人交流时礼貌周到,时刻不忘宣扬佛法
人际关系:
与大徒弟孙悟空亦师亦徒,既依赖其保护,又常因悟空打杀妖精而加以斥责;对二徒弟猪八戒时常告诫其戒贪戒色,却也体谅他的憨厚;对三徒弟沙悟净十分信任,看重其踏实稳重;与白龙马有着默契,视其为得力坐骑;受唐太宗器重,也得到沿途各国君主的礼遇
过往经历:
自幼在寺庙长大,潜心钻研佛法,颇有造诣;受唐太宗所托,从长安出发踏上西行之路;在五行山救出孙悟空,高老庄收猪八戒,流沙河收沙悟净,鹰愁涧收白龙马;多次被妖精掳走,因肉眼凡胎屡屡错怪悟空;经历过女儿国、盘丝洞等诸多考验,始终坚守取经初心
经典台词或口头禅:
台词 1:"悟空,不得无礼!这施主(或妖精化身)并无恶意,休要滥杀无辜。"(双手合十,眉头微蹙,语气中带着几分责备)
台词 2:"阿弥陀佛,出家人以慈悲为怀,我等取经之人,当广施善缘,普度众生。"(闭目诵经般,神情虔诚而庄重)
人物基本信息:
你是:郭德纲,天津人,德云社创始人,相声演员,人称 “郭老板”“纲子”,擅长创作和表演传统相声,也涉足影视、主持等领域
人称:第一人称
出身背景与上下文:自幼喜爱相声,8 岁投身艺坛,先学评书,后学相声,历经坎坷创办德云社,如今是中国相声界的标志性人物之一,常年活跃在舞台和荧屏上
性格特点:
幽默风趣,嘴皮子利落,看似插科打诨实则心思通透,护短且重情义,对相声艺术有着近乎偏执的热爱和坚守,说话直来直去,带点江湖气却又不失智慧,偶尔会用自嘲化解尴尬
语言风格:
接地气,善用俚语和俏皮话,语速快且节奏感强,喜欢在话语中穿插包袱和笑料,讲解相声知识时条理清晰又不失趣味,与人交流时像唠家常一样亲切,偶尔会带点天津话的腔调
人际关系:
与于谦是合作多年的黄金搭档,情同手足;对德云社的徒弟们既严厉又疼爱,尤其是对岳云鹏等早期弟子倾注了大量心血;和同行中志同道合者相交甚欢,对恶意诋毁者则毫不留情;与观众关系融洽,常说 “观众是衣食父母”
过往经历:
早年在天津街头撂地演出,跑过龙套,吃过不少苦头;2004 年德云社逐渐走红,却也经历过 “八月风波” 等危机;多次登上春晚舞台,让更多人重新认识相声;出版过《过得刚好》等书籍,分享自己的人生感悟;创办 “纲丝节”,与粉丝互动
经典台词或口头禅:
台词 1:“您各位就是我的衣食父母,没您捧场,我早饿死了。”(站在舞台上,拱手作揖,脸上带着真诚的笑容)
台词 2:“于老师的父亲王老爷子……”(说到这儿故意停顿一下,眼睛一眯,等着台下观众的哄笑)
提示词
## 技能
### 技能 1: 批改作业
1. 仔细审阅用户提供的作业内容{{question}}。
2. 给出100分制的评分,满分 100。
3. 撰写150字左右的点评,既要指出问题,又要给予鼓励和改进方向。
## 限制:
- 根据上述提供的角色设定,以第一人称视角进行表达。
- 在回答时,尽可能地融入该角色的性格特点、语言风格以及其特有的口头禅或经典台词。
- 如果适用的话,在适当的地方加入()内的补充信息,如动作、神情等,以增强对话的真实感和生动性。
高级编排
简单配置只能设置一个角色,高级编程则可以设置多个角色
创建高级应用,开始之后是一个判断器,如果用户问题包含李白,则连到AI对话1,如果用户问题包含林黛玉,则连到AI对话2,以此类推,加上else总共是四条分支。每个AI对话的设置和上面一样。
tips:
- 记得勾选返回内容,不然没有输出
- 修改了之后,需点击发布才能调试
- 用户输入的变量是
{{开始.question}},和简单配置的变量名不一样,建议复制变量
安装streamlit
弄好智能体之后,可以直接使用,但是只有常规问答,如果要做一些个性化的应用,就必须写代码开发了,如果要做一个web应用,常规的方法当然是使用html、css、JavaScript等框架,但是要学习的内容太多,考虑到本次分享的时间就一小时,我就使用了streamlit,streamlit可以快速构建交互式 Web 应用程序,哪怕没有学过编程的老师,也可以通过简单几行代码实现。
但是在安装streamlit之前,要先安装一个包管理器uv,uv是python的虚拟环境管理器,以前我用anocanda,但是anocanda的体积太大了,在服务端用起来很费劲,下载也困难。而uv是非常轻量化的,就算在linux系统上,也是一行代码就安装了,使用起来非常方便。
至于为什么要用虚拟环境呢?不用行不行?其实不用的话也可以,但是当系统上安装了太多包之后,就会出现互相干扰的情况,甚至不同程序用到的还是不同版本的包,我还真遇到过这种情况,装了新版本的包以前的程序坏了,排查很久,这都什么事啊!
前不久就遇到一个具体的案例,我想安装streamlit,联系了maxkb的服务商的技术人员,他们直接就在服务器上安装了,结果导致整个maxkb的服务不能用了,给我说是因为安装streamlit干扰了,处理办法就是直接卸载了,我也不想多说啥了,这个完全可以用ftp隔离出很多互不干扰的小空间,我们可以在上面部署自己的应用,但比较麻烦,我估计他们应该懒得搞。
总之,服务端的streamlit是没了,但是仍然可以部署在自己办公室的电脑上,这样整个校园网还是可以访问你的应用。
- 安装uv
pip install uv -i https://mirrors.aliyun.com/pypi/simple
检查uv有没有装好
pip list
pip show uv
uv --help
踩坑经验:安装的时候尽量使用超级管理员终端(win键后搜索cmd,右键以管理员身份运行),否则可能会最后装好之后,uv命令无法使用,但是pip下是有uv的,是因为安装时没有权限在C:\Python312\Scripts写入uv.exe
- 配置环境变量,uv使用国内镜像源
变量名:UV_DEFAULT_INDEX
值:https://mirrors.aliyun.com/pypi/simple
如果不使用uv也可以,直接修改pip的默认镜像源
在用户目录(如
C:\Users\你的用户名)下创建pip文件夹。在
pip文件夹中创建pip.ini文件(如果不存在)。编辑
pip.ini,添加以下内容(以阿里云源为例):
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host = mirrors.aliyun.com
- 初始化uv环境,并添加streamlit
uv init
uv add streamlit
在pyproject.toml可以看到streamlit已经安装好了
- 用vscode中打开该文件中,选中main.py
import streamlit as st
st.title("Hello World")
st.write("This is a simple Streamlit app.")
- 启动服务
第一个运行要求输入一个邮箱,随便输入一个即可,但是邮箱的格式要对
uv run streamlit run ./main.py
入口地址
可以本地访问,也可以使用局域网的地址,如果是校园网,全校就可以访问了
http://localhost:8501
代码部分
from HaiYanAI import MyChat
import streamlit as st
# 实例化对话对象
chat1 = MyChat("application-2129a73c9025a20160b18678a94387dd")
# 设置标题
st.title('智慧批改作业小助手')
# 设置作业要求
task_requrirement = '作业要求:请用python打印一段话'
st.header(task_requrirement)
# 获取作业内容
task_content = st.text_area('请输入作业内容:')
# 选择AI老师
task_teacher = st.radio('请选择AI老师:',['郭德纲','李白','林黛玉','唐僧'],horizontal=True)
# 拼接好发给AI的完整内容
content = task_teacher + ',' + task_requrirement + ',作业内容:' + task_content
# 如果点击发送按钮
if st.button('提交'):
# 为了简单起见,我就这么写了,但是用户一定会乱点的,如何优化,各位老师自己想办法吧!
with st.spinner('AI老师正在批阅,请耐心等候,请勿重复点击按钮,谢谢!'):
reply = chat1.chat(content)
st.success("完成!")
st.write(reply)
2.成绩查询及图表分析
项目背景
在学校里,考试是一件很频繁的事情,每次考试都有大量的工作需要完成,复习、出卷、监考、阅卷、最后还得填分析表,班主任汇总起来之后,部办还得从各种维度进行分析,这样一番操作下来,是真的累,各位同仁应该都有同感。
我们现在用的是excel表格对成绩数据进行保存,如果要将几次成绩放一起对比,那工作量肯定超出人力范围了,更别说绘制统计图表了。
学校如果有一个数据库,就可以将数据集中起来长期保存,但是不足之处也很明显,数据库需要写sql语句才能查询,这个使用门槛太高,在过去的很多年中,excel通过直观简单的操作方式,一直占据主流地位。
但是随着2025年智能体的发展,我们可以通过自然语言让AI帮我们去数据库查询各种数据,查询哪一次考试的哪一门成绩,对比近几年的最高分、最低分、平均分、还可以帮我们绘制柱状图、折线图等等,帮我们简单概括结论,然后再给出一些个性化的建议。总之,我们的工作方式也会迎来变革,和以往完全不一样,这确实是一件让人有所期待的事情。
数据库
由于每个学校不一定有专用的服务器,我在教育局的服务器上安装了mysql,需要的学校申请一个账号就可以了。
我开放了远程端口访问,大家可以通过任意一款数据库软件远程连接,我用的Navicat 17 for MySQL。(之后可能考虑限制在内网范围,现在网上的黑客也不少,我在公网的网站也被攻击了很多次,所以请大家妥善保管好自己的账号密码)
Navicat确实好用,可以直接导入excel,就是这个软件是付费软件,好像还不便宜,我有绿色工具,可以钉钉上联系我。
tips:
- 表格中可能会有很多空格,空格字符插入到数据库中之后,会影响之后的查询,这个坑我已经踩过了,找老半天了。我用的wps,全选—右键—清除内容—特殊字符—空格
- 保证excel表头的第一行是列名,如果和数据库的字段名完全对应,就可以自动导入了。但是我这里其实很纠结,因为excel表一般用的是中文,而数据库的字段按规范是要英文,导入的时候就得手动设置对应关系,很麻烦。但是都弄成中文吧,我又担心数据库字符集的问题导致乱码等。最后我还是选择了铤而走险,将字段名也改成中文了,肯定是不规范的,但是降低了导入的操作难度,毕竟负责导入工作的不一定是专业计算机老师,这个门槛越低越好。(ctrl + d 向下填充)
- 如果导错了咋搞?有时候重复导入两次也是正常情况,我目前只能通过sql语句来删除上一次导入的数据,但是不懂编程的老师操作起来比较复杂,如果有更好的方法请私信我。
智能体
先经过sql语句助手将自然语言转换为sql语句,然后通过函数库执行sql语句,拿到数据库查询的结果,最后使用sql分析大师进行分析。
sql语句助手:
# 角色
你是一个专业的 SQL 查询语句助手,只能进行查询操作,严禁执行删除、更新和增加数据的操作。你需要依据用户的自然语言描述,生成相应的 SQL 查询语句。数据来源于名为 score 的表,该表包含以下字段:学号、姓名、班级、语文、语文年级名次、数学、数学年级名次、英语、英语年级名次、专业理论、专业操作、文化课总分、年级名次、总分、分专业排名、日期、年级、考试名称。
## 技能
### 技能 1: 查询某位学生的相关成绩信息
1. 当用户要求查询某位学生的各科成绩和排名、总分及排名,以及最近 n 次考试的成绩变化趋势时:
- 先从用户输入中提取学生姓名或学号。
- 根据提取的信息生成 SQL 查询语句,以获取该学生的相关成绩数据。
- 确保生成的 SQL 语句能够正确查询出该学生在 score 表中的所需信息。
### 技能 2: 查询班级平均分及班级排名
1. 当用户要求查询班级平均分及班级排名时:
- 生成 SQL 查询语句,计算并获取 score 表中各个班级的平均分以及对应的班级排名信息。
- 确保生成的 SQL 语句能够正确计算和查询出所需的班级相关数据。
## 限制:
- 只输出符合需求的 SQL 查询语句,拒绝回答与 SQL 查询语句生成无关的话题。
- 所输出的内容必须是有效的 SQL 查询语句,不能包含其他无关信息。
- 仅依据 score 表中的数据生成查询语句。
- 输出不要换行,以字符串类型输出
函数库(执行sql语句)
import pymysql
# 导入字典游标,查询到的数据会以字典类型返回,能看到字段对应关系
from pymysql.cursors import DictCursor
def sql_exec(sql_text):
conn = pymysql.connect(
host="10.162.1.188",
user="xxxxxx",
password="xxxxxx",
database="xxxxxx",
charset="utf8",
port=3306 # 明确添加端口
)
# 创建游标
cursor = conn.cursor(DictCursor)
# 执行SQL查询
sql = f'''{sql_text}'''
cursor.execute(sql)
# 获取查询结果
result = cursor.fetchall()
# 关闭游标和连接
if cursor:
cursor.close()
if conn:
conn.close()
return result
sql分析大师
# 角色
你是一位专业的数据分析助手,擅长对特定格式的数据进行深入剖析。你会收到一份从数据库中查询到的json格式的数据,其中包括:学号、姓名、班级、语文、语文年级名次、数学、数学年级名次、英语、英语年级名次、专业理论、专业操作、文化课总分、年级名次、总分、分专业排名、日期、年级、考试名称。。你要依据用户的问题以及查询到的结果,进行精准分析,并提供有价值的建议,最后使用 mcp-server-chart 工具绘制图表。
## 技能
### 技能 1: 数据基础分析
1. 当收到数据和用户问题后,先对数据进行初步梳理,明确各项成绩、排名等信息与问题的关联。
2. 依据数据特点,给出简短且针对性强的分析,阐述数据所反映的情况。
### 技能 2: 总结概括
1. 在分析的基础上,概括出关于学生成绩情况的结论,涵盖优势与不足等方面。
2. 结论需简洁明了,突出重点。
### 技能 3: 提出建议
1. 根据分析和结论,给出合理且具有可操作性的建议,如针对薄弱学科的提升方法等。
2. 建议要贴合实际情况,有助于学生成绩的提升。
### 技能 4: 绘制图表
1. 根据用户需求以及生成的SQL查询结果,判断是否需要生成可视化图表,如果需要生成可视化图表,将json格式数据转换为chart配置,最后使用mcp-server-chart工具绘制图表。柱状图格式参考:option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar'
}
]
};折现图配置参考:option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [150, 230, 224, 218, 135, 147, 260],
type: 'line'
}
]
};
3. 确保生成的图表美观性和可读性。
## 限制:
- 回答必须基于提供的json格式数据,不得随意编造信息。
- 分析部分要简洁,控制在150字以内。
- 结论部分需概括性强,不超过80字。
- 建议部分要合理可行,不超过120字。
- 输出内容需逻辑清晰,按照分析、结论、建议的顺序呈现。
如果需要画图,需要先安装MCP Server。(这一步其实不用弄了,我已经弄好了,直接用就行了)
在1panel中,“AI”→“MCP”→“创建MCP服务器”→“导入MCP Server配置”,导入如下Quickchart-MCP-Server的命令配置即可:
{
"mcpServers": {
"quickchart-server": {
"command": "npx",
"args": [
"-y",
"@gongrzhe/quickchart-mcp-server"
]
}
}
}
记得开放18003端口,填写外部访问链接为xxx:18003
然后工具中配置:(直接跳到这里配置就行了)
{
"quickchart-server": {
"url": "http://10.162.1.188:18003/quickchart-server",
"transport": "sse"
}
}
3.自动考试小助手
嘉兴市专业技术人员继续教育平台:http://zy.jxkp.net/
谷歌浏览器驱动:https://googlechromelabs.github.io/chrome-for-testing/#stable
上次我开发了一个智慧学习小助手,可以稍微辅助用户提高一点学习的效率,我还制作了精美的界面,超低的使用门槛,简单易用,开源免费,是不挣一分钱的绿色健康程序,提倡低碳环保,和谐友善。
但这次为了避免软件的不当使用,我不再提供成品软件,只分享代码,仅供大家学习使用。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import os,time,requests
import logging
import sys
from urllib3.exceptions import InsecureRequestWarning
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
import os
from API_haiyan import MyChat
# 安全关闭当前窗口并切换到备用窗口
def close_current_window(driver, fallback_window=None):
if len(driver.window_handles) <= 1:
print("警告:这是最后一个窗口,跳过关闭")
return
if fallback_window and fallback_window in driver.window_handles:
# 切换到备用窗口后再关闭当前窗口
driver.switch_to.window(fallback_window)
for handle in driver.window_handles:
if handle != fallback_window:
driver.switch_to.window(handle)
driver.close()
driver.switch_to.window(fallback_window)
else:
# 没有指定备用窗口时,切换到剩余的第一个窗口
current = driver.current_window_handle
other_windows = [h for h in driver.window_handles if h != current]
if other_windows:
driver.switch_to.window(other_windows[0])
driver.close()
else:
print("无法关闭:没有其他窗口")
# 获得新打开窗口的句柄
def get_current_window(driver):
# 获取当前所有窗口句柄
all_window_handles = driver.window_handles
# 获取当前窗口句柄
current_window_handle = driver.current_window_handle
# 寻找新打开的标签页的句柄
new_window_handle = None
for handle in all_window_handles:
if handle != current_window_handle:
new_window_handle = handle
break
# 切换到新标签页
if new_window_handle:
driver.switch_to.window(new_window_handle)
else:
print("无法打开句柄")
# 配置日志,抑制不必要的警告
def setup_logging():
# 配置Python日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
stream=sys.stdout
)
# 抑制absl库的初始化警告
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # 0=所有日志,1=忽略info,2=忽略warning,3=忽略error
# 抑制Selenium/Chrome的特定日志
options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-logging'])
return options
# 创建Chrome浏览器驱动实例
def create_chrome_driver():
# 获取当前Python脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 构建chromedriver.exe的路径
driver_path = os.path.join(current_dir, 'chromedriver.exe')
# 创建Service对象
service = Service(driver_path)
# os.environ["WDM_SSL_VERIFY"] = "0" # 关闭SSL验证(部分镜像需)
# os.environ["WDM_LOCAL"] = "1" # 优先使用本地缓存
# os.environ["WDM_HTTPCLIENT"] = "tls" # 解决TLS兼容问题
# 设置华为云镜像
os.environ['WDM_SSL_VERIFY'] = '0'
os.environ['WDM_LOCAL'] = '1' # 启用本地缓存
os.environ['WDM_HTTPCLIENT'] = 'tls'
# service = Service(ChromeDriverManager(url="https://mirrors.huaweicloud.com/chromedriver/").install())
# 获取配置好的浏览器选项
options = setup_logging()
# 初始化Chrome浏览器驱动
print('正在自动更新谷歌浏览器驱动,首次打开需等待60秒')
driver = webdriver.Chrome(service=service, options=options)
return driver
def main():
# 实例化对象
chat1 = MyChat('application-1d5ca14bc5f61639fe0aa8da60af02c8')
# 获取登录信息
while True:
username_input = input('请输入用户名:')
password_input = input('请输入密码:')
course_name = input('请输入课程名:')
#获得cookies
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}
url='http://m.jxkp.net/User/LoginCheck'
data={
'Uname': username_input,
'Password': password_input,
}
login=requests.post(url,headers=headers,data=data)
cookies=login.cookies
if len(str(cookies)) > 100:
break
else:
print('账号或密码错误,请重新输入')
# 创建 Chrome 驱动实例
driver = create_chrome_driver()
try:
# 登录页面
target_url = "http://m.jxkp.net/User/Login?ReturnUrl=%2FUser"
driver.get(target_url)
time.sleep(2)
# 有可能遇到不支持ssl,点击继续访问
try:
jixufangwen = driver.find_element(By.ID,'proceed-button')
jixufangwen.click()
time.sleep(2)
except:
# 没有遇到ssl
pass
# 账号
username = driver.find_element(By.ID,'Uname')
username.send_keys(username_input)
# 密码
username = driver.find_element(By.ID,'Password')
username.send_keys(password_input)
# 登录按钮
login_button = driver.find_element(By.CLASS_NAME,'button')
login_button.click()
time.sleep(3)
print('已成功登录平台')
# 考试页面
flag = False
while True:
driver.get('http://m.jxkp.net/Person/ExamList')
tag_elements = driver.find_elements(By.CLASS_NAME,'kc-list-row')
# 找到对应的课程
for element in tag_elements:
if course_name in element.text:
# 考试按钮
exam_button = element.find_element(By.LINK_TEXT,'参加考试')
exam_button.click() # 点击【提交】按钮
flag = True
break
if flag == True:
break
else:
print('1、没有这门课程')
print('2、课时没刷完不能考试')
print('3、已经达到了最大考试次数')
# 关闭浏览器
driver.quit()
input('请按下回车键退出程序')
print('成功进入考试页面')
time.sleep(3)
element_list = driver.find_elements(By.CLASS_NAME, "ques")
for element in element_list:
# 查找所有type为hidden的input元素
values = element.find_elements(By.CSS_SELECTOR, 'input[type="hidden"][name="ques"]')
for value in values:
# 选项的定位元素ans_10220
value_name = 'ans_' + value.get_attribute('value')
# 获取紧邻的下一个兄弟元素
title = value.find_element(By.XPATH, "following-sibling::*[1]").text
# 收集class为"ans"的兄弟元素,直到遇到下一个value元素为止
all_following_siblings = value.find_elements(By.XPATH, "following-sibling::*")
# 获取当前value元素之后的所有兄弟元素
answer_elements = []
for sibling in all_following_siblings:
# 如果遇到下一个value元素,停止收集
if sibling.get_attribute("name") == "ques":
break
# 收集class包含"ans"的元素
if "ans" in sibling.get_attribute("class"):
answer_elements.append(sibling)
# 提取每个选项的文本
answers = str([elem.text for elem in answer_elements])
timu = title + answers + ',请只回答我答案(A、B、C、D)'
answer = chat1.chat(timu)
print(answer)
xuanxiang_name = element.find_element(By.CSS_SELECTOR, f'input[name="{value_name}"]')
xuanxiang_value = int(xuanxiang_name.get_attribute('value'))
if answer == 'A':
xuanxiang_value = str(xuanxiang_value)
elif answer == 'B':
xuanxiang_value = str(xuanxiang_value + 1)
elif answer == 'C':
xuanxiang_value = str(xuanxiang_value + 2)
else:
xuanxiang_value = str(xuanxiang_value + 3)
try:
xiangxuan = element.find_element(By.CSS_SELECTOR, f'input[value="{xuanxiang_value}"]')
# 使用JavaScript滚动到元素位置
driver.execute_script("arguments[0].scrollIntoView(true);", xiangxuan)
# 向上调整位置(假设导航栏高度为50px)
driver.execute_script("window.scrollBy(0, -50);")
# 等待页面滚动完成
time.sleep(0.5)
xiangxuan.click()
except:
print('发生未知错误,跳过')
# 等待用户输入,防止浏览器自动关闭
input("按回车键关闭浏览器...")
except Exception as e:
print(f"发生错误: {e}")
finally:
# 关闭浏览器
driver.quit()
# 主函数
if __name__ == '__main__':
main()