diff --git a/deepsearcher/agent/deep_search.py b/deepsearcher/agent/deep_search.py index 3d6fdb6..860c225 100644 --- a/deepsearcher/agent/deep_search.py +++ b/deepsearcher/agent/deep_search.py @@ -23,9 +23,11 @@ COLLECTION_ROUTE_PROMPT = """ SUB_QUERY_PROMPT = """ 为了能够全面的回答这个问题,请你尝试把原本的问题拆分或扩展为几个子问题 -不可以太多,但是也不可以太少,请根据问题复杂程度来决定子问题的数量 +请你使用自顶向下和自底向上两种方向来思考如何拆分问题 +子问题的数量不可以太多,但是也不可以太少,应当保证问题的回答全面性,请根据问题复杂程度来决定子问题的数量 如果原问题本身非常简单,没有必要进行拆分,则保留输出原问题本身 -需要保证每个子问题都具体、清晰、不可分(原子性),最终返回一个字符串列表 +需要保证每个子问题都具体、清晰、不可分(原子性,即不可以再包含更细分的子问题),子问题中不要包含"请你回答"、"请你总结"、"请你分析"等祈使类型词语 +你需要最终返回一个字符串列表 原问题: {original_query} @@ -36,9 +38,10 @@ SUB_QUERY_PROMPT = """ 示例输出(例子中的数量不是要求): [ "什么是机器学习?", - "机器学习的使用目的是什么?", - "机器学习和深度学习的区别是什么?", - "机器学习的历史演进过程?" + "机器学习的使用目的", + "机器学习的常用算法", + "机器学习的历史演进过程", + "机器学习和深度学习的区别是什么?" ] @@ -49,26 +52,27 @@ SUB_QUERY_PROMPT = """ RERANK_PROMPT = """ -根据当前的问题和获取到的文档片段 -请你对当前获取到的文档是否能帮助回答这个问题(直接或间接,全面或部分,都可以)给出一个快速判断 -对于每一个文档片段,你只应该返回"YES"或者"NO"(需要注意顺序和数量) +根据当前的问题和获取到的文档片段(文档片段包裹都在标签中并有对应的连续的id) +请你对当前获取到的文档片段是否能帮助回答这个问题(直接或间接、全面或部分,都可以,但需要有实际有效内容)给出一个快速判断 +对于每一个文档片段,你只应该返回"True"或者"False"(需要注意顺序和数量) 问题: {query} 检索到的文档片段: {chunks} -例如,给定4个chunks(实际检索到的文档片段不一定是4个),返回: ["YES", "NO", "YES", "YES"] +例如,假如给出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: """ REFLECT_PROMPT = """ -根据原问题和子问题,以及获取到的文档片段,请你决定是否要生成更多的问题。 -如果已经获得的文档片段没能覆盖所有的子问题,这意味着这些文档无法被检索到。 -你可以尝试生成相似但些许不同的问题来尝试重新检索,但是也可以根据获得到的文档片段进行批评思考,生成新的问题来保证原问题的回答的准确和全面 -如果没有真的必要继续研究(取决于你的判断),返回一个空列表 +根据原问题和子问题,以及获取到的文档片段,请你决定是否要生成更多的问题,这些问题将被用于后续的思考和搜索。 +你应该根据已经获得到的文档片段进行批评思考,生成其他新的问题来保证原问题的回答的准确和全面,请你使用自顶向下和自底向上两种方向来思考如何生成新问题。 +如果已经获得的文档片段没能覆盖所有的子问题,这意味着有关这些问题的文档无法被检索到,你应该根据你自己的知识补充思考。 +需要保证每个新的问题都具体、清晰、不可分(原子性)并且不可以和之前的问题重复,新的问题中不要包含"请你回答"、"请你总结"、"请你分析"等祈使类型词语 +如果没有真的必要继续研究(取决于你的判断),返回一个空列表。 原问题: {original_query} @@ -83,23 +87,19 @@ REFLECT_PROMPT = """ SUMMARY_PROMPT = """ -你是一个内容分析专家,请你根据提供的问题和检索到的信息生成详尽的长文回答。 -如果检索到的信息不足以回答问题或者必须添加额外信息才能能回答,你应该使用你的知识来进行补充, -这种情况下,你自己提供的信息需要使用例如"your knowledge here[^0]"引用,注意,这里的"[^0]"的序号0是固定的,表示你的知识,下文当中有文末引用的例子 -同时,你应该根据提供的信息生成文内引用和文末参考资料列表,来自文档切片的reference引用从[^1]开始 -如果多个片段是相同的来源或者一个片段可以回答多个问题,文内引用可以引用多次,但文末只引用一次来源,即文末的引用列表中不能有重复的来源。 +你是一个内容分析专家 +请你综合已经提出的问题和检索到的信息,以原问题为中心,生成详细准确、层次分明、尽可能长的回答。 +如果检索到的信息不足以回答问题,你应该使用你的知识来进行扩展补充。 +注意,不要逐个回答问题,而是应该综合所有问题和信息,生成一个完整的回答。 +同时,你应该根据提供的信息生成文内引用"[^index]"(markdown文内引用)。 +如果你自己提供的信息需要使用"[^0]"引用,即你提供的信息使用固定index=0。 +来自的引用序号从[^index]从index=1开始,来源需要与前文中的"href"一致 +不需要对每个分配一个引用,而是相同共用一个引用。 +另外,如果回答的内容文内引用需要引用多个,请添加多个[^index]到句尾。 -例子: -文内引用示例(使用markdown脚注): -"XGBoost是非常强大的集成学习模型[^2]" -(必须使用 "[^index]",这里的index是对应的的id) - - -文末引用示例 (需要与前文reference的href一致,不需要对每个chunk分配一个引用,而是每一个referecen共用一个引用): -[^0]: AI Generated -[^2]: files/docs/chap_001_003_models.md +"XGBoost是非常强大的集成学习模型。[^1]但是XGBoost的缺点是计算复杂度高,需要大量的计算资源。[^0]" @@ -241,7 +241,7 @@ class DeepSearch(BaseAgent): continue # 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 content = self.llm.chat( @@ -262,14 +262,14 @@ class DeepSearch(BaseAgent): relevance_list = self.llm.literal_eval(content) if not isinstance(relevance_list, list): raise ValueError("Response is not a list") - except (ValueError, SyntaxError): + except Exception as _: # Fallback: if parsing fails, treat all chunks as relevant log.color_print(f"Warning: Failed to parse relevance response. Treating all chunks as relevant. Response was: {content}") - relevance_list = ["YES"] * len(retrieved_results) + relevance_list = ["True"] * len(retrieved_results) # Ensure we have enough relevance judgments for all chunks while len(relevance_list) < len(retrieved_results): - relevance_list.append("YES") # Default to relevant if no judgment provided + relevance_list.append("True") # Default to relevant if no judgment provided # Filter relevant chunks based on LLM response accepted_chunk_num = 0 @@ -278,9 +278,9 @@ class DeepSearch(BaseAgent): # Check if we have a relevance judgment for this chunk is_relevant = ( i < len(relevance_list) and - "YES" in relevance_list[i].upper() and - "NO" not in relevance_list[i].upper()) if i < len(relevance_list - ) else True + "True" in relevance_list[i] and + "False" not in relevance_list[i] + ) if i < len(relevance_list) else True if is_relevant: all_retrieved_results.append(retrieved_result) @@ -296,7 +296,7 @@ class DeepSearch(BaseAgent): def _generate_more_sub_queries( self, original_query: str, all_sub_queries: list[str], all_retrieved_results: list[RetrievalResult] ) -> list[str]: - chunks = self._format_chunks(all_retrieved_results) + chunks, _ = self._format_chunks(all_retrieved_results) reflect_prompt = REFLECT_PROMPT.format( original_query=original_query, all_sub_queries=all_sub_queries, @@ -394,7 +394,7 @@ class DeepSearch(BaseAgent): if not all_retrieved_results or len(all_retrieved_results) == 0: send_info(f"'{original_query}'没能找到更多信息!") return "", [] - chunks = self._format_chunks(all_retrieved_results) + chunks, refs = self._format_chunks(all_retrieved_results) send_info(f"正在总结 {len(all_retrieved_results)} 个查找到的文档片段") summary_prompt = SUMMARY_PROMPT.format( original_query=original_query, @@ -402,25 +402,33 @@ class DeepSearch(BaseAgent): chunks=chunks ) response = self.llm.chat([{"role": "user", "content": summary_prompt}]) - final_answer = self.llm.remove_think(response) - send_answer(final_answer) - return self.llm.remove_think(response), all_retrieved_results + response = self.llm.remove_think(response) + refs + send_answer(response) + 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放到字典中 - references = defaultdict(list) + ref_dict = defaultdict(list) for result in retrieved_results: - references[result.reference].append(result.text) - chunks = [] + ref_dict[result.reference].append(result.text) + formated_chunks = [] + formated_refs = ["\n\n[^0]: AI 生成\n"] chunk_count = 0 - for i, reference in enumerate(references): - formated = f"\n" + "".join( + for i, reference in enumerate(ref_dict): + formated_chunk = "".join( [ - f"\n{chunk}\n\n" - for j, chunk in enumerate(references[reference]) + ( + f"" + + f"\n{chunk}\n" + + f"\n" + ) + for j, chunk in enumerate(ref_dict[reference]) ] - ) + f"\n" - print(formated) - chunks.append(formated) - chunk_count += len(references[reference]) - return "".join(chunks) + ) + print(formated_chunk) + formated_chunks.append(formated_chunk) + chunk_count += len(ref_dict[reference]) + formated_refs.append(f"[^{i + 1}]: " + str(reference) + "\n") + formated_chunks = "".join(formated_chunks) + formated_refs = "".join(formated_refs) + return formated_chunks, formated_refs diff --git a/deepsearcher/config.yaml b/deepsearcher/config.yaml index fef4343..5b59538 100644 --- a/deepsearcher/config.yaml +++ b/deepsearcher/config.yaml @@ -2,7 +2,7 @@ provide_settings: llm: provider: "OpenAILLM" config: - model: "Qwen/Qwen3-30B-A3B-Thinking-2507" + model: "Qwen/Qwen3-32B" api_key: "sk-fpzwvagjkhwysjsozfybvtjzongatcwqdihdxzuijnfdrjzt" base_url: "https://api.siliconflow.cn/v1" @@ -83,5 +83,5 @@ query_settings: max_iter: 3 load_settings: - chunk_size: 1024 - chunk_overlap: 512 + chunk_size: 2048 + chunk_overlap: 1024 diff --git a/deepsearcher/llm/openai_llm.py b/deepsearcher/llm/openai_llm.py index 1af6046..eacf374 100644 --- a/deepsearcher/llm/openai_llm.py +++ b/deepsearcher/llm/openai_llm.py @@ -48,9 +48,9 @@ class OpenAILLM(BaseLLM): model=self.model, messages=messages, stream=True, - temperature=0.6, - top_p=0.8, - presence_penalty=1.2 + temperature=0.8, + top_p=0.9, + presence_penalty=1.4 ) as stream: # stream到控制台测试 content = "" diff --git a/deepsearcher/online_query.py b/deepsearcher/online_query.py index 096124a..48203a5 100644 --- a/deepsearcher/online_query.py +++ b/deepsearcher/online_query.py @@ -3,7 +3,7 @@ from deepsearcher import configuration from deepsearcher.vector_db.base import RetrievalResult -def query(original_query: str, max_iter: int | None = None) -> tuple[str, list[RetrievalResult]]: +def query(original_query: str, **kwargs) -> tuple[str, list[RetrievalResult]]: """ Query the knowledge base with a question and get an answer. @@ -20,6 +20,7 @@ def query(original_query: str, max_iter: int | None = None) -> tuple[str, list[R - A list of retrieval results that were used to generate the answer """ default_searcher = configuration.default_searcher + max_iter = kwargs.get("max_iter", 3) return default_searcher.query(original_query, max_iter=max_iter) diff --git a/deepsearcher/templates/html/index.html b/deepsearcher/templates/html/index.html index f9620c7..53d77e9 100644 --- a/deepsearcher/templates/html/index.html +++ b/deepsearcher/templates/html/index.html @@ -23,9 +23,9 @@
-

DeepSearcher 智能搜索系统

+

DeepSearcher 智能深度搜索系统

- 基于大型语言模型和向量数据库的企业知识管理系统,支持私有数据搜索和在线内容整合,提供准确答案和综合报告。 + 基于大型语言模型和向量数据库的知识管理系统,支持私有数据搜索和在线内容整合,提供准确答案和综合报告。

@@ -204,7 +204,7 @@
-

DeepSearcher © 2025 | 企业知识管理与智能问答系统

+

DeepSearcher © 2025 | 智能深度搜索系统

diff --git a/deepsearcher/templates/static/js/app.js b/deepsearcher/templates/static/js/app.js index 3afb3e2..65ed29e 100644 --- a/deepsearcher/templates/static/js/app.js +++ b/deepsearcher/templates/static/js/app.js @@ -276,7 +276,7 @@ document .filter((path) => path); setButtonLoading(button, true); - showStatus('loadStatus', '正在加载文件...', 'loading'); + showStatus('loadStatus', ' 正在加载文件...', 'loading'); hideResult(); hideProcessResult(); @@ -363,7 +363,7 @@ document .filter((url) => url); setButtonLoading(button, true); - showStatus('webLoadStatus', '正在加载网站内容...', 'loading'); + showStatus('webLoadStatus', ' 正在加载网站...', 'loading'); hideResult(); hideProcessResult(); diff --git a/deepsearcher/vector_db/milvus.py b/deepsearcher/vector_db/milvus.py index 720bef9..2376b22 100644 --- a/deepsearcher/vector_db/milvus.py +++ b/deepsearcher/vector_db/milvus.py @@ -152,7 +152,7 @@ class Milvus(BaseVectorDB): self, collection: str, vector: np.ndarray | list[float], - top_k: int = 3, + top_k: int = 4, query_text: str = None, *args, **kwargs, @@ -163,7 +163,7 @@ class Milvus(BaseVectorDB): Args: collection (Optional[str]): Collection name. If None, uses default_collection. vector (Union[np.array, List[float]]): Query vector for similarity search. - top_k (int, optional): Number of results to return. Defaults to 5. + top_k (int, optional): Number of results to return. Defaults to 4. query_text (Optional[str], optional): Original query text for hybrid search. Defaults to None. *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. diff --git a/docs/intro_docs/milvus_benchmark.md b/docs/intro_docs/milvus_benchmark.md index 66bdefd..b1e24e5 100644 --- a/docs/intro_docs/milvus_benchmark.md +++ b/docs/intro_docs/milvus_benchmark.md @@ -24,7 +24,13 @@ -
点击查看测试中使用的术语详情
+| 术语 | 说明 | +| ---- | ------------------------------------------------------------ | +| nq | 一次搜索请求中要搜索的向量数量 | +| topk | 搜索请求中每个向量(以 nq 为单位)的最近向量数 | +| ef | [HNSW 索引](https://milvus.io/docs/v2.2.x/index.md)特有的搜索参数 | +| RT | 从发送请求到接收响应的响应时间 | +| QPS | 每秒成功处理的搜索请求数 | @@ -76,7 +82,7 @@ -
服务器配置(群集)
+```yaml queryNode: replicas: 1 resources: limits: cpu: "12.0" memory: 8Gi requests: cpu: "12.0" memory: 8Gi``` @@ -93,7 +99,7 @@ -
服务器配置(单机)
+```yaml standalone: replicas: 1 resources: limits: cpu: "12.0" memory: 16Gi requests: cpu: "12.0" memory: 16Gi``` @@ -112,7 +118,7 @@ -
服务器配置(群集)
+```yaml queryNode: replicas: 1 resources: limits: cpu: "8.0" /"12.0" /"16.0" /"32.0" memory: 8Gi requests: cpu: "8.0" /"12.0" /"16.0" /"32.0" memory: 8Gi``` @@ -135,7 +141,7 @@ -
服务器配置(群集)
+```yaml queryNode: replicas: 1 / 2 / 4 / 8 resources: limits: cpu: "8.0" memory: 8Gi requests: cpu: "8.0" memory: 8Gi``` diff --git a/main.py b/main.py index e9e5eda..4b34478 100644 --- a/main.py +++ b/main.py @@ -112,7 +112,7 @@ def load_files( examples=[256], ), force_rebuild: bool = Body( - False, + True, description="Whether to force rebuild the collection if it already exists.", examples=[False], ), @@ -141,7 +141,7 @@ def load_files( batch_size=batch_size if batch_size is not None else 8, force_rebuild=force_rebuild, ) - return {"message": "Files loaded successfully."} + return {"message": "成功加载"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @@ -198,48 +198,7 @@ def load_website( batch_size=batch_size if batch_size is not None else 8, force_rebuild=force_rebuild, ) - return {"message": "Website loaded successfully."} - except Exception as 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) - - return { - "result": result_text, - "messages": message_stream.get_messages_as_dicts() - } + return {"message": "成功加载"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @@ -297,7 +256,7 @@ async def perform_query_stream( def run_query(): try: print(f"Starting query: {original_query} with max_iter: {max_iter}") - result_text, retrieval_results = query(original_query, max_iter) + result_text, retrieval_results = query(original_query, max_iter=max_iter) print(f"Query completed with result length: {len(result_text) if result_text else 0}") print(f"Retrieved {len(retrieval_results) if retrieval_results else 0} documents") return result_text, None diff --git a/test.py b/test.py index 171e4c8..29cff4e 100644 --- a/test.py +++ b/test.py @@ -16,7 +16,7 @@ load_from_local_files( paths_or_directory="docs", collection_name="default", collection_description="a general collection for all documents", - force_rebuild=True, batch_size=8 + force_rebuild=True, batch_size=16 ) @@ -25,4 +25,4 @@ load_from_local_files( # load_from_website(urls=website_url) # Query -result = query("Write a comprehensive report about Milvus.") # Your question here +result = query("Write a comprehensive report about Milvus.", max_iter=1) # Your question here