Skip to content
A · AI 应用开发入门第 2 课⏱ 15 分钟

流式输出和多轮对话

学完你能
  • 用流式输出让回答边生成边显示
  • 用 messages 数组实现多轮对话,知道 system / user / assistant 各自的作用
  • 明白为什么对话越长越贵,以及怎么控制
流光回答与记忆卷轴概括流式多轮对话
流光回答与记忆卷轴概括流式多轮对话AI 生成配图

上一课的请求要等模型把整段回答写完才返回,长一点的回答要等十几秒。聊天应用里大家习惯看到字一个个蹦出来,这就是流式输出。另外,模型本身不记得上一轮说过什么,「记住上下文」是你的程序做的,这就是多轮对话。

流式输出:边生成边显示 ​

光点逐个飘出,表示流式输出逐段显示
光点逐个飘出,表示流式输出逐段显示AI 生成配图

请求里加上 stream: true,接口会改成 SSE(Server-Sent Events)格式,一小段一小段地推送:

python
import os
from openai import OpenAI

client = OpenAI(base_url="https://hivegpt.cn/v1", api_key=os.environ["HIVEGPT_API_KEY"])

stream = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "写一首关于秋天的四行小诗"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()
js
import OpenAI from 'openai'

const client = new OpenAI({ baseURL: 'https://hivegpt.cn/v1', apiKey: process.env.HIVEGPT_API_KEY })

const stream = await client.chat.completions.create({
  model: 'gpt-5.5',
  messages: [{ role: 'user', content: '写一首关于秋天的四行小诗' }],
  stream: true,
})
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || '')
}
process.stdout.write('\n')
bash
curl -N https://hivegpt.cn/v1/chat/completions \
  -H "Authorization: Bearer $HIVEGPT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.5","stream":true,
       "messages":[{"role":"user","content":"写一首关于秋天的四行小诗"}]}'
# 会看到一行行 data: {...},最后一行是 data: [DONE]

几个要点:

  • 流式时每个片段在 choices[0].delta.content 里,不是 message.content;把所有片段拼起来就是完整回答。
  • 有的片段没有文字(比如第一个只带角色、最后一个只带 finish_reason),代码里要判断一下再用。
  • 总耗时不会变短,变短的是用户看到第一个字的时间。

网页里怎么用

浏览器不能直接拿着 Key 调接口(会泄露 Key)。常见做法是:你的后端调 HiveGPT 的流式接口,再把内容通过 SSE 或 WebSocket 转发给网页。A 路线的结业项目会完整做一遍。

多轮对话:自己维护 messages ​

模型每次请求都是「失忆」的。要让它记住前面的内容,就把之前的对话全部带上:

python
messages = [
    {"role": "system", "content": "你是一个耐心的编程老师,每次回答不超过 100 字。"},
]

def ask(question):
    messages.append({"role": "user", "content": question})
    resp = client.chat.completions.create(model="gpt-5.5", messages=messages)
    answer = resp.choices[0].message.content
    messages.append({"role": "assistant", "content": answer})  # 记下模型的回答
    return answer

print(ask("Python 里的列表和元组有什么区别?"))
print(ask("那它们哪个更快?"))  # 「它们」指什么,模型从上一轮知道

三种角色:

角色作用
system设定身份、语气和规则,放在最前面,整段对话都生效
user用户说的话
assistant模型之前的回答,由你的程序存下来再发回去

在下面试试多轮对话:先问一个问题,再接着追问(页面里是一次性返回,流式效果请在本地运行上面的代码)。

▶ 多轮对话:先问,再追问
系统提示词(这次请求一起发送,点开查看)
你是一个耐心的编程老师,每次回答不超过 100 字。
登录后运行登录 HiveGPT 后每天有免费运行次数

对话越长越贵 ​

对话卡片被反复带上,说明多轮上下文成本
对话卡片被反复带上,说明多轮上下文成本AI 生成配图

每一轮都要把之前的全部内容重新发一遍,所以第 10 轮的请求包含前 9 轮的所有文字,输入 token 一路上涨。常用的控制办法:

  1. 只保留最近几轮:比如只带最近 10 条消息,最早的丢掉。
  2. 摘要:对话太长时,让模型把前面的内容总结成一段,替换掉原始记录。
  3. system 写短:system 每轮都会发送,写得越长,每轮都多花一份钱。

另外,每个模型都有上下文长度上限,超过会直接报错(提示对话内容太长)。上面两个办法同时也是在防止超限。

小结 ​

  • stream: true + 读 delta.content,就能边生成边显示。
  • 模型不记事,多轮对话靠你把历史 messages 一起发过去。
  • 历史越长越贵,也可能超出上下文上限,要裁剪或摘要。

下一课解决另一个工程问题:让模型稳定地返回 JSON,程序可以直接解析,不用再写一堆正则。

代码示例在页面里运行时使用 HiveGPT 的模型接口。延伸阅读来自 JavaGuide(Apache-2.0),版权归原作者。