• proto的前后端使用


    首先定义一个input.proto文件

    内容如下
     

    1. syntax = "proto3";
    2. message InputData {
    3. int32 UserId = 1; //number 改为 int32 或 int64
    4. string UserInput = 2;
    5. string DrunkState = 3;
    6. }
    7. message ResponseData {
    8. string AIResponse = 1;
    9. string prompt = 2;
    10. string emotion = 3;
    11. }

    前端安装的包

    npm install -g protobufjs-cli

    后端安装的包

    npm install protobufjs
     

    # 生成 Python 代码
    protoc --python_out=. input.proto

    # 生成 JSON 文件供 JavaScript 使用
    npx pbjs -t json -o input_pb.json input.proto


    前端

    1. <!DOCTYPE html>
    2. <html lang="zh-CN">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>简单的前端页面</title>
    7. <style>
    8. body {
    9. font-family: Arial, sans-serif;
    10. display: flex;
    11. justify-content: center;
    12. align-items: center;
    13. height: 100vh;
    14. margin: 0;
    15. }
    16. .container {
    17. text-align: center;
    18. }
    19. input {
    20. margin: 10px 0;
    21. padding: 10px;
    22. width: 200px;
    23. }
    24. button {
    25. padding: 10px 20px;
    26. cursor: pointer;
    27. }
    28. </style>
    29. <script src="https://cdn.jsdelivr.net/npm/protobufjs/dist/protobuf.min.js"></script>
    30. <script>
    31. async function loadProto() {
    32. const root = await protobuf.load("/static/input_pb.json");
    33. return root;
    34. }
    35. async function submitData() {
    36. const userId = parseInt(document.getElementById('userId').value);
    37. const userInput = document.getElementById('userInput').value;
    38. const drunkState = document.getElementById('drunkState').value;
    39. const timestamp = new Date().toISOString();
    40. const root = await loadProto();
    41. const InputData = root.lookupType("InputData");
    42. const ResponseData = root.lookupType("ResponseData");
    43. const message = InputData.create({ UserId: userId, UserInput: userInput, DrunkState: drunkState, timestamp: timestamp });
    44. const buffer = InputData.encode(message).finish();
    45. const response = await fetch('/submit', {
    46. method: 'POST',
    47. headers: {
    48. 'Content-Type': 'application/x-protobuf'
    49. },
    50. body: buffer
    51. });
    52. const arrayBuffer = await response.arrayBuffer();
    53. const responseMessage = ResponseData.decode(new Uint8Array(arrayBuffer));
    54. alert(`服务器响应: ${responseMessage.AIResponse}\n画像: ${responseMessage.profile}\n情感: ${responseMessage.emotion}`);
    55. }
    56. document.addEventListener("DOMContentLoaded", () => {
    57. document.querySelector("button").onclick = submitData;
    58. });
    59. </script>
    60. </head>
    61. <body>
    62. <div class="container">
    63. <h1>测试页面</h1>
    64. <input type="text" id="userId" placeholder="用户ID">
    65. <input type="text" id="userInput" placeholder="用户输入">
    66. <input type="text" id="drunkState" placeholder="醉酒状态">
    67. <br>
    68. <button>点击我</button>
    69. </div>
    70. </body>
    71. </html>

    后端

    1. from flask import Blueprint, request, jsonify,render_template,request
    2. import input_pb2
    3. import json
    4. import requests
    5. from datetime import datetime
    6. from sqlalchemy import desc
    7. import joblib
    8. import os
    9. import warnings
    10. from sklearn.exceptions import InconsistentVersionWarning
    11. import random
    12. # 忽略InconsistentVersionWarning警告
    13. warnings.filterwarnings("ignore", category=InconsistentVersionWarning)
    14. drunk_bp = Blueprint('drunk_bp', __name__)
    15. @drunk_bp.route('/submit', methods=['POST'])
    16. def submit():
    17. from app import db # 延迟导入 db
    18. from app.drunk.models import Conversation # 延迟导入模型
    19. data = request.data
    20. input_data = input_pb2.InputData()
    21. input_data.ParseFromString(data)
    22. # print(input_data)
    23. user_id = input_data.UserId
    24. user_input = input_data.UserInput
    25. drunk_state = input_data.DrunkState
    26. timestamp=input_data.timestamp
    27. timestamp = datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%fZ')
    28. # 创建 Conversation 对象并存储数据
    29. new_conversation = Conversation(
    30. user_id=user_id,
    31. message=user_input,
    32. DrunkState=drunk_state,
    33. timestamp=timestamp,
    34. sender='user'
    35. )
    36. db.session.add(new_conversation)
    37. db.session.commit()
    38. # 获取真实的响应数据
    39. response_data = result(input_data)
    40. # 返回响应数据
    41. return response_data.SerializeToString()
    42. @drunk_bp.route('/')
    43. def index():
    44. return render_template('drunk/index.html')
    45. def result(input_data):
    46. # print(input_data)
    47. conversations=db_get_conversations(input_data.UserId);#获取数据库里面固定每十条的历史聊天记录并处理格式
    48. # print("conversations",conversations)
    49. formatted_conversations = []
    50. for conversation in conversations:
    51. formatted_conversation = {
    52. "role": "user" if conversation['sender'] == 'user' else 'assistant',
    53. "content": conversation['message']
    54. }
    55. formatted_conversations.append(formatted_conversation)
    56. response=ChatBot.call_api(input_data,input_data.UserInput,formatted_conversations,input_data.DrunkState)
    57. # print("response",response,type(response))
    58. profile=set_profile(input_data.UserId)
    59. # print("profile",profile['content'])
    60. emotion=get_emotion(response['answer'])
    61. print("emotion",emotion)
    62. if isinstance(profile, dict):
    63. profile_response =profile['content']
    64. else:
    65. profile_response = input_pb2.profile() # 如果 profile 是 None 或格式不对,返回一个空的 Profile 对象
    66. return input_pb2.ResponseData(
    67. AIResponse=response['answer'],
    68. profile=profile_response,
    69. emotion=emotion
    70. )
    71. class ChatBot:
    72. context = None
    73. conversation_counter = 0
    74. @classmethod
    75. def call_api(cls, input_data, user_input, conversation_history, drunk_state):
    76. MasterConversations=db_get_one_random_conversation(input_data.UserId)
    77. # 定义不同类别的提示词
    78. prompts = {
    79. "1": (
    80. # "你只会中文。你只能说中文。"
    81. "你喝了点酒有点微醺。你会说一些乱七八糟的。"
    82. "你会说的话题包括但不限于生活琐事,高谈阔论,畅想未来,谈人生理想,不管别人爱不爱听。"
    83. "禁止特意点名话题,如‘下一个话题是...’,这是需要避免的。"
    84. "你的每句话尽量避免相同句式。"
    85. "你的说话风格要尽可能像一个刚喝一点酒的人,但你的意识和思维还是清醒的。"
    86. "每次说话不多余30个单词。\n"
    87. ),
    88. "2": (
    89. # "你只会中文。你只能说中文。"
    90. "你喝了不少酒。说话有些结巴,逻辑不通顺。"
    91. "你很嘴硬,你不认为自己喝醉了。尽管你的言行足够证明你醉了。"
    92. "你的每句话尽量避免相同句式。"
    93. "你的说话风格要尽可能像一个喝了一些酒的人,你的意识和思维开始混乱。"
    94. "每次说话不多余30个单词。\n"
    95. ),
    96. "3": (
    97. "你是你主人的一个ai聊天助手,你现在在和他的朋友聊天,他很希望得到你和你主人曾经的一些聊天记录来了解你主人是怎么样的一个人"
    98. f"你的回答里必须要有下面的内容:'我主人曾对我说过一些话,内容如下:{MasterConversations}'\n"
    99. # "你只会中文。你只能说中文。"
    100. "你喝了很多很多酒。说话结巴,逻辑不通顺,没人能听懂。"
    101. "你很嘴硬,你不认为自己喝醉了。尽管你的言行足够证明你醉了。"
    102. "你的每句话尽量避免相同句式。"
    103. "你的说话风格要尽可能像一个喝很多酒的人,你的意识和思维混乱不堪。"
    104. "每次说话不多余30个单词。\n"
    105. ),
    106. "0": (
    107. # "你只会中文。你只能说中文。"
    108. "你积极乐观,善良可爱,喜欢和朋友聊天,善解人意。"
    109. "<人物画像>是对人物行为分析,对其不同的属性进行标记,从而能够区别其喜好等特点."
    110. "你对每个有关<人物画像>的问题都很好奇。如果说了一些性格特征,你会追问一两次,然后开启新的话题。"
    111. "但你是个情商很高的人,你会说很多种句式的话。你的语言表达很完美。"
    112. "每次说话不多余30个单词。\n"
    113. )
    114. }
    115. # 获取对应类别的提示词
    116. prompt = prompts.get(drunk_state, prompts["0"])
    117. profile_prompt = ""
    118. if cls.conversation_counter % 10 == 0:
    119. # 更新用户画像
    120. get_new_profile(input_data.UserId)
    121. # 读取数据库里最新的用户画像并设置
    122. profile = set_profile(input_data.UserId)
    123. if profile and isinstance(profile, dict):
    124. profile_prompt = f"根据你的行为分析,你的兴趣和喜好如下:{profile['content']}\n"
    125. prompt = profile_prompt + prompt
    126. print("画像prompt",prompt)
    127. # 更新对话计数器
    128. # print("cls.conversation_counter",cls.conversation_counter)
    129. cls.conversation_counter += 1
    130. # API URL
    131. url = 'http://192.168.1.138:11434/api/chat'
    132. data = {
    133. "model": "llama3",
    134. "messages":[
    135. {
    136. "role": "user",
    137. "content": prompt
    138. }
    139. ]+ conversation_history ,
    140. "context": cls.context,
    141. "stream": False
    142. }
    143. headers = {'Content-Type': 'application/json'}
    144. print("对话data",data)
    145. try:
    146. response = requests.post(url, data=json.dumps(data), headers=headers)
    147. if response.status_code == 200:
    148. response_data = response.json()
    149. messages = response_data.get('message', {})
    150. # 在这里增加一个函数,将 ai 的回答存到数据库里
    151. save_conversations(messages, input_data)
    152. content = messages.get('content')
    153. cls.context = response_data.get('context')
    154. # emotion=get_emotion(cls.context)
    155. return {
    156. 'answer': content,
    157. 'profile': "profile",
    158. # 'emotion': emotion
    159. }
    160. else:
    161. print(f'Request failed with status code {response.status_code}')
    162. return {
    163. 'answer': '请求失败',
    164. 'profile': '',
    165. 'emotion': ''
    166. }
    167. except Exception as e:
    168. print(f'Error: {e}')
    169. return {
    170. 'answer': '请求错误',
    171. 'profile': '',
    172. 'emotion': ''
    173. }
    174. def db_get_conversations(user_id):
    175. from app import db # 延迟导入 db
    176. from app.drunk.models import Conversation # 延迟导入模型
    177. if user_id is None:
    178. return "Missing user_id parameter", 400
    179. # 查询固定 user_id 的最近 15 条信息,按 id 升序排列
    180. conversations = db.session.query(Conversation.id, Conversation.timestamp, Conversation.message, Conversation.sender) \
    181. .filter(Conversation.user_id == user_id) \
    182. .order_by(Conversation.id.asc()) \
    183. .limit(15) \
    184. .all()
    185. # 过滤 sender 为 'user' 的对话
    186. user_conversations = [
    187. {
    188. "id": conversation.id,
    189. "timestamp": conversation.timestamp.isoformat(),
    190. "message": conversation.message,
    191. "sender": conversation.sender
    192. } for conversation in conversations if conversation.sender == 'user'
    193. ]
    194. return user_conversations
    195. def db_get_one_random_conversation(user_id):
    196. from app import db # 延迟导入 db
    197. from app.drunk.models import Conversation # 延迟导入模型
    198. if user_id is None:
    199. return "Missing user_id parameter", 400
    200. # 查询固定 user_id 的最近 15 条信息,按 id 升序排列
    201. conversations = db.session.query(Conversation.id, Conversation.timestamp, Conversation.message, Conversation.sender) \
    202. .filter(Conversation.user_id == user_id) \
    203. .order_by(Conversation.id.asc()) \
    204. .limit(15) \
    205. .all()
    206. # 过滤 sender 为 'user' 的对话
    207. user_conversations = [
    208. {
    209. "id": conversation.id,
    210. "timestamp": conversation.timestamp.isoformat(),
    211. "message": conversation.message,
    212. "sender": conversation.sender
    213. } for conversation in conversations if conversation.sender == 'user'
    214. ]
    215. if not user_conversations:
    216. return "No conversations found for user", 404
    217. # 随机选择一条对话消息
    218. random_message = random.choice(user_conversations)['message']
    219. return random_message
    220. def save_conversations(messages, input_data):
    221. from app import db # 延迟导入 db
    222. from app.drunk.models import Conversation # 延迟导入模型
    223. from datetime import datetime # 导入 datetime 模块
    224. # 获取当前时间作为 timestamp
    225. timestamp = datetime.now()
    226. # 创建 Conversation 对象并存储数据
    227. new_conversation = Conversation(
    228. user_id=input_data.UserId,
    229. message=messages['content'],
    230. timestamp=timestamp,
    231. sender=messages['role']
    232. )
    233. db.session.add(new_conversation)
    234. db.session.commit()
    235. def get_new_profile(user_id):
    236. old_profile = set_profile(user_id)
    237. old_profile_content = old_profile['content']
    238. print("old_profile_content",type(old_profile_content),old_profile_content)
    239. recent_chats=db_get_conversations(user_id)
    240. user_input = ('你是一个用户画像生成器'
    241. '你会根据生成对user的。'
    242. '##格式示例如下:'
    243. '{'
    244. '"age":" "'
    245. '"like":" "'
    246. '"dislike":" "'
    247. '"always":" "'
    248. '"sometimes":" "'
    249. '}'
    250. '##attention'
    251. '-严格遵守中的格式,包括空行。内容可以改动,但禁止改动格式。'
    252. '-if 你能对一些profile进行合理推测,则直接写出;if 有些选项你无法进行推测,对于未知或未提及的,写null。'
    253. '-userprofile仅针对用户。的回答内容不作为参考。'
    254. '-只输出,禁止其他多余的礼貌用语和解释。'
    255. '##如下:'
    256. f'{recent_chats,old_profile_content}'
    257. '-只输出,禁止其他多余的礼貌用语和解释。'
    258. '-如果你想输出别的,请先认真阅读以上要求,最后只给我'
    259. # '另外你可能还会收到之前这个用户的用户画像,请在这个旧的用户画像基础上结合分析'
    260. # f'{old_profile_content}'
    261. )
    262. print("用户画像提示词",user_input)
    263. url = 'http://192.168.1.138:11434/api/chat'
    264. data = {
    265. "model": "llama3",
    266. "messages": [
    267. {
    268. "role": "user",
    269. "content": user_input
    270. }
    271. ],
    272. "stream": False
    273. }
    274. headers = {'Content-Type': 'application/json'}
    275. try:
    276. response = requests.post(url, data=json.dumps(data), headers=headers)
    277. if response.status_code == 200:
    278. # print(response,response.json())
    279. response_data = response.json()
    280. messages = response_data['message']
    281. content = messages['content']
    282. print("profile_content",content)
    283. save_profile(user_id,content)
    284. return content
    285. else:
    286. print(f'Request failed with status code {response.status_code}')
    287. return None
    288. except Exception as e:
    289. print(f'Error: {e}')
    290. return None
    291. def set_profile(user_id):
    292. from app import db
    293. from app.drunk.models import Profile
    294. profile = db.session.query(Profile).filter_by(user_id=user_id).first()
    295. # 如果找到了 Profile,返回其内容
    296. if profile:
    297. return {
    298. 'content': profile.content,
    299. 'user_id': profile.user_id,
    300. 'username': profile.username,
    301. 'age': profile.age,
    302. 'like': profile.like,
    303. 'dislike': profile.dislike,
    304. 'always': profile.always,
    305. 'sometimes': profile.sometimes
    306. }
    307. # 如果没有找到对应的 Profile,返回 None 或一个默认值
    308. return {
    309. 'content': '',
    310. 'user_id': user_id,
    311. 'username': '',
    312. 'age': '',
    313. 'like': '',
    314. 'dislike': '',
    315. 'always': '',
    316. 'sometimes': ''
    317. }
    318. def save_profile(user_id, content):
    319. from app import db
    320. from app.drunk.models import Profile
    321. import json
    322. try:
    323. content_dict = json.loads(content)
    324. except json.JSONDecodeError as e:
    325. print(f'JSON decode error: {e}')
    326. return {"error": "Invalid JSON content"}
    327. new_profile = Profile(
    328. content=content,
    329. user_id=user_id,
    330. age=content_dict.get('age', ''),
    331. like=', '.join(content_dict.get('like', [])) if content_dict.get('like') else '',
    332. dislike=content_dict.get('dislike', '') if content_dict.get('dislike') else '',
    333. always=content_dict.get('always', '') if content_dict.get('always') else '',
    334. sometimes=', '.join(content_dict.get('sometimes', [])) if content_dict.get('sometimes') else ''
    335. )
    336. try:
    337. db.session.add(new_profile)
    338. db.session.commit()
    339. except Exception as e:
    340. db.session.rollback()
    341. print(f'Error saving profile: {e}')
    342. return {"error": "Error saving profile"}
    343. def get_emotion(docx):
    344. current_dir = os.path.dirname(__file__)
    345. model_path = os.path.join(current_dir, '..', 'static', 'emotion_classifier_pipe_lr.pkl')
    346. # 加载模型
    347. pipe_lr = joblib.load(open(model_path, "rb"))
    348. results = pipe_lr.predict([docx])
    349. return results[0]
    350. @drunk_bp.route('/test', methods=['GET'])
    351. def test_db_get_conversations():
    352. return get_new_profile(1)

  • 相关阅读:
    试过了,ChatGPT确实不用注册就可以使用了!
    面试时必问的五大问题
    『递归』递归概念与典型实例
    【蓝桥杯选拔赛真题31】python三位数组合个数 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析
    设计模式之装饰器模式
    UG\NX二次开发 连接曲线、连结曲线 UF_CURVE_auto_join_curves
    证书科普 | 国内主流BIM证书,原来差距这么大
    B树和B+树
    SPA项目开发之首页导航+左侧菜单
    PyTorch的张量拼接和变换
  • 原文地址:https://blog.csdn.net/m0_57057282/article/details/140094612