kb_search.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. """
  2. 知识库检索工具
  3. 用于在聊天中检索自建知识库内容
  4. """
  5. from typing import List, Dict, Optional
  6. from ..core.qdrant_client import get_qdrant_manager
  7. from ..utils.embedding_service import get_embedding_service
  8. from ..db.knowledge_base import search_chunks_by_ids
  9. def search_knowledge_base(query: str, tenant_id: str, top_k: int = 5) -> List[Dict]:
  10. """
  11. 在知识库中搜索相关内容
  12. :param query: 用户查询文本
  13. :param tenant_id: 租户ID(appName)
  14. :param top_k: 返回Top K个结果
  15. :return: 检索结果列表 [{"content": "...", "score": 0.95, "doc_id": "..."}, ...]
  16. """
  17. try:
  18. # 1. 将查询文本向量化
  19. embedding_service = get_embedding_service()
  20. query_vector = embedding_service.generate_single_embedding(query, tenant_id)
  21. if not query_vector:
  22. return []
  23. # 2. 在 Qdrant 中进行向量相似度搜索
  24. qdrant = get_qdrant_manager()
  25. results = qdrant.search_vectors(tenant_id, query_vector, top_k)
  26. # 3. 从 MongoDB 获取完整的切片内容
  27. if results:
  28. vector_ids = [r["vector_id"] for r in results]
  29. chunks = search_chunks_by_ids(vector_ids)
  30. # 合并 Qdrant 的相似度分数和 MongoDB 的完整内容
  31. vector_id_to_chunk = {chunk["vector_id"]: chunk for chunk in chunks}
  32. enriched_results = []
  33. for result in results:
  34. chunk = vector_id_to_chunk.get(result["vector_id"])
  35. if chunk:
  36. enriched_results.append({
  37. "content": chunk["content"],
  38. "score": result["score"],
  39. "doc_id": chunk["doc_id"],
  40. "chunk_index": chunk["chunk_index"],
  41. })
  42. return enriched_results
  43. return []
  44. except Exception as e:
  45. print(f"知识库检索失败: {str(e)}")
  46. return []
  47. def format_knowledge_context(search_results: List[Dict]) -> str:
  48. """
  49. 将检索结果格式化为上下文文本,用于注入到 prompt
  50. :param search_results: 检索结果列表
  51. :return: 格式化的上下文文本
  52. """
  53. if not search_results:
  54. return ""
  55. context_parts = ["<知识库相关信息>"]
  56. for idx, result in enumerate(search_results, 1):
  57. context_parts.append(f"\n[文档片段 {idx}]")
  58. context_parts.append(result["content"])
  59. context_parts.append("\n</知识库相关信息>")
  60. return "\n".join(context_parts)
  61. def get_custom_knowledge_tools(tenant_id: str) -> List[Dict]:
  62. """
  63. 构造自定义知识库检索工具配置
  64. 用于替代豆包原生的知识库工具
  65. :param tenant_id: 租户ID
  66. :return: 工具配置列表
  67. """
  68. # 注意:这里返回空列表,因为我们将知识库检索前置到prompt中
  69. # 而不是作为工具让模型调用
  70. return []