From 4e5b1c31e35828cf74e0d1ae9f6acb077866428f Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Wed, 23 Apr 2025 20:32:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=BA=86=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E9=97=AE=E9=A2=98=EF=BC=8C=E7=9B=AE=E5=89=8D=E4=BB=A5?= =?UTF-8?q?http=E8=BF=94=E5=9B=9E=E5=92=8C=E6=9C=80=E5=90=8E=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=B5=81=E5=BC=8F=E8=BE=93=E5=87=BA=E4=B8=BA=E6=A0=87?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/__pycache__/ai_agent.cpython-312.pyc | Bin 30059 -> 37417 bytes core/__pycache__/contentGen.cpython-312.pyc | Bin 22965 -> 26182 bytes core/ai_agent.py | 106 ++++--- examples/demo_robust_streaming.py | 315 ++++++++++++++++++++ 4 files changed, 381 insertions(+), 40 deletions(-) create mode 100644 examples/demo_robust_streaming.py diff --git a/core/__pycache__/ai_agent.cpython-312.pyc b/core/__pycache__/ai_agent.cpython-312.pyc index ce80f7e0f6fd7323440b09b1bceb1605f14605c8..30058014e44352d714e96e9e66fcc9780ed98920 100644 GIT binary patch literal 37417 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 Z8z;#**?EV8+aFRlB}m|lgXnU3{$HK%2MYiI delta 9638 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 delta 1403 zcma)6TTmNS7~ZqV=2mhMf;J&Zfd|`aClIk^P|Hj!mcapP+Zjh3J1Q(48CxC-9cm{L zD1-!>n;xJokS4UL3Zx-+3ntuBwaPeZN5>Z*97e}IyNP{h?1TE~*{pK0FYd#c7eYA4jC=6gK>hE z2H~d$6QJOl!SJ}uPGU%&yHEt>4q3aBR&K#4hs+L3w1x0hOQBqqE8GSfjqjA@*%h?< z9G1ld8oLyq+8{Lp1S13i2-@t{oB;GA{YrJg?VWO5sK;6atsURpW>}C_wYyz^0F@CI{ z8+CD$ZWQU~0|OXDvHAN;9u7J)BPn*#hj00rFq^o+`2)y5ikwVlIL4+Xx!zfnoacu_ zoI8L53A8lAE-VOaVOQqrCG@_BkM-~&{FXnRivQYkURAv5Y2tlxH1Uz3<9;tQ(Ubn5 zmt78X;UwpcVI6j5T=a#N(SQ@hlDJZoIuj%b0=wttKH-M0ue;AUz5H?v%fQ>^#%^xb zk4r=hd7LOPbAM@UivgRp_|%(i?XP#Xb)Ia+muzBDG%g5<#wP~R(75n-x)-??xXZKr z`6NG>#OD^Kr>w=Dt!FzsawHMtzs|>A$Tg0qIuR8*ob_XrO?cDu&UDIU+ly{o6yV%o z)@jp&H#zqxmq_rwDRvoVraeL@Fkmwjh9oeGp5SHbVCJET^tRR!0|jPToX zbvL_+y$z%BD=!{9+{lf@aSm7zoBk)9#YJ{`By%;0BBSXKuVq|Ac+~tP-h-eR%jV#P zeVT}-`<788gj@r-4Eep>rLee3!Wy~bLjAuJ#jQ(($DvETKCJ)LAOutaCSHcfsHHJ)K8Zs#OuE7PtO?pG^*KN(GwIy*Ssinv z@@2C6nFHop3;Fr>15`}~d8a~IYoP8tcd(#VPpxVxe6y;jFtlor2)`}Lx@u~5=fS+X zD(b5$s;-(rRTK#tn0wFK self.stream_chunk_timeout: + logging.warning(f"Stream chunk timeout: No new chunk received for {self.stream_chunk_timeout} seconds after previous chunk.") + if response_text: + response_text += "\n\n[注意: 流式传输中断,内容可能不完整]" + partial_response = response_text + return partial_response + raise Timeout(f"Stream stalled: No new chunk received for {self.stream_chunk_timeout} seconds after previous chunk.") + + # 检查首次响应超时:如果很长时间没收到第一个块 + if not received_any_chunk and current_time - stream_start_time > self.timeout: + logging.warning(f"Initial response timeout: No chunk received in {self.timeout} seconds since stream started.") + raise Timeout(f"No initial response received for {self.timeout} seconds.") + + # 检查全局超时(保留作为安全措施) if current_time - stream_start_time > global_timeout: logging.warning(f"Global timeout reached after {global_timeout} seconds.") if response_text: @@ -290,26 +306,18 @@ class AI_Agent(): logging.error("Global timeout with no content received.") raise Timeout(f"Global timeout after {global_timeout} seconds with no content.") - # 检查流块超时 - if current_time - last_chunk_time > self.stream_chunk_timeout: - logging.warning(f"Stream chunk timeout: No chunk received for {self.stream_chunk_timeout} seconds.") - if response_text: - response_text += "\n\n[注意: 由于流式传输超时,内容可能不完整]" - partial_response = response_text - return partial_response - raise Timeout(f"No chunk received for {self.stream_chunk_timeout} seconds.") - - last_chunk_time = current_time # 更新最后一次接收块的时间 - + # 处理接收到的数据块 if chunk.choices and chunk.choices[0].delta and chunk.choices[0].delta.content: content = chunk.choices[0].delta.content response_text += content + received_any_chunk = True # 标记已收到块 + last_chunk_time = current_time # 更新最后块时间戳 logging.info("Stream completed successfully.") return response_text # 成功完成 except Timeout as e: - logging.warning(f"Stream chunk timeout: {e}. Retrying if possible ({retries + 1}/{self.max_retries}).") + logging.warning(f"Stream timeout: {e}. Retrying if possible ({retries + 1}/{self.max_retries}).") if response_text: partial_response = response_text last_exception = e @@ -417,11 +425,29 @@ class AI_Agent(): chunk_iterator = iter(stream) last_chunk_time = time.time() start_time = time.time() # 记录开始时间用于全局超时检查 + received_any_chunk = False # 标记是否收到过任何块 while True: try: - # 检查全局超时 current_time = time.time() + + # 检查流块超时:只有在已经开始接收数据后才检查 + if received_any_chunk and current_time - last_chunk_time > self.stream_chunk_timeout: + logging.warning(f"Stream chunk timeout: No new chunk received for {self.stream_chunk_timeout} seconds after previous chunk.") + if full_response: + logging.info(f"Returning partial response of length {len(full_response)} due to chunk timeout.") + # 通知回调函数超时情况 + timeout_msg = "\n\n[注意: 流式传输中断,内容可能不完整]" + callback(timeout_msg, full_response + timeout_msg if accumulate else None) + return full_response + timeout_msg + raise Timeout(f"Stream stalled: No new chunk received for {self.stream_chunk_timeout} seconds after previous chunk.") + + # 检查首次响应超时:如果很长时间没收到第一个块 + if not received_any_chunk and current_time - start_time > self.timeout: + logging.warning(f"Initial response timeout: No chunk received in {self.timeout} seconds since stream started.") + raise Timeout(f"No initial response received for {self.timeout} seconds.") + + # 检查全局超时(保留作为安全措施) if current_time - start_time > global_timeout: logging.warning(f"Global timeout reached after {global_timeout} seconds.") if full_response: @@ -434,24 +460,16 @@ class AI_Agent(): logging.error("Global timeout with no content received.") raise Timeout(f"Global timeout after {global_timeout} seconds with no content.") - # 检查流块超时 - if current_time - last_chunk_time > self.stream_chunk_timeout: - logging.warning(f"Stream chunk timeout: No chunk received for {self.stream_chunk_timeout} seconds.") - if full_response: - logging.info(f"Returning partial response of length {len(full_response)} due to chunk timeout.") - # 通知回调函数超时情况 - timeout_msg = "\n\n[注意: 由于流式传输超时,内容可能不完整]" - callback(timeout_msg, full_response + timeout_msg if accumulate else None) - return full_response + timeout_msg - raise Timeout(f"No chunk received for {self.stream_chunk_timeout} seconds.") - + # 获取下一个数据块 chunk = next(chunk_iterator) - last_chunk_time = time.time() + # 处理接收到的数据块 if chunk.choices and chunk.choices[0].delta and chunk.choices[0].delta.content: content = chunk.choices[0].delta.content full_response += content callback(content, full_response if accumulate else None) + received_any_chunk = True # 标记已收到块 + last_chunk_time = current_time # 更新最后块时间戳 elif chunk.choices and chunk.choices[0].finish_reason == 'stop': logging.info("Stream with callback finished normally.") return full_response @@ -460,7 +478,7 @@ class AI_Agent(): logging.info("Stream iterator with callback exhausted normally.") return full_response except Timeout as e: - logging.warning(f"Stream chunk timeout: {e}. Retrying if possible ({retries + 1}/{self.max_retries}).") + logging.warning(f"Stream timeout: {e}. Retrying if possible ({retries + 1}/{self.max_retries}).") last_exception = e break except (APITimeoutError, APIConnectionError, RateLimitError) as e: @@ -584,11 +602,27 @@ class AI_Agent(): stream_start_time = time.time() last_chunk_time = time.time() + received_any_chunk = False # 标记是否收到过任何块 try: async for chunk in stream: - # 检查全局超时 current_time = time.time() + + # 检查流块超时:只有在已经开始接收数据后才检查 + if received_any_chunk and current_time - last_chunk_time > self.stream_chunk_timeout: + logging.warning(f"Async stream chunk timeout: No new chunk received for {self.stream_chunk_timeout} seconds after previous chunk.") + if full_response: + timeout_msg = "\n\n[注意: 流式传输中断,内容可能不完整]" + yield timeout_msg + return # 结束生成器 + raise Timeout(f"Async stream stalled: No new chunk received for {self.stream_chunk_timeout} seconds after previous chunk.") + + # 检查首次响应超时:如果很长时间没收到第一个块 + if not received_any_chunk and current_time - stream_start_time > self.timeout: + logging.warning(f"Async initial response timeout: No chunk received in {self.timeout} seconds since stream started.") + raise Timeout(f"No initial response received for {self.timeout} seconds.") + + # 检查全局超时(保留作为安全措施) if current_time - stream_start_time > global_timeout: logging.warning(f"Async global timeout reached after {global_timeout} seconds.") if full_response: @@ -599,27 +633,19 @@ class AI_Agent(): logging.error("Async global timeout with no content received.") raise Timeout(f"Async global timeout after {global_timeout} seconds with no content.") - # 检查流块超时 - if current_time - last_chunk_time > self.stream_chunk_timeout: - logging.warning(f"Async stream chunk timeout: No chunk received for {self.stream_chunk_timeout} seconds.") - if full_response: - timeout_msg = "\n\n[注意: 由于流式传输超时,内容可能不完整]" - yield timeout_msg - return # 结束生成器 - raise Timeout(f"Async: No chunk received for {self.stream_chunk_timeout} seconds.") - - last_chunk_time = current_time # 更新最后一次接收块的时间 - + # 处理接收到的数据块 if chunk.choices and chunk.choices[0].delta and chunk.choices[0].delta.content: content = chunk.choices[0].delta.content full_response += content yield content + received_any_chunk = True # 标记已收到块 + last_chunk_time = current_time # 更新最后块时间戳 logging.info("Async stream completed normally.") return except Timeout as e: - logging.warning(f"Async stream chunk timeout: {e}. Retrying if possible ({retries + 1}/{self.max_retries}).") + logging.warning(f"Async stream timeout: {e}. Retrying if possible ({retries + 1}/{self.max_retries}).") last_exception = e except (APITimeoutError, APIConnectionError, RateLimitError) as e: logging.warning(f"Async API error during streaming: {type(e).__name__} - {e}. Retrying if possible.") diff --git a/examples/demo_robust_streaming.py b/examples/demo_robust_streaming.py new file mode 100644 index 0000000..92ab9b1 --- /dev/null +++ b/examples/demo_robust_streaming.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +演示改进后的流式处理超时机制 + +此脚本展示TravelContentCreator中AI_Agent类中优化后的流式处理超时机制: +1. 更智能的流块超时检测 - 只在收到初始块后才检测流块超时 +2. 首次响应超时检测 - 检测是否在合理时间内收到第一个响应块 +3. 全局超时保护 - 防止整体处理时间过长 +""" + +import os +import sys +import asyncio +import time +import logging +import random +from pathlib import Path +from datetime import datetime + +# 添加项目根目录到Python路径 +project_root = str(Path(__file__).parent.parent) +if project_root not in sys.path: + sys.path.insert(0, project_root) + +from core.ai_agent import AI_Agent, Timeout + +# 配置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + datefmt='%H:%M:%S' +) + +# 示例提示词 +SYSTEM_PROMPT = "你是一个专业的旅游内容创作助手,请根据用户的提示生成相关内容。" +# 普通提示词 +NORMAL_PROMPT = "请生成一段关于杭州西湖的简短介绍,不超过200字。" +# 长提示词,可能需要更长处理时间 +LONG_PROMPT = """请按照以下格式为我创建一份详尽的云南七日旅游攻略: +1. 每日行程计划(早中晚三餐和活动) +2. 每个景点的历史背景和特色 +3. 当地特色美食推荐和品尝地点 +4. 交通建议(包括景点间如何移动) +5. 住宿推荐(每个地区的优质酒店或民宿) +6. 注意事项(天气、海拔、装备等) +7. 购物指南(值得购买的纪念品和特产) + +请确保涵盖昆明、大理、丽江和香格里拉等主要旅游地点,并考虑季节特点和当地习俗。提供实用且详细的信息。 +""" + +def print_separator(title): + """打印分隔线和标题""" + print("\n" + "="*60) + print(f" {title} ".center(60, "=")) + print("="*60 + "\n") + +def print_with_timestamp(message, end='\n'): + """打印带有时间戳的消息""" + timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3] + print(f"[{timestamp}] {message}", end=end, flush=True) + +def demo_sync_stream(): + """演示同步流式响应方法的改进超时机制""" + print_separator("同步流式响应的超时机制") + + # 创建AI_Agent实例,设置较短的超时便于测试 + agent = AI_Agent( + base_url=os.environ.get("API_BASE", "https://api.openai.com/v1"), + model_name=os.environ.get("MODEL_NAME", "gpt-3.5-turbo"), + api=os.environ.get("API_KEY", "your_api_key"), + timeout=15, # 15秒请求超时 + stream_chunk_timeout=5, # 5秒流块超时 + max_retries=1 # 最小重试减少测试时间 + ) + + print_with_timestamp("开始生成内容(同步流式方法)...") + start_time = time.time() + + try: + # 使用同步流式方法 + result = agent.generate_text_stream( + SYSTEM_PROMPT, + NORMAL_PROMPT, + temperature=0.7, + top_p=0.9, + presence_penalty=0.0 + ) + + end_time = time.time() + + print_with_timestamp(f"生成完成! 耗时: {end_time - start_time:.2f}秒") + print_with_timestamp(f"内容长度: {len(result)} 字符") + + # 检查是否包含超时或错误提示 + if "[注意:" in result: + print_with_timestamp("检测到警告信息:") + warning_start = result.find("[注意:") + warning_end = result.find("]", warning_start) + if warning_end != -1: + print_with_timestamp(f"警告内容: {result[warning_start:warning_end+1]}") + + except Exception as e: + print_with_timestamp(f"生成过程中出错: {type(e).__name__} - {e}") + + # 关闭agent + agent.close() + +def demo_callback_stream(): + """演示回调流式响应方法的改进超时机制""" + print_separator("回调流式响应的超时机制") + + # 创建AI_Agent实例 + agent = AI_Agent( + base_url=os.environ.get("API_BASE", "https://api.openai.com/v1"), + model_name=os.environ.get("MODEL_NAME", "gpt-3.5-turbo"), + api=os.environ.get("API_KEY", "your_api_key"), + timeout=15, + stream_chunk_timeout=5, + max_retries=1 + ) + + # 定义回调函数 + def callback(chunk, accumulated=None): + """处理流式响应的回调函数""" + if chunk: + # 实时打印内容,不换行 + print_with_timestamp(f"收到块({len(chunk)}字符): {chunk[:10]}...", end="\r") + + print_with_timestamp("开始生成内容(回调流式方法)...") + start_time = time.time() + + try: + # 使用回调流式方法 + result = agent.generate_text_stream_with_callback( + SYSTEM_PROMPT, + LONG_PROMPT, # 使用更长的提示 + temperature=0.7, + top_p=0.9, + presence_penalty=0.0, + callback=callback, + accumulate=True # 启用累积模式 + ) + + end_time = time.time() + print_with_timestamp(f"\n生成完成! 耗时: {end_time - start_time:.2f}秒") + print_with_timestamp(f"内容长度: {len(result)} 字符") + + # 检查是否包含超时或错误提示 + if "[注意:" in result: + print_with_timestamp("检测到警告信息:") + warning_start = result.find("[注意:") + warning_end = result.find("]", warning_start) + if warning_end != -1: + print_with_timestamp(f"警告内容: {result[warning_start:warning_end+1]}") + + except Exception as e: + print_with_timestamp(f"生成过程中出错: {type(e).__name__} - {e}") + + # 关闭agent + agent.close() + +async def demo_async_stream(): + """演示异步流式响应方法的改进超时机制""" + print_separator("异步流式响应的超时机制") + + # 创建AI_Agent实例 + agent = AI_Agent( + base_url=os.environ.get("API_BASE", "https://api.openai.com/v1"), + model_name=os.environ.get("MODEL_NAME", "gpt-3.5-turbo"), + api=os.environ.get("API_KEY", "your_api_key"), + timeout=15, + stream_chunk_timeout=5, + max_retries=1 + ) + + print_with_timestamp("开始生成内容(异步流式方法)...") + start_time = time.time() + full_response = "" + chunk_count = 0 + + try: + # 使用异步流式方法 + async_stream = agent.async_generate_text_stream( + SYSTEM_PROMPT, + NORMAL_PROMPT, + temperature=0.7, + top_p=0.9, + presence_penalty=0.0 + ) + + # 异步迭代流 + async for content in async_stream: + # 累积完整响应 + full_response += content + chunk_count += 1 + + # 每收到5个块打印一次进度 + if chunk_count % 5 == 0: + print_with_timestamp(f"已收到 {chunk_count} 个块,当前内容长度: {len(full_response)}字符") + + except Exception as e: + print_with_timestamp(f"生成过程中出错: {type(e).__name__} - {e}") + + end_time = time.time() + print_with_timestamp(f"生成完成! 耗时: {end_time - start_time:.2f}秒") + print_with_timestamp(f"共收到 {chunk_count} 个块,内容长度: {len(full_response)}字符") + + # 检查是否包含超时或错误提示 + if "[注意:" in full_response: + print_with_timestamp("检测到警告信息:") + warning_start = full_response.find("[注意:") + warning_end = full_response.find("]", warning_start) + if warning_end != -1: + print_with_timestamp(f"警告内容: {full_response[warning_start:warning_end+1]}") + + # 关闭agent + agent.close() + +async def simulate_delayed_response(): + """模拟延迟响应的场景""" + print_separator("模拟延迟响应") + + # 创建带有非常短超时的AI_Agent实例 + agent = AI_Agent( + base_url=os.environ.get("API_BASE", "https://api.openai.com/v1"), + model_name=os.environ.get("MODEL_NAME", "gpt-3.5-turbo"), + api=os.environ.get("API_KEY", "your_api_key"), + timeout=10, + stream_chunk_timeout=3, # 非常短的流块超时 + max_retries=1 + ) + + # 自定义处理器模拟延迟 + class DelayedProcessor: + def __init__(self): + self.chunks = [] + self.total_chars = 0 + + async def process_chunk(self, chunk): + self.chunks.append(chunk) + self.total_chars += len(chunk) + # 打印进度 + print_with_timestamp(f"收到块 #{len(self.chunks)}, 内容: {chunk[:10]}...", end="\r") + + # 随机延迟模拟处理时间 + delay = random.uniform(0.1, 4.0) # 0.1-4秒,有时会超过超时设置 + if len(self.chunks) % 5 == 0: + print_with_timestamp(f"\n模拟处理延迟: {delay:.2f}秒") + await asyncio.sleep(delay) + + # 返回处理后的块 + return f"[处理完成: {chunk}]" + + processor = DelayedProcessor() + print_with_timestamp("开始生成内容(带处理延迟)...") + start_time = time.time() + full_response = "" + + try: + # 使用异步流式方法 + async_stream = agent.async_generate_text_stream( + SYSTEM_PROMPT, + NORMAL_PROMPT, + temperature=0.7, + top_p=0.9, + presence_penalty=0.0 + ) + + # 异步迭代流,添加处理延迟 + async for content in async_stream: + # 模拟处理延迟 + processed = await processor.process_chunk(content) + full_response += processed + + except Exception as e: + print_with_timestamp(f"\n生成过程中出错: {type(e).__name__} - {e}") + + end_time = time.time() + print_with_timestamp(f"\n生成完成! 耗时: {end_time - start_time:.2f}秒") + print_with_timestamp(f"共处理 {len(processor.chunks)} 个块,总字符数: {processor.total_chars}") + + # 检查是否包含超时或错误提示 + if "[注意:" in full_response: + print_with_timestamp("检测到警告信息:") + warning_start = full_response.find("[注意:") + warning_end = full_response.find("]", warning_start) + if warning_end != -1: + print_with_timestamp(f"警告内容: {full_response[warning_start:warning_end+1]}") + + # 关闭agent + agent.close() + +async def main(): + """主函数""" + print_with_timestamp("测试改进后的流式处理超时机制...") + + # 1. 测试同步流式响应 + demo_sync_stream() + + # 2. 测试回调流式响应 + demo_callback_stream() + + # 3. 测试异步流式响应 + await demo_async_stream() + + # 4. 模拟延迟响应 + await simulate_delayed_response() + + print_with_timestamp("\n所有测试完成!") + +if __name__ == "__main__": + # 运行异步主函数 + asyncio.run(main()) \ No newline at end of file