Browse Source

fix: 移除多余的非流式消息接口

refactor: 调整参考文献的生成方法
    现在是生成固定的参考文献表,而不是AI自动生成
chore: 调整提示词和LLM配置
chore: 调整前端样式
main
tanxing 3 days ago
parent
commit
f063a2d72f
  1. 72
      deepsearcher/agent/deep_search.py
  2. 2
      deepsearcher/config.yaml
  3. 6
      deepsearcher/llm/openai_llm.py
  4. 20
      deepsearcher/templates/html/index.html
  5. 2
      deepsearcher/templates/static/js/app.js
  6. 41
      main.py
  7. 2
      test.py

72
deepsearcher/agent/deep_search.py

@ -63,7 +63,7 @@ RERANK_PROMPT = """
例如假如给出4个chunks实际检索到的文档片段不一定是这么多返回4个"True"或者"False"注意这只是一个示例不代表实际判断: ["True", "False", "True", "True"] 例如假如给出4个chunks实际检索到的文档片段不一定是这么多返回4个"True"或者"False"注意这只是一个示例不代表实际判断: ["True", "False", "True", "True"]
使用的语言与问题相同 使用的语言与问题相同
你需要返回的是 a python list of str without any addtional content: 你需要返回的是 a python list of str(bool) without any addtional content:
""" """
@ -87,35 +87,19 @@ REFLECT_PROMPT = """
SUMMARY_PROMPT = """ SUMMARY_PROMPT = """
你是一个内容分析专家请你根据提供的问题和检索到的信息生成详细层次分明尽可能长的回答 你是一个内容分析专家
请你综合已经提出的问题和检索到的信息以原问题为中心生成详细准确层次分明尽可能长的回答
如果检索到的信息不足以回答问题你应该使用你的知识来进行扩展补充 如果检索到的信息不足以回答问题你应该使用你的知识来进行扩展补充
注意不要一个子问题一个子问题的回答而是应该仔细分析子问题之间的关系子问题和原问题之间的关系 注意不要逐个回答问题而是应该综合所有问题和信息生成一个完整的回答
同时你应该根据提供的信息生成文内引用和文末参考资料列表使用markdown脚注 同时你应该根据提供的信息生成文内引用"[^index]"(markdown文内引用)
如果你自己提供的信息需要使用"your knowledge here[^0]"引用 如果你自己提供的信息需要使用"[^0]"引用即你提供的信息使用固定index=0
注意这里的"[^0]"的序号0是固定的表示你的知识文末引用使用"[^0]: AI 生成", 来自<chunk><reference>的引用序号从[^index]从index=1开始来源需要与前文<reference>中的"href"一致
来自<chunk><reference>的引用序号从[^1]开始来源需要与前文<reference>中的"href"一致不需要对每个<chunk>分配一个引用而是相同<reference><chunk>共用一个引用 不需要对每个<chunk>分配一个引用而是相同<reference><chunk>共用一个引用
另外如果回答的内容文内引用需要引用多个<reference>请添加多个[^index]到句尾 另外如果回答的内容文内引用需要引用多个<reference>请添加多个[^index]到句尾
如果多个片段是相同的来源或者一个片段可以回答多个问题文内引用可以引用多次但文末只引用一次来源即文末的引用列表中不能有重复
例子:
<EXAMPLE> <EXAMPLE>
文内引用示例: "XGBoost是非常强大的集成学习模型。[^1]但是XGBoost的缺点是计算复杂度高,需要大量的计算资源。[^0]"
"XGBoost是非常强大的集成学习模型[^2]"
文末引用示例:
正确例子
[^0]: AI 生成
[^1]: files/docs/machine_learning.md
[^2]: files/docs/chap_001_003_models.md
错误例子
[^0]: AI 生成
[^1]: files/docs/chap_001_003_models.md
[^2]: files/docs/machine_learning.md
[^3]: files/docs/chap_001_003_models.md错误这是重复引用
[^5]: files/docs/machine_learning.md错误也这是重复引用
</EXAMPLE> </EXAMPLE>
@ -257,7 +241,7 @@ class DeepSearch(BaseAgent):
continue continue
# Format all chunks for batch processing # Format all chunks for batch processing
chunks = self._format_chunks(retrieved_results) chunks, _ = self._format_chunks(retrieved_results)
# Batch process all chunks with a single LLM call # Batch process all chunks with a single LLM call
content = self.llm.chat( content = self.llm.chat(
@ -312,7 +296,7 @@ class DeepSearch(BaseAgent):
def _generate_more_sub_queries( def _generate_more_sub_queries(
self, original_query: str, all_sub_queries: list[str], all_retrieved_results: list[RetrievalResult] self, original_query: str, all_sub_queries: list[str], all_retrieved_results: list[RetrievalResult]
) -> list[str]: ) -> list[str]:
chunks = self._format_chunks(all_retrieved_results) chunks, _ = self._format_chunks(all_retrieved_results)
reflect_prompt = REFLECT_PROMPT.format( reflect_prompt = REFLECT_PROMPT.format(
original_query=original_query, original_query=original_query,
all_sub_queries=all_sub_queries, all_sub_queries=all_sub_queries,
@ -410,7 +394,7 @@ class DeepSearch(BaseAgent):
if not all_retrieved_results or len(all_retrieved_results) == 0: if not all_retrieved_results or len(all_retrieved_results) == 0:
send_info(f"'{original_query}'没能找到更多信息!") send_info(f"'{original_query}'没能找到更多信息!")
return "", [] return "", []
chunks = self._format_chunks(all_retrieved_results) chunks, refs = self._format_chunks(all_retrieved_results)
send_info(f"正在总结 {len(all_retrieved_results)} 个查找到的文档片段") send_info(f"正在总结 {len(all_retrieved_results)} 个查找到的文档片段")
summary_prompt = SUMMARY_PROMPT.format( summary_prompt = SUMMARY_PROMPT.format(
original_query=original_query, original_query=original_query,
@ -418,29 +402,33 @@ class DeepSearch(BaseAgent):
chunks=chunks chunks=chunks
) )
response = self.llm.chat([{"role": "user", "content": summary_prompt}]) response = self.llm.chat([{"role": "user", "content": summary_prompt}])
final_answer = self.llm.remove_think(response) response = self.llm.remove_think(response) + refs
send_answer(final_answer) send_answer(response)
return self.llm.remove_think(response), all_retrieved_results return response, all_retrieved_results
def _format_chunks(self, retrieved_results: list[RetrievalResult]): def _format_chunks(self, retrieved_results: list[RetrievalResult]) -> tuple[str, str]:
# 以referecen为key,把chunk放到字典中 # 以referecen为key,把chunk放到字典中
references = defaultdict(list) ref_dict = defaultdict(list)
for result in retrieved_results: for result in retrieved_results:
references[result.reference].append(result.text) ref_dict[result.reference].append(result.text)
chunks = [] formated_chunks = []
formated_refs = ["\n\n[^0]: AI 生成\n"]
chunk_count = 0 chunk_count = 0
for i, reference in enumerate(references): for i, reference in enumerate(ref_dict):
formated = "".join( formated_chunk = "".join(
[ [
( (
f"<reference id='{i + 1}' href='{reference}'>" + f"<reference id='{i + 1}' href='{reference}'>" +
f"<chunk id='{j + 1 + chunk_count}'>\n{chunk}\n</chunk id='{j + 1 + chunk_count}'>" + f"<chunk id='{j + 1 + chunk_count}'>\n{chunk}\n</chunk id='{j + 1 + chunk_count}'>" +
f"</reference id='{i + 1}'>\n" f"</reference id='{i + 1}'>\n"
) )
for j, chunk in enumerate(references[reference]) for j, chunk in enumerate(ref_dict[reference])
] ]
) )
print(formated) print(formated_chunk)
chunks.append(formated) formated_chunks.append(formated_chunk)
chunk_count += len(references[reference]) chunk_count += len(ref_dict[reference])
return "".join(chunks) formated_refs.append(f"[^{i + 1}]: " + str(reference) + "\n")
formated_chunks = "".join(formated_chunks)
formated_refs = "".join(formated_refs)
return formated_chunks, formated_refs

2
deepsearcher/config.yaml

@ -83,5 +83,5 @@ query_settings:
max_iter: 3 max_iter: 3
load_settings: load_settings:
chunk_size: 4096 chunk_size: 2048
chunk_overlap: 1024 chunk_overlap: 1024

6
deepsearcher/llm/openai_llm.py

@ -48,9 +48,9 @@ class OpenAILLM(BaseLLM):
model=self.model, model=self.model,
messages=messages, messages=messages,
stream=True, stream=True,
temperature=0.7, temperature=0.8,
top_p=0.8, top_p=0.9,
presence_penalty=1.3 presence_penalty=1.4
) as stream: ) as stream:
# stream到控制台测试 # stream到控制台测试
content = "" content = ""

20
deepsearcher/templates/html/index.html

@ -9,7 +9,7 @@
<body> <body>
<div class="container"> <div class="container">
<header> <header>
<h1>DeepSearcher 智能搜索系统</h1> <h1>DeepSearcher 智能深度搜索系统</h1>
<p class="app-description">基于大型语言模型和向量数据库的知识管理系统,支持私有数据搜索和在线内容整合,提供准确答案和综合报告。</p> <p class="app-description">基于大型语言模型和向量数据库的知识管理系统,支持私有数据搜索和在线内容整合,提供准确答案和综合报告。</p>
</header> </header>
@ -21,12 +21,12 @@
<input type="text" id="filePaths" placeholder="例如: /path/to/file1.pdf,/path/to/file2.txt"> <input type="text" id="filePaths" placeholder="例如: /path/to/file1.pdf,/path/to/file2.txt">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="collectionName">集合名称(可选)</label> <label for="collectionName">集合名称</label>
<input type="text" id="collectionName" placeholder="例如: my_collection"> <input type="text" id="collectionName" placeholder="例如: default">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="collectionDesc">集合描述(可选)</label> <label for="collectionDesc">集合描述</label>
<textarea id="collectionDesc" rows="2" placeholder="例如: 这是一个测试集合"></textarea> <textarea id="collectionDesc" rows="2" placeholder="例如: This is a general collection for all queries"></textarea>
</div> </div>
<button id="loadFilesBtn">加载文件</button> <button id="loadFilesBtn">加载文件</button>
<div id="loadStatus" class="status"></div> <div id="loadStatus" class="status"></div>
@ -39,12 +39,12 @@
<input type="text" id="websiteUrls" placeholder="例如: https://example.com/page1,https://example.com/page2"> <input type="text" id="websiteUrls" placeholder="例如: https://example.com/page1,https://example.com/page2">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="webCollectionName">集合名称(可选)</label> <label for="webCollectionName">集合名称</label>
<input type="text" id="webCollectionName" placeholder="例如: web_collection"> <input type="text" id="webCollectionName" placeholder="例如: default">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="webCollectionDesc">集合描述(可选)</label> <label for="webCollectionDesc">集合描述</label>
<textarea id="webCollectionDesc" rows="2" placeholder="例如: 来自网站的内容"></textarea> <textarea id="webCollectionDesc" rows="2" placeholder="例如: This is a general collection for all queries"></textarea>
</div> </div>
<button id="loadWebsiteBtn">加载网站</button> <button id="loadWebsiteBtn">加载网站</button>
<div id="webLoadStatus" class="status"></div> <div id="webLoadStatus" class="status"></div>
@ -79,7 +79,7 @@
</main> </main>
<footer> <footer>
<p>DeepSearcher © 2025 | 企业知识管理与智能问答系统</p> <p>DeepSearcher © 2025 | 智能深度搜索系统</p>
</footer> </footer>
</div> </div>

2
deepsearcher/templates/static/js/app.js

@ -199,7 +199,7 @@ function handleStreamMessage(data) {
// 处理answer类型,显示查询结果 // 处理answer类型,显示查询结果
console.log('Processing answer message:', message.content.substring(0, 100) + '...'); console.log('Processing answer message:', message.content.substring(0, 100) + '...');
// 将结果内容显示在结果区域 // 将结果内容显示在结果区域
if (message.content && message.content !== "==== FINAL ANSWER====") { if (message.content) {
document.getElementById('resultText').textContent = message.content; document.getElementById('resultText').textContent = message.content;
showResult(); showResult();
} }

41
main.py

@ -203,47 +203,6 @@ def load_website(
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/query/")
def perform_query(
original_query: str = Query(
...,
description="Your question here.",
examples=["Write a report about Milvus."],
),
max_iter: int = Query(
3,
description="The maximum number of iterations for reflection.",
ge=1,
examples=[3],
),
):
"""
Perform a query against the loaded data.
Args:
original_query (str): The user's question or query.
max_iter (int, optional): Maximum number of iterations for reflection. Defaults to 3.
Returns:
dict: A dictionary containing the query result and token consumption.
Raises:
HTTPException: If the query fails.
"""
try:
# 清空之前的消息
message_stream = get_message_stream()
message_stream.clear_messages()
result_text, _ = query(original_query, max_iter=max_iter)
return {
"result": result_text,
"messages": message_stream.get_messages_as_dicts()
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/query-stream/") @app.get("/query-stream/")
async def perform_query_stream( async def perform_query_stream(
original_query: str = Query( original_query: str = Query(

2
test.py

@ -25,4 +25,4 @@ load_from_local_files(
# load_from_website(urls=website_url) # load_from_website(urls=website_url)
# Query # Query
result = query("Write a comprehensive report about Milvus.", max_iter=2) # Your question here result = query("Write a comprehensive report about Milvus.", max_iter=1) # Your question here

Loading…
Cancel
Save