GLM-4.7-Flash实战案例:用Python requests流式解析response.iter_lines()

你是不是也遇到过这种情况:调用一个大模型API,问了一个稍微复杂点的问题,然后就开始盯着屏幕发呆,看着那个转圈圈的加载图标,心里默默数着秒,不知道它到底是在思考还是在摸鱼?

特别是当你想做一个实时对话应用,或者需要处理长文本生成时,这种“等待-响应”的模式简直让人抓狂。用户可能等了几十秒,最后只看到一段完整的文字“啪”一下弹出来,中间没有任何反馈,体验感直接降到冰点。

今天我就来分享一个实战技巧,用Python的requests库配合GLM-4.7-Flash的流式API,实现真正的实时响应。你问完问题,模型一边思考一边回答,文字像打字一样逐个蹦出来,那种感觉,用过就回不去了。

1. 为什么需要流式响应?

在深入代码之前,我们先搞清楚一个问题:流式响应到底解决了什么痛点?

1.1 传统API调用的局限性

传统的API调用是这样的:

import requests

response = requests.post("http://127.0.0.1:8000/v1/chat/completions", json={
    "model": "GLM-4.7-Flash",
    "messages": [{"role": "user", "content": "请写一篇关于人工智能未来发展的文章,不少于1000字"}],
    "stream": False  # 注意这里
})

# 等待...等待...等待...
# 可能10秒、20秒甚至更久

result = response.json()
print(result["choices"][0]["message"]["content"])

这种模式有几个明显的问题:

  1. 用户体验差:用户看着空白屏幕干等,不知道程序是卡了还是在工作
  2. 内存压力大:如果生成的内容很长,需要等全部生成完才能返回,服务器和客户端都要缓存完整响应
  3. 无法中途停止:一旦开始生成,即使发现回答方向不对,也无法中途取消
  4. 响应时间不可控:用户不知道要等多久,可能失去耐心直接关闭页面

1.2 流式响应的优势

流式响应(Streaming Response)就像打开了一个水龙头,数据一点一点地流出来:

  • 实时反馈:模型生成一个字,客户端就收到一个字
  • 降低延迟:第一个token几乎立即返回,用户马上能看到响应
  • 内存友好:不需要缓存完整响应,边生成边处理
  • 可交互性:可以中途停止生成,或者根据已生成内容调整后续问题

GLM-4.7-Flash的API原生支持流式输出,我们只需要学会怎么“接住”这些流出来的数据就行了。

2. 基础流式调用:从简单开始

我们先从一个最简单的例子开始,看看流式API的基本用法。

2.1 最基本的流式调用

import requests
import json

def basic_streaming():
    """最基本的流式调用示例"""
    url = "http://127.0.0.1:8000/v1/chat/completions"
    
    # 注意stream参数设置为True
    response = requests.post(
        url,
        json={
            "model": "/root/.cache/huggingface/ZhipuAI/GLM-4.7-Flash",
            "messages": [{"role": "user", "content": "用一句话介绍Python语言的优点"}],
            "temperature": 0.7,
            "max_tokens": 100,
            "stream": True  # 关键参数!
        },
        stream=True  # requests库的stream参数也要设置为True
    )
    
    # 逐行读取流式响应
    for line in response.iter_lines():
        if line:
            # 每行数据格式:data: {...}
            line_text = line.decode('utf-8')
            
            # 跳过心跳包和结束标记
            if line_text.startswith("data: "):
                data_str = line_text[6:]  # 去掉"data: "前缀
                
                # 如果是"[DONE]",表示流结束
                if data_str == "[DONE]":
                    print("\n[流式响应结束]")
                    break
                
                try:
                    data = json.loads(data_str)
                    # 提取生成的文本内容
                    if "choices" in data and len(data["choices"]) > 0:
                        delta = data["choices"][0].get("delta", {})
                        content = delta.get("content", "")
                        if content:
                            print(content, end="", flush=True)  # 实时打印
                except json.JSONDecodeError:
                    print(f"解析失败: {data_str}")
    
    print()  # 最后换行

