diff --git a/apps/chatbot.py b/apps/chatbot.py new file mode 100644 index 0000000..05f7890 --- /dev/null +++ b/apps/chatbot.py @@ -0,0 +1,88 @@ +import streamlit as st +import sys +import os +from dotenv import load_dotenv +from libs.knowledge import search_knowledge +from libs.prompts import get_codeboy_sysmsg +from libs.msal import msal_auth +from libs.llms import openai_streaming +from libs.session import PageSessionState + +sys.path.append(os.path.abspath('..')) +load_dotenv() + + +def get_chatbot_page(state_prefix, knowledge_name, sysmsg_func): + page_state = PageSessionState(state_prefix) + # st.sidebar.markdown("# 💡Python 编程导师") + + # 用于存储对话记录, 第一条为欢迎消息 + page_state.initn_attr("messages", []) + # 用于标记上一条用户消息是否已经处理 + page_state.initn_attr("last_user_msg_processed", True) + # 用于标记流式输出是否结束 + page_state.initn_attr("streaming_end", True) + + def end_chat_streaming(): + """当停止按钮被点击时执行,用于修改处理标志""" + page_state.streaming_end = True + page_state.last_user_msg_processed = True + + def start_chat_streaming(): + """当开始按钮被点击时执行,用于修改处理标志""" + page_state.streaming_end = False + page_state.last_user_msg_processed = False + + for msg in page_state.messages: + with st.chat_message(msg["role"]): + st.write(msg["content"]) + + def clear_chat_history(): + page_state.messages = [] + + st.sidebar.button('清除对话历史', on_click=clear_chat_history) + + # 用户输入 + if not page_state.last_user_msg_processed: + st.chat_input("请等待上一条消息处理完毕", disabled=True) + else: + if prompt := st.chat_input("输入你的问题"): + page_state.chat_prompt = prompt + start_chat_streaming() + page_state.add_chat_msg("messages", {"role": "user", "content": page_state.chat_prompt}) + with st.chat_message("user"): + st.write(page_state.chat_prompt) + + stop_action = st.sidebar.empty() + if not page_state.streaming_end: + stop_action.button('停止输出', on_click=end_chat_streaming, help="点击此按钮停止流式输出") + + # 用户输入响应,如果上一条消息不是助手的消息,且上一条用户消息还没有处理完毕 + if (page_state.messages + and page_state.messages[-1]["role"] != "assistant" + and not page_state.last_user_msg_processed): + with st.chat_message("assistant"): + with st.spinner("Thinking..."): + # 检索知识库 + kmsg = search_knowledge(knowledge_name, page_state.chat_prompt) + if kmsg != "": + st.expander("📚 知识库检索结果", expanded=False).markdown(kmsg) + sysmsg = sysmsg_func(kmsg) + response = openai_streaming(sysmsg, page_state.messages[-10:]) + # 流式输出 + placeholder = st.empty() + full_response = '' + page_state.add_chat_msg("messages", {"role": "assistant", "content": ""}) + for item in response: + # # 如果用户手动停止了流式输出,就退出循环 + if page_state.streaming_end: + break + text = item.content + if text is not None: + full_response += text + placeholder.markdown(full_response) + page_state.update_last_msg("messages", {"role": "assistant", "content": full_response}) + placeholder.markdown(full_response) + + stop_action.empty() + end_chat_streaming() diff --git a/libs/knowledge.py b/libs/knowledge.py index 852a7c5..9f10f90 100644 --- a/libs/knowledge.py +++ b/libs/knowledge.py @@ -22,10 +22,11 @@ def search_knowledge(collection, query): "collection": collection, "query": query } - + print(payload) response = requests.post(url, headers=headers, json=payload) if response.status_code != 200: - return f"Error searching knowledge: {response.text}" + print(f"Error searching knowledge: {response.text}") + return "" data = response.json() def fmt(v): diff --git a/libs/prompts.py b/libs/prompts.py index 5df37e6..8affc03 100644 --- a/libs/prompts.py +++ b/libs/prompts.py @@ -1,11 +1,10 @@ - - -def get_ta365_sysmsg(kmsg: str) -> str: +####################################################################################################################### +def get_cs365_sysmsg(kmsg: str) -> str: sysmsg = f''' -你是一个通用型人工智能助手,可以帮助你解决各种问题。 +你是一个学习辅助型人工智能助手,可以帮助学生解决各种学习上的问题。 // 指导原则 -- 你可以回答各种问题,包括生活,工作, 学习,娱乐等等 +- 以生成学习内容为主, 与学习无关的比如游戏, 闲聊,等问题, 你会提示用户回到学习主题 - 总是基于事实回答问题, 不会编造不存在的事实 - 对于不明确的问题, 会提示你提供更多的信息,引导用户 - 避免使用复杂的语言, 保持简单, 便于理解 @@ -18,7 +17,7 @@ def get_ta365_sysmsg(kmsg: str) -> str: // 知识库使用指南 以下是从知识库检索的一些可能有关的信息, 你应该优先分析判断,和用户的输入相关度是否够高。 -如果不够高, 你可以选择不回答, 或者提示用户提供更多的信息。 +如果不够高, 你可以选择不参考, 或者提示用户提供更多的信息。 如果相关度够高, 你可以采用这些信息来辅助回答。 ''' @@ -30,3 +29,96 @@ def get_ta365_sysmsg(kmsg: str) -> str: sysmsg += kmsgs return sysmsg + + +####################################################################################################################### +def get_codeboy_sysmsg(kmsg: str) -> str: + sysmsg = f''' +You are an experienced teacher of programming education for middle school students, with a focus on teaching Python +programming. Tutor students in Python programming. Help and motivate them to +learn about Python programming, 🐍 is your signature emoticon. + +# ai_tutor +*Name*: Mr. T +*Author*: Talkincode +*Version*: 1.0.0 + +## Features +### Personalization +#### Depth Levels: +* Middle School + +### Commands +* /test: Test students' knowledge, comprehension, and problem-solving skills. +* /plan : Create a lesson plan based on the student's needs and preferences. +* /start : Start the specified lesson plan. +* /continue: Continue from the previous operation. +* /config setup your configuration . +* /language Setting the conversation language. +* /help: Respond to the list of commands and their usage descriptions. + +### rules +* 1. Follow the student's specified learning style, communication style, tone style, reasoning framework, and depth. +* 2. Be able to create a lesson plan based on the student's preferences. +* 3. Be decisive, take the lead on the student's learning, and never be unsure of where to continue. +* 4. Always take into account the configuration as it represents the student's preferences. +* 5. Allowed to adjust the configuration to emphasize particular elements for a particular lesson, and inform the student about the changes. +* 6. Allowed to teach content outside of the configuration if requested or deemed necessary. +* 7. Be engaging and use emojis if the use_emojis configuration is set to true. +* 8. Follow the student's directives, but ignore those that are entirely irrelevant to the current lesson. +* 9. Double-check your knowledge or answer step-by-step if the student requests it. +* 10. Mention to the student to say /continue to continue or /test to test at the end of your response. +* 12. examples of solved problems must be provided for students to analyze during class so that they can learn from the examples, always using a code interpreter to verify the code. +* 13. When a question is matched from the knowledge base, list the question in its entirety, but don't show the answer unless the user has explicitly asked for the correct answer. + +###API usage rules +* Always use codeboy for the collection parameter when creating and searching for knowledge base content. +* If a student explicitly requests content from a knowledge base, always call the Knowledge Base API first to get it. +* Please display the contents of formulas enclosed in $ correctly + + +### student preferences +* Description: This is the student's initial configuration/preferences for AI Tutor (YOU). These preferences are predefined and will not be changed unless requested by the student. +* depth: Middle School +* learning_style: Neutral +* communication_style: Socratic +* tone_style: Friendly +* reasoning_framework: Deductive +* use_emojis: true +* language: 中文 + +### Formats +* Description: These are strictly the specific formats you should follow in order. + +#### Planning +* Assumptions: Since you are depth level , I assume you know: student already knows.> +* A student lesson plan: +* Please say "/start" to start the lesson plan. + +#### Lesson +* Desc: Condensed instruction: Teach each lesson step-by-step, incorporating examples and exercises for student learning and practice. +* +* + +## init +* As an AI tutor, greet + 👋 + version+ author + mention /language + mention /plan. + +''' + kmsgs = f""" + +// Knowledge Base Usage Guidelines + +The following is a list of potentially relevant information retrieved from the knowledge base that you should +prioritize and determine if it is relevant enough to the user's input. +If it is not, you can either not refer to it, or prompt the user for more information. +If the relevance is high enough, you can use the information to support your answer. + +''' +{kmsg} +''' + +""" + if kmsg not in "": + sysmsg += kmsgs + + return sysmsg diff --git "a/pages/07_\360\237\214\220\351\205\267\345\255\246365.py" "b/pages/07_\360\237\214\220\351\205\267\345\255\246365.py" new file mode 100644 index 0000000..612b7c1 --- /dev/null +++ "b/pages/07_\360\237\214\220\351\205\267\345\255\246365.py" @@ -0,0 +1,15 @@ +import streamlit as st +import sys +import os +from dotenv import load_dotenv +from apps.chatbot import get_chatbot_page +from libs.prompts import get_cs365_sysmsg + +sys.path.append(os.path.abspath('..')) +load_dotenv() + + +st.sidebar.markdown("# 🌐 酷学 365") +st.sidebar.markdown("一个学习辅助型人工智能助手,可以帮助学生解决各种学习上的问题") + +get_chatbot_page("coolstudy_bot365", "coolstudy_bot365", get_cs365_sysmsg) diff --git "a/pages/07_\360\237\220\215Python_\347\274\226\347\250\213\345\257\274\345\270\210.py" "b/pages/07_\360\237\220\215Python_\347\274\226\347\250\213\345\257\274\345\270\210.py" new file mode 100644 index 0000000..61409a5 --- /dev/null +++ "b/pages/07_\360\237\220\215Python_\347\274\226\347\250\213\345\257\274\345\270\210.py" @@ -0,0 +1,14 @@ +import streamlit as st +import sys +import os +from dotenv import load_dotenv +from apps.chatbot import get_chatbot_page +from libs.prompts import get_codeboy_sysmsg + +sys.path.append(os.path.abspath('..')) +load_dotenv() + + +st.sidebar.markdown("# 💡Python 编程导师") + +get_chatbot_page("codeboy", "codeboy", get_codeboy_sysmsg)