Abort Generation

Abort Generation 是用户在 AI 流式输出过程中取消生成的机制,涉及前端请求中断、后端停止推理和状态同步。

#type / concept #status / evergreen #tech / frontend #tech / ai

[!info] related notes

Abort Generation

一句话定义

Abort Generation 是用户在 AI 流式输出过程中取消生成的完整机制。它不只是前端断开连接,还涉及后端停止 LLM 推理、停止工具执行、保存已生成内容和状态同步。

它解决什么问题

LLM 生成可能持续几秒到几分钟。用户可能:

  • 发现 Agent 理解错了意图,想重新输入
  • 不想等了,想用已生成的部分内容
  • 发现 Agent 走偏了,想中断
  • 页面要关闭,需要清理资源

如果没有取消机制,Agent 会继续生成无用的 token,浪费算力和费用。

核心原理

取消的完整流程

用户点击"停止"按钮


前端: AbortController.abort()


fetch 请求被取消 → 后端检测到连接关闭


后端: 停止 LLM 流式生成


后端: 如果正在执行工具 → 尝试取消工具执行


后端: 保存已生成内容到数据库 (status: cancelled)


后端: 通知 AI Service Run 被取消


前端: 更新消息状态为 cancelled,显示已生成的部分内容

前端实现

function useChat() {
  const [messages, setMessages] = useState([]);
  const abortRef = useRef<AbortController | null>(null);

  const sendMessage = async (content: string) => {
    abortRef.current = new AbortController();

    // 添加用户消息
    setMessages(prev => [...prev, { role: 'user', content }]);

    // 创建 assistant 消息占位
    const assistantMsg = { role: 'assistant', content: '', status: 'streaming' };
    setMessages(prev => [...prev, assistantMsg]);

    try {
      const response = await fetch('/api/chat', {
        method: 'POST',
        body: JSON.stringify({ message: content }),
        signal: abortRef.current.signal,
      });

      const reader = response.body!.getReader();
      // ... 流式读取
    } catch (err) {
      if (err.name === 'AbortError') {
        // 用户取消,保留已生成内容
        setMessages(prev => prev.map((msg, i) =>
          i === prev.length - 1
            ? { ...msg, status: 'cancelled' }
            : msg
        ));
      }
    }
  };

  const stopGeneration = () => {
    abortRef.current?.abort();
  };

  return { messages, sendMessage, stopGeneration };
}

后端感知取消

// Go 后端检测连接关闭
func chatHandler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    // 当前端断开连接时,ctx 会被 cancel
    go func() {
        <-ctx.Done()
        // 连接关闭,通知 AI Service 停止
        aiService.CancelRun(runID)
    }()

    // 调用 AI Service
    stream, _ := aiService.Chat(ctx, request)
    for event := range stream {
        // 转发事件
        writeSSE(w, event)
    }
}
# Python AI Service 检测取消
async def agent_run(context, cancel_event):
    async for event in engine.run(context):
        if cancel_event.is_set():
            # 保存已生成内容
            save_partial_result(context.run_id)
            yield DoneEvent(status="cancelled")
            return
        yield event

常见设计模式

1. 保留已生成内容

取消后保留已经生成的部分内容,而不是丢弃。

2. 状态机管理

消息状态: streamingcancellingcancelled

3. 资源清理

取消时清理 LLM 连接、工具执行、临时文件。

常见坑

  1. 取消后内容丢失: 用户取消时已经生成的内容被丢弃
  2. 后端不停止: 前端断开了,后端继续生成,浪费资源
  3. 不清理 AbortController: 组件卸载时没有 abort
  4. 取消状态不同步: 前端显示取消了,后端还在跑
  5. 并发取消: 快速点击发送和取消导致状态混乱

参考资料

创建于 2026/6/30 更新于 2026/7/15