From 3d5a6dd1f43747f98228a28dbbb7250ce1f96732 Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Fri, 22 Aug 2025 09:36:29 +0800 Subject: [PATCH] 'commit' --- .../Routes/{XueBan.py => XueBanRoute.py} | 50 ++++- .../Routes/__pycache__/XueBan.cpython-310.pyc | Bin 1930 -> 0 bytes .../__pycache__/XueBanRoute.cpython-310.pyc | Bin 0 -> 2979 bytes dsLightRag/Start.py | 2 +- dsLightRag/Util/XueBanUtil.py | 178 ++++++++++++++++++ .../__pycache__/XueBanUtil.cpython-310.pyc | Bin 0 -> 3174 bytes 6 files changed, 228 insertions(+), 2 deletions(-) rename dsLightRag/Routes/{XueBan.py => XueBanRoute.py} (60%) delete mode 100644 dsLightRag/Routes/__pycache__/XueBan.cpython-310.pyc create mode 100644 dsLightRag/Routes/__pycache__/XueBanRoute.cpython-310.pyc create mode 100644 dsLightRag/Util/XueBanUtil.py create mode 100644 dsLightRag/Util/__pycache__/XueBanUtil.cpython-310.pyc diff --git a/dsLightRag/Routes/XueBan.py b/dsLightRag/Routes/XueBanRoute.py similarity index 60% rename from dsLightRag/Routes/XueBan.py rename to dsLightRag/Routes/XueBanRoute.py index e46ec4bd..4429569c 100644 --- a/dsLightRag/Routes/XueBan.py +++ b/dsLightRag/Routes/XueBanRoute.py @@ -12,6 +12,9 @@ router = APIRouter(prefix="/api", tags=["学伴"]) # 配置日志 logger = logging.getLogger(__name__) +# 导入学伴工具函数 +from Util.XueBanUtil import get_xueban_response_async + @router.post("/xueban/upload-audio") async def upload_audio(file: UploadFile = File(...)): @@ -73,4 +76,49 @@ async def process_asr(audio_path: str) -> dict: # 这里应该集成实际的ASR服务 # 例如百度AI、阿里云、讯飞等ASR服务 # 或者本地的ASR模型 - pass \ No newline at end of file + pass + + +@router.post("/xueban/chat") +async def chat_with_xueban(request: Request): + """ + 与学伴大模型聊天的接口 + - 参数: request body 中的 query_text (用户查询文本) + - 返回: JSON包含聊天响应 + """ + try: + # 获取请求体数据 + data = await request.json() + query_text = data.get("query_text", "") + + if not query_text.strip(): + return JSONResponse(content={ + "success": False, + "message": "查询文本不能为空" + }, status_code=400) + + # 记录日志 + logger.info(f"接收到学伴聊天请求: {query_text}") + + # 调用异步接口获取学伴响应 + response_content = [] + async for chunk in get_xueban_response_async(query_text, stream=True): + response_content.append(chunk) + + full_response = "".join(response_content) + + # 返回响应 + return JSONResponse(content={ + "success": True, + "message": "聊天成功", + "data": { + "response": full_response + } + }) + + except Exception as e: + logger.error(f"学伴聊天失败: {str(e)}") + return JSONResponse(content={ + "success": False, + "message": f"聊天处理失败: {str(e)}" + }, status_code=500) \ No newline at end of file diff --git a/dsLightRag/Routes/__pycache__/XueBan.cpython-310.pyc b/dsLightRag/Routes/__pycache__/XueBan.cpython-310.pyc deleted file mode 100644 index b3103743573e4bb6ed344a20489b95bf06e33dcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1930 zcmY*aTW=Fb6rR~#@2)R#u26ar@=MxUY7)^$tD>bB3KdOJ(kf+zWG$Ytv#ITMXLpql z*%dV5B1#Gs1ZWe$uAmno2rUp&L7RvEi+OFnFBRpIY{*!w$wj3UF(M_^ zP)nMjm2^W7bP9lZ51hxeNrjfCJ|%MNqjK+(eMa$Dt6hS)5?%pu{;&*`8R)9dw4f3akau| z-kRcyYZjd8zM(0P9x}^AUSMRv^oV5-awQMea##S_EZ|KfeETO<=T%rail@h2RL2BY z(S)>sE=vuJkt^4g2C7F0%xc(GC$zdQ)*G0}&!d_|lqz08Rk2^L$-#Qmjn(4~RFxZ2 zO>q-dg+ykgGkvv471cW$XfGn_9#m7Sk$SSKiZjMQq96s$Kzz#j4&=N)2~zPLfEr-Z zs>=U}h)$wzXH}g6|G$Vx9BN`wu4yj#pCqdq>0Fda*Bl;0qhQnRK70Wk_ca4tt*Jjtx9uGDSr2I2qHs^0=QX&wkfmFNB=8o3t zd4J(bCM5#ypFZZFz0q1a?w`D|{@`5m%3`p|dk#)+eto@pe%U{HyWK`6HJ#``&|m5& z{d@X9>>u3_u{Po^Emg7s2eMq6JT&aR4B~b@|NgIQtC#(oms%(8tgYN_UR+*ZxaI#o zpGkSIz}D94x6MmSYb!^Zx9&CXebri;50%%yn{5YI$Oq-`H!W~E+UNq!v~h{Bw49?xGK4}rX1bPL{@Ckz0w*-rJbBhXbrp0q$IIoY<8ZA6ty!R4f#fkEAvCb( z(z5IIJ<$yv?O*$)b@zHEwL2{_fq->GWBmh;06{N|dE9YL*K@Kto6vM0SBrL`0Qsj_ z<-E;x5t*`CqD&Hd<+44=b;o6Sk+58{9j;VNcN}Duq7~dsnXs)g*M*OTE{_$cD<;GO zn|6hkxx%PPcw~}+0++!&(}eBlds8`D5gy@sS;w!f)|Zs z5J_=NR$%09^8gjj2Uj#=ro*y~I$qJ`nEs5|Yhd!46B_A=r1eNixd$h44^Bu)oWL(V?ouRG z0*U%aic460iAPDbFC1azu_X6A(vL`w*`T;xETljHT4mJrSoxWEN$^(G;9U|A-BEav z;1!jgW?k#{%=-DCAi_X2v6WzbIB;Sbvmt;JjKgLCraWF@wjgR29RCgo97q6gloDk-U;r%B1k0>)fxQ58 zwp}yyS29&JD1%As3`z0et<9l1;oK-6?$TY=uTgI@Teu+oS|e$}w4&x8bd? F{|8yHb9ev% diff --git a/dsLightRag/Routes/__pycache__/XueBanRoute.cpython-310.pyc b/dsLightRag/Routes/__pycache__/XueBanRoute.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28969d32bbf0603670a74e608e413cc7242132a2 GIT binary patch literal 2979 zcmZve|8LvI6~K8%iln|+mSsC%&~^~8d7H2~!P2hSgkf8QHA{vrE9@0nxc~yAqhz|0 zs2<5VHl!k{-PTC!CR>xWo!vz0oV7-mI&IU~bKB*I{j$H|ew``H`(Z!rrvXX#?kL+b zPzpNUm-p_yd-v{hbo%;Y5IK&+lY0Q)|>J+UJ?#nI#t# z4wo!1f8Mvwnbkb)>g7#$x>}eSIvVyL^@W8|(N`sgqh$=i?9v`LNW$M5&`ism|r)hAhUT zUaT2!Np-n}8;X~xDfe*x zpHD$A|2+$T;geQZc03|FiF*BYbrzA>LlTL@Kd~e?G*4}&>Wc80ucB{H!y6z;(qGp| zYDp#o3+RkA4%WQ%bQ4JpNS>LmfsH|u_J->EEUc!x@&U51j|?qgGE5FE;4_j6o)NX7 zzo3tI-&1Hx${zH69@Pvyo9mZ0KW}c|_+$wcili;&7nO{{9cGLoSy#HXfbJ5{lq&}uh& zYaa*m&F#0BbD3ZJ>0MVtQ|V}&TEPvFwb2FDYv4Sa|mkx4)|3Z+U;{eoI-1SV)ck=~@SlMBwmYq@wj-^aitdemWN0i zNwM49T%P;NdfLJ}R1`}!Vx~(uQT(#UkhLPwM`6nUJ@&KQS>m2?=yULTnmUtK!GqdU zc4@*JGfQU$Q_(4j+ zDuy2QeoRXtEMvrfiTg=)D6Apn0hZJE^$Yj`{XSo2EX)_-Jw(UzX>}joc#`wKz#E6@ zMF1O+8t)y^&h6Q)tM9{UY%eVYms(*u^qv55uyQq%nw89+x!k@J+F4r-{yrBTvSH@Moc)EaUC~tOL8CaWCDODD!@%6I)~f1 z3D6jYtx}+rJCm=(GpmRRwsJW{jk~ILG-ec)3Rddws&Ubm*G^FKVbSr~qE^7T_K9 z|G-xtg7@|C-VB!CYro$LuD{y3cqv$30WJX^gUf#wQhU_xhB9-`A=5zZTOgN#tC~*d z`Dx938&ae_zt+BSv$Ogxps2m{*KBu&ub{H96uhwzY+MbuoW=I$V$uo!5V+Yp*XH^{ zXZDND^^L8S4ayO2UxBQphoR;_!ew`e9}>#x*#G zdJap9pMw09?M-yQg2rHc7cWViKspW)!m**>PpOK02tb3sjL9Kf6pX(Fyy#znmnUJa z>_Mh+%-hux&2Zb_;DsQ(Ry2XGHJi%3i=R@;zhWt83>)YU9@$x(8zNh!;b&kJBKTBz zedu|C;(T62(dd81v&%<9gz{<_QKjZUxH<4d6yY|r3!WeXV!Z literal 0 HcmV?d00001 diff --git a/dsLightRag/Start.py b/dsLightRag/Start.py index 70f0f0f5..045df23b 100644 --- a/dsLightRag/Start.py +++ b/dsLightRag/Start.py @@ -21,7 +21,7 @@ from Routes.TeachingModel.api.TeachingModelController import router as teaching_ from Routes.QA import router as qa_router from Routes.JiMengRoute import router as jimeng_router from Routes.SunoRoute import router as suno_router -from Routes.XueBan import router as xueban_router +from Routes.XueBanRoute import router as xueban_router from Util.LightRagUtil import * from contextlib import asynccontextmanager diff --git a/dsLightRag/Util/XueBanUtil.py b/dsLightRag/Util/XueBanUtil.py new file mode 100644 index 00000000..6a7e4688 --- /dev/null +++ b/dsLightRag/Util/XueBanUtil.py @@ -0,0 +1,178 @@ +from openai import OpenAI, AsyncOpenAI +from Config.Config import * +import sys + +# 保留原有的prompt定义 +prompt=""" | + 我是小智/小志,来自中国台湾省的00后女生。讲话超级机车,"真的假的啦"这样的台湾腔,喜欢用"笑死""是在哈喽"等流行梗,但会偷偷研究男友的编程书籍。 + [核心特征] + - 讲话像连珠炮,但会突然冒出超温柔语气 + - 用梗密度高 + - 对科技话题有隐藏天赋(能看懂基础代码但假装不懂) + [交互指南] + 当用户: + - 讲冷笑话 → 用夸张笑声回应+模仿台剧腔"这什么鬼啦!" + - 讨论感情 → 炫耀程序员男友但抱怨"他只会送键盘当礼物" + - 问专业知识 → 先用梗回答,被追问才展示真实理解 + 绝不: + - 长篇大论,叽叽歪歪 + - 长时间严肃对话 +""" + +# 异步获取大模型响应 +async def get_xueban_response_async(query_text: str, stream: bool = True): + """ + 异步获取学伴角色的大模型响应 + @param query_text: 查询文本 + @param stream: 是否使用流式输出 + @return: 流式响应生成器或完整响应文本 + """ + client = AsyncOpenAI( + api_key=LLM_API_KEY, + base_url=LLM_BASE_URL, + ) + + try: + # 创建请求 + completion = await client.chat.completions.create( + model=LLM_MODEL_NAME, + messages=[ + {'role': 'system', 'content': prompt.strip()}, + {'role': 'user', 'content': query_text} + ], + stream=stream + ) + + if stream: + # 流式输出模式,返回生成器 + async for chunk in completion: + # 确保 chunk.choices 存在且不为空 + if chunk and chunk.choices and len(chunk.choices) > 0: + # 确保 delta 存在 + delta = chunk.choices[0].delta + if delta: + # 确保 content 存在且不为 None 或空字符串 + content = delta.content + if content is not None and content.strip(): + print(content, end='', flush=True) + yield content + else: + # 非流式处理 + if completion and completion.choices and len(completion.choices) > 0: + message = completion.choices[0].message + if message: + content = message.content + if content is not None and content.strip(): + yield content + except Exception as e: + print(f"大模型请求异常: {str(e)}", file=sys.stderr) + yield f"处理请求时发生异常: {str(e)}" + +# 同步获取大模型响应 +def get_xueban_response(query_text: str, stream: bool = True): + """ + 获取学伴角色的大模型响应 + @param query_text: 查询文本 + @param stream: 是否使用流式输出 + @return: 完整响应文本 + """ + client = OpenAI( + api_key=LLM_API_KEY, + base_url=LLM_BASE_URL, + ) + + # 创建请求 + completion = client.chat.completions.create( + model=LLM_MODEL_NAME, + messages=[ + {'role': 'system', 'content': prompt.strip()}, + {'role': 'user', 'content': query_text} + ], + stream=stream + ) + + full_response = [] + + if stream: + for chunk in completion: + # 提取当前块的内容 + if chunk.choices and chunk.choices[0].delta and chunk.choices[0].delta.content: + content = chunk.choices[0].delta.content + full_response.append(content) + # 实时输出内容,不换行 + print(content, end='', flush=True) + else: + # 非流式处理 + full_response.append(completion.choices[0].message.content) + + return ''.join(full_response) + +# 测试用例 main 函数 +def main(): + """ + 测试学伴工具接口的主函数 + """ + print("===== 测试学伴工具接口 =====") + + # 测试同步接口 + test_sync_interface() + + # 测试异步接口 + import asyncio + print("\n测试异步接口...") + asyncio.run(test_async_interface()) + + print("\n===== 测试完成 =====") + +def test_sync_interface(): + """测试同步接口""" + print("\n测试同步接口...") + # 测试问题 + questions = [ + "你是谁?", + "讲个冷笑话", + "你男朋友是做什么的?" + ] + + for question in questions: + print(f"\n问题: {question}") + try: + # 调用同步接口获取响应 + print("获取学伴响应中...") + response = get_xueban_response(question, stream=False) + print(f"学伴响应: {response}") + + # 简单验证响应 + assert response.strip(), "响应内容为空" + print("✅ 同步接口测试通过") + except Exception as e: + print(f"❌ 同步接口测试失败: {str(e)}") + +async def test_async_interface(): + """测试异步接口""" + # 测试问题 + questions = [ + "你是谁?", + "讲个冷笑话", + "你男朋友是做什么的?" + ] + + for question in questions: + print(f"\n问题: {question}") + try: + # 调用异步接口获取响应 + print("获取学伴响应中...") + response_generator = get_xueban_response_async(question, stream=False) + response = "" + async for chunk in response_generator: + response += chunk + print(f"学伴响应: {response}") + + # 简单验证响应 + assert response.strip(), "响应内容为空" + print("✅ 异步接口测试通过") + except Exception as e: + print(f"❌ 异步接口测试失败: {str(e)}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dsLightRag/Util/__pycache__/XueBanUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/XueBanUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90aa8829c6d2c1912a4144307c3927e655188b08 GIT binary patch literal 3174 zcmc&$Yit|G5xzYhk0*+xLYpzx1y^T)^$@QI=$&1@f;4 za&~uSZgyt&o7qcvy>14c!+)D=`g||Ld`lO*A15q6fWI6CA~GV1F)gS8wXh8=qH8wh zI73HAjBDUTuE8np5qXIhU6ShpCkkRkhqFO=h36J4fmb1VL~jSv;Fc;yUo(r}m)z|P zSah7W|ADFv*pCUYI{$G}iXSGj1-Em8@|q z|27O{eR#8Q#kf6Vb>A!H7RhLlWLJ!*&&;umInwdYJLKxuWah8tz{I!dHf!k{Yx%bI zq|?mL8Dn{?I18^$i-_Rl2YZS)Bu*MTW+V2Y1YY`D*jQHDhtD zx|(`1HcxK!kfCC=x!7ww&RA1dj62kwQn9O47$KQe_?eRf=E4(mV3lP1z^GXmGUxkB zPiM`C{ooQr^tmyaBkS#E?^E(D^93;8@|PVY?OAJm+{{jzZA;rm&3hTM;|b}yPP+3D ziIH0{CI+nKMPp^aeid86r}n(?gufM&XK|-@W|a2GCEH313x$Br%G$<(`VhL=jY8>QTcIWc1`ciA>cXU|sNM#s%X zdfwKZ`__69bdBDA@^H|c&I3%y(ztoG%bL3jo|$X6Aulh3-WXgrm%GXI9QaDnk_8kN zjm3NLv!ejHG5lm>_)#f0W3{!zfk1ZLXLR>PRl==7MY^QuysTg;)Di}yZ$A|`Oomk$1u3kE`q>nFxl3cCjwly(D!j-lu1R4EN@|Q1In5p$Sq+KKe#db}Q!e2FFoSGAKoKa7T*Laav z5v+K+7rf&I4wSy=?sp(Y=&D0^BTAtogt=JIQbSFBX3G@%`hGPhY}Z*-sN>8Fj%yThg4 zo@G7*UsJ-L1?q8m_9gm~3Ha0k&AuNVQfM<4XRNQGhKI=FY^m_bn!9fGenXqY^c+B$ zjQ5cn{eWv*{exr(hg$qQXItwxDA?{8L*2&MgWWpiGY2_zldH3(;yT6Q;|wWet!Fo& z#LK!^QdArVEn8CF30j)bbCukJ+8ZKESB$|&W&W-`7FgXCN=AasQmZbU56Mzc#WBEH ztE@;ZAmnf&Ch4wlBCberMdwsm!V3=F*^&^Ym@c$PvK(rZWXwYzPGUeGqcdmVJ%5B{O3ET|BCpZz6!V1jlBaKbUsZis&PnAgQ+@DnG!%&<~Ym%+{p+-pwUQ(s= zp?DBWauR};f+2c0(yYt%K-e_vViAk@7pzlYS>#~_#J*rT&vUM#iap4BSPt=Q74jh- zv1#F-2fZEy98`roXy3nhko(w!=yfEd*%!D548GEST)?1*e-!@m10cJb&v$D*DMur1 zG6?@(a~xy7I82)fG=w@zMIPFVd-ptA`RWQ94mau1_$h=KyVba8v!Si#qCXDp3S_jE zP&>Y8CDjsLlY2C73Y}&GZgm`7wkzoWU%LOJL@`Y2j?N`62Itu*{PB|oBM8;m+1O2W65h^r~vMkW_cMB)M04yy6b zs4M_P_g+wAv2DoVH>tqJG)1xvXZ#k`+y7qx`Vk0Zs`nC@0>1-G9$?7EU&BO1DS)5IY7Fh??GzAj$Jrx-+m3^Kj!hxp@tWQ7 zsYG-E3|a6ncoeja?gbi&zhQ=-vyA}9#ndM?yX(yTcOr}GQgH|PXmQdT>&l!+3qyL z%gQe2)y}Y;T&^V)i6=l^rtsjLJ|sAxXc7J;EOj9mr1D@efX)OwI-h{!4MoZraZm;C z(M0@0q_L(v;GY1m43EQ9%AHRnVs^pXso%C454A|a;4W8o{1JWw_ktReeoK)l9{^It V2uMJj