公司动态

【Bug已解决】Assistant reprocesses previous prompt. 解决方案

📅 2026/7/19 3:41:18
【Bug已解决】Assistant reprocesses previous prompt. 解决方案
【Bug已解决】Assistant reprocesses previous prompt. 解决方案原始报错线索Assistant reprocesses previous prompt.助手把上一条 prompt 又处理了一遍用户明明只发了一次却得到两次响应。一、现象长什么样用户发一条消息期待一个回复结果助手回了两次相同或几乎相同的内容或某些「动作」被执行了两次发两封邮件、建两个任务用户确认只点了一次发送往往发生在「网络重发 / UI 重绘 / 事件冒泡」场景下——同一条输入被系统从不同路径递送了两次。 根因是输入消费没有幂等保护且上一次处理的事件/状态残留被当成了「新输入」再次处理。二、背景为什么一条输入会被处理两次输入从「用户敲击」到「被处理」往往经过多层UI 事件 → 网络传输 → 队列 → 处理器任何一层若「重复递送」且下游不防重就会双处理UI 层按钮连点、事件冒泡网络层超时重试把同一条请求发两次队列层消费确认失败消息被重新投递状态层上一次处理的「最后一条输入」没清重启后又被读出来当新输入。三、为什么重复处理根因3.1 输入无唯一 ID / 指纹系统无法区分「这是同一条重发的」还是「这是新的」于是都处理。3.2 消费不幂等处理器对同一条输入执行了副作用发消息、建任务重复执行就有重复副作用。3.3 状态残留「上一条 prompt」存在某处状态里被误当成新输入再次喂给处理器。3.4 重试无去重网络重试把同一条带相同内容的请求发两次下游照单全收。四、最小可运行复现无去重双处理下面演示「同内容输入被处理两次」def process_prompt_bad(prompt, state): # 错误直接处理不检查是否重复 state[last_response] f回复: {prompt} state[responses].append(state[last_response]) return state[last_response] if __name__ __main__: state {responses: []} process_prompt_bad(你好, state) process_prompt_bad(你好, state) # 同内容又来一次 - 双处理 print(响应数:, len(state[responses])) # 2重复了无去重相同内容被处理两次根因 3.1/3.2。五、解决方案一输入指纹去重给每条输入算指纹内容哈希 可选序号已处理过的直接跳过import hashlib class DeduplicatingProcessor: def __init__(self): self.seen set() def fingerprint(self, prompt, user_id): # 内容 用户 时间窗可选构成指纹 raw f{user_id}:{prompt} return hashlib.sha256(raw.encode()).hexdigest()[:16] def process(self, prompt, user_id, handler): fp self.fingerprint(prompt, user_id) if fp in self.seen: return None, duplicate # 去重不重复处理 self.seen.add(fp) return handler(prompt), ok if __name__ __main__: p DeduplicatingProcessor() h lambda x: f回复:{x} print(p.process(你好, u1, h)) # (回复:你好, ok) print(p.process(你好, u1, h)) # (None, duplicate) 拦截指纹去重同内容二次到达被拦解决 3.1呼应第 110/120 篇幂等。六、解决方案二消费幂等带唯一请求 ID从输入端就带「请求 ID」下游用 ID 保证「同一 ID 只产生一次副作用」class IdempotentHandler: def __init__(self): self.processed_ids set() def handle(self, request_id, prompt, side_effect): if request_id in self.processed_ids: return already_done # 幂等不重复副作用 # 执行副作用发消息/建任务 result side_effect(prompt) self.processed_ids.add(request_id) return result def send_message(text): return f已发送: {text} if __name__ __main__: h IdempotentHandler() print(h.handle(req-001, 你好, send_message)) # 已发送 print(h.handle(req-001, 你好, send_message)) # already_done重试安全 print(h.handle(req-002, 你好, send_message)) # 已发送不同 ID请求 ID 幂等网络重试同 ID不重复副作用。七、解决方案三状态不残留上一条输入「上一条 prompt」不该成为「下次自动处理的输入源」。处理完即清或明确区分「历史」与「待处理队列」class InputQueue: def __init__(self): self.pending [] # 仅待处理 self.history [] # 已处理历史不参与再处理 def enqueue(self, prompt): self.pending.append(prompt) def consume_one(self, handler): if not self.pending: return None prompt self.pending.pop(0) # 取出即移除不残留 result handler(prompt) self.history.append(prompt) # 进历史但历史不会被再消费 return result if __name__ __main__: q InputQueue() q.enqueue(你好) print(q.consume_one(lambda x: f回复:{x})) # 回复:你好 print(待处理剩余:, q.pending) # [] 不残留 print(q.consume_one(lambda x: x)) # None不会把历史当新输入待处理队列「取出即移除」历史与待处理分离杜绝残留重处理。八、跨层防重策略UI 层按钮防连点debounce / disable until response网络层重试带相同请求 ID下游幂等队列层消费确认ack后才删消息失败重投但下游幂等处理器层指纹去重 ID 幂等第五、六节可观测记录「重复拦截次数」。九、排查清单「助手重复处理上一条 prompt」按下面排查输入有无指纹/请求 ID能否区分重复第五、六节消费是否幂等重复执行是否有重复副作用第六节上一条输入是否残留待处理与历史是否分离第七节UI 是否防连点网络重试是否带相同 ID队列是否确认消费ack 机制日志是否记重复拦截数是否只在特定场景重发/重绘出现定位递送层。十、小结「助手重复处理上一条 prompt」的根因是输入无去重、消费不幂等、且上一条输入状态残留被再处理。通用修复指纹去重内容哈希构成指纹已处理直接跳过第五节呼应第 110/120 篇请求 ID 幂等带唯一 ID同 ID 只产生一次副作用重试安全状态不残留待处理队列「取出即移除」历史与待处理分离跨层防重UI 防连点 网络带 ID 队列 ack 处理器幂等第八节。 一句话一条输入只要「带唯一身份 下游幂等消费 处理完即从待办移除」就绝不会被处理两次。把去重和幂等做成输入链路的标配助手就不会再把你的「你好」回答两遍——这与第 110 篇幂等记账、第 120 篇幂等恢复、第 129 篇重试安全共同体现「任何会被重复递送的输入都必须有去重与幂等保护」。