if __name__ == "__main__":
    basic_streaming()

运行这个代码,你会看到文字一个一个地打印出来,就像有人在打字一样。这就是流式响应的魅力所在。

2.2 理解响应格式

流式API返回的数据格式比较特殊,我们需要理解它的结构:

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"GLM-4.7-Flash","choices":[{"index":0,"delta":{"content":"你"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"GLM-4.7-Flash","choices":[{"index":0,"delta":{"content":"好"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"GLM-4.7-Flash","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}

data: [DONE]

每个数据块(chunk)包含:

  • id: 本次对话的唯一ID
  • choices[0].delta.content: 本次生成的文本内容(可能是一个字、一个词或一个标点)
  • choices[0].finish_reason: 结束原因(当生成完成时不为null)

最后会有一个特殊的 data: [DONE] 标记,表示流式响应结束。

3. 实战进阶:构建完整的流式对话系统

现在我们来构建一个更实用的流式对话系统,包含错误处理、超时控制、上下文管理等功能。

3.1 带错误处理的流式客户端

import requests
import json
import time
from typing import Generator, Optional, Dict, Any

class GLMStreamingClient:
    """GLM-4.7-Flash流式API客户端"""
    
    def __init__(self, base_url: str = "http://127.0.0.1:8000"):
        """
        初始化客户端
        
        Args:
            base_url: API服务器地址
        """
        self.base_url = base_url.rstrip('/')
        self.api_url = f"{self.base_url}/v1/chat/completions"
        self.session = requests.Session()
        
        # 配置会话
        self.session.headers.update({
            "Content-Type": "application/json",
            "Accept": "text/event-stream"  # 重要:告诉服务器我们需要流式响应
        })
    
    def stream_chat(
        self,
        messages: list,
        model: str = "/root/.cache/huggingface/ZhipuAI/GLM-4.7-Flash",
        temperature: float = 0.7,
        max_tokens: int = 2048,
        timeout: int = 60
    ) -> Generator[str, None, None]:
        """
        流式对话生成器
        
        Args:
            messages: 对话消息列表,格式:[{"role": "user", "content": "..."}]
            model: 模型名称
            temperature: 温度参数,控制随机性
            max_tokens: 最大生成token数
            timeout: 超时时间(秒)
            
        Yields:
            每次生成的文本片段
            
        Raises:
            requests.exceptions.RequestException: 网络或API错误
            ValueError: 参数错误或响应解析错误
        """
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens,
            "stream": True
        }
        
        try:
            # 发送请求,设置stream=True启用流式响应
            response = self.session.post(
                self.api_url,
                json=payload,
                stream=True,
                timeout=timeout
            )
            
            # 检查HTTP状态码
            response.raise_for_status()
            
            # 检查Content-Type,确保是流式响应
            content_type = response.headers.get("Content-Type", "")
            if "text/event-stream" not in content_type:
                print(f"警告:服务器返回的不是流式响应,Content-Type: {content_type}")
            
            # 逐行处理流式响应
            buffer = ""  # 用于处理跨行的数据
            for line in response.iter_lines(decode_unicode=True, chunk_size=1):
                if line:
                    # 处理可能的行缓冲
                    buffer += line
                    
                    # 检查是否是一个完整的数据行
                    if buffer.endswith('\n') or '\n' in buffer:
                        lines = buffer.split('\n')
                        for single_line in lines:
                            if single_line.strip():
                                yield from self._process_line(single_line.strip())
                        buffer = ""
                    else:
                        # 继续累积
                        continue
            
            # 处理最后可能剩余的数据
            if buffer:
                yield from self._process_line(buffer)
                
        except requests.exceptions.Timeout:
            raise Exception(f"请求超时({timeout}秒)")
        except requests.exceptions.RequestException as e:
            raise Exception(f"请求失败: {str(e)}")
    
    def _process_line(self, line: str) -> Generator[str, None, None]:
        """处理单行流式数据"""
        # 跳过空行和注释
        if not line or line.startswith(':'):
            return
        
        # 解析SSE格式:data: {...}
        if line.startswith("data: "):
            data_str = line[6:]  # 去掉"data: "前缀
            
            # 结束标记
            if data_str == "[DONE]":
                return
            
            try:
                data = json.loads(data_str)
                
                # 提取生成的文本
                if "choices" in data and data["choices"]:
                    choice = data["choices"][0]
                    delta = choice.get("delta", {})
                    
                    # 返回文本内容
                    if "content" in delta and delta["content"]:
                        yield delta["content"]
                    
                    # 检查是否结束
                    finish_reason = choice.get("finish_reason")
                    if finish_reason:
                        # 可以在这里处理结束逻辑
                        pass
                        
            except json.JSONDecodeError as e:
                print(f"JSON解析错误: {e}, 原始数据: {data_str}")
            except KeyError as e:
                print(f"数据格式错误,缺少键: {e}")
    
    def close(self):
        """关闭会话"""
        self.session.close()

# 使用示例
def example_conversation():
    """完整的对话示例"""
    client = GLMStreamingClient()
    
    try:
        # 第一轮对话
        print("用户: 你好,请介绍一下你自己")
        print("AI: ", end="", flush=True)
        
        messages = [
            {"role": "user", "content": "你好,请介绍一下你自己"}
        ]
        
        full_response = ""
        for chunk in client.stream_chat(messages, temperature=0.8):
            print(chunk, end="", flush=True)
            full_response += chunk
        
        print("\n" + "="*50)
        
        # 第二轮对话(保持上下文)
        messages.append({"role": "assistant", "content": full_response})
        messages.append({"role": "user", "content": "你刚才说你是AI助手,那你能帮我做什么呢?"})
        
        print("用户: 你刚才说你是AI助手,那你能帮我做什么呢?")
        print("AI: ", end="", flush=True)
        
        full_response = ""
        for chunk in client.stream_chat(messages):
            print(chunk, end="", flush=True)
            full_response += chunk
            
        print()
        
    finally:
        client.close()

if __name__ == "__main__":
    example_conversation()

这个客户端类提供了完整的错误处理、超时控制和上下文管理,可以直接用在生产环境中。

3.2 添加速率限制和进度显示

在实际应用中,我们可能还需要控制生成速度,或者显示生成进度:

import threading
import queue

class EnhancedGLMClient(GLMStreamingClient):
    """增强版客户端,支持速率限制和进度显示"""
    
    def stream_chat_with_progress(
        self,
        messages: list,
        max_tokens: int = 2048,
        words_per_minute: Optional[int] = None,
        show_progress: bool = True
    ) -> Generator[str, None, None]:
        """
        带进度显示的流式对话
        
        Args:
            words_per_minute: 每分钟生成字数(模拟打字速度)
            show_progress: 是否显示进度条
        """
        start_time = time.time()
        token_count = 0
        
        # 计算延迟(模拟打字效果)
        delay_per_char = 0
        if words_per_minute and words_per_minute > 0:
            # 假设平均每个中文字符=2个英文字符
            chars_per_minute = words_per_minute * 2
            delay_per_char = 60.0 / chars_per_minute
        
        for chunk in self.stream_chat(messages, max_tokens=max_tokens):
            # 应用速率限制
            if delay_per_char > 0:
                time.sleep(delay_per_char * len(chunk))
            
            # 更新计数
            token_count += len(chunk)
            
            # 显示进度
            if show_progress:
                elapsed = time.time() - start_time
                speed = token_count / elapsed if elapsed > 0 else 0
                progress = min(100, (token_count / max_tokens) * 100)
                
                # 简单的进度显示
                print(f"\r生成进度: {progress:.1f}% | 速度: {speed:.1f}字/秒", end="")
            
            yield chunk
        
        if show_progress:
            print()  # 换行

# 使用示例
def example_with_progress():
    """带进度显示的示例"""
    client = EnhancedGLMClient()
    
    messages = [
        {"role": "user", "content": "写一篇关于机器学习在医疗领域应用的短文,约300字"}
    ]
    
    print("开始生成...")
    print("-" * 50)
    
    full_response = ""
    for chunk in client.stream_chat_with_progress(
        messages,
        max_tokens=500,
        words_per_minute=120,  # 模拟120字/分钟的"打字速度"
        show_progress=True
    ):
        print(chunk, end="", flush=True)
        full_response += chunk
    
    print("\n" + "-" * 50)
    print(f"生成完成,总字数: {len(full_response)}")
    
    client.close()

4. 实际应用场景

流式响应不仅仅是为了好看,在实际应用中有很多重要用途。

4.1 实时聊天应用

from flask import Flask, Response, render_template_string
import json

app = Flask(__name__)

# 简单的HTML页面
CHAT_HTML = """
<!DOCTYPE html>
<html>
<head>
    <title>GLM-4.7-Flash 实时聊天</title>
    <style>
        body { font-family: Arial; max-width: 800px; margin: 0 auto; padding: 20px; }
        #chat { border: 1px solid #ccc; height: 400px; overflow-y: scroll; padding: 10px; margin-bottom: 10px; }
        .user { color: blue; margin: 5px 0; }
        .ai { color: green; margin: 5px 0; }
        #input { width: 70%; padding: 5px; }
        button { padding: 5px 15px; }
    </style>
</head>
<body>
    <h2>GLM-4.7-Flash 实时聊天</h2>
    <div id="chat"></div>
    <input id="input" type="text" placeholder="输入消息...">
    <button onclick="sendMessage()">发送</button>
    
    <script>
        const chatDiv = document.getElementById('chat');
        const input = document.getElementById('input');
        
        function addMessage(role, content) {
            const div = document.createElement('div');
            div.className = role;
            div.innerHTML = `<strong>${role}:</strong> ${content}`;
            chatDiv.appendChild(div);
            chatDiv.scrollTop = chatDiv.scrollHeight;
        }
        
        async function sendMessage() {
            const message = input.value.trim();
            if (!message) return;
            
            input.value = '';
            addMessage('user', message);
            
            // 创建AI消息容器
            const aiMessageDiv = document.createElement('div');
            aiMessageDiv.className = 'ai';
            aiMessageDiv.innerHTML = '<strong>AI:</strong> ';
            chatDiv.appendChild(aiMessageDiv);
            
            try {
                const response = await fetch('/chat', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ message: message })
                });
                
                if (!response.ok) throw new Error('请求失败');
                
                const reader = response.body.getReader();
                const decoder = new TextDecoder();
                let aiText = '';
                
                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;
                    
                    const chunk = decoder.decode(value);
                    const lines = chunk.split('\n');
                    
                    for (const line of lines) {
                        if (line.startsWith('data: ')) {
                            const dataStr = line.slice(6);
                            if (dataStr === '[DONE]') break;
                            
                            try {
                                const data = JSON.parse(dataStr);
                                if (data.choices && data.choices[0].delta.content) {
                                    aiText += data.choices[0].delta.content;
                                    aiMessageDiv.innerHTML = `<strong>AI:</strong> ${aiText}`;
                                    chatDiv.scrollTop = chatDiv.scrollHeight;
                                }
                            } catch (e) {
                                console.error('解析错误:', e);
                            }
                        }
                    }
                }
                
            } catch (error) {
                aiMessageDiv.innerHTML += `<span style="color:red">错误: ${error.message}</span>`;
            }
        }
        
        // 回车发送
        input.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') sendMessage();
        });
    </script>
</body>
</html>
"""

@app.route('/')
def index():
    return render_template_string(CHAT_HTML)

@app.route('/chat', methods=['POST'])
def chat():
    """流式聊天API"""
    from GLMStreamingClient import GLMStreamingClient
    
    data = request.json
    user_message = data.get('message', '')
    
    client = GLMStreamingClient()
    messages = [{"role": "user", "content": user_message}]
    
    def generate():
        try:
            for chunk in client.stream_chat(messages):
                # 格式化为Server-Sent Events格式
                data = {
                    "choices": [{
                        "delta": {"content": chunk},
                        "finish_reason": None
                    }]
                }
                yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
            
            # 发送结束标记
            yield "data: [DONE]\n\n"
            
        finally:
            client.close()
    
    return Response(generate(), mimetype='text/event-stream')

if __name__ == '__main__':
    app.run(debug=True, port=5000)

这个简单的Web应用展示了如何将流式API集成到实际产品中,用户可以看到实时的打字效果。

4.2 长文本生成与处理

对于长文本生成,流式响应可以边生成边处理,避免内存溢出:

def process_long_document(prompt: str, output_file: str):
    """
    生成长文档并实时保存
    
    Args:
        prompt: 生成提示
        output_file: 输出文件路径
    """
    client = GLMStreamingClient()
    
    messages = [
        {"role": "user", "content": f"请生成以下主题的长文档:{prompt}\n\n要求:结构清晰,内容详实,不少于2000字。"}
    ]
    
    print(f"开始生成文档: {prompt}")
    print("-" * 50)
    
    # 实时写入文件
    with open(output_file, 'w', encoding='utf-8') as f:
        total_chars = 0
        start_time = time.time()
        
        for chunk in client.stream_chat(messages, max_tokens=4000):
            # 实时写入
            f.write(chunk)
            f.flush()  # 确保立即写入磁盘
            
            # 实时统计
            total_chars += len(chunk)
            elapsed = time.time() - start_time
            
            # 进度显示
            progress = (total_chars / 2000) * 100  # 基于目标2000字
            speed = total_chars / elapsed if elapsed > 0 else 0
            
            print(f"\r进度: {min(progress, 100):.1f}% | 已生成: {total_chars}字 | 速度: {speed:.1f}字/秒", end="")
            
            # 可以在这里添加实时处理逻辑
            # 比如:关键词提取、情感分析、格式检查等
    
    print(f"\n生成完成!文档已保存到: {output_file}")
    print(f"总字数: {total_chars},耗时: {time.time() - start_time:.1f}秒")
    
    client.close()

# 使用示例
process_long_document("人工智能在医疗诊断中的应用", "医疗AI报告.txt")

4.3 批量处理与管道

流式响应还可以用于构建数据处理管道:

import asyncio
import aiohttp
from concurrent.futures import ThreadPoolExecutor

class AsyncGLMClient:
    """异步流式客户端,支持并发处理"""
    
    def __init__(self, base_url: str = "http://127.0.0.1:8000", max_workers: int = 5):
        self.base_url = base_url.rstrip('/')
        self.api_url = f"{self.base_url}/v1/chat/completions"
        self.executor = ThreadPoolExecutor(max_workers=max_workers)
    
    async def process_batch_streaming(self, prompts: list, callback=None):
        """
        批量处理提示词,使用流式响应
        
        Args:
            prompts: 提示词列表
            callback: 每个提示词处理完成后的回调函数
        """
        async with aiohttp.ClientSession() as session:
            tasks = []
            for i, prompt in enumerate(prompts):
                task = self._process_single_streaming(session, prompt, i, callback)
                tasks.append(task)
            
            await asyncio.gather(*tasks)
    
    async def _process_single_streaming(self, session, prompt: str, index: int, callback):
        """处理单个提示词的流式响应"""
        payload = {
            "model": "/root/.cache/huggingface/ZhipuAI/GLM-4.7-Flash",
            "messages": [{"role": "user", "content": prompt}],
            "temperature": 0.7,
            "max_tokens": 1000,
            "stream": True
        }
        
        print(f"[任务{index}] 开始处理: {prompt[:50]}...")
        
        try:
            full_response = ""
            async with session.post(self.api_url, json=payload) as response:
                response.raise_for_status()
                
                async for line in response.content:
                    if line:
                        line_text = line.decode('utf-8').strip()
                        if line_text.startswith("data: "):
                            data_str = line_text[6:]
                            if data_str == "[DONE]":
                                break
                            
                            try:
                                data = json.loads(data_str)
                                if "choices" in data and data["choices"]:
                                    delta = data["choices"][0].get("delta", {})
                                    content = delta.get("content", "")
                                    if content:
                                        full_response += content
                                        # 可以在这里实时处理每个chunk
                                        # print(f"[任务{index}] 收到: {content}")
                            except:
                                pass
            
            print(f"[任务{index}] 处理完成,生成{len(full_response)}字")
            
            if callback:
                await callback(index, prompt, full_response)
                
        except Exception as e:
            print(f"[任务{index}] 处理失败: {str(e)}")

# 使用示例
async def main():
    prompts = [
        "写一首关于春天的诗",
        "解释什么是机器学习",
        "用Python写一个快速排序算法",
        "介绍深度学习的基本概念",
        "写一段产品推广文案"
    ]
    
    client = AsyncGLMClient()
    
    async def process_callback(index, prompt, response):
        """处理完成后的回调"""
        print(f"\n[回调] 任务{index}完成:")
        print(f"提示: {prompt[:30]}...")
        print(f"响应: {response[:100]}...\n")
    
    await client.process_batch_streaming(prompts, process_callback)

# 运行
asyncio.run(main())

5. 性能优化与最佳实践

在实际使用中,我们还需要考虑一些性能优化和最佳实践。

5.1 连接池与超时设置

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class OptimizedGLMClient:
    """优化版的GLM客户端,包含连接池和重试机制"""
    
    def __init__(self, base_url: str = "http://127.0.0.1:8000"):
        self.base_url = base_url.rstrip('/')
        self.api_url = f"{self.base_url}/v1/chat/completions"
        
        # 创建带连接池的Session
        self.session = requests.Session()
        
        # 配置重试策略
        retry_strategy = Retry(
            total=3,  # 最大重试次数
            backoff_factor=1,  # 重试间隔
            status_forcelist=[429, 500, 502, 503, 504],  # 需要重试的状态码
            allowed_methods=["POST"]  # 只对POST方法重试
        )
        
        # 创建适配器
        adapter = HTTPAdapter(
            max_retries=retry_strategy,
            pool_connections=10,  # 连接池大小
            pool_maxsize=10
        )
        
        # 挂载适配器
        self.session.mount("http://", adapter)
        self.session.mount("https://", adapter)
        
        # 设置超时
        self.timeout = (10, 60)  # (连接超时, 读取超时)
        
        # 设置请求头
        self.session.headers.update({
            "Content-Type": "application/json",
            "Accept": "text/event-stream",
            "User-Agent": "GLM-Streaming-Client/1.0"
        })
    
    def stream_with_timeout(self, messages: list, **kwargs):
        """带超时控制的流式请求"""
        payload = {
            "model": "/root/.cache/huggingface/ZhipuAI/GLM-4.7-Flash",
            "messages": messages,
            "stream": True,
            **kwargs
        }
        
        try:
            response = self.session.post(
                self.api_url,
                json=payload,
                stream=True,
                timeout=self.timeout
            )
            
            # 设置流式读取超时
            response.raw.read = self._timeout_wrapper(response.raw.read)
            
            return response
            
        except requests.exceptions.Timeout:
            raise Exception("请求超时,请检查网络连接或服务器状态")
        except requests.exceptions.RequestException as e:
            raise Exception(f"请求失败: {str(e)}")
    
    def _timeout_wrapper(self, read_method):
        """包装read方法,添加超时控制"""
        def wrapper(*args, **kwargs):
            import socket
            # 设置socket超时
            socket.setdefaulttimeout(5)  # 5秒读取超时
            try:
                return read_method(*args, **kwargs)
            except socket.timeout:
                raise requests.exceptions.Timeout("读取数据超时")
            finally:
                socket.setdefaulttimeout(None)
        return wrapper

5.2 错误处理与重试

def robust_streaming_request(client, messages, max_retries=3):
    """健壮的流式请求,包含错误重试"""
    for attempt in range(max_retries):
        try:
            response = client.stream_with_timeout(messages)
            
            # 检查响应状态
            if response.status_code != 200:
                if response.status_code == 429:
                    print("请求过于频繁,等待后重试...")
                    time.sleep(2 ** attempt)  # 指数退避
                    continue
                elif response.status_code >= 500:
                    print(f"服务器错误({response.status_code}),重试中...")
                    time.sleep(1)
                    continue
                else:
                    response.raise_for_status()
            
            return response
            
        except requests.exceptions.Timeout:
            print(f"超时 (尝试 {attempt + 1}/{max_retries})")
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt  # 指数退避
                print(f"等待 {wait_time} 秒后重试...")
                time.sleep(wait_time)
            else:
                raise Exception("多次重试后仍然超时")
                
        except Exception as e:
            print(f"请求失败: {str(e)} (尝试 {attempt + 1}/{max_retries})")
            if attempt < max_retries - 1:
                time.sleep(1)
            else:
                raise
    
    raise Exception("所有重试尝试都失败了")

5.3 内存优化技巧

对于长时间运行的流式请求,内存管理很重要:

def memory_efficient_streaming(client, messages, chunk_size=1024):
    """
    内存高效的流式处理
    
    Args:
        chunk_size: 每次处理的数据块大小
    """
    response = robust_streaming_request(client, messages)
    
    # 使用生成器逐步处理,避免一次性加载所有数据
    buffer = b""
    for chunk in response.iter_content(chunk_size=chunk_size):
        buffer += chunk
        
        # 按行分割处理
        while b'\n' in buffer:
            line, buffer = buffer.split(b'\n', 1)
            
            if line:
                yield from process_chunk(line.decode('utf-8'))
    
    # 处理剩余数据
    if buffer:
        yield from process_chunk(buffer.decode('utf-8'))

def process_chunk(chunk_data):
    """处理单个数据块"""
    lines = chunk_data.strip().split('\n')
    for line in lines:
        if line.startswith('data: '):
            data_str = line[6:]
            if data_str == '[DONE]':
                return
            
            try:
                data = json.loads(data_str)
                if "choices" in data and data["choices"]:
                    content = data["choices"][0].get("delta", {}).get("content", "")
                    if content:
                        yield content
            except json.JSONDecodeError:
                # 忽略解析错误,继续处理下一个
                pass

6. 总结

通过本文的实战案例,我们深入探讨了如何使用Python的requests库配合GLM-4.7-Flash的流式API。从最基础的流式调用到完整的生产级客户端,再到各种实际应用场景,我希望你能掌握这项实用的技术。

6.1 关键要点回顾

  1. 流式响应的核心价值:实时反馈、降低延迟、内存友好、可交互性强

  2. 技术实现要点

    • 设置 stream=True 参数
    • 使用 response.iter_lines() 逐行读取
    • 解析SSE(Server-Sent Events)格式数据
    • 正确处理 [DONE] 结束标记
  3. 生产环境考虑

    • 完善的错误处理和重试机制
    • 连接池和超时控制
    • 内存优化和性能监控
    • 异步处理和并发控制

6.2 实际应用建议

在实际项目中应用流式API时,我建议:

  1. 根据场景选择:不是所有场景都需要流式响应,简单的问答可以直接用普通API
  2. 用户体验优先:对于需要实时反馈的场景(如聊天、长文本生成),流式响应能显著提升体验
  3. 监控和优化:监控响应时间、错误率、内存使用等指标,持续优化
  4. 兼容性考虑:确保前端能正确处理流式数据,有降级方案

6.3 进一步探索

掌握了基础之后,你还可以进一步探索:

  1. 结合WebSocket:对于需要双向通信的场景,可以结合WebSocket实现更实时的交互
  2. 流式处理管道:将流式响应集成到数据处理管道中,实现边生成边处理
  3. 自定义协议:根据业务需求设计更高效的流式传输协议
  4. 性能优化:针对大规模并发场景进行深度优化

流式API技术正在成为大模型应用的标配,掌握这项技术能让你构建出更流畅、更智能的应用。希望本文的实战案例能帮助你在实际项目中更好地应用GLM-4.7-Flash的流式能力。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