From 42a8dc3fd79ed4e55ccefd95b46524c76f764c74 Mon Sep 17 00:00:00 2001 From: jesxion Date: Mon, 13 Apr 2026 12:20:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20AI=20=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. generate_reply: 改为优先关键词匹配,不匹配则调用 AI 2. _ai_generate_reply: 改进 prompt,加入对话上下文、微信风格要求 3. 要求回复简洁(50字以内),符合聊天风格 --- config.example.yaml | 5 +- src/core/__pycache__/engine.cpython-311.pyc | Bin 20171 -> 21401 bytes src/core/engine.py | 63 ++++++++++++++------ 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/config.example.yaml b/config.example.yaml index be203b5..ae56d68 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -42,7 +42,7 @@ wechat: # 回复规则 rules: - # 关键词回复示例 + # 关键词回复示例(优先匹配) - keywords: - 你好 - hi @@ -51,7 +51,8 @@ rules: reply_content: "您好,有什么可以帮您的?" enabled: true - # AI 回复示例(无匹配关键词时,使用 LLM 生成回复) + # AI 回复模式(无匹配关键词时,使用 LLM 生成回复) + # 这是默认模式,会根据对话上下文生成自然回复 - keywords: [] reply_type: AI reply_content: "" diff --git a/src/core/__pycache__/engine.cpython-311.pyc b/src/core/__pycache__/engine.cpython-311.pyc index bedaa9d604c71eb07859938458130fd70444eedd..b435368c29b6824782d963d51334df63fb999fdd 100644 GIT binary patch delta 3786 zcmaJ@dr*^C7XNOZBqZ_(ArJ}%rAnwGudWreiUts&)~)NhySBqJNs3rV;!Pr!2Ehj! zc_{T#K`Sk`Xz4?sW@qb*YPZgAAG6!gVWv#l?slep0cNLTy3@{d`q!TGeMnK6eslTV zbIi$~k`6t)qH(U~_DJr^EZj3CziO+PXPcg}RIj{9IPPuu6FITXe&Q-Rc)I6 z=7_0Fr>9=JaOcL^$s4_VtDkQT%ybV;4|nn%S7%x~@7(C%FCGfNaxQ%NeE8gPk;(hd zOpe~1>K-%|mz(DAp*AUJ-LvD`yv^Q3h+$z(ZzhPsJ^j;f906~z(bVV=?6t5myRQ8t z$^waM;UXH=xoBlfM8WBkyDK7`Qk4zBw&*jOrCJ~Xx|9<-W3MBqTLI(i1Y=LdsfwU! z-AEY#jIo5r#}gh8COkfovUogYWia8M5s`BVDfW3xi7wCt{Pn*2JHzs%WbSCgqH2a>q5fK}{|wf1%6zT$eStZcMjkT(>5u znH>;(Y(mEi5l*5iL`%VN4|8Q`mH%l)(1J>ZB^43PV)s?{3aMj!k1ZrHf=uYL9~LU75Zq(WeHF zh6g=9@Ichtxuu*Zel96o21hfSqQTGlW$Dr+Uo`t*t)ZDTMIVT^so(_(R0CI1X>`I6O-=T?bY_ zV%rJ(U-UvcIWP*JzDgeflCA&<$vn07_MgeFM#(cKFKU9_&qVFbgR)Rh55E`gzZ&iw z68D64}6- zdgmhF<)44rnb%K*`)`25>Cx-qzW4c)?fivNbzZh%6P)$W>U`KlXg1;j!za6Ddax_-Bcn6NPQw0Em%4#7^IEUE z;9gbUe{}NN+mqMY!-v6D$JF&Rlh^z+Er<9~hGAdpRzI3;Vjsn2?eJn1z)gy$F}bL- z-sLG&*RFt6{VdJS&JM}EZaXzgLNe;CvkOUv{Mz?>ym6xC{DxO(n#lt$TNIz#98FC? z(sYDP2*n5+5%K{-GVAVYPe|_e&{`L*M2@Otzt!&Y)H)p$eVa9OHEORxFd?9?kV1?x zB&~PX(6z`GE`A5fqNv^NaylU3U9q?(cDDv*p$W70_}^^X3oG{koDEv1>|s^nH%SkV zC#~|APZ%=B4J!w&gLKe3Zpia*j>x2mvRlc}K@(-afWI^n#U+`b|7q6=z*M@_W@GBB z%g-+FHxE8FX3QBk=0L|a7<(E{HT3T5r(=e!aYI&@Y$7%>Xk6QC1Ax*0tT>Mt#2Iib zc5RoUOYw~+9y+)o_j^vM&Gnc57|&^SCo9`3gUK5P_l?*_+#|Mke6SG5n5KAKsVNR> ziX#g4tgf)=3tjH#y4*2c{cGKWre&<%uJj2nW+GoiN=GPm#dOiM>*?n-v(d$(zh-j1bCx zm$HidJ#Zvtqhw1x+@IKdYU~b=iGS}h-!;s)4Vq&p1rddQ%TWUYR$EBfP)!}RjvBfT zS8{8ey_(Vn6nj`~n!)TA*ltfXg-Wcl*VlT4N~BG|cSDtMCi)O+A7$Ip63M55nzRj) zx_@3tLs32t2u!dpq&FC zq;)y#>Z)oT9y{GvT_>JHQR}Y4qX^OA`(ppjOldxZ>PoBA;jmjhA$65t=yX`^v+@DbV`y|e!XCCS&!wb)K>9iMUEXu?!r|0zE4AAl?%hrg zb+SGAOB8}hBRiG9G+MYF>_2obyOpn3;Q443`!fGe#%d%j03kWGyXu|~DWJc2>paxT zo-9ZsXPBcPMG{K_?FD)n*%dhd*ki;ZY^ZYE9kwd$lGH4?Ll6=m6BDot8CeKI7Vrl( zHYqU;11z;Lg=`P3DRdDd{%#E^s$DMdcsG4RXgJJ1EXp9(z;w}ivJA#N%Vw9XPP=5eaEvZ>?YT<%SREKSmuU2vX|}J&|8b1=~09e z2we!L5U?KT83fFon92d9ln9p*gxiZkN`)|rU_?kk5FT$0QV16VPZ#ebawM%U#_0q9 z+*m}&4yLk{k|wszl1C1(!1m?%EHkh0EMQ-f{U!=9-0DTE8U#XhS{h1T|6 aWf{rYPe?+9TNu9=_&>~-?qjyCHU9^Y5T>U9 delta 2779 zcmZ`)4^UJ09e=;~l1B&xNPr{|fnZR0AZVGDKcYg2D2ga@+p4PqOI`ysB-t-7m2x29 zs)G(I_@mWUnYin<_@~7-TZ>h1&TY4K+wFqKPWEnH*>yp^_0IHe-Fmz2ZolsfI(6>l z-RFJ(|9-#kpI=^o8~tViCB3cFX&L%_?)hU!?b`Q}%!zCrA_a+V!Q036a-AF~;R&D- zE#*})j$vG?=UJd}8JS7+Wjw6mmo%ncW-^}WHEHC$&X_aeb8*d#5EnF1uiN8i7KcV7 zr+yH5^JwJA;mEs}$)%)HQkeW}&26e4V4^(vS8{RxLYjGedz8_|Z&YB&F`sH0uZl8A zmocAWUPw+I>7NvCCKrX1ixzThr~9G|r!Q4tbj!Rcd$J^KT0`GY%-LrW-be`N)y%|E zpzo~NK4-Rv&Gz{m+g#35Ve=zzRLz*5igMi3G?NE0F9*@_|*ceG^irAL&7+lcDlW2jB z)A#b^SdFQl>*Ejdu7qaBrBDaZ0j8VV!yG_@A(%|PYU-^IS2&nNWsz$nGJN#T;LDLW z&sAE3c~qp0ymBOR?ES?Lh872>?_C{Q92s6b@xk2_KZ*<-k9>6O-qqnsZR1_o3f@5B zOK8T$p|Ri=o)VGi%ZsmF?%|59-|AuaS#c4Vt)lQxqz&$*U+tag4`aLd%gn&y#M#K` z(A|MUx8Hav)^_))glGrVg0%KOI23sL?EDlhtJ30)Q59=cP3h(eHbogq{TGJnqC8_>F=r~Ca!%nX=bVZ5 zJFDA_Vo3IyvZ_;z;Y`OQPl3Kuw(0!y`Im&5-Lr+&bA{D0DaF|o$0$F_FC-f1JehW* zFX&Y5Ml`;L{M%Hi4ks3U^$Ee|?b-GuyPmnB*Vb~}4NI1NBX?t?n$k+1ob2(uw9q|s z3PSzIGq0dlay2s-{h0hIv(AyGHcT}{8Ty%NRz8ozDx=zXDKW~>&-GO0^Ej*tby`kx z$Vs@I67&iw&55EeVzaJB7edWeJG-o%rY+WSZqX}QBOksW8J&*2JS8OKF7n>;WrpuS z%GFMf*oHBtH1?5CmzxByA`VDxSd!H(VpoTR8$jDlApjF%#mj&nBsn%Cx)fSxt7h|` zu-M?~#dm(#C?sHI9WEeB*h?l>WT$t5_aOkKFJi$-c*RO|J@k(i-$SUK^ygayWlKlE zjE*mZ%>Yk!{aJni`Z)A?{w{FcAS}5aeBf&O|E_~VAqs~s3LkUj&}NN78eR+59Ta4p z-{hO*0v~~KCe^6dm$Y3 ze(V@3l{yJ7cra{?W2_5Hu+JeL{&21=J(Sb-cRQRaJ)#ys`L1*G(~}O(qvB!{jNG6 ztpf8{VU!Mp>ImxCn&2g?+ORu-FM`#6l50;jLppl&kJJDLV@IgLUc{njNw;J5&KJQm zeyIgS7lv}m2{`Bx11(Np&?^ZVrSP(vHd9bkS_~6dL+LoB`e%TB8lao}-BF6ZL4?gG z`ynzO1Q-VRKEP3c0f1KlAZ@WMo&^f-PCN~u6jB3JJitc)nE)05WstuO6aWMc)YDuI zJowG4jNwpqT@6x$fpbis9y+yUJwjvT*48@IO|t7t=?&CWza1SW6ZI8_!w`a{aKB9B zLl82b;j#d{y*0UAlP(R?DD%hu*~hMZ@|AIRSg#7 zFTne^0E(5$-zK$FRaZkMnjk|BdB{)RX?UH7rg5?a)uxfw9eWCYLRo>uKLx*cW3=)Q z1ZIzH^gQ`&hYj_RxW+>|u%=Fm;qS?7ja$({@@eDO`JY3SZc@2(JsKo0>};qT2Z2&U zr2rGao~Iyl^wz*?U|~k_9{?Up`!$dOv=Ys(uPi_bnK&J!Fo<%T$LDr;c-{0`wlwwf lN|mmWiKZ;{=g^g=EojX?gwmqSlkp2h{ str: - """生成回复内容""" + """生成回复内容 + + 策略:优先关键词匹配,如果没有匹配则使用 AI 生成回复 + """ # 先检查关键词规则(取最新消息) latest_content = chat_snapshot.messages[0].get("content", "") if chat_snapshot.messages else "" + # 先检查关键词匹配 for rule in self._rules: - if not rule.enabled: + if not rule.enabled or rule.reply_type != "keyword": continue - if rule.reply_type == "keyword": - # 关键词匹配 - for keyword in rule.keywords: - if keyword in latest_content: - logger.info(f"关键词匹配: {keyword}") - return rule.reply_content - - elif rule.reply_type == "AI": - # AI 生成回复 - return self._ai_generate_reply(chat_snapshot) + for keyword in rule.keywords: + if keyword in latest_content: + logger.info(f"关键词匹配: {keyword}") + return rule.reply_content + + # 没有关键词匹配,使用 AI 生成回复 + for rule in self._rules: + if not rule.enabled or rule.reply_type != "AI": + continue + logger.info("使用 AI 生成回复") + return self._ai_generate_reply(chat_snapshot) + + # 如果没有配置 AI 回复规则,也尝试调用 AI + if not self._rules: + logger.info("无规则配置,使用 AI 生成回复") + return self._ai_generate_reply(chat_snapshot) return "" @@ -104,22 +114,39 @@ class MessageProcessor: """AI 生成回复""" try: # 构造 prompt - prompt = f"""当前聊天: {chat_snapshot.chat_name} -历史消息(按时间倒序): + chat_name = chat_snapshot.chat_name + messages = chat_snapshot.messages[:10] # 取最新10条(VLM返回的是倒序) + + prompt = f"""你是微信聊天助手,正在和「{chat_name}」对话。 + +对话历史(最新在前): """ - for msg in chat_snapshot.messages[:10]: # 取最新10条(VLM返回的是倒序) + for msg in messages: sender = "我" if msg.get("is_self") else "对方" - prompt += f"- [{sender}] {msg.get('content', '')}\n" + content = msg.get("content", "") + time = msg.get("time", "") + prompt += f"[{time}] {sender}:{content}\n" prompt += """ -请生成一条合适的回复,只返回回复内容,不要其他文字。""" +请根据对话上下文,生成一条自然的回复。 +要求: +1. 回复要符合微信聊天风格,轻松友好 +2. 简洁明了,不要太长(50字以内) +3. 如果对方提问,尽量回答问题 +4. 如果对方分享事情,给予适当回应 +5. 只返回回复内容,不要其他文字""" + + logger.debug(f"AI 回复 prompt:\n{prompt[:300]}...") # 调用 LLM response = self.llm_client.chat([ {"role": "user", "content": prompt} ]) - return response.get("text", "") + text = response.get("text", "").strip() + logger.info(f"AI 生成回复: {text[:50]}...") + return text + except Exception as e: logger.error(f"AI 生成回复失败: {e}") return ""