个人微信机器人接口开发:多轮上下文对话怎么设计,机器人更懂人?
机器人答得不好,一半原因是"没记住之前说过啥"。多轮对话的核心是三件事:滑窗保留最近 N 轮原文、更早的压成摘要、会话过期自动清空。
一、滑动窗口:保留最近 6-8 轮原文
全部历史塞给模型肯定超上限。做法是只保留最近 6-8 轮的完整原文,更早的压成摘要。滑窗比固定取最后 N 条好——保留的是一段连续对话,不会打断上下文连贯性。
窗口大小要调——对话短(FAQ 类)用 6 轮足够,对话长(咨询方案类)用 8 轮甚至 10 轮。超过窗口长度才触发摘要。
二、历史摘要:聚焦用户诉求而非对话流水
摘要不是把前 30 轮对话缩成一段流水账——"用户先说 A,机器人说 B,用户又说 C"。而是聚焦:用户的核心诉求是什么、已经达成了什么、还没解决什么。
好的摘要示例:"用户想退去年双 11 买的耳机,已告知保修 1 年,用户对物流费 15 元有疑问,提到之前客服说过可以免。" 模型拿到这样的摘要,不会忘了物流费这件事。
三、会话过期:超时自动清空上下文
同一个客户隔三天再来,他可能换了新问题,但机器人还记着上周聊的订单号和耳机,答得就不对。做法是会话有 TTL(比如 30 分钟),过期自动清空滑窗和摘要,当成新会话。
TTL 到期前如果用户又发消息,重置 TTL 继续同一会话。边界场景(比如用户半夜留消息)宁可重置别混淆。
多轮对话设计对照
| 组件 | 做法 | 关键点 | 常见坑 |
|---|---|---|---|
| 滑动窗口 | 保留最近 6-8 轮原文 | 连续不截断 | 全量塞超上限 |
| 历史摘要 | 聚焦诉求+已达成+未解决 | 200 字内 | 流水账式摘要 |
| 会话 TTL | 30 分钟过期清空 | 到期重置 | 隔三天混对话 |
多轮上下文实现
import time, collections
class ContextManager:
WINDOW_SIZE = 8 # 最近 N 轮原文
TTL_SECONDS = 1800 # 30 分钟
SUMMARY_MAX = 200 # 摘要 200 字
def __init__(self, model):
self.model = model
self.histories = {} # wxid -> deque
self.summaries = {} # wxid -> 摘要文本
self.expires = {} # wxid -> 过期时间
def _evict_expired(self):
# 每次调用前扫一次过期会话
now = time.time()
for wxid in list(self.expires.keys()):
if self.expires[wxid] < now:
del self.histories[wxid]
del self.summaries[wxid]
del self.expires[wxid]
def get_messages(self, wxid, new_content):
self._evict_expired()
self.expires[wxid] = time.time() + self.TTL_SECONDS
# 构建给模型的消息列表
msgs = []
if wxid in self.summaries:
msgs.append({
"role": "system",
"content": f"之前对话摘要:{self.summaries[wxid]}"})
hist = self.histories.setdefault(wxid, collections.deque())
msgs.extend(list(hist))
msgs.append({"role": "user", "content": new_content})
return msgs
def add_exchange(self, wxid, user_text, bot_text):
hist = self.histories.setdefault(wxid, collections.deque())
hist.append({"role": "user", "content": user_text})
hist.append({"role": "assistant", "content": bot_text})
# 滑窗溢出时做摘要
if len(hist) > self.WINDOW_SIZE * 2:
old = list(hist)[:-self.WINDOW_SIZE * 2]
new_summary = self._summarize(old, self.summaries.get(wxid))
self.summaries[wxid] = new_summary[:self.SUMMARY_MAX]
# 只保留最近窗口
kept = list(hist)[-self.WINDOW_SIZE * 2:]
hist.clear()
hist.extend(kept)
def _summarize(self, old_pairs, prev_summary):
# 摘要:聚焦诉求、已达成、未解决
transcript = " ".join(
f"{p['role']}: {p['content']}" for p in old_pairs)
prompt = (
f"把这段对话压缩成200字以内的摘要,聚焦:"
f"1. 用户核心诉求 2. 已达成的结论 3. 未解决的问题。\n"
)
if prev_summary:
prompt += f"之前的摘要:{prev_summary}\n"
prompt += f"本次新增对话:{transcript}\n"
return self.model.chat([
{"role": "system",
"content": "你是客服对话摘要助手"},
{"role": "user", "content": prompt},
]).get("content", "")
落地建议
多轮对话用滑动窗口保留最近 6-8 轮完整原文,更早的压成摘要。摘要别写成流水账,聚焦"用户诉求、已达成、未解决"三要素,200 字以内。会话有 TTL(比如 30 分钟),到期自动清空滑窗和摘要,隔两三天再来别混淆上下文。微信侧的消息接口由Eyun这类个人微信API平台 提供,滑窗与摘要逻辑在自建服务实现,接口字段以平台开发文档为准。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)