diff --git a/backend/app/api/chat.py b/backend/app/api/chat.py index ddc4bc1..7a9e055 100644 --- a/backend/app/api/chat.py +++ b/backend/app/api/chat.py @@ -13,6 +13,7 @@ from litestar.response import Stream from app.graph.builder import get_chat_graph from app.schemas import ChatRequest +from app.store.memory import get_checkpointer from app.store.postgres import Conversation, Message, async_session_factory from app.tools import resolve_tools from app.tools.search import set_search_model @@ -118,8 +119,32 @@ async def _stream_response(request: ChatRequest) -> AsyncIterator[bytes]: except Exception as exc: logger.error("SSE stream error for conversation %s: %s", request.conversation_id, exc, exc_info=True) + + error_msg = str(exc) + user_hint = "请求处理出现错误,请重试" + + # Detect checkpoint pollution: a prior tool crash left an AIMessage + # with tool_calls but no corresponding ToolMessage. LangGraph refuses + # to continue the thread. Purge the thread so the next request + # starts from a clean state. + if "tool_calls" in error_msg and "ToolMessage" in error_msg: + try: + checkpointer = await get_checkpointer() + await checkpointer.adelete_thread(request.conversation_id) + logger.warning( + "Purged polluted checkpoint for thread %s", + request.conversation_id, + ) + user_hint = "对话状态异常,已自动重置,请重新发送消息" + except Exception: + logger.warning( + "Failed to purge checkpoint for thread %s", + request.conversation_id, + exc_info=True, + ) + error_data = json.dumps( - {"type": "error", "content": "请求处理出现错误,请重试"}, + {"type": "error", "content": user_hint}, ensure_ascii=False, ) yield f"data: {error_data}\n\n".encode("utf-8")