黑暗序幕
97.99M · 2026-04-08
你问一句,AI 憋半天,然后一次性吐出一大段:
你说:介绍Python
(等待...等待...等待...)
AI:Python是一种高级编程语言,以其简洁的语法、强大的功能和跨平台的特性,广泛应用于数据科学、人工智能、Web开发等多个领域。
缺点:等得心慌,不知道 AI 在干嘛,是不是卡死了。
你问一句,AI 边想边说,一个字一个字蹦出来:
你说:介绍Python
AI:Python是...一种高级...编程语言...以其简洁的语法...
优点:
data = {
"model": "deepseek-ai-qwen-32b",
"messages": messages
# 没有 stream 参数,默认是 False
}
response = requests.post(url, headers=headers, json=data)
result = response.json() # 等完整响应,一次性拿到
data = {
"model": "deepseek-ai-qwen-32b",
"messages": messages,
"stream": True # 开启流式!
}
# 注意:requests 也要加 stream=True
response = requests.post(url, headers=headers, json=data, stream=True)
# 一块一块读,不是一次性读
for line in response.iter_lines():
# 处理每一块数据
区别就两处:
"stream": Truestream=True,然后用 iter_lines() 循环读{
"choices": [
{
"message": {
"role": "assistant",
"content": "Python是一种高级编程语言..."
}
}
]
}
一个完整的 JSON,content 里是全部内容。
每一块叫 chunk,格式是这样的:
data: {"choices":[{"delta":{"content":"Python"}}]}
data: {"choices":[{"delta":{"content":"是一种"}}]}
data: {"choices":[{"delta":{"content":"高级编程"}}]}
data: {"choices":[{"delta":{"content":"语言"}}]}
data: [DONE]
特点:
data: delta.content 里(不是 message.content 了)data: [DONE] 表示结束| 词 | 是啥 |
|---|---|
| stream | 流式开关,True=边生成边吐,False=憋完再吐 |
| chunk | 每一块数据,流式响应就是一堆 chunk 组成的 |
| delta | 增量,每个 chunk 只包含新生成的那一点点内容 |
| iter_lines() | 一行一行读响应的方法 |
delta vs message:
message.content(完整内容)delta.content(增量内容,每次只有一点点)# -*- coding: utf-8 -*-
import requests
import sys
import json
sys.stdout.reconfigure(encoding='utf-8')
url = "http://xxx:port/v1-openai/chat/completions"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer your-api-key"
}
messages = [{"role": "user", "content": "介绍Python"}]
data = {
"model": "deepseek-ai-qwen-32b",
"messages": messages,
"stream": True # 开启流式
}
# requests 也要 stream=True
response = requests.post(url, headers=headers, json=data, stream=True)
print("AI:", end="", flush=True)
full_reply = ""
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
# 跳过结束标记
if line == "data: [DONE]":
continue
# 解析 chunk
if line.startswith("data: "):
json_str = line[6:] # 去掉 "data: " 前缀
try:
chunk = json.loads(json_str)
# 处理 choices 为空的情况
if "choices" in chunk and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
content = delta.get("content", "")
if content:
print(content, end="", flush=True) # 立即打印
full_reply += content # 收集完整内容
except:
pass
print() # 换行
# 存到 messages(跟以前一样)
messages.append({"role": "assistant", "content": full_reply})
# 请求体里的 stream
data = {
"stream": True # 告诉 API:我要流式输出
}
# requests 里的 stream
response = requests.post(..., stream=True) # 告诉 requests:我要边接收边读
两个都要加!漏一个都不行。
for line in response.iter_lines():
# 每一行是一个 chunk
iter_lines() 会等待服务器推送每一行数据,来了就处理,不用等全部到齐。
print(content, end="", flush=True)
end="":不换行,接着打印flush=True:立即显示,不等缓冲区满full_reply = ""
full_reply += content # 每个 chunk 的内容都累加
为什么要收集?因为最后要存到 messages 里,下次对话要用。
流式输出时,每块只有一点点,必须拼起来才是完整回复。
import json
chunk = json.loads(json_str) # 把 JSON 字符串转成 Python 字典
json 是 Python 内置模块,用来处理 JSON 数据:
| 功能 | 示例 |
|---|---|
| 解析 JSON 字符串 | json.loads('{"name": "小明"}') → {"name": "小明"} |
| 把对象转成 JSON | json.dumps({"name": "小明"}) → '{"name": "小明"}' |
| 读取 JSON 文件 | json.load(open("data.json")) |
不用安装,Python 自带,直接 import json 就能用。
try:
chunk = json.loads(json_str) # 尝试解析 JSON
except:
pass # 出错了就跳过,不报错
含义:
try:尝试执行这段代码except:如果出错了,执行这里pass:什么都不做,跳过为什么要有这个?
流式输出的数据不一定每行都是有效 JSON:
data: {"choices":[{"delta":{"content":"你好"}}]} ← 能解析
data: {"choices":[]} ← choices 为空,但能解析
data: [DONE] ← 不是 JSON,解析会出错
如果不用 try-except,遇到解析失败的行程序就崩了。
用 except: pass 就是:解析失败就跳过,继续处理下一行。
python week1/03_stream_chat.py
效果:
你说:介绍Python
AI:Python是一种高级编程语言,以其简洁的语法...
(字一个个蹦出来,像真人打字一样)
你说:quit
拜拜!
| 特性 | 普通输出 | 流式输出 |
|---|---|---|
| 等待体验 | 憋完再吐,等得心慌 | 边想边说,看着舒服 |
| 响应格式 | 一个完整 JSON | 一堆 chunk(data: {...}) |
| 内容字段 | message.content | delta.content |
| 读取方式 | response.json() | iter_lines() 循环 |
| 代码复杂度 | 简单 | 稍复杂,要拼完整回复 |
咋回事:漏了 flush=True,或者 requests 没加 stream=True
咋办:检查两处 stream=True 都加了,print 要有 flush=True
咋回事:有些 chunk 格式不标准,或者有空行
咋办:加 try-except,跳过解析失败的行
咋回事:又是 Windows 控制台的问题
咋办:别忘了 sys.stdout.reconfigure(encoding='utf-8')
咋回事:流式输出时忘了收集完整回复
咋办:用 full_reply += content 把每块内容拼起来,最后存到 messages
咋回事:eval() 不是正经的 JSON 解析方法,遇到特殊格式会出错
咋办:用 json.loads() 代替 eval()
# 错误写法
chunk = eval(json_str) # 不安全,容易报错
# 正确写法
import json
chunk = json.loads(json_str) # 标准的 JSON 解析方法
为啥 eval() 不行:
eval() 是执行 Python 代码,不是解析 JSONtrue vs True)eval() 有安全风险,可能执行恶意代码咋回事:有些 chunk 的 choices 是空数组,访问 choices[0] 就报错
咋办:加个长度检查
# 错误写法
delta = chunk["choices"][0].get("delta", {}) # choices 为空就报错
# 正确写法
if "choices" in chunk and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
今天干了啥:
学会了流式输出,AI 边想边说,体验更丝滑
理解了 stream=True、chunk、delta 这些概念
做了一个支持流式输出的聊天程序
明天干嘛:试试调整参数,让 AI 更有创意(temperature)或者更老实(max_tokens)。
记于 2026-04-05,AI 学习第三天,流畅度拉满!
碧蓝航线手游入口-日服/美服/官服官网直达(无删减立绘)
Spring Boot @Qualifier深度解密:从“按名查找”到“分组批量注入”,一文掌握它的全部“隐藏技能”。
2026-04-08
2026-04-08