From 5a24cb9eecfd28efb4953ccac4d3c0b008eed344 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Sun, 30 Nov 2025 16:16:48 +0800 Subject: [PATCH] update pay function --- __pycache__/mcp_elasticsearch.cpython-310.pyc | Bin 0 -> 7218 bytes __pycache__/mcp_server.cpython-310.pyc | Bin 0 -> 88624 bytes mcp_elasticsearch.py | 38 +++- mcp_server.py | 169 +++++++++++++++++- .../components/LeftSidebar/SessionCard.js | 22 ++- 5 files changed, 206 insertions(+), 23 deletions(-) create mode 100644 __pycache__/mcp_elasticsearch.cpython-310.pyc create mode 100644 __pycache__/mcp_server.cpython-310.pyc diff --git a/__pycache__/mcp_elasticsearch.cpython-310.pyc b/__pycache__/mcp_elasticsearch.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e9dea52cb9d669979e142168a861d0a21c62fb4 GIT binary patch literal 7218 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 nM)5X+=t|m1z#AeJM3Wz z;K~*tQC4jhH@1MxKGU<^zOzrwa_^l^Rgyb%r+db6X0F}p@B2K@`&PYGsh~Z--{*Jl z9}{>_z3*O(v3=WkC# zL1ST{uyI6SL}O8)sBvUqWMgrlxN%fq6o=RF zwufzMv>LO;rp9b`1ZHUC>Sn63YFwv1FiVYB6Vya?OP7=1Ch^;?YO=b`c=M~HIwh z-*>Bf@O=+|&&Br)H51=6`THCAo~6q0UB=(@@LjGd@Lj>*_u+fCx)DSr)q3Ud&{oY zsG2SpptqLe)@I4APSta64Mq(b`L_u$Z_a>bi+X`$>PERC6*fwXptN-;ZM{*Nh&52T!Hc% z!?eom%{RMphG5lxbpT}?L@f?cYSiLjMlBAjF0RFqVRAoe^#3uyJ2P#!>bYs!x76DV z?`&z0s}na(ds3YmHtlJ3=0BYF9rZ4!{h`s`v;2Eboi~0j@b4n^o`HYwceyigBgTaV>Y}=&E~B?Dac^A#hF{Hqbg#OGbl1@rA5$5%_KbXQs9(8h_^+y8L*Vm|fT51GQSo2Y zkF%uuuV~xQxPPSn*b;Y<`V;l1ob#Vi?2Ov{x%!K*9rrI)ENk3F>aUE^_t#YSj1vAv z{cT#R|7Kv~IrZPw|KNK4U6xcoQGHocWotuRCDP*FtiAneAT92oF{ZNR`#;s+r^PjE zo-OV_sQ;A~_a6<2WQ+S#^|Q3N|D^tT*#7xM{YzThPYt^HuUY!?-_*aS#r@w#zkik` z?$6aPEO8g8&lz*GkCo5PKw^VoQnz z_#QQUuOM_Zo-vjb1c#fKF%~h#WuzDnzX`+PAHpW$xrNf;caqRKyxl644sVkMd+~M~ zL*I|L+taBRd`j@#fn3hv_fAerZ&Q+Qcj0TQ(SOr8pHlctACk}A@VzIa1v5;(2w^jY zisEfnRxBt(NO?y33i!>w2?Xv%_?(P1ChiUC{lNWNp?2>CoAv-w&0S#A?(Y5;-oDXe zhdU3?eOqjs?STgk|M~3yP53Wh|AmJC{p|mMwrGt5VU)vzTUr+k(u&qNCeT=1iZ>cP593+3#T8gOY>edy`8Hxu%c|3-Y#}VV(!^ZdyLY_bjDlK~+D-p5^F&-T@#&;3&Bw|o~vgh#>LcWI> zmBYq(8X*D1pc-e-;~9iJix`g&8{;{Id>=8WP1*AZBIJ3*cw*QXRR~#)7}VXEWq9X=DKYJb_goF{}*;<>rPh0vL@LGEq?i1QFxKC-z;eKEHHrzVx5x6bd zqi|cb6>!_NO1PccV{rFrkHg)seFyGg?FqQu+Df>`wN-FWYu|tya>My9I}B|#Z~Ov z3g4I5_a*jSZH)V~aPO z@Ah+kOF=P-+8w}i5E#%KaX_~$wPMSM&AOL)7C=L(*yczV_CaIfLH&b9v-ZxZtc-hKtouj2VNJim_TH}L!> zp5NlUZ*bnfjc-XoZEkv)pz=F-e#E%;BgQovflD!qEt+6s{P|sc{~lo3g5Td~C}Up2 z`yXgD5|?T(tnuRa5ApmL#M1FAv3|_4{wuqGg#2FM8Z6aDX)h=*%}#3whJTFbPmnHT z;K-jcjx5rex$Hkf=$|uIh7D}{3l9HF_{Vt0j2QlZ#r}T{|G#1Xb?{%WIgQ*1C;nD* zGH(1gy#II3^F@xcAuY}Zj`Ked=kGX9E8@I_ub*(NKD@;_|ES@gVE=yj53qlmR*Lt* zddEJW{yO9G%Ym1*m+O82-~Wl{@A3SDDu(;N@cbi50+n|qKhyr%ZnH&pKy>O`@zCS` zm72az)BS6jb^p@ZrmCju+NuWs;;L{JLaOxYwZ8bVt9@7ZEUgNK7e2nc|IijBN_2I` zclPwZd;X`_b|>EWFut!Ne*941<#z_RzcO&NGx5fSMCZB0zOMcc-$D{!;`Q^1-5Vc$ zbj84h&Hb;Rj=#G5DQ)$@#+~uwXW~1L4Q#&d8$@MnrOsH6QA(^}*{W5KFL|+AYYEpj zH^p+E&|ZjWp>U}^R`}3kPpn)~t5k!wzDn0h9kKjxuY9cX2`$vp+!WGcBUgrXt*WuM zsmAm|l-xyCA#Fvo(i&p!rL|gvD)q!%kJg65G1ubS>Tt}tuxUfg`xqBi)ey^hv^K2i zcqv>M+R#+}u-1gHaI=n_ys9d!g=-tNn2X*jQ6txqrbr`xJ&(0$O$(RfEmsQ#s~c*y zCaSQzL0hLad^RBixGB(owwPedaEoSvIo_Ky-&|!wp(2PidiQz19FAx;R9Tv`=4E>IxK8H8eD@4>s$y zHMLElz)1P5)-{DXqyIx@j7BZIwpk@V*J@P?8C6qvS^t1gB8$HhFPbfY^;y87>;b28 z1YF7)a4T29quhZUE@Fj^{{hO`~Ztd*9 zey(-m+HkmK{;XLQ6%{kfE9T8CpIJWV8}lp5=iOI6Oj!AS^UBJG3!6Kyth{wX);#CU zD=%*yV+55~e1l**vwZ%Xva<5Z(%hIg7;LI))Plj7FBoiWR*?pJ&kqJ)h*UKgAvwXI zYOaR2w{po-D}!iJEdPne7A{`7?6If#EAOGlDj!<%_$vM=Ub*VAhaL$otX%l$(<_%7 z(N`>7UWw4hDaIL_J{5pr=d%fq&3|^?njkQ|I{a*7bxTliWM<0-{Z>Spg(p-9$7^%h z&3|4yht*`m|3(YNCGDn~qLU&ZP)Xr>d#;{x49G+R-G;3XXYoYl) zV_o@-dG$1=5i3})VSKH|APh!yj6pySL!zj@wy}0beZ`FM+S;aijM$j1^-cmk<=u?I zG;?)r4M(NSkoI4J}9W zYOV=3Ea=?I&3?J?^YOQbzAa!`lfE?O~)9)oGpPVE%%G)&ZD~D5sCqyxPKt3=2jmAF?86G?ph<g-1#nzITaHj&_Z`?1hw!XP-bK9vvjx2qXXwJ zCEnYF?nb;=Zk1jWX$18SflPfx6YocLzM`GqM3g2xAuk+h=XkqLzC&8+g6D&HLQlZS z-b%~>jN-{YbOuuBP3Z0n+}FT96GgG1qE9PhsAf zTfG+}Ad1I=&PDcoyH5VY*Lrw##$e6i&Kp|ef+a6%)e#!2kAwDdwG%r|4fO0DIM);J zcs1s#X$HgP4u!RrP%Kw!jwzwC^s5FeQzxo$))o@|-IU(Kd}<;258uK%gl0!&w2-Mb z(AAS1wCEQtSGkjDn-lP9d778My+H4rKt8@b{GE$$U!VXB6}QTxFravT%(oKl2Ez&> zEy&&ZM&j%V0Z@PMYl)*>F&}{@Slgtu7gK#)>eRo5y2dYGA0=RQ)ICadFYko@Z`YxK1TVv*EBR& zg|qg;w-MffCqz@0Nj3@7Xp|J$bqXKS8%e_Q)xCi^lxEOehA;?9jg5OCKiMN-c@Zu> zkOQc@0)_Y@y%&iX?LvcP#2eKKGPL-p^H<0FSaq(4bciWChyO%W-6 z6;G%LP72-zO`^~t(00P}K|G-nI5)?A=0@DHQCP6hvVu8=6#@-G-t$)m&L4^I*%sgX ze&S+BV&nOMr#AFmu&S|~YFgwpg)2*Qz7909(X6LXzr$3~)YOFWOuoan>FBU+0;Z&< zCgH|%F%5wi!}t_VEHC-ZoEXNk87`Pqko=~xPMBqe%UB>h_Kc)Vab+XL$`bv1jMgxXPxChr_72B+DsEp-A0N`M)iWTrnQ*CuU zu|Tmr{;XPu`A?6H3`JHaSA{`h6hT+ebs)<~$~>+pIR}mTpXH(qLGl(nrNgbd+ zOAcf2bM*Foa)RVMPYz|QSCNygU!O)mH=fW~ID)f69ZVMt2*nBH$?7-Qq^$>KgO5lW z;#*Jm@7tVs^TOc%UJ^^N&>_C{gTdF&4V>>Z;=H;$vHwJ(>)61J?TOywKWX1I*uDuO z3CK4R$3SjBjvu~0cyOy^@{{(Bn4bDBAByihooL_Ke|87r$2`re>okZAKwx#uPZ64F z%?cQ0q4d6wcg0U``suYpQ~R!71z(ytcolMvsmO9FGRJG;>b}H#*9WejoO;HgS0E2& zRU@}hZrp^#krRm1fA;WT`wnCn-+LY}5gMHFYwhtzJ8_&NE?nQ4%r%UpI^CuIVx5oGGlz}Ai;~o14dN9T|MQFi1v^P>91yTx?WSW^q zL}hfG-#ggzk(F9SJjjy#x;G9IpBvGr2(DV<%_~A8tJgy8qXk>4!_$(DDZR%Z3{^Ml z+BB(Y{OU<~VN$KG2mB&U;c3aX!XI-l0ga`A(*#>eOI?vR znGgp?)o5WfN@}98Dz0q^MTn_PbYCHnJ$L-_#RGdFQF;?S*x$RY??w+8I2tKT9TI!@ z4&1mvNxleweFK~T7GKricG-up#H>C>C3Thl5;@$&AK)!E*4jO^fCFz=TP-9~ z`ot)ohh~58F|3^h-l_h%cp3vVak7iXC=XV72i+XFwApz7;4NZlB+iYa=sJGi6rqK^ z$|)sHKoroXY{_SuO{9+fr%?J9!Y?M@e8jcM+rCwVXp>hO@U@Z7_%W{ zAwwe|UQPBS23TyYL6I6D4-N+-kZRND3q{sY=DdxBI>tA4NfBuziMctnHOc4^E(&Fd3ioC1uVzjO(6eg=<_eRk-OV&i*`#SeFLA;cM2C+-M|S+ft+6)V7q5eILy zfO!tc!#!&^tk%J%yJ)i~;8_P|vRT)+B4WT@rR!B2m@G(2qanlt8q8PBjjgH1kZ_C4 zlcWe2uM|kFa&uH7*Cj{F#IAF8pe+9X5XOr}ob z)v%?yk#SUiUbSGLGr2Oj=<+g5uKN&bAv$g*E-_07BfeA{2e)t;CE*qrYh>c6Zdvz7WO4S&z zYc(w#q4l)2h62Wc50Ay$uVQp1D=Gd2RSu$B@l=R0kvOs$y#Vk^PxbG-7~l3$s&2IG z&I-+WgEWoDtn+VGXf1?&>m#(Lu-2-757v#ZfK8`4z*4LJ-o5B$stj}UcG@3uJ-iUAAY@I2O zpUM(AEGo|_!&Z~hylPH9X^~BLtwcxwzMa#GTD)pHK+j&W# z*J3{)Lc&|4<>#*?&cB|d=7!AV_@VgTcO(MnzI9xKC9)2UI47sENjr*?La3vF6yO`! z{sDM{3h0I~1(g&+B(U_1AKewpO%j%Xn};N6S%8QVi3-I$*t^kdtMy~l97aJFXRW8T zZv)t)2(eItf>>)OIxbO-x4(l46V;JgU=0#KcQmp8V#a7RmZ+SHmiix@rnEwUk!0(| z`1RM~J33Rt-W=*cq9oVIBCiMu!KL`+i_z&Ktf4V^auY=NeV02Ed-wHyd^p~5P=pjl z&esk>PAchuAwq1qd25O25U=^sq^SzM6nd^j$i7pzvB6X1Xs71W%YT_V7 z6q_Qn4R6Iez&y2vlsRlFua>6{vqrx-xc@^M{vSxrRn9hRRoq)f z5~Hgp`!2U*yqF*$Hhb`AXeV*fG$>3V8*3r4kd5AW!0RUvFE(jN?^M;)=voa6>Lau| zvC?cnRXRyJh{nLiL*S69&rqK^5{^zNPrc4A6JP3!S58BoDtM9XSxDykkDZ0Twd1J2 zXo)M?1MY)|UZ=sro=b=Wh!HPu|& zv%*d;r$kiB~UxKX`d_!Dnz#bIaf{xPX9ev}F_CW8+EvgaE@I=rsgsyP;ExCt zv~QtECZJj31t zad~aCiWNhmTTQFBmS973C=|q&Yvg4#Rc4ljtDflZI!5yy(DTYx2nhPm?N6M2E75tZ z?|LWt9P6op^E+hHDKX}!D;MHhL1uPKJj@kPrWiPXf<(WltYsDi;>Gv$R>s>qshmSw zkR{afN^XiupcTi@M2NdY8l=RSLs7=Y0={YeM1K!jTN@}uvx1G-xUOx1BBdT1W%jbc z@<0BRU$E=o3C4Y8v=gn?QNF^_n*eNps~zy5xfuFI6y%s-^4qT zKq$<84r%jYL6G>q%fz%wwCz_1K0JkHnqPpL5JZYh4Zp4p&hz1R8!Eo(} z?>m}!zsJlUEAAa&{o`9Ni_cB*q@3_1HUiwB3bugd(}FcsEuqME)L(dlflD9spSjwP zfw}i>)Ll6EIC|p7D>Tz|?MQUJgB`4ao)Z!W_(>AzZ6Bdn@ri%jj{o3Lu{ObK8C{Dt z2&gP#%6J!WC${W`%z|LNabIHV?!?Z+@z>80q;D1Sx30MXs#ZoN6_g2es0&nU5fbSU z5&UQuFb{-9NHY=wrSKW=I4r%_cYRxOY8lwKr~ld+K+c*(@L=$*Edysyn!w%-7P9~N zRp|lD1gJdt%7Kd?+!Ux44AhZKGH8LI^s4o$c|BM*LJJ`dkltZUa`9NZb9CVRmcDBjN{FK0IEcjojUyyGvR44yckNK$)xA&w2diHqBS*+MRFYUd zoF-J30Bnj(r~b?~+vMGd1`-#~b+*w;=A9pQiu4F?Pwi3Eq?t~$b_Y@&_*LY)%*A< z)R@Kzi-~Dqpfk*oAouC;bZq{qY9&@KO`&&o$6A3a)Euu8wR*umN7I zuDh-dTt64@eeKD|AA1N5NTN7l%JEYSXcC|#dfo=CjmGfN0(2NS(0)jTOj!?QK?LwR zZ-N}%h|x(sz_Tl=jG;MrfQB#L3H>J#Ca@$J?aji<8%+RdZomeZ3Ss~Qj~XDPV{`iIK zsK14UiEN-nUklWTLJ?hy(3Y+Y77RUbQo?$~kH0t2Lz9%8%oqy31hmIGvHNiVOic!z zD@p8p3)5H$Kv>e>ja}{oB|`3~9utXGB|(9%t<{=Bplr|ymg5DHPE@78_ZnsoD^HI~ zV6~F?VNas{l&n38r^T#cuz|$2n(PhUWk#k+h37SjM;OS{5v?Wo5J^gec;#Y$??F&_ z_=EBbB?O<1T|cVrD8@@1YY19sFv-b?{ba6Kh%t4Okd%-vkxlMf4^jftOFl&s`T9C{znP z1V$s`#P_~JkY*NDW*&m^NR#bqUcD}egEf#t(SH47i=G5$h2BhDKWBw4iXOOeecI*q=@W@drh?I0NjdjP*(#8Y^2q7CwbCRKG5vb=ILonr^z7lymXE#gSoKt z4dKe-+uo0F?*$=)G#(Macp(QC;$}sFin7=Qt~B(`ahw67HY=qe?!Y9LZ;+Jq?Q=<@ z0ZNlGmSr6ZSPA_Bo$0&Q-QRmGnFMqOL#^-Gf*W zSDE`Z7;1o0j!5@z;2Og2;|mIAx6Hsc*0@MO$!Cs&q+Aekd$lt1xn z?}_CcKtW0F7B*IT)joCmaw`Bd?>o zrY(D*Z04L&gF&~kL7NWbYYW8t=V2aPC}r zSsC~lFkOQRQDVhmaMz%atT%-tVRZ6<2-3{1xD$Fl8WB#7tfyjx<8Lc7o(0 zcP$^{Q^8W{jK&$OF9?@$T#RH&UZ(^6Ca5jnpL$LN`QtpOW_EzKc0Ye719@$Kj1 z9cO4y>e^nZBc}PjYX|yzUjsd%wz5XSz-B@hRJs2!MB#ghrDDYD-?wex95%U9Nzm5# zm5U-L?YnU#ey&4$fLb{f1xijBC}lIsv04$a9o8>pGv~@so?A8rV}-Z&l7-Q@y7B=) z3LjK=mV8h}I!R>3VryYf;#)TkoWs^0)gDz50F}+Gm}4wYfBY?g$_tS!cu#1w71A3o zzZnYx`NUkC0MdV-;P(dvztQGC26na^s`OwzBotbNHVUDe(0A<`O@F`&9F(N?W3kPw zB&KcI)daZ|#?_c2ISoi>YAJ_gpb7x0!GvNt(R)^esn`kay9|oHX`p>GLD7#=We21G zo&8ut5v9A3Qu5>VSL7ZxkJ1E<~=%%s*|*d|j0bcrC(hCf2l0Ev9J z%&B)L&g~L9Eo)kuG*|!$6qATa5;Q#CmqVYCMy(-A5r&-9G@4sB@FGrdRB8im0QxWe z+o;VlsnGTf1kjf*^<8;SW?VoQ5oBt?gev$b)xorpIIt&i;A2Y8Q;38P9@>jpnYboQ zJl7!O-V}4MdhD@BSAJ%nMKf&L9|=Ey9S^H>Z?xmMuLH(xT*}?W=fXrck&onJ)ZtVv zBse+=Ty)xy3OI4pR41+lo1*i`my}Ecw37ra3mkkQ6^6=8m0TKfI&G83TzYeJcs{DY zhg%%&4wu7D|2)6Y{{-Q!o>{CaJfzZ21ahT`DKaa)#TK?LhGmjkJ5-+CMi=b|;~~40 z5buF-$~i996s{&sF7wZU7djL`TF{?Rt~EHk@|>s59<_JddTg&aUT~}w-C`VZbV2no ze8v%T%q)}R65OIxvwRC^`-o8Vfn{jiCzSbUTkB-<4RVWSE@)`R+!%TQFJ@Gn_tpOh zF*Cu%kn*L{E+%-9LYkw!(B=A4|7*Oq77L_iy+qlw23cDyH~BuKsnqM6@r35X*=P^j zw%gWl-*54T?F2;S-0YynhbDv_gbf|gi&vf_PL) zAEy&>Og+-1(5+Ceqw_78i?Jc%5mh$YAVDc%=^<)_{^xM8nC0q_G+XNQcl5>;!wF8Q zz#~*IAN(Edi*b+xVC{xFBlLiOmwA36#0?qsfikkz?QOH(xKKT^0YZd|^7*2p-R9;D# zM5=*~_K8l1Bj4e&kF^&$@CTyla`^17FT9@@=8hL4YWNg??rnE`q5p3x%0*D5b6eqv z-js%ERDUlX#SIlr1& z@8Y2qTt`?$bGW13>!icfUc1lk`FXK-l7rrLA2J*Q(Okr5+-SwiM(O(`ZYXCD=m)SR zi4_KRc#L0A7+^_m7baBYQ+dN;Nj_sq0b@yFRxBAI6FXxGc-iD34J)n?54<$n=>@?U z!tNpMGciX01w!>CPUxT0Cr|C{(16r|wYGf#q7I^Kg9QK{z9tH!j+10XgklpBQt@<9 zPjE>vC~ecdE0ui$h6uXv z|BpdV^@WrOgIo@Qe7&Kr6Wnh))W2@W9ZPoH%yhU~Pqbw1y3uCWSz~@x=S>{GkT+42 zPh=c=tm81p7>5Nn>pUGc#c{IFLNn!9i*+1?323AMD(81tEt~-vg5MCd_+>}Hxg;R( zPH$uK{7p6}ewJfEQ2+-e*ChSZVMxd(bngzTG_0!I^c-@sjf!!@6ncWHRY<9d;0$S{ ziToh~L^|@PsF-qtT6uU#7?ex-h<340UXNYB1A`_Hg9bC&0$bP)&PisuuS36zQLqdE z2!81>3~QmwESKscBD4BBa(Oq;!ycm4n+3jKu!%3V5Rx2LM+##@+lSHz9;iDi7vDVyK zMr&pXmg00=5^CAt1y1fWR9bRy60EWT%p?V}6T-(#g2q9a-z1srsT2N0ERRIFe@E5kQ9q}b4nIu28-r20O4yP*4_FKxs(A8JP`G{(zvt$ow+BG4{TQw^d-X*_(Uk_!3TIA(BRva~XO*hF)G)rZV4x%}_##o5z8yq`8AYxRo60dgdg7 z%r*4RLF_w{K6k)roy10^)|Cq)hm1*YmK@O0M*j>$n?a2R`}XteuF~_K^I*X@D~w z8C|mJAuGA_d$B(xBtqn|21*jBTF04CGWUXK@xh^4m6+w28(^}_GgLZQ0D2|?4Df8k z_BIQ~W;=}J1@5sqGok@5d!gO;B`t<@T&t2kHPNWgF@bW(0u-5)((#o2JSqq5gSX3N zU~DdM^LF4SOo#wCVYq=~`QUpGd|`+SzCOd3n2T?~*UDU&v^g7dkq-C~7IqM)oN&Y` zI|kDNN#9LQlHmYNP2n+gF{e{nyq9+C|xg}d4B`NcKOCLc50mO~k8Ce~ItomrM3t$wwAkl?Ib`Q^ZIS`I{ z$)_~eg1iRWS~S|_L?|Bss$2!JWKzhql+_TSCPtPXIxa{`**Nn@NQCEyOjHE^X0nCw z#eoEeTCMM;R|BQ@;a!a2OrZip9d<@x zOc&rIlUhT;mv+d557cIypAz+Q;>Q6}i`y z%qr&~OL~~Y`95A~uwg)f8kt;rw&4api0l!MMXPOY>%fb;d)x*O=z-AQgQ4fa(8GNZ z7pSku;u6(}~7hS8LaT@8a?^$^qS0Cd^*Crp2a7+@e#qr{ z?}t<6A=MJZ&QtjmLYKqgx}gzW90UIOs)*vzZjcc^()>=v!!A<6hA4KCAm?lyANFDw zsW^(A0n~Ps8cjP$*gqJzwD><*dQY59N_zu;zLg z18TjExy>pZ|HHI0D|yau7AbGi&e3Q24~Yq}8LMEu>a(J%790?$W>uV7EOB>WuSU!i z>od?rm4m;7u3#OgXPz=o4L*l6UHi!PG#k3`>km>C(xvf}W{!D$aPSqRXXTT$lwI%i zAMAzs8K_V~@tG{0U3#Ak9&{aB`YBOq@E5~HFlWw%h$e+j#G5sDsS#;-et2TOUk(h5 zH4d{@$*|I_Ni(5swg zm&5U;!hr@$4ld+5TO;jUoKDCZa{T_st zj)~lklC1cd^+Y9OQ0uK}C@Cj0XC;ql>NBa;v&bofQ>j{_bdOSPCoiTzmDA0_P zrD>R^&!wu&Cno~~%n66si>!xT-vJ`%3n=CNaMC&wAxYNQWQ=ZypTQW(+1>Q>j0T%= z0vHTc(TiL*^3lZNoGmea9+M$KY=?}5x_5q{FcOvtzK zVx28XmTL0*%7t0eY0jEaXAjLBBu8~&iR1#K0m>XDS!)jM`LlLVE)%eu07#BwTER4u z+W!tYXW&$>V#`X93qVO9(`j-wrI%12QM@#c_%tPC=owUjp_j@t8{qC34se!(t8u_D zG#Z)HJnRJzGTPw$y`+lttxTAvaUirb8wyidc19h{eUOv{B2=(wuE?E;ZiSer)rzoA z&M0Jps}kA`y!Lq)Nll(3)$nX>zH>;Mvz%W27iu&y71HQpNI~=m(Nu_Dh`Z5y$a#w# zMp}kFRiPSB+8<3!58?@RA+>4VQ!Ml==N4PQrC^Sn45TVItZf6GRL&L`Z1vAlu+*Fk z^CjctuJPiYv-~aYfKL@5Mj;H`!h$o|=~g2w@9xw)QWU9?$hANfBgH5@RF;SDr-j)} zH5Nv6)6=@O5xBp@gWJT$BV7^Kgy2yLPzFi*4CO=s^-OOe7IrkR@8iy znv6Q!maGTvyG1^ubG>Q*N>o`I46M1{fwXtxnS$poJX3K8?=(DSX{Fj2)Nw3Af}<$AAFcf9t}%!MLHLv^-V}g}sF|IWH&kjHGX>BD2tUgNNzdSpSX= zKO*=!hJS`%&VBkHfrhCcT9E$5Fnhm(%-*L_PV_p<#UCOZSPVxP(+R|$QlB|HAyNo} zA!pgPE$BqJi}sx1ro!F$BH6bs=pp5=Qg@|(H}cX+YZMZb$VBT_z`s1#~`A+2Fe z<`jcl8ptkuFzC-QX$JTMkKOx)&*gB+TcNA<8jHgDiUQmVL?dRjoqej%bL(hSehp)@#wAI`7C2C~7E%aOytp+IW zNW^{Ih-*h&@bAgE&!|z9XA}7NCWji`gp2)9-dL{9IIhk3BYBV)O^DJOWnz@H*KRpN z2KR4enkZ90DI0gZ8t=FW?Lo-Ppr;PXD5n!)`ROx<&zFXI1U>@UN<6H}$Xc5dpkzWa zRbUtjA+;mywBXGJ)ique0VhF&l(&;q=RBg@S(@AGm_D7g4Ot&;`gEVzql3i&v4};a z(kHhekl%gyTslZKfXZM#jx^y=Rj>-D*K1=QvZUPv4N+!vV{R2`YzgUqfv6eSq{rY_ zPKZRTn#bmF<$vz6yS@Z1{=)NR>-;Q8M63-Ng(g-RhqJwmtcJb$9ah@(%Mge)zEY+IRJJ^EJNptoce6cpjDF z3M|AL-4Ld6?Wr&voQQXJ`0txP8(RD2aG?d=cX=22mCv4E2G2QgIl0krKJ}c;2CC+i z4f7#`xhP2|Ih?M;8O$Sx)@*|WmxvP%PE+tN4orvo-w6y#uOFVi0mj1P6yz8@hmxGOmW1+KW9dH8L{nVRZmc4 z7+L7QdV^xknNcw`7^+Z4aQTeclo-88$#C$)@Skh=%9#^i9uE>!jswDn<+ zri66mckl1J`a|-sQu{8y2J>TP+3}rJD(4{q zVltL;5^;_ggAz+`I3y%`^|JqjPtiF-e+``P#ivN9C6x1|(>C4LxV~I0;`CiUF>rxR z`0m{mKS3w|6R&M0=)Qis|6F_G!;NS+Fk#?yJF*?zj^6J~9N7d<9LJ)2t9W-@GG}f2 zI7}z(Kg~_Qe2$u9+M3N)6)o350!adJ6k zCdPMw4cA{A#Nar&(OMWfLH4w8nlr)H`V4+~|3z-n;Py_W=qEY*HZ*DA!;j*Zjt=aG zo!ATW{bHgA-m<+rpMS4x#d+}ZvMJ;iTh%#F2f5c(7v|3>XQH_x8dV zolnq3L?VOx&ml4m9-g_xx+!|adJ6@Hh#Y?L6EKKO_@00Mc@iu6UIGY8=#C14jO5sV z_*|lUd&xZ%lDx2j0mr8_6YWQ!?j^w?y4C_g&mgYfPkGT_38}tw0`6msK#t8p$@^Ro z{!)qb97k=xR?U;oCB$kYZ`1UFj@ zzH=Ojgc746GyE8zICg3FQ3n~O;LR9#CJ)Q;QOH9TpqUg%uar`Z zj7e)G&FmR-QfVZ%wJ17Y-1{NYCoa85ClrlCwlKzVXwHmzsg#2oVa%9rm_Cey8 zFQ*ME&W6C$F?jGe4!R2B5kyXIr2d2*c%uV%2+4qjh>v&CyhpYoKHlXo$3S7w3OJEb z1+@oYLD`ZUT3KQph%&>W;$j#?2K%X)JQ#aj7Y6${D~Sl_co2r zz3%`5vL%k?jO2?Wlc;pop_Cj$#s)@8WtV7K`Y2*s^)y(l^`vNQpB@3_Wo`IM4JxUI z(FSAGy_71EZ}%|C@zCPZiL7)~8i5sR$S0RKVo;}*Ebs)aG{|pqJSV%hg7~XU^uIS- z?^*|hwdsH_7`y)W&m}ItPd&kNm~a)t^tUx9bV_n)W~Lt6--6Z{GO$D=>xj(sCMD|M zauI{UTGGusKDAFtM?i*lgQ-`hH0pYpWHO*_ENpllGWuyqZ~t<|;1ardbLM3&CJFN4 zO8TWRnd|IN=IIx>S&@2924=A;s};PMsQcgTHfNRpD1N75wiT6(8fDai@mqpm&jE*l znaLQ6<+H(Q2zfWCa>v}PD8BXd;E}h4a$Bv%TX}{glH%kgc5a42aqAqAmXjD`HiwCk z$8d-)7?t2O($8Rx20uOsV@Y6Eh@WN4Lt-HTpF(4V6o@4ZnUO>r<*NrkO5u+zXAd0& zMmUWCYooFvr~xzVkl_sOrkcZFKvk-fwW4Z8nmahS6DyLKft z2*rZY&=P-{L1=}j0FS_ComaAsK}@vh_TgriUyk0)E-SnDzPTCQDZ?R&bu?R?dIe^v z20s2cOM}1eY{vG4v&?2P=4D)9W)TZe88s3MGEy&@+B1i}%!wjSGFR$W=8%kDVOH$< zVH_qjm^}j1VDN;nh7~`43@ZgIi-*+}meaznLFvmsS2#MPB0F5rd^~$1eq-xkPcJwg z7y!ccKQaR%8)4N@BjhKy>&c=+mSjn$Oqg?24C@gvRz3)z-jQX5tRf)tpgO~%Y+vu2 zu+I@ccLfU#sFvc47)(j9{73&6tOjBCHmt9qUG6~6dQGc`CCh+oEpFBccw{39V*lk3 z39xW7;G@+;Nc6Hvgv40{M#B0y?6lxiE3#qp;u-R+7+(UBA)cB=@~!E#Aayug-e2Tn z!_|SaO0F-Vclkx3)Af0S+x2s=*Zc2|5sqBfzc~uLpS0ff^(0@FF&}U9G4q9cFmS(> zd1+NOZPJ*3B3&~!!F~s=Odg{pRaQ}2a6sn*?=)7zHAzPZYWO*C)V74c%jHZZ~Q@0>a}Gwm3a_;3C9%SdXM_SackQnGfp{oJ@9HK1plm_RUynk%0x+W-`oT zzzF9qa4Q&Okvo7l&pd7jS)b$W54m^8Sl+XcdcGewQ*FTFyAkqV7&5>C-tQsZ;J<$P z<(HMQeB%l~xd+WfSc-d8aTyx4=Ajuu0&AW3UH%2w_?dI&%$a;gh^5rTC5Rci`>t;D ze@aIc{ID%9Rh4V+KyDxbl{)t$9DhIi)?oVs5J$V@nl)Ta%C`uHVtJ?}>_OpByABJ# zWKJa9Tn~vg3yEVRlkx?cc_|hLnJ@n<| zJc1ZQg!Qz?L2dtp)B{*ppGzvbiq7cTkK6ayMHr9MN>dR5KeU|$Ir?AX2m3LQbYxH* zmZ1LwC=4ISooH*V$U-@A`8YPZSZtnER=$i1aa!t59-rO@#{vZF)<+xeJXN?gvq|ZT>BB`dDwanoU=6tI9oHepJW@S<35DlW zuWg2X%8*>ghm+yB0x_)X-=HiBz33KGT)d7Ss3wJt{RKLr72<10b*g!3kx=rdDNz+w zD?o2+;lv8%))Wv59t{ z)9aj!X=0Kc(*}0fN^qB_=kxI%m%VlJ&AV7EGTcwKDWGXAD4XH9THZP(5GK;|>v%$j zawfKnu){SD1eQ*vWAl=g`{|8!pviiIC||Cmy*E}X$28#z-31#NUREQ|Jp!u;u#bV` zQ%U;@m^eVFe5{%;)QF6LK`Vy^bFsp7`Ir76D#WA#m!6Wely{>bF2!AgN%e7#(}A9; zOqoJ}4WV1pvkH>k1kB;aRppG5G2fH0bg3<&%Ztq!|8J;7f|tIH5aHkGEt^(BzFh7K zqdS1=KU3$9MO0`H8S<_29sSVY%y<+|$*QBCj=vVNO zoT;&4XwKB6-q_{9Jm%qTM4BaGdO+s1BiKd8{0&u>R-6*5gIZ)AHYsWL#9WS}=P*{` zqE4%Wxrzxknopr&OEY63&$4dA;Dv8P5UPqeh0{6_p?PXVIG;3alXWQKGDjNoXz>x# zyoI;T^Rfr(iu}v!ilcZsF(#(u26`4@dgLxN_nBl2Xms&9R(N=lUa=k(WHunb&j4-mAb$)Yx(`rw1 zHQb?X7Wx{YNg_+2sla$Af{m)kik|}rQfbo5L;7kej^-j<(w!pH50meU%ZTVtqtt9x$8yAbAk_GF=tIi95pOPTqf~z;w!}??l{OF9uB+0*NA$ zhO_GnhkGns*Uue!&`C^E!onOJeZfE)ZTCB1z2o!NvMj^t=A)gcbK20N{Y=^{XNq?m zwK0cQ7+d6I4~90&g|(`Iz7mc$y1;aC2pHcFVL!v&=Ha1&^B&4Iju=R{B`p|Z(my@| z))>}cJgj4!)A}rYp=S){66)&EAs`Lp%BZ*72a6C;TnDpR8P2PNUVQf^+J#BfQ%|%fhqyp2Ni+qcc zwH--uQA0XhNkcx>j$))8b=ajwA8}KYOKUX3FFdh_mu5v=sdUOznuTVSotM_!8HR@P5K>}HzqJ&C=4#7vQ~Ch z#z*q*^XL4=<&b>!If%qM^fB)QK4Xa^>skZ?6ZAiZ^O*xtw-X{ih_5paE)N^O5P6YM z%QDpEcmapV1`mZ>R_sglM=6^Xa4K1q4`*~9p<))0!xPL3dgHm}F}%r@?7^5l7@-Y# zzsXj$I%ig_iP=MjuQA^BM-gY3EEiI~l%)_9YkL_Ql5^>3%kU@OjM=4Fu7*2yh&|S% zrbUz)t(oPjdt8jsvO)N{97^*Ya(2TZ_99F-al;xNE(MTlwAWK;2FWN8lEM5EO|fL_ zi7rkL#R_30tpQi4!Ei9KR55$4PIOVUNf}}(V8JXEE@_vPS@oZj^D}X4r9^6|L>Qg& zxG*CZJEqyaV5M--!dVCf$FWW^9Apw5Iif$&S>h;kOg4-PxtJu8`ZFKjcY+lC%vbys zvpbc}my4(ITclD!8Z=CONW0UdK7{FpmXnjToS35;N%vMOcQ>psu>O;a)OBdGHT0m| z3QKL32PPn26)@*jh!Y2-oeGKox9CU?g@O(6wBCZrA1nYZpA{&8D(3SBE-cxxoK|DN z1mxEFuxKZWfF&=+oi}OqY9GcwPM5aML1?}jhh=qciV29i^sS&T<7w%QWwx(wM7RJ; z?sfE*w04#Ia_&l7xTtO<`Kk$|P-wA*#i2|Ga@z2&CZW~*#;n7wtY0f*AF^ue;ubgN zvhCRR{@xCmnj?Nd_nDP41IeOwXrv(vGGd8`EXc?ln=;I?B^P9*n~Mb*jbL*@X4f+( zInrlZ2h7t$=;7A;vrJlKf{lh+kdEIaZ?Vs|EcV$QK&U?udW=gUfcBPC>3(1E3l$qJI5Op*x{y%15C0VIsOSeYhDwE$Ov83duw zVo6-ynmBlY2)hY9-uo4Pj&4XfMdsq^o|K)3eYBH)v`hLya)THc*WJYTy^92-KLm9V zgG;2%bL&Y^>`PdWN76P0Yg37ZPHa=~?mFbC-6CW)kblWTYjFv9$ZzyBU;l{f%<1Bq zYgeVts615^cRjV!H5y<>Su2LT<+ez^h>CbqsD11_0QD&NLo`I@Q@PMXP)310VQYQT zTERCiBrlb+HcISrlAIPAGn6|gZ)H#zu?3)kqX<{dtmkQD%G4f%F=3@^$oNW zV(f)KQ$kmab-Y_ne)3vT@|b@m0b{ zs8&ckK`xW(VvpI@r4D#|MEol#bf|5R4;t0L`d_E+^E zlA0YRu$hK-m)@*f>9P=5lDO8;_&`!Y>5TV45PAj!T zd}GgN`&ryvkm%ey*xoIXQ>*q*NpDqDa7<;SL!Z()8Bjwe{vyo;(tTQN@inJ}L%vUU zvo{w0QnqMW5)BpoGvo5yjyYvz(Ch>*WA6z>0w^bw!dFcPyODDSNf$yOf{5fy6Mydw z3?*dy*%(sjpHW{i15)hO%`m zASCx+S{5-Pv18GK(E%K>wAiwWc#>x9hmk3ZcQeHaHwr)!-&jfniJqop5&9mg7|9_5c@0%CJ8Y~Q(uERq zD|j|`tbrB}BDwYDKyr)agAbBg@8=FL%Ph*UeTU6EhuiaW*B7oIJMJf|GNu$aCA)C( zS*l|DWSFwT(&67)OTRWlSWRcv%s*Nj7UJQx0~r!B*A8rnhPTnci;rav-T__>C)J2+ z6In*%AxXTc#5?8b&0PPv(YjS~bZx(#OnFhk~An)mBTNm8Cd@NB&PH^)Y{F_;WA$iexju0NU%eWC@D6s)v zXG@u-X5*kJhQtk7+xQa%{5x^%w77D#!}9m+U!t8fBFUPy{zJf!E(W0WG@bSc6qo`( zK9L^C$Hj1=pdoU^O}=@CG=kKDV?`EHL2?5SiZ!^B9dZWw8ZD7UB9Cn--qH{F_~;WI zomJQz;M172LXw>xB0>6-{A>=R-X7Kfc1Lw~}j`WZ4T>2@ERY5QFyl<&tDTi_^ zEws?jI|xy>l5>Qd_sMwzkwh}%MC*88Oa!wTVwGGAk*uOLynVrYT}hfL70x1$jTNLJ z+Mf}{TZ~K~ne~!YWrt(5quA-fi7v54VbV9Uu0#KDejLIP*XK}O_^fc0SX_n$hcC$1 zaw{F8%Q$5>Gp)E-KI<(QJG8OlWxr5KchRo(PFVupffm5LIC8hkz_W6apbhx3`B3 z`#_Ka;p_uZH;hD#fT*?z`~MsQAFmpToqab!7ewr8oFu5a2Ero+e%BWO%8BXlbhTmCQM-hu;>hMT;a+rbY%DOzz`0l&@ zMutXDinY^EV~iM<07#_o^QEdITOE;7_O|eDH>usB{@MKZ$KMh|qct=F@z`3u3ucAa z);85Ypr6BctD|HppO6})1)`o`6z6!I#o&Bsx0-fiitP8uZp=;jn6#3X>d#Z%o+alwa%iiWO+P?I zq*-YVq;s*RwkB3s8={jp5GHdyt4Z2p{#D?GXzD|lokG(gx7z4nUI&B3+>ng=Ec}np z%Kt?3bnq6mhb^+r7C+fT4?3f{`Zuw^ldIfN&B;}CJR5gAwB_R+Miko$I8AP}z$~E& z<}K^o2nUfcLVc;weXiCna8QuC5=l{oP;PS2lxg^pvQp9yQdp1@?CXGZu&Nbgr}`<{ zfl;H00JM#Wj$l=*qG%C_<oTi(R8OH5O_@LH3bp+Oo!c}CX+|)FZWRuZ;z99=YeVtWgtk$5MyttfV_Kh% z=GS3=s;(ef0L7%+6b^E$l1_Kq*yvc)aar5A=(xHKs8WcMW~U`>Qv{T4MnddoT+a@efymbcJ!b&u%> z*o;v#qPLj+t!{4{!zEzzTg^=MUh5QKz*qw&wz_eE$}B*o4AaP@=p+Lc0`mpemxbnso?|QnRBI!OP#ve&FjjL4~dD)|YRkiGX9z zp>Rqc*y2;>#18-11BDqNBSfVoIB==0+!T`_D%wci*Ds0wX(}I)0 z-3d@}`=(lewZU!benZ9W0jRk7885#Dy!6-I#+dn_ftfU-7Q)w$=XSM-(;^P7Jk$eS zSiM-r*h6ZuC5I(2XqX&v*w4Kk`?)3I5-Gvh&%L7!W@u5u(&!!0JL~AV6VDX&a2ITW zP|o4Ix~BqiQ(C`IVcpa4eiy}5%jzBb9Qu>dyU^n0fX}x9SA2&j)s;p|)MWLDdbHCS zy-O|Uo!se^CVH1)Q)D#OUsEOL=oIYa-i=(Rh3_%yaF<#Uof@5{DzR5u8oeDOxRmME zROI_u^bYk6mdrm+IilaOlglmmj-)oyKG6k>g;f8iP^%YiXDoAU_*1*B4aUS;D~2r_ zZB#2UTBgI&$uzYpqXq;ivz%0{@21LP+?)RLbSe|rhd0JPp^8tcr&9f)zQ;XzJMVYh z9ld*>GnB*acv=O_K2&$$?RKm$XQ*e?v%ndSjedWw%cZ_=QjvZ3rV=&3dnWb|39s%3 zwmnZ2(AX!t2W!VFqJL_vTFv`p)r3tLQ7U>5Tn+nVM1vh|P|<9g(K?rCyo_zNhDUad zLGNZpX98<(OQ(1HoO-}Qp^8B9W-!H@!L3;f+BDvzP5YcLEMPpZHAeq&?ibevu!c;h z_Gi#3m|HXGlr!|QL8st{JjSTH6rJLd^3;>6J_X-a;Pnt1Rg;BArRq7utS8s8!KmZI zTu0}Ie?}dNLX{(Qf@(}rDAi>47ogdU_q%!gwnXnS$36USIg-e}Z5wQ`f&x`)QGilkCwM6UyHFpI~x zNsZQXxP=-Su_mJhWzjt3^m;4JQL`XbnZa@v8u_G2VOK9k%S@_`dEg!c6J7(9%K_!v z7|I)>dK*u2l>rDS4)?A zDVl@dZtTL21D;Nf&We_)XeT&Gu2Y*>qho4QhH|bkX1%O_5G^xt19iRCsB6riCPk3j zOi=C7=~P#Ym5n5`F=_(}z_qbbf=z0?+MLQyjpqB6M(uNNfKhy=Pq%Z9TZ9f-bLIMM zMfz>2uvAVa^`RV7)TeZNgtiy05+OPlgo_nFehtUWu^z!ug!X;dyEJa*vuq8D8nD=c zS&57Gsv3t#jm9Ex5d!#=F+_Nze*nfjvS4U9ZF(Fy54{puC1Kfv+xh5vLe{Cp={#9d z$x6av_GJ+<`ZJK9=*gv5OICTXt|zyS$w_rcweA>NzC@Kv7|nRRt4-P{*r*Z{%GcU) z{?#fXgP~pu$9@M(L@rb7*dpKAx2f`c?%OI_hekKAF=gTQeMGO+p;9PHb$S)OaYnOH z!<2%W*b+={8%zL-F1TM;NVZhhDDfINt4iGr5m^b_EzWn}^Aq_7q^cIX?h@za{Swzg)iANDHiBeYX)QHdtK zaK9Wn!HJy2o>PfkulDyIp)T#%p6JApxeF=t0qBi8kvtg^Kfaxo)ofG1s#i~dBcVRa z^Tk5qGW2xT|0jXKP zPbQQwX)29Csh%i+=zftphyI6@@o;VVBU+Y25Jzebjv}Zy z1ab`lWm65C(6lPJ8CJDPYQu^b0Ut>=>4q$wOh%{FD_D(+3*SzKKS8 zc!2*yfNpB(d|e5931kF}6tWHUHtIGcPgByZe}quE@aiJH^^lW+Xe80ToASJbz^rD8 z(xjV15zQ28qhqd6{#VKACFdF(lQNN9c}P7?H65K)R`nC)oFpe(JxTqjjOuxcVr;}$ z1bUoF5#N7m#{8#d4Cx{Lr)JE%|7SE~{_oP3AVIo*hMafEd6yj8bkonmF?ZWG1gqCZ zn(8ByKujz;1(@xKxDy&sZa4f?zEKIwg2lS~1yq9D^Ua`)fqCvO`2^g@vV4 zhmq0v^e3lRV(M_n`0`ml+aKltX1lPTVJelR)JGyS)5}=gJSMKfB2pZoYVz<_l6+9g@>8J461Ea-tb>NKPix zr2jrb&)7}2D8)zom})p2Tl5j-Yv+`D;jGr3v!HF$x;+$(RuE<>tVLE3%@uK}J}vhq2@#;9zhVTsds z<4FAM0Xo&ncF+C#pHShnu@mvb;Up3#J*9a#fOei?X%@yw<-inN+VB6C}M2Gw8`g)gQzKA)LK*RbM3-(UzzgWn_XPbk8Addt}1V&$LTqX1rZ zMd^*#TfDEuO27A!AMcJWqc?`!Ldq|-6P7~(KOko*1rSIr2UQ{Cw;r!xE|)gu<@myW z3jGc_yU95~&bKM_V|sg%oZlpeMtW>ysA`=CVu!XF+tPr@atcnJBi%-y&r=RrHnjBL zqEOy`|82YlMwp5VhKbccVbX(lzGT(fPKh>>^Bg%0DuPlVpJJOQk^{6~9$bTakXKh# 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 str: """ 保存聊天消息 @@ -118,6 +122,8 @@ class ESClient: message: 消息内容 plan: 执行计划(可选) steps: 执行步骤(可选) + session_title: 会话标题(可选,通常在首条消息时设置) + is_first_message: 是否是会话首条消息 Returns: 文档ID @@ -136,6 +142,8 @@ class ESClient: "message_embedding": embedding if embedding else None, "plan": plan, "steps": steps, + "session_title": session_title, + "is_first_message": is_first_message, "timestamp": datetime.now(), "created_at": datetime.now(), } @@ -157,10 +165,10 @@ class ESClient: limit: 返回数量 Returns: - 会话列表,每个会话包含:session_id, last_message, last_timestamp + 会话列表,每个会话包含:session_id, title, last_message, last_timestamp """ try: - # 聚合查询:按 session_id 分组,获取每个会话的最后一条消息 + # 聚合查询:按 session_id 分组,获取每个会话的最后一条消息和标题 query = { "query": { "term": {"user_id": user_id} @@ -180,7 +188,15 @@ class ESClient: "top_hits": { "size": 1, "sort": [{"timestamp": {"order": "desc"}}], - "_source": ["message", "timestamp", "message_type"] + "_source": ["message", "timestamp", "message_type", "session_title"] + } + }, + # 获取首条消息(包含标题) + "first_message": { + "top_hits": { + "size": 1, + "sort": [{"timestamp": {"order": "asc"}}], + "_source": ["session_title", "message"] } } } @@ -193,11 +209,21 @@ class ESClient: sessions = [] for bucket in result["aggregations"]["sessions"]["buckets"]: - session_data = bucket["last_message_content"]["hits"]["hits"][0]["_source"] + last_msg = bucket["last_message_content"]["hits"]["hits"][0]["_source"] + first_msg = bucket["first_message"]["hits"]["hits"][0]["_source"] + + # 优先使用 session_title,否则使用首条消息的前30字符 + title = ( + last_msg.get("session_title") or + first_msg.get("session_title") or + first_msg.get("message", "")[:30] + ) + sessions.append({ "session_id": bucket["key"], - "last_message": session_data["message"], - "last_timestamp": session_data["timestamp"], + "title": title, + "last_message": last_msg["message"], + "last_timestamp": last_msg["timestamp"], "message_count": bucket["doc_count"], }) diff --git a/mcp_server.py b/mcp_server.py index 6a3c043f..7e4e2494 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -1845,6 +1845,24 @@ class MCPAgentIntegrated: for tool in tools ]) + # 获取当前时间信息 + from datetime import datetime + now = datetime.now() + current_time_info = f"""## 当前时间 +- **日期**: {now.strftime('%Y年%m月%d日')} +- **时间**: {now.strftime('%H:%M:%S')} +- **星期**: {['周一', '周二', '周三', '周四', '周五', '周六', '周日'][now.weekday()]} +- **A股交易时间**: 上午 9:30-11:30,下午 13:00-15:00 +- **当前是否交易时段**: {'是' if (now.weekday() < 5 and ((now.hour == 9 and now.minute >= 30) or (10 <= now.hour < 11) or (now.hour == 11 and now.minute <= 30) or (13 <= now.hour < 15))) else '否'} + +**时间语义理解**: +- "今天/当天" = {now.strftime('%Y-%m-%d')} +- "最近/近期" = 最近 5-10 个交易日 +- "短线" = 5-20 个交易日 +- "中线" = 1-3 个月 +- "长线" = 6 个月以上 +""" + return f"""你是"价小前",北京价值前沿科技公司的AI投研聊天助手。 ## 你的人格特征 @@ -1854,6 +1872,8 @@ class MCPAgentIntegrated: - **性格**: 专业、严谨、友好,擅长用简洁的语言解释复杂的金融概念 - **服务宗旨**: 帮助投资者做出更明智的投资决策,提供数据驱动的研究支持 +{current_time_info} + ## 可用工具 {tools_desc} @@ -1950,15 +1970,30 @@ class MCPAgentIntegrated: 只返回JSON,不要其他内容。""" - async def create_plan(self, user_query: str, tools: List[dict]) -> ExecutionPlan: - """阶段1: 使用 Kimi 创建执行计划(带思考过程)""" + async def create_plan(self, user_query: str, tools: List[dict], chat_history: List[dict] = None) -> ExecutionPlan: + """阶段1: 使用 Kimi 创建执行计划(带思考过程和历史上下文)""" logger.info(f"[Planning] Kimi开始制定计划: {user_query}") messages = [ {"role": "system", "content": self.get_planning_prompt(tools)}, - {"role": "user", "content": user_query}, ] + # 添加会话历史(多轮对话上下文) + if chat_history: + # 限制历史消息数量,避免 context 过长 + recent_history = chat_history[-10:] # 最近10条消息 + for msg in recent_history: + role = "user" if msg.get("message_type") == "user" else "assistant" + content = msg.get("message", "") + # 截断过长的历史消息 + if len(content) > 500: + content = content[:500] + "..." + messages.append({"role": role, "content": content}) + logger.info(f"[Planning] 添加了 {len(recent_history)} 条历史消息到上下文") + + # 添加当前用户问题 + messages.append({"role": "user", "content": user_query}) + # 使用 Kimi 思考模型 response = self.kimi_client.chat.completions.create( model=self.kimi_model, @@ -2229,13 +2264,16 @@ class MCPAgentIntegrated: user_query: str, tools: List[dict], tool_handlers: Dict[str, Any], + chat_history: List[dict] = None, ) -> AgentResponse: """主流程(非流式)""" logger.info(f"[Agent] 处理查询: {user_query}") + if chat_history: + logger.info(f"[Agent] 带有 {len(chat_history)} 条历史消息") try: - # 阶段1: Kimi 制定计划 - plan = await self.create_plan(user_query, tools) + # 阶段1: Kimi 制定计划(带历史上下文) + plan = await self.create_plan(user_query, tools, chat_history) # 阶段2: 执行工具 step_results = await self.execute_plan(plan, tool_handlers) @@ -2271,6 +2309,46 @@ class MCPAgentIntegrated: message=f"处理失败: {str(e)}", ) + async def generate_session_title(self, user_message: str, assistant_response: str) -> str: + """生成会话标题(简短概述),使用 DeepMoney 模型""" + try: + messages = [ + { + "role": "system", + "content": "你是一个标题生成器。根据用户问题和AI回复,生成一个简短的会话标题(10-20个字)。只返回标题文本,不要任何其他内容。" + }, + { + "role": "user", + "content": f"用户问题:{user_message[:200]}\n\nAI回复:{assistant_response[:500]}\n\n请生成一个简短的会话标题:" + } + ] + + # 使用 DeepMoney 模型(更轻量,适合简单任务) + response = self.deepmoney_client.chat.completions.create( + model=self.deepmoney_model, + messages=messages, + temperature=0.3, + max_tokens=100, + ) + + title = response.choices[0].message.content.strip() + + # 处理 DeepMoney 的 ... 标签,只保留 之后的内容 + if "" in title: + title = title.split("")[-1].strip() + + # 清理可能的引号 + title = title.strip('"\'') + # 限制长度 + if len(title) > 30: + title = title[:27] + "..." + return title + + except Exception as e: + logger.error(f"[Title] 生成标题失败: {e}") + # 降级:使用用户消息的前20个字符 + return user_message[:20] + "..." if len(user_message) > 20 else user_message + async def process_query_stream( self, user_query: str, @@ -2282,9 +2360,15 @@ class MCPAgentIntegrated: user_avatar: str = None, cookies: dict = None, model_config: dict = None, # 新增:动态模型配置 + chat_history: List[dict] = None, # 新增:历史对话记录 + is_new_session: bool = False, # 新增:是否是新会话(用于生成标题) ) -> AsyncGenerator[str, None]: """主流程(流式输出)- 逐步返回执行结果""" logger.info(f"[Agent Stream] 处理查询: {user_query}") + if chat_history: + logger.info(f"[Agent Stream] 带有 {len(chat_history)} 条历史消息") + if is_new_session: + logger.info(f"[Agent Stream] 这是新会话,将在完成后生成标题") # 将 cookies 存储为实例属性,供工具调用时使用 self.cookies = cookies or {} @@ -2309,11 +2393,26 @@ class MCPAgentIntegrated: # 阶段1: 使用选中的模型制定计划(流式,带 DeepMoney 备选) yield self._format_sse("status", {"stage": "planning", "message": "正在制定执行计划..."}) + # 构建消息列表(包含历史对话上下文) messages = [ {"role": "system", "content": self.get_planning_prompt(tools)}, - {"role": "user", "content": user_query}, ] + # 添加历史对话(最近 10 轮,避免上下文过长) + if chat_history: + recent_history = chat_history[-10:] # 最近 10 条消息 + for msg in recent_history: + role = "user" if msg.get("message_type") == "user" else "assistant" + content = msg.get("message", "") + # 截断过长的历史消息 + if len(content) > 500: + content = content[:500] + "..." + messages.append({"role": role, "content": content}) + logger.info(f"[Agent Stream] 已添加 {len(recent_history)} 条历史消息到上下文") + + # 添加当前用户查询 + messages.append({"role": "user", "content": user_query}) + reasoning_content = "" plan_content = "" use_fallback = False @@ -2650,6 +2749,17 @@ class MCPAgentIntegrated: "steps": [{"tool": step.tool, "arguments": step.arguments, "reason": step.reason} for step in plan.steps] }, ensure_ascii=False) + # 如果是新会话,生成会话标题 + session_title = None + if is_new_session: + try: + session_title = await self.generate_session_title(user_query, final_summary) + logger.info(f"[Title] 新会话标题: {session_title}") + except Exception as title_error: + logger.error(f"[Title] 生成标题失败: {title_error}") + # 降级:使用用户消息的前 20 个字符 + session_title = user_query[:20] + "..." if len(user_query) > 20 else user_query + es_client.save_chat_message( session_id=session_id, user_id=user_id, @@ -2659,8 +2769,13 @@ class MCPAgentIntegrated: message=final_summary, plan=plan_json, steps=steps_json, + session_title=session_title, # 新会话时保存标题 ) logger.info(f"[ES] Agent 回复已保存到会话 {session_id}") + + # 如果生成了标题,通过 SSE 发送给前端 + if session_title: + yield self._format_sse("session_title", {"title": session_title}) except Exception as e: logger.error(f"[ES] 保存 Agent 回复失败: {e}", exc_info=True) @@ -2751,6 +2866,17 @@ async def agent_chat(request: AgentChatRequest): # ==================== 会话管理 ==================== # 如果没有提供 session_id,创建新会话 session_id = request.session_id or str(uuid.uuid4()) + is_new_session = not request.session_id + + # 获取会话历史(用于多轮对话) + chat_history = [] + if not is_new_session: + try: + history = es_client.get_chat_history(session_id, limit=20) # 最近20条 + chat_history = history + logger.info(f"加载会话历史: {len(chat_history)} 条消息") + except Exception as e: + logger.error(f"获取会话历史失败: {e}") # 保存用户消息到 ES try: @@ -2761,6 +2887,7 @@ async def agent_chat(request: AgentChatRequest): user_avatar=request.user_avatar or "", message_type="user", message=request.message, + is_first_message=is_new_session, ) except Exception as e: logger.error(f"保存用户消息失败: {e}") @@ -2796,14 +2923,16 @@ async def agent_chat(request: AgentChatRequest): } }) - # 处理查询 + # 处理查询(传入会话历史实现多轮对话) response = await agent.process_query( user_query=request.message, tools=tools, tool_handlers=TOOL_HANDLERS, + chat_history=chat_history, ) # 保存 Agent 回复到 ES + session_title = None try: # 将执行步骤转换为JSON字符串 steps_json = json.dumps( @@ -2814,6 +2943,11 @@ async def agent_chat(request: AgentChatRequest): # 将 plan 转换为 JSON 字符串(ES 中 plan 字段是 text 类型) plan_json = json.dumps(response.plan.dict(), ensure_ascii=False) if response.plan else None + # 如果是新会话,生成会话标题 + if is_new_session: + session_title = await agent.generate_session_title(request.message, response.final_summary) + logger.info(f"生成会话标题: {session_title}") + es_client.save_chat_message( session_id=session_id, user_id=request.user_id or "anonymous", @@ -2823,13 +2957,15 @@ async def agent_chat(request: AgentChatRequest): message=response.final_summary, # 使用 final_summary 而不是 final_answer plan=plan_json, # 传递 JSON 字符串而不是字典 steps=steps_json, + session_title=session_title, ) except Exception as e: logger.error(f"保存 Agent 回复失败: {e}", exc_info=True) - # 在响应中返回 session_id + # 在响应中返回 session_id 和 title response_dict = response.dict() response_dict["session_id"] = session_id + response_dict["session_title"] = session_title return response_dict @app.post("/agent/chat/stream") @@ -2872,9 +3008,21 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request): f"subscription_type: {user_subscription}" ) - # 如果没有提供 session_id,创建新会话 + # 判断是否是新会话 + is_new_session = not chat_request.session_id session_id = chat_request.session_id or str(uuid.uuid4()) + # ==================== 加载历史对话(多轮对话记忆)==================== + chat_history = [] + if not is_new_session: + try: + # 加载该会话的历史消息(最近 20 条) + history = es_client.get_chat_history(session_id, limit=20) + chat_history = history + logger.info(f"[Stream] 已加载 {len(chat_history)} 条历史消息") + except Exception as e: + logger.error(f"[Stream] 加载历史消息失败: {e}") + # 保存用户消息到 ES try: es_client.save_chat_message( @@ -2884,6 +3032,7 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request): user_avatar=chat_request.user_avatar or "", message_type="user", message=chat_request.message, + is_first_message=is_new_session, # 标记是否为首条消息 ) logger.info(f"[ES] 用户消息已保存到会话 {session_id}") except Exception as e: @@ -2940,6 +3089,8 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request): user_avatar=chat_request.user_avatar, cookies=cookies, # 传递 cookies 用于认证 API 调用 model_config=model_config, # 传递选中的模型配置 + chat_history=chat_history, # 传递历史对话(多轮对话记忆) + is_new_session=is_new_session, # 传递是否是新会话(用于生成标题) ), media_type="text/event-stream", headers={ diff --git a/src/views/AgentChat/components/LeftSidebar/SessionCard.js b/src/views/AgentChat/components/LeftSidebar/SessionCard.js index 63a4ef23..4e3f4d6c 100644 --- a/src/views/AgentChat/components/LeftSidebar/SessionCard.js +++ b/src/views/AgentChat/components/LeftSidebar/SessionCard.js @@ -40,16 +40,22 @@ const SessionCard = ({ session, isActive, onPress }) => { - - {session.title || '新对话'} + + {session.title || session.last_message?.substring(0, 30) || '新对话'} - {new Date(session.created_at || session.timestamp).toLocaleString('zh-CN', { - month: 'numeric', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - })} + {(() => { + const dateStr = session.created_at || session.last_timestamp || session.timestamp; + if (!dateStr) return '刚刚'; + const date = new Date(dateStr); + if (isNaN(date.getTime())) return '刚刚'; + return date.toLocaleString('zh-CN', { + month: 'numeric', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); + })()} {session.message_count && (