diff --git a/langchain-chat/configs/model_config.py b/langchain-chat/configs/model_config.py
index 403269a..f84c294 100644
--- a/langchain-chat/configs/model_config.py
+++ b/langchain-chat/configs/model_config.py
@@ -75,7 +75,7 @@ MAX_TOKENS = None
MAX_CUT_TOKENS = 30 * 1024
TEMPERATURE = 0.7
-DEEPSEEK_MODELS = ["deepseek-reasoner", "deepseek-chat"]
+DEEPSEEK_MODELS = ["deepseek-r1", "deepseek-reasoner", "deepseek-chat"]
CAST_MODELS = ["kexie_0.5b"]
ONLINE_LLM_MODEL = {
# 本地部署的大模型 API (10.102.24.75:3000)
diff --git a/langchain-chat/configs/prompt_config.py b/langchain-chat/configs/prompt_config.py
index 55b3121..c1b5d3b 100644
--- a/langchain-chat/configs/prompt_config.py
+++ b/langchain-chat/configs/prompt_config.py
@@ -552,28 +552,28 @@ PROMPT_TEMPLATES = {
,
"Topic Recommend Assistant":
- '<角色> 你是由浪潮开发的选题推荐助手。角色> \n\n'
- '<|im_start|>system 今天的日期为:{{time}} <|im_end|>'
+ '<角色> 你是由浪潮开发的选题推荐助手。角色>\n\n'
+ '今天的日期为:{{time}}\n'
'<选题样例>'
'**有色金属尾矿等大宗固废资源化及综合治理模式**'
'**机械产品的数字化设计与制造战略研究**'
'**材料延寿与可持续发展战略研究综合报告**'
'**民营科技企业创新机制研究**'
- '**水产养殖业“十四五"规划战略研究报告 **'
- '**污水资源化能源化的工程科技发展与战略研究污水资源化能源化的工程科技发展与战略研究报告**'
+ '**水产养殖业"十四五"规划战略研究报告**'
+ '**污水资源化能源化的工程科技发展与战略研究综合报告**'
'**油价大幅波动情景下我国的油气勘探战略研究**'
'**洞庭湖大水脉研究咨询报告**'
'**流程工业与循环经济**'
- '**流程工业装备绿色化、智能化与在役再制造“海上风电场建设重大工程问题研究"咨询项目研究结题报告 **'
+ '**流程工业装备绿色化、智能化与在役再制造**'
+ '**海上风电场建设重大工程问题研究咨询项目研究结题报告**'
'选题样例>\n'
- '你的任务是根据user输入的方向或者主题,依据多学科研究现状、前沿动态和发展趋势,为科研人员提供选题推荐。\n'
- '注意!当user不明确的话只推荐一个选题!!\n'
- '【重要指令】:如果用户的问题不是一个选题推荐的请求,禁止使用工具!!!然后你要给出友好回复,比如"抱歉,我无法回答这个问题"。\n'
- '【重要指令】:如果用户的问题是一个选题推荐的请求,请使用工具!!!\n'
- '【重要指令】:你只能回答有关选题推荐的问题!!!!!!如果回答了别的问题,会有十分恶劣的后果!!!!!\n'
- '现在开始:\n'
- '<|im_start|>user {{input}} <|im_end|>'
- '<|im_start|>assistant <|im_end|>\n'
+ '你的任务是根据用户输入的方向或主题,依据多学科研究现状、前沿动态和发展趋势,为科研人员提供选题推荐。\n'
+ '注意:当用户问题不明确时,只推荐一个选题;当用户明确要求多个时,按要求数量给出。\n'
+ '【行为准则】:\n'
+ '1. 用户问题与"研究方向/选题/课题/报告/趋势/调研"等沾边时,使用工具检索资料后给出有帮助的回答。\n'
+ '2. 即使用户的问法不是"推荐选题"原话(如"写一份研究报告"、"有哪些研究重点"),也应理解为相关需求并提供选题建议或研究方向梳理。\n'
+ '3. 仅当用户问题明显与科研选题/研究方向无关(如闲聊、生活问题、纯技术求解),才礼貌说明本助手专注于科研选题推荐。\n'
+ '用户问题:{{input}}\n'
,
"Topic Recommend Assistant_with_history":
diff --git a/langchain-chat/server/agent/tools/search_tool.py b/langchain-chat/server/agent/tools/search_tool.py
index aa74f45..3522dd5 100644
--- a/langchain-chat/server/agent/tools/search_tool.py
+++ b/langchain-chat/server/agent/tools/search_tool.py
@@ -111,9 +111,11 @@ def rag_search(query: str,uid):
knowledge_base_name=knownledge,
expr=expr_param
)
-
+ logging.info(f"[RAG诊断] kb={knownledge!r} expr={expr_param!r} 召回 {len(doc_list)} docs")
+
if len(doc_list)==0:
- return result,source_docs
+ # 修 bug: 原代码 return 导致首个 KB 空就放弃全部 KB;改 continue 继续尝试下一个
+ continue
titles = temp["title"]
doc_list,title = utils.remove_docs1(titles,doc_list)
titles.extend(title)
@@ -264,12 +266,15 @@ def search_tool(query: str):
result3 = []
# 获取结果
result1,sourcedocs = future1.result()
+ # 诊断:看 rag_search 实际召回多少
+ logging.info(f"[RAG诊断] rag_search 返回 result1={len(result1) if isinstance(result1, list) else type(result1).__name__}, sourcedocs={len(sourcedocs) if isinstance(sourcedocs, list) else type(sourcedocs).__name__}, kb={search.get('knowledge_name')}, query={search.get('query')!r}")
result2 = {}
if "type" in utils.get_shared_variable(time_based_uuid):
result2[0] =[]
result2[1] = []
else:
result2 = future2.result()
+ logging.info(f"[RAG诊断] zhipu 返回 result2[0]={len(result2[0]) if isinstance(result2[0], list) else type(result2[0]).__name__}, result2[1]={len(result2[1]) if isinstance(result2[1], list) else type(result2[1]).__name__}")
# if "type" in utils.get_shared_variable(time_based_uuid):
# result2[0] =[]
# result2[1] = []
diff --git a/langchain-chat/server/chat/agent_v2.py b/langchain-chat/server/chat/agent_v2.py
index bf6c441..a4935fe 100644
--- a/langchain-chat/server/chat/agent_v2.py
+++ b/langchain-chat/server/chat/agent_v2.py
@@ -12,6 +12,7 @@ LangGraph 版 Agent runner。
import asyncio
import json
import logging
+import re
from typing import AsyncIterable, List, Optional
from langgraph.prebuilt import create_react_agent
@@ -26,10 +27,20 @@ from server.chat.tools_v2 import make_tools
logger = logging.getLogger(__name__)
+_CHAT_MARKER_RE = re.compile(r"<\|im_(?:start|end)\|>")
+
+
+def _strip_chat_markers(text: str) -> str:
+ """剥掉 prompt 内嵌的 Qwen chat template 标记,避免模型 echo 泄漏到答案。"""
+ return _CHAT_MARKER_RE.sub("", text or "")
+
+
def _build_system_prompt(user_prompt_name: str, query: str, think_content: str) -> str:
"""复用旧版 Think Test Bak + 用户业务 prompt 的拼装逻辑,但简化为单条 system message。"""
- base = get_prompt_template("agent_chat", "Think Test Bak")
user = get_prompt_template("llm_chat", user_prompt_name) if user_prompt_name else ""
+ user = _strip_chat_markers(user)
+ think_content = _strip_chat_markers(think_content)
+
parts = []
parts.append("你是浪潮开发的智能专家。回答用户问题前可以使用工具检索资料。")
parts.append("严格要求:")
diff --git a/langchain-chat/server/chat/chat_test.py b/langchain-chat/server/chat/chat_test.py
index da7ec4b..e6074a2 100644
--- a/langchain-chat/server/chat/chat_test.py
+++ b/langchain-chat/server/chat/chat_test.py
@@ -123,7 +123,9 @@ async def chat_test(
query = query if len(query)<20000 else TextRank(query,num_sentences=70)
query = query if len(query)<20000 else TextRank(query,num_sentences=10)
if model_name == "R1-70B":
- model_name = DEEPSEEK_MODELS[0]
+ # 本地代理 deepseek-r1 把 reasoning 放 content 里,能被 callback 捕获;
+ # 官方 deepseek-reasoner 把 reasoning 放独立的 reasoning_content 字段,旧版 langchain callback 取不到
+ model_name = "deepseek-r1"
elif model_name in ["QIANWEN", "Qwen1.5-32B-Chat"]:
model_name = LLM_MODELS[0]
if prompt_name == "customer_service":
@@ -590,6 +592,8 @@ async def chat_test(
if stream:
menu = 0 #0处于deepseek思考过程中的状态1处于生成正文状态
include_think = False #是否包含思考(源码修改的手动拼接的思考标签)
+ include_think1 = False
+ r1_thinking_done = False # R1: 见到 之前默认在思考态
async for token in callback.aiter():
if not utils.get_shared_variable(time_based_uuid)["status"]:
logging.info("\n==============================STOPPED==============================\n")
@@ -633,19 +637,24 @@ async def chat_test(
yield json.dumps({"text": token}, ensure_ascii=False)
else:
if model_name in DEEPSEEK_MODELS:
- if "" in token:
- include_think = True
- token = token.replace("","")
- logger.info(f"think:{token}")
- yield json.dumps({"think": token}, ensure_ascii=False)
+ # R1 流式输出特点:默认从 reasoning 开始(不带 开标签),
+ # 看到 才切换到正式答案。
+ if not r1_thinking_done:
+ if "" in token:
+ before, _, after = token.partition("")
+ if before:
+ yield json.dumps({"think": before}, ensure_ascii=False)
+ r1_thinking_done = True
+ if after.strip():
+ yield json.dumps({"text": after}, ensure_ascii=False)
+ else:
+ # 兼容偶发输出 开标签的场景:剥掉后直接 yield think
+ if "" in token:
+ token = token.replace("", "")
+ if token:
+ yield json.dumps({"think": token}, ensure_ascii=False)
else:
- if menu == 1:
- yield json.dumps({"text": token}, ensure_ascii=False)
- if menu == 0 and include_think:
- yield json.dumps({"text": token}, ensure_ascii=False)
- menu = 1
- if not include_think:
- yield json.dumps({"text": token}, ensure_ascii=False)
+ yield json.dumps({"text": token}, ensure_ascii=False)
else:
yield json.dumps(
{"text": token, "message_id": message_id},