第18章 构建 AI 聊天机器人
第 13 章中,你已经搭建了聊天机器人的界面。现在来看看,当这个机器人接上真实的 language model 时会发生什么。
聊天机器人远不止一个文本框
一个实用的 AI 聊天机器人需要处理以下事项:
用户消息
对话历史
系统指令
模型调用
模型回复
错误
可能的流式输出
Gradio 界面只是整个系统的一部分。
基础的模型循环
典型的聊天机器人流程大致如下:
def respond(message, history):
messages = build_messages(history, message)
response = model.generate(messages)
return response
系统指令
系统指令用来定义助手的角色。
例如:
SYSTEM_PROMPT = """
You are a helpful Python tutor.
Explain concepts clearly.
Avoid unnecessary jargon.
Provide examples when useful.
"""
你可以在模型请求中带上这条指令。
构建消息
对话类模型通常要求结构化的消息格式。
概念上:
messages = [
{
"role": "system",
"content": SYSTEM_PROMPT
},
{
"role": "user",
"content": "What is a list?"
},
{
"role": "assistant",
"content": "A list is..."
}
]
具体格式取决于所用模型的 API。
加入当前消息
如果 history 中已有往轮次,就把新消息追加进去:
messages.append({
"role": "user",
"content": message
})
然后将完整对话发给模型。
响应
模型可能返回:
response = client.chat.completions.create(...)
你的应用从中提取生成内容即可。
错误处理
API 调用可能失败。
例如:
def respond(message, history):
try:
response = call_model(message, history)
return response
except Exception:
return (
"I couldn't generate a response right now. "
"Please try again."
)
生产环境中,底层错误只记入日志,用户端展示一条安全的提示即可。
API 密钥
如果聊天机器人调用了外部 API,切勿将密钥硬编码在公开共享的源码中。
不要这样做:
API_KEY = "sk-secret-value"
应改用环境变量或部署密钥。
相关内容将在第 22 章介绍。
流式输出
流式输出能显著提升聊天机器人的响应体验。
与其:
response = model.generate(...)
return response
不如:
for chunk in model.stream(...):
yield chunk
这样界面可以逐段展示回复内容。
对话长度
对话会不断变长,最终将完整历史发给模型可能效率低下,甚至超出 context window。
常见策略包括:
只保留最近的几条消息
对较早的消息做摘要
使用滑动窗口
将关键信息单独存储
示例:限制历史长度
一个简单的策略如下:
MAX_MESSAGES = 20
def trim_history(history):
return history[-MAX_MESSAGES:]
具体截断多少条,取决于所用模型和实际场景。
用户体验
聊天机器人应明确告知用户它能做什么、不能做什么,以及它期望什么形式的输入。
例如:
gr.Markdown(
"""
# Python Tutor
Ask questions about Python programming.
"""
)
这样有助于设定用户预期。
动手试试
构建一个 AI 辅导聊天机器人。
包含以下内容:
一个 system prompt
对话历史
一个模型
一个清晰的标题
示例问题
错误处理
然后添加一个主题选择器,选中的主题要写进 system instructions 里。
本章要点
一个真正的 AI 聊天机器人需要结合 UI、对话历史、提示词和模型推理。
System instructions 有助于设定机器人的行为。
消息格式取决于模型 API 的要求。
API 出错时要妥善处理。
绝不要硬编码 API 密钥。
流式输出可以提升聊天机器人的响应速度。
长对话需要做好上下文管理。