main
HuangHai 4 weeks ago
parent b3c27489a9
commit d5adc3690d

@ -108,58 +108,24 @@ async def generate_stream(client, milvus_pool, collection_manager, query):
else: else:
logger.warning("未找到相关历史对话,请检查查询参数或数据。") logger.warning("未找到相关历史对话,请检查查询参数或数据。")
prompt = f"""根据以下关于'{query}'的相关信息,# Role: 信息检索与回答助手 prompt = f"""
信息检索与回答助手
## Profile 根据以下关于'{query}'的相关信息
- language: 中文
- description: 这是一个专门设计来根据提供的材料检索信息并回答相关问题的助手它能够快速准确地从大量文本中提取关键信息并以清晰简洁的方式回答用户的问题 基本信息
- background: 该助手基于先进的自然语言处理技术能够理解和处理复杂的查询提供准确的信息 - 语言: 中文
- personality: 冷静客观高效 - 描述: 根据提供的材料检索信息并回答问题
- expertise: 信息检索文本分析问答系统 - 特点: 快速准确提取关键信息清晰简洁地回答
- target_audience: 需要快速获取信息的用户如研究人员学生专业人士等
相关信息
## Skills {context}
1. 信息检索 回答要求
- 文本搜索: 能够在大量文本中快速搜索关键词或短语 1. 准确无误来源可靠
- 语义理解: 理解用户的查询意图即使查询语句不完全符合标准格式 2. 客观公正避免主观判断
- 结果筛选: 从搜索结果中筛选出最相关的信息 3. 使用HTML格式返回包含适当的段落列表和标题标签
4. 确保内容结构清晰便于前端展示
2. 信息处理 """
- 文本摘要: 提供文本的简要摘要帮助用户快速了解主要内容
- 关键信息提取: 提取文本中的关键信息如日期地点人物等
- 数据整合: 将来自不同来源的信息进行整合提供全面的回答
## Rules
1. 基本原则
- 准确性: 提供的信息必须准确无误确保来源可靠
- 客观性: 回答问题时保持客观避免主观判断
- 完整性: 尽可能提供完整的信息满足用户的需求
2. 行为准则
- 及时响应: 快速响应用户的查询提供及时的信息
- 清晰表达: 使用简洁明了的语言确保用户能够理解回答
- 保密性: 严格遵守保密协议不泄露用户的个人信息
3. 限制条件
- 不提供猜测性信息: 只提供有据可查的信息不进行猜测
- 不传播不实信息: 确保提供的信息真实可靠不传播不实信息
- 不涉及敏感内容: 避免回答涉及敏感内容的问题
## Workflows
- 目标: 根据提供的材料回答用户的问题
- 步骤 1: 接收并理解用户的查询确定查询意图
- 步骤 2: 在提供的材料中搜索相关信息筛选出最相关的信息
- 步骤 3: 对搜索到的信息进行处理提取关键信息并进行整合
- 预期结果: 提供准确清晰完整的回答满足用户的需求
## Initialization
作为信息检索与回答助手你必须遵守上述Rules按照Workflows执行任务
相关信息
{context}"""
response = client.chat.completions.create( response = client.chat.completions.create(
model="deepseek-chat", model="deepseek-chat",
@ -168,13 +134,10 @@ async def generate_stream(client, milvus_pool, collection_manager, query):
{"role": "user", "content": prompt} {"role": "user", "content": prompt}
], ],
temperature=0.3, temperature=0.3,
stream=True stream=False
) )
for chunk in response: yield {"data": response.choices[0].message.content}
if chunk.choices[0].delta.content:
yield {"data": chunk.choices[0].delta.content}
await asyncio.sleep(0.01)
except Exception as e: except Exception as e:
yield {"data": f"生成报告时出错: {str(e)}"} yield {"data": f"生成报告时出错: {str(e)}"}
finally: finally:
@ -202,15 +165,14 @@ async def rag_stream(request: Request):
except Exception as e: except Exception as e:
logger.error(f"请求解析失败: {str(e)}") logger.error(f"请求解析失败: {str(e)}")
raise HTTPException(status_code=400, detail="无效的请求格式") raise HTTPException(status_code=400, detail="无效的请求格式")
"""RAG+DeepSeek流式接口""" """RAG+DeepSeek接口"""
return EventSourceResponse( async for chunk in generate_stream(
generate_stream( request.app.state.deepseek_client,
request.app.state.deepseek_client, request.app.state.milvus_pool,
request.app.state.milvus_pool, request.app.state.collection_manager,
request.app.state.collection_manager, query_request.query
query_request.query ):
) return chunk
)
if __name__ == "__main__": if __name__ == "__main__":

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>教育知识问答</title> <title>小学数学大模型问答</title>
<style> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
@ -12,42 +12,44 @@
padding: 20px; padding: 20px;
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.container { .container {
background: white; background: white;
padding: 30px; padding: 30px;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
} }
h1 { h1 {
color: #333; color: #333;
text-align: center; text-align: center;
margin-bottom: 30px; margin-bottom: 30px;
} }
.data-area { .data-area {
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 5px; border-radius: 5px;
padding: 15px; padding: 15px;
min-height: 300px; min-height: 300px;
max-height: 400px; max-height: 400px;
min-width: 600px; /* Added to ensure sufficient width */
width: 100%; /* Ensure it takes full available width */
overflow-y: auto; overflow-y: auto;
background-color: #f8f9fa; background-color: #f8f9fa;
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
font-size: 14px; font-size: 14px;
line-height: 1.6; line-height: 1.6;
white-space: pre-line; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
margin-bottom: 20px; margin-bottom: 20px;
} }
.input-area { .input-area {
display: flex; display: flex;
gap: 10px; gap: 10px;
} }
#questionInput { #questionInput {
flex: 1; flex: 1;
padding: 10px; padding: 10px;
@ -55,7 +57,7 @@
border-radius: 5px; border-radius: 5px;
font-size: 16px; font-size: 16px;
} }
#submitBtn { #submitBtn {
background-color: #007bff; background-color: #007bff;
color: white; color: white;
@ -66,141 +68,129 @@
cursor: pointer; cursor: pointer;
transition: background-color 0.3s; transition: background-color 0.3s;
} }
#submitBtn:hover { #submitBtn:hover {
background-color: #0056b3; background-color: #0056b3;
} }
.status { .status {
text-align: center; text-align: center;
margin-bottom: 20px; margin-bottom: 20px;
font-weight: bold; font-weight: bold;
} }
.status.connecting { .status.connecting {
color: #ffc107; color: #ffc107;
} }
.status.connected { .status.connected {
color: #28a745; color: #28a745;
} }
.status.error { .status.error {
color: #dc3545; color: #dc3545;
} }
.status.completed { .status.completed {
color: #17a2b8; color: #17a2b8;
} }
.model {
margin-bottom: 30px;
padding: 15px;
border-left: 4px solid #007bff;
background: #f8f9fa;
word-wrap: break-word;
white-space: pre-wrap;
overflow-wrap: break-word;
}
.model-title {
color: #007bff;
margin-bottom: 15px;
}
.model-definition {
font-weight: bold;
}
.model-expression {
font-family: monospace;
background: #e9ecef;
padding: 5px;
}
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>教育知识问答</h1> <h1>小学数学大模型问答</h1>
<div id="status" class="status">准备就绪</div> <div id="status" class="status">准备就绪</div>
<div class="data-area" id="dataArea">等待问题...</div> <div class="data-area" id="dataArea">等待问题...</div>
<div class="input-area"> <div class="input-area">
<input type="text" id="questionInput" placeholder="请输入您的问题,例如:小学数学的学习方法"> <input type="text" id="questionInput" placeholder="请输入您的问题,例如:小学数学的学习方法">
<button id="submitBtn" onclick="submitQuestion()">提问</button> <button id="submitBtn" onclick="submitQuestion()">提问</button>
</div>
</div> </div>
</div>
<script> <script>
let eventSource = null; let eventSource = null;
let textBuffer = ''; let textBuffer = '';
function submitQuestion() { function submitQuestion() {
const question = document.getElementById('questionInput').value.trim(); const question = document.getElementById('questionInput').value.trim();
if (!question) { if (!question) {
alert('请输入问题!'); alert('请输入问题!');
return; return;
} }
const statusDiv = document.getElementById('status'); const statusDiv = document.getElementById('status');
const dataArea = document.getElementById('dataArea'); const dataArea = document.getElementById('dataArea');
const submitBtn = document.getElementById('submitBtn'); const submitBtn = document.getElementById('submitBtn');
// 清空之前的数据 // 清空之前的数据
dataArea.textContent = ''; dataArea.textContent = '';
textBuffer = ''; textBuffer = '';
// 禁用按钮 // 禁用按钮
submitBtn.disabled = true; submitBtn.disabled = true;
submitBtn.textContent = '处理中...'; submitBtn.textContent = '处理中...';
// 更新状态 // 更新状态
statusDiv.textContent = '正在连接...'; statusDiv.textContent = '正在连接...';
statusDiv.className = 'status connecting'; statusDiv.className = 'status connecting';
// 使用fetch发送POST请求并处理SSE流 // 使用fetch发送POST请求并获取完整响应
fetch('/api/rag', { fetch('/api/rag', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ query: question }) body: JSON.stringify({query: question})
}) })
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
const reader = response.body.getReader(); return response.json();
const decoder = new TextDecoder(); })
.then(data => {
statusDiv.textContent = '连接成功,等待回答...'; dataArea.innerHTML = data.data;
statusDiv.className = 'status connected'; statusDiv.textContent = '回答完成';
statusDiv.className = 'status completed';
function readStream() { submitBtn.disabled = false;
reader.read().then(({ done, value }) => { submitBtn.textContent = '提问';
if (done) {
statusDiv.textContent = '回答完成';
statusDiv.className = 'status completed';
submitBtn.disabled = false;
submitBtn.textContent = '提问';
return;
}
const chunk = decoder.decode(value, { stream: true });
// SSE数据通常以 'data: ' 开头,并以 '\n\n' 结束
const lines = chunk.split('\n').filter(line => line.trim() !== '');
for (const line of lines) {
if (line.startsWith('data: ')) {
let data = line.substring(6);
if (data.trim() === '[DONE]') {
statusDiv.textContent = '回答完成';
statusDiv.className = 'status completed';
submitBtn.disabled = false;
submitBtn.textContent = '提问';
reader.cancel(); // 停止读取流
return;
}
textBuffer += data;
dataArea.textContent = textBuffer;
dataArea.scrollTop = dataArea.scrollHeight;
}
}
readStream(); // 继续读取下一块
}).catch(error => {
console.error('SSE连接错误:', error);
statusDiv.textContent = `连接错误或已断开: ${error.message}`;
statusDiv.className = 'status error';
submitBtn.disabled = false;
submitBtn.textContent = '提问';
});
}
readStream();
}) })
.catch(error => { .catch(error => {
console.error('Fetch错误:', error); console.error('请求错误:', error);
statusDiv.textContent = `请求发送失败: ${error.message}`; statusDiv.textContent = `请求错误: ${error.message}`;
statusDiv.className = 'status error'; statusDiv.className = 'status error';
submitBtn.disabled = false; submitBtn.disabled = false;
submitBtn.textContent = '提问'; submitBtn.textContent = '提问';
}); });
} }
</script> </script>
</body> </body>
</html> </html>

Loading…
Cancel
Save