From 9bf1dd1210623406e9de1050c528851905fbe20a Mon Sep 17 00:00:00 2001 From: python Date: Tue, 9 Dec 2025 15:01:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96AI=E6=9C=8D=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E6=8F=90=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=AF=B9API=E8=BF=94=E5=9B=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=9A=84=E5=A4=84=E7=90=86=E8=83=BD=E5=8A=9B=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E8=BF=9BJSON=E8=A7=A3=E6=9E=90=E5=92=8C=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E5=9C=A8=E6=8F=90=E5=8F=96=E6=95=B0=E6=8D=AE=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=97=B6=E8=83=BD=E5=A4=9F=E8=BF=94=E5=9B=9E=E7=A9=BA?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=80=8C=E4=B8=8D=E6=8A=9B=E5=87=BA=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=EF=BC=8C=E5=90=8C=E6=97=B6=E8=AE=B0=E5=BD=95=E8=AF=A6?= =?UTF-8?q?=E7=BB=86=E7=9A=84=E8=B0=83=E8=AF=95=E4=BF=A1=E6=81=AF=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E9=AB=98=E5=AE=B9=E9=94=99=E6=80=A7=E5=92=8C=E5=8F=AF?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/ai_service.cpython-312.pyc | Bin 6470 -> 50804 bytes services/ai_service.py | 303 +++++++++--------- 2 files changed, 159 insertions(+), 144 deletions(-) diff --git a/services/__pycache__/ai_service.cpython-312.pyc b/services/__pycache__/ai_service.cpython-312.pyc index 0f0bdb9bfd6d1e67a134b34b3cf7c0d04d9fd1c2..5df4db390be915150283d2f312dcb865c2e2c708 100644 GIT binary patch literal 50804 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%0Ab1#&j9M%TAQR2>$}4I8y>ce zYQ6?)h^aVTe;L@oY(=8RQ@1Q^qeA-@#9Ju7fhLhiV!K187uM*-0mK;by2eh(=(cv) z=#9-c4%&E8!~9o&b6b$VD_=IV+;Va}M^npQ6d|zW&0xuo8cn_C7i=TOX}I~(W1QX- ziCsa0>DZ>Dn|izYD?P@Fo=6;_iz!EvtZo>H&+sHo_ZSO%B4LjnLW7}-m}CgpM(6dY zX)=g{n%@GK0|v4636Q{5D|95H6>DHzU+#W@Jn7p2P{J8Dom>3n-^LDD3i!T~7;Df} zo`sPM+Ym&2kp>}1Df$AZJWgrs(|89VQUmlT52u5~P&z|yb2k{i3Z$J$--c4LP61Uk zr8T7U5~`4f2z?2YSG|DMOCj8Kn4dx=D3((r^^2W8SfM58(D_!F&1%(R=mGQPDd( zcy9FEIhJ7b!yjCGXW)-i0*qlI!5^vUu|sEtH&zke{H6Qj{1u3ALLL4$e>MI$e@)^W ztBjR-?da>HA3Z|=(cv6w2m}x;Xxp{d9}{c$zxgAszIs#^fj6!hW0@mOMWM~l4N|!x zbiy0u9eehmJVJP55rdD*BZN00$0zcLXW1J?jDC1Z7U6%Ri0dysC5!OCQN;Lb4E=tn zgf})(S=F*RYAsTVlM;QQP~j2@B8dJoB}(Wpv^hw5fZlu&^n#l{n)Y!vq`zTi!li$T zuIaLwNhGu|lp9)`yVwQUlZdU~(d^U)IE2|Lk>;Fuqie?t(?{a7-SOF;_}rebFG9mo zam8@bmj*>jA*}7kn0t3XeESOSyIpa7MSn8gcN^i=r}RGb!hGCR+&8Un4sR|TF_*c` zWghcP*CJn{*_)pEN+b;HrxZez+CMD?pOIXE=G&N&g3Uf1)H&?2tKSY0S*v8S4)YhS zyz==AR&k_RMsvrr%r! zBjIW8aNI0A5CSRK;qaMXLNr)5IB}=L#n?=U%3{DlR909ACrP_XrQ;GTqugAdmFp={4m-$RCu zx_|`Otq!V-4Q@w-lrPc=vqFp;BePpCJ8yucLRi6%#F83h8J5UfTz7^gTSL;Zhaa_v zN&Y8f{-RZp)>8D_In#{+Y zf?2zO#EP(4CM_stG}@=EWYnEc87WxRR11cYY2-25&Z-mWxYZQEG{&})kJQG5qDpB< z{tB-wpQU{A<-By6skna#LymPs#^|PmKcTScZ4nE284E{$)-~%V?nBut~jF0 z>f75|9Zh*Oa6dyrnCJ{wkQL$^m7{w19!~{LhGA&#UGI*`xPsf8>yFp;?ee6QvWc@# z_56Y>mh3YTry}~Z`uBS*b6j;^++x1)`R~B|>OjV+J-u3Qc0S!+-)|pS?#aFfLi5Sl z{i;_MT+T1(uX!t?w+>g*_ie##zLvQomTI@9+GByZhPQAAjJI}OG)=owNM>A_&sqR` zwv`tRxmR-XU>UZHzLQeYOe$BzCiFxxim~3B;tRJiZ8@9G7iqKYX|s2>G{LX4*&ga_ zXkqGjHeZ;{W^Ze>*|;pAK$t8bJgDHXtSC6*`*Ae*xI%IsAZI!`&E%XU=K?t&kwZ4< zxbKtmIyrQnz|mO%cbZ-q5l@6SM{68MlM6@P!_jIX(71H|!qJM$9I}GvFoH+=gyK?& z@r#gTP3AGm78VsFx$?A~@enG&&xQG5 zR57KBEI^N`0RN@}rCah0##<%<@K*hHcU>{1%W}-l@+kv6*PrMvY8uwQu_n$KP8PI1 zD~;Q!I48{-4A9&UE}|E4SVtGWuAmkTO*cAop>%)@Hp;bV`qR7h8fGro~p>-aw7b0uq$MMyS%pM7r zKWS7r=_6snfA8ctocB`Mw^u(~{Uk)PYJ3;Mo-ybc+ka!b9Qzs=Z0`gH9)Vkwa6eh| kWV7;DdmPcdcgo^}Qymr`hpH(v7?Lc92df1$n3Rxz05!;PIRF3v delta 2303 zcmZ`)ZEzFE8QzobPG3F?ED3)I+oJLZhvN_8l)*GH80^MQsBPjBQ{$Na<2ir!>T*EvC#k%rKoAhEV8CJI?e+WxE}_m&}xzB026Kl!&%7{nPHA zaO*PDow;Ys-5tIc+rFV~TyOkZS8Ln;L}?Jhr~={9TEA4^O5`3YzuZ{&bZy&c zG*LIKs!?TEeZ4;_)hSX)3doVckbJBz5UC&8ULP3ttMvgnjNjrd_yF&0)biU8w)D1j ztSu>8G=r>Y{18-OWJGhU3*jRneMey&XB^HA_Vtb!3gLvQ3MWk!hpsXjtH98pLUZBy z+e-xZ?dm_MK&14Cq$ARo`&;EeEDR&6(jSB){ew~@K3bceyp=w6W#vcbR(||J`oilE zZvJfL{H657_xj1a5h(zbe%x-Z8P6xtlp_R=gHOGcSDL1plC^smz56EEKbSqAZt-3z zo+?h(G$;4?7PkygW7%?f7wtDc;z32#lM~83m!wH4xpn7aSrZ-Q&hTKfXUV-|-o0bd zy>o*7qq!(cRs$N3pc+FsYAIy8@r>2YAtug@;m^P1G9qCN?+LoB#A1jWMKv?K zd6ylYWntrzP`MyfPFE~#dv0OdbIT6*eMd1W!LL{wn7@+keFy(uD73Igx6(z%06urw ziQTqhh9mY(_(hwY;cpzaam=A|5 z+#$C0Bz(-w^u&cH9%61fDkZ^C8NQuY>0ptID4L~hGytQjBuBJj-03Rbt~q^hR7L)P z>N_T>!@gWKtyroGUQAo3(O_@hzsZv1r1^+yyrBI109*CQG=mTH=582W>R&YDS;!cdN> zt8*k=3?Fc~8WKiNtw%EjhGj}xM5~%+3PVNl4?$Ezw1jD3V1R~MrL`%6FF5*|&;}yv zl+erweme#gRngd}tf*)s4PaALzZz3~0hugI^PsUR&0r($aSk;6Z`L$Z)E^JY{vdjW z4kYoEs2ousWv~(psi+43#VM9)3{>*S0FvMTv>f#+h!yX3RSG+apxDU?j)M$)Lv{5T zo}xEGzbqx`J3xJ0?no(R)=J;2jA)4MAZ|Rlxq3WTno~3Ht(?713UusQ;&_5L>9apg zz5PM@-B$?(*9KDRJ^1KXE9a-z7k@%Mk_Mf824<{N*%Gs%o=)c2?Sm|d&% zlp15z0C~52=#+D;Dp04c`U0^WMCPZoN~?$5p?s8>jy=^gIP81(pK6CB#vv zra&)MPZ4S*(p8W8Ip@6>bTQd;IN3ii-{a3xM`o|V z$Q@#)8#93Rv+CW}n~9t}H=OJ}{Bb0!J2KXCZqu|Q14uXf^}7`}`*d-x^%1q?PBBh5 zFIv#INQVRpU!l-QK;zYTREiAc(xE3t&xwBK03D@MfaXCnKO2^Vu@G!Vkf`*|m1+VR oBf~JC8;lH_H3P=_39x?xY<~vT^Pu`4{7VLA(-&4iJ3Z3B0n_uvumAu6 diff --git a/services/ai_service.py b/services/ai_service.py index b41e0f9..6cc2ca0 100644 --- a/services/ai_service.py +++ b/services/ai_service.py @@ -309,165 +309,179 @@ class AIService: raise Exception(error_message) result = response.json() - - # 提取AI返回的内容 - if 'choices' in result and len(result['choices']) > 0: - raw_content = result['choices'][0]['message']['content'] - # 调试:打印原始返回内容(前500字符) - print(f"[AI服务] API返回的原始内容(前500字符): {raw_content[:500]}") - - # 处理思考过程标签(支持多种可能的标签格式) - content = raw_content - - # 处理 标签(DeepSeek-R1常用格式) - if '' in content: - parts = content.split('') - if len(parts) > 1: - content = parts[-1].strip() - print(f"[AI服务] 检测到 标签,提取标签后的内容") - - # 处理 标签 - elif '' in content: - parts = content.split('') - if len(parts) > 1: - content = parts[-1].strip() - print(f"[AI服务] 检测到 标签,提取标签后的内容") - - # 处理 ... 标签 - elif '' in content and '' in content: - reasoning_start = content.find('') - if reasoning_start != -1: - content = content[reasoning_start + 11:].strip() - print(f"[AI服务] 检测到 标签,提取标签后的内容") - - # 清理后的内容(前500字符) - print(f"[AI服务] 清理后的内容(前500字符): {content[:500]}") - - # 尝试解析JSON(使用增强的修复机制) - extracted_data = self._extract_json_from_text(content) - if extracted_data: - print(f"[AI服务] JSON解析成功,提取到 {len(extracted_data)} 个字段") - print(f"[AI服务] 原始字段名: {list(extracted_data.keys())}") - # 规范化字段名并映射到正确的字段编码 - normalized_data = self._normalize_field_names(extracted_data, output_fields) - print(f"[AI服务] 规范化后的字段名: {list(normalized_data.keys())}") - # 打印关键字段的值用于调试 - for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth']: - if key in normalized_data: - print(f"[AI服务] 规范化后 {key} = '{normalized_data[key]}'") - # 规范化日期格式 - normalized_data = self._normalize_date_formats(normalized_data, output_fields) - # 再次打印关键字段的值用于调试 - for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth']: - if key in normalized_data: - print(f"[AI服务] 日期格式化后 {key} = '{normalized_data[key]}'") - # 后处理:从已有信息推断缺失字段 - normalized_data = self._post_process_inferred_fields(normalized_data, output_fields) - # 打印后处理后的关键字段 - for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth', 'target_organization', 'target_position']: - if key in normalized_data: - print(f"[AI服务] 后处理后 {key} = '{normalized_data[key]}'") - # 即使提取的字段不完整,也返回结果(更宽容的处理) - if any(v for v in normalized_data.values() if v): # 至少有一个非空字段 - print(f"[AI服务] 返回提取的数据(包含 {sum(1 for v in normalized_data.values() if v)} 个非空字段)") - # 记录成功的对话 + # 提取AI返回的内容 + if 'choices' in result and len(result['choices']) > 0: + raw_content = result['choices'][0]['message']['content'] + + # 调试:打印原始返回内容(前500字符) + print(f"[AI服务] API返回的原始内容(前500字符): {raw_content[:500]}") + + # 处理思考过程标签(支持多种可能的标签格式) + content = raw_content + + # 处理 标签(DeepSeek-R1常用格式) + if '' in content: + parts = content.split('') + if len(parts) > 1: + content = parts[-1].strip() + print(f"[AI服务] 检测到 标签,提取标签后的内容") + + # 处理 标签 + elif '' in content: + parts = content.split('') + if len(parts) > 1: + content = parts[-1].strip() + print(f"[AI服务] 检测到 标签,提取标签后的内容") + + # 处理 ... 标签 + elif '' in content and '' in content: + reasoning_start = content.find('') + if reasoning_start != -1: + content = content[reasoning_start + 11:].strip() + print(f"[AI服务] 检测到 标签,提取标签后的内容") + + # 清理后的内容(前500字符) + print(f"[AI服务] 清理后的内容(前500字符): {content[:500]}") + + # 尝试解析JSON(使用增强的修复机制) + extracted_data = self._extract_json_from_text(content) + if extracted_data: + print(f"[AI服务] JSON解析成功,提取到 {len(extracted_data)} 个字段") + print(f"[AI服务] 原始字段名: {list(extracted_data.keys())}") + # 规范化字段名并映射到正确的字段编码 + normalized_data = self._normalize_field_names(extracted_data, output_fields) + print(f"[AI服务] 规范化后的字段名: {list(normalized_data.keys())}") + # 打印关键字段的值用于调试 + for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth']: + if key in normalized_data: + print(f"[AI服务] 规范化后 {key} = '{normalized_data[key]}'") + # 规范化日期格式 + normalized_data = self._normalize_date_formats(normalized_data, output_fields) + # 再次打印关键字段的值用于调试 + for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth']: + if key in normalized_data: + print(f"[AI服务] 日期格式化后 {key} = '{normalized_data[key]}'") + # 后处理:从已有信息推断缺失字段 + normalized_data = self._post_process_inferred_fields(normalized_data, output_fields) + # 打印后处理后的关键字段 + for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth', 'target_organization', 'target_position']: + if key in normalized_data: + print(f"[AI服务] 后处理后 {key} = '{normalized_data[key]}'") + # 即使提取的字段不完整,也返回结果(更宽容的处理) + if any(v for v in normalized_data.values() if v): # 至少有一个非空字段 + print(f"[AI服务] 返回提取的数据(包含 {sum(1 for v in normalized_data.values() if v)} 个非空字段)") + # 记录成功的对话 + if self.ai_logger: + self.ai_logger.log_conversation( + prompt=prompt, + api_request=api_request_info, + api_response=result, + extracted_data=normalized_data, + error=None, + session_id=session_id + ) + return normalized_data + else: + print(f"[AI服务] 警告:提取的数据全部为空,但继续返回(允许部分字段为空)") + # 记录对话(即使数据为空) + if self.ai_logger: + self.ai_logger.log_conversation( + prompt=prompt, + api_request=api_request_info, + api_response=result, + extracted_data=normalized_data, + error="提取的数据全部为空", + session_id=session_id + ) + return normalized_data + + # 如果无法提取JSON,记录错误但尝试更宽容的处理 + print(f"[AI服务] 警告:无法从内容中提取完整JSON,尝试备用解析方法") + print(f"[AI服务] 清理后的内容(前500字符): {content[:500]}") + + # 尝试从文本中提取 + parsed_data = self._parse_text_response(content, output_fields) + if parsed_data and any(v for v in parsed_data.values() if v): # 至少有一个非空字段 + print(f"[AI服务] 使用备用方法解析成功,提取到 {len(parsed_data)} 个字段") + # 记录对话 if self.ai_logger: self.ai_logger.log_conversation( prompt=prompt, api_request=api_request_info, api_response=result, - extracted_data=normalized_data, + extracted_data=parsed_data, error=None, session_id=session_id ) - return normalized_data - else: - print(f"[AI服务] 警告:提取的数据全部为空,但继续返回(允许部分字段为空)") - # 记录对话(即使数据为空) - if self.ai_logger: - self.ai_logger.log_conversation( - prompt=prompt, - api_request=api_request_info, - api_response=result, - extracted_data=normalized_data, - error="提取的数据全部为空", - session_id=session_id - ) - return normalized_data - - # 如果无法提取JSON,记录错误但尝试更宽容的处理 - print(f"[AI服务] 警告:无法从内容中提取完整JSON,尝试备用解析方法") - print(f"[AI服务] 清理后的内容(前500字符): {content[:500]}") - - # 尝试从文本中提取 - parsed_data = self._parse_text_response(content, output_fields) - if parsed_data and any(v for v in parsed_data.values() if v): # 至少有一个非空字段 - print(f"[AI服务] 使用备用方法解析成功,提取到 {len(parsed_data)} 个字段") - # 记录对话 + return parsed_data + + # 如果所有方法都失败,尝试最后一次修复尝试 + print(f"[AI服务] 所有解析方法都失败,尝试最后一次修复...") + # 尝试使用jsonrepair(如果可用)进行最后修复 + if JSONREPAIR_AVAILABLE: + try: + repaired_content = repair_json(content) + if repaired_content: + try: + extracted_data = json.loads(repaired_content) + if extracted_data and isinstance(extracted_data, dict): + print(f"[AI服务] 使用jsonrepair最后修复成功,提取到 {len(extracted_data)} 个字段") + normalized_data = self._normalize_field_names(extracted_data, output_fields) + normalized_data = self._normalize_date_formats(normalized_data, output_fields) + normalized_data = self._post_process_inferred_fields(normalized_data, output_fields) + # 记录对话 + if self.ai_logger: + self.ai_logger.log_conversation( + prompt=prompt, + api_request=api_request_info, + api_response=result, + extracted_data=normalized_data, + error=None, + session_id=session_id + ) + return normalized_data + except json.JSONDecodeError: + pass + except Exception as e: + print(f"[AI服务] jsonrepair最后修复也失败: {e}") + + # 如果所有方法都失败,返回空字典而不是抛出异常(更宽容) + # 这样至少不会导致整个调用失败,前端可以显示部分结果 + error_msg = f"无法从API返回内容中提取JSON数据。原始内容长度: {len(raw_content)}, 清理后内容长度: {len(content)}" + print(f"[AI服务] 警告:{error_msg}") + print(f"[AI服务] 完整内容: {content}") + # 返回一个包含所有输出字段的空字典,而不是抛出异常 + empty_result = {field['field_code']: '' for field in output_fields} + print(f"[AI服务] 返回空结果(包含 {len(empty_result)} 个字段,全部为空)") + # 记录失败的对话 if self.ai_logger: self.ai_logger.log_conversation( prompt=prompt, api_request=api_request_info, api_response=result, - extracted_data=parsed_data, - error=None, + extracted_data=empty_result, + error=error_msg, session_id=session_id ) - return parsed_data - - # 如果所有方法都失败,尝试最后一次修复尝试 - print(f"[AI服务] 所有解析方法都失败,尝试最后一次修复...") - # 尝试使用jsonrepair(如果可用)进行最后修复 - if JSONREPAIR_AVAILABLE: - try: - repaired_content = repair_json(content) - if repaired_content: - try: - extracted_data = json.loads(repaired_content) - if extracted_data and isinstance(extracted_data, dict): - print(f"[AI服务] 使用jsonrepair最后修复成功,提取到 {len(extracted_data)} 个字段") - normalized_data = self._normalize_field_names(extracted_data, output_fields) - normalized_data = self._normalize_date_formats(normalized_data, output_fields) - normalized_data = self._post_process_inferred_fields(normalized_data, output_fields) - # 记录对话 - if self.ai_logger: - self.ai_logger.log_conversation( - prompt=prompt, - api_request=api_request_info, - api_response=result, - extracted_data=normalized_data, - error=None, - session_id=session_id - ) - return normalized_data - except json.JSONDecodeError: - pass - except Exception as e: - print(f"[AI服务] jsonrepair最后修复也失败: {e}") - - # 如果所有方法都失败,返回空字典而不是抛出异常(更宽容) - # 这样至少不会导致整个调用失败,前端可以显示部分结果 - error_msg = f"无法从API返回内容中提取JSON数据。原始内容长度: {len(raw_content)}, 清理后内容长度: {len(content)}" - print(f"[AI服务] 警告:{error_msg}") - print(f"[AI服务] 完整内容: {content}") - # 返回一个包含所有输出字段的空字典,而不是抛出异常 - empty_result = {field['field_code']: '' for field in output_fields} - print(f"[AI服务] 返回空结果(包含 {len(empty_result)} 个字段,全部为空)") - # 记录失败的对话 - if self.ai_logger: - self.ai_logger.log_conversation( - prompt=prompt, - api_request=api_request_info, - api_response=result, - extracted_data=empty_result, - error=error_msg, - session_id=session_id - ) - return empty_result - else: - error_msg = "API返回格式异常:未找到choices字段或choices为空" - # 记录错误 + return empty_result + else: + error_msg = "API返回格式异常:未找到choices字段或choices为空" + # 记录错误 + if self.ai_logger: + self.ai_logger.log_conversation( + prompt=prompt, + api_request=api_request_info, + api_response=result, + extracted_data=None, + error=error_msg, + session_id=session_id + ) + raise Exception(error_msg) + + except Exception as e: + # 如果发生异常,记录错误日志 + error_msg = str(e) if self.ai_logger: self.ai_logger.log_conversation( prompt=prompt, @@ -477,7 +491,8 @@ class AIService: error=error_msg, session_id=session_id ) - raise Exception(error_msg) + # 重新抛出异常,让上层处理 + raise def _extract_json_from_text(self, text: str) -> Optional[Dict]: """