Files
course-agent-od/backend/src/rag/conversation_chains.py
T
pengxiao 19b6cdcbd8 refactor: simplify thinking process UI and reduce noise
- Remove redundant status steps (understanding, preparing, generating)
  from SSE stream — only emit retrieved docs and reasoning content
- ThinkingProcess: only show when there's actual reasoning or docs
- Collapse header shows concise state: thinking count or doc count
- Clean up unused icon imports

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 14:26:30 +08:00

135 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
LangChain 1.0 对话链(Normal Mode
"""
from typing import List, Dict, Any, Optional
from langchain_core.runnables import RunnablePassthrough
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from ..llm.siliconflow import get_llm_client
class ConversationChain:
"""标准对话链(LangChain 1.0"""
def __init__(
self,
system_prompt: Optional[str] = None,
model: Optional[str] = None
):
"""初始化对话链
Args:
system_prompt: 系统提示词
model: 可选的模型名称,如 deepseek-ai/DeepSeek-V3, Qwen/QwQ-32B
"""
_sf_client = get_llm_client(model=model)
self.llm = _sf_client.llm
self.client = _sf_client
self.system_prompt = system_prompt or "你是一个专业的国土空间规划知识问答助手。请基于你的知识回答用户的问题。"
# 创建带历史的Prompt模板
self.prompt = ChatPromptTemplate.from_messages([
SystemMessage(content=self.system_prompt),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{question}")
])
# 创建对话链
self.chain = self._create_chain()
def _create_chain(self):
"""创建对话链"""
return (
self.prompt
| self.llm
| StrOutputParser()
)
def invoke(self, question: str, chat_history: List[Dict] = None) -> Dict[str, Any]:
"""同步调用"""
history_messages = self._format_history(chat_history or [])
answer = self.chain.invoke({
"question": question,
"chat_history": history_messages
})
return {
"answer": answer,
"sources": [],
"metadata": {
"mode": "normal",
"has_history": bool(chat_history)
}
}
async def ainvoke(self, question: str, chat_history: List[Dict] = None) -> Dict[str, Any]:
"""异步调用"""
history_messages = self._format_history(chat_history or [])
answer = await self.chain.ainvoke({
"question": question,
"chat_history": history_messages
})
return {
"answer": answer,
"sources": [],
"metadata": {
"mode": "normal",
"has_history": bool(chat_history)
}
}
async def astream_with_thinking(self, question: str, chat_history: List[Dict] = None):
"""流式调用(包含思考过程,捕获推理模型的真实推理内容)"""
import time
start_time = time.time()
history_messages = self._format_history(chat_history or [])
# 直接使用原始 OpenAI SDK 捕获推理内容
prompt_value = await self.prompt.ainvoke({
"question": question,
"chat_history": history_messages
})
prompt_messages = prompt_value.to_messages()
content_started = False
async for event_type, content in self.client.stream_with_reasoning(prompt_messages):
if event_type == "reasoning":
yield {"type": "thinking", "stage": "reasoning", "message": content}
elif event_type == "content":
if not content_started:
content_started = True
yield {"type": "chunk", "content": content}
# 完成
total_time = time.time() - start_time
yield {
"type": "complete",
"metadata": {
"total_time": round(total_time, 2)
}
}
def _format_history(self, chat_history: List[Dict]) -> List:
"""格式化聊天历史为LangChain消息格式"""
messages = []
for msg in chat_history:
if msg["role"] == "user":
messages.append(HumanMessage(content=msg["content"]))
elif msg["role"] == "assistant":
messages.append(AIMessage(content=msg["content"]))
return messages
def create_conversation_chain(system_prompt: Optional[str] = None, model: Optional[str] = None) -> ConversationChain:
"""创建对话链实例
Args:
system_prompt: 系统提示词
model: 可选的模型名称,如 deepseek-ai/DeepSeek-V3, Qwen/QwQ-32B
"""
return ConversationChain(system_prompt=system_prompt, model=model)