v2.0,还原models_server等原方法代码,更改groundcontrol拉起方式
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
"collection_name":"drone_docs",
|
"collection_name":"drone_docs",
|
||||||
"model_config_llamacpp":
|
"model_config_llamacpp":
|
||||||
{
|
{
|
||||||
"n_ctx":512,
|
"n_ctx":40960,
|
||||||
"n_threads":4,
|
"n_threads":4,
|
||||||
"n_gpu_layers":36,
|
"n_gpu_layers":36,
|
||||||
"n_seq_max":256,
|
"n_seq_max":256,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -213,6 +213,39 @@ class Models_Client:
|
|||||||
返回:
|
返回:
|
||||||
包含推理结果的字典
|
包含推理结果的字典
|
||||||
"""
|
"""
|
||||||
|
# # 处理图像
|
||||||
|
# image_data = []
|
||||||
|
|
||||||
|
# if images:
|
||||||
|
# # for img in images:
|
||||||
|
# try:
|
||||||
|
# # image_data.append(PIL_image_to_base64(image=img))
|
||||||
|
# image_data= images
|
||||||
|
# except Exception as e:
|
||||||
|
# return {"error": f"处理图像base64对象失败: {str(e)}"}
|
||||||
|
|
||||||
|
# elif image_paths:
|
||||||
|
# for path in image_paths:
|
||||||
|
# try:
|
||||||
|
# image_data.append(PIL_image_to_base64(image_path=path))
|
||||||
|
# except Exception as e:
|
||||||
|
# return {"error": f"处理图像 {path} 失败: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# if not image_data:
|
||||||
|
# return {"error": "未提供有效的图像数据"}
|
||||||
|
|
||||||
|
# payload = {
|
||||||
|
# "user_prompt": prompt,
|
||||||
|
# "image_data": image_data,
|
||||||
|
# "max_tokens": max_tokens,
|
||||||
|
# "temperature": temperature,
|
||||||
|
# "top_p": top_p,
|
||||||
|
# "system_prompt": system_prompt,
|
||||||
|
# "stop": stop
|
||||||
|
# }
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 将 Pydantic 模型转换为 JSON 字符串
|
# 将 Pydantic 模型转换为 JSON 字符串
|
||||||
if hasattr(request, 'model_dump_json'):
|
if hasattr(request, 'model_dump_json'):
|
||||||
|
|||||||
@@ -818,7 +818,6 @@ def test_inference(inference_type=0):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
# main()
|
||||||
# test_inference(2)
|
# test_inference(2)
|
||||||
# logger.info("work finish")
|
logger.info("work finish")
|
||||||
|
|
||||||
@@ -85,7 +85,90 @@ def PIL_image_to_base64( image_path: Optional[str] = None, image: Optional[PIL_I
|
|||||||
image.save(buffered, format="JPEG", quality=90)
|
image.save(buffered, format="JPEG", quality=90)
|
||||||
return base64.b64encode(buffered.getvalue()).decode('utf-8')
|
return base64.b64encode(buffered.getvalue()).decode('utf-8')
|
||||||
|
|
||||||
# Removed ROS specific functions (ros_image2pil_image, ros_image2dict) as they depend on rospy/cv_bridge
|
# def ros_image2pil_image(self, ros_image_msg:Sensor_Image,supported_image_formats):
|
||||||
|
# """回调函数:将ROS Image转为PIL Image并处理"""
|
||||||
|
# try:
|
||||||
|
# bridge_cv = CvBridge()
|
||||||
|
# # 1. 将ROS Image消息转为OpenCV格式(默认BGR8编码)
|
||||||
|
# # 若图像编码不同(如rgb8),需指定格式:bridge.imgmsg_to_cv2(ros_image_msg, "rgb8")
|
||||||
|
# # 尝试转换为OpenCV格式
|
||||||
|
# if ros_image_msg.encoding in supported_image_formats:
|
||||||
|
# cv_image = bridge_cv.imgmsg_to_cv2(ros_image_msg, desired_encoding=ros_image_msg.encoding)
|
||||||
|
# else:
|
||||||
|
# # 尝试默认转换
|
||||||
|
# cv_image = bridge_cv.imgmsg_to_cv2(ros_image_msg)
|
||||||
|
# logger.warning(f"转换不支持的图像编码: {ros_image_msg.encoding}")
|
||||||
|
|
||||||
|
|
||||||
|
# # 2. OpenCV默认是BGR格式,转为PIL需要的RGB格式
|
||||||
|
# rgb_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
|
# # 3. 转为PIL Image格式
|
||||||
|
# pil_image = PIL_Image.fromarray(rgb_image)
|
||||||
|
|
||||||
|
# # 此处可添加对PIL Image的处理(如显示、保存等)
|
||||||
|
# rospy.loginfo(f"转换成功:PIL Image尺寸 {pil_image.size}")
|
||||||
|
# return pil_image
|
||||||
|
# # 示例:显示图像(需要PIL的显示支持)
|
||||||
|
# # pil_image.show()
|
||||||
|
|
||||||
|
# except CvBridgeError as e:
|
||||||
|
# rospy.logerr(f"转换失败:{e}")
|
||||||
|
# except Exception as e:
|
||||||
|
# rospy.logerr(f"处理错误:{e}")
|
||||||
|
|
||||||
|
|
||||||
|
# def ros_image2dict(self, image_msg: Sensor_Image,
|
||||||
|
# supported_image_formats=['bgr8', 'rgb8', 'mono8'],
|
||||||
|
# max_dim:int =2000) :
|
||||||
|
# """
|
||||||
|
# 将ROS图像消息转换为字典格式,便于在上下文中存储
|
||||||
|
|
||||||
|
# 参数:
|
||||||
|
# image_msg: ROS的Image消息
|
||||||
|
|
||||||
|
# 返回:
|
||||||
|
# 包含图像信息的字典,或None(转换失败时)
|
||||||
|
# """
|
||||||
|
# try:
|
||||||
|
# # 尝试转换为OpenCV格式
|
||||||
|
# if image_msg.encoding in self.supported_image_formats:
|
||||||
|
# cv_image = self.bridge.imgmsg_to_cv2(image_msg, desired_encoding=image_msg.encoding)
|
||||||
|
# else:
|
||||||
|
# # 尝试默认转换
|
||||||
|
# cv_image = self.bridge.imgmsg_to_cv2(image_msg)
|
||||||
|
# logger.warning(f"转换不支持的图像编码: {image_msg.encoding}")
|
||||||
|
|
||||||
|
# # 图像预处理:调整大小以减少数据量
|
||||||
|
# h, w = cv_image.shape[:2]
|
||||||
|
# if max(h, w) > max_dim:
|
||||||
|
# scale = max_dim / max(h, w)
|
||||||
|
# cv_image = cv2.resize(
|
||||||
|
# cv_image,
|
||||||
|
# (int(w * scale), int(h * scale)),
|
||||||
|
# interpolation=cv2.INTER_AREA
|
||||||
|
# )
|
||||||
|
|
||||||
|
# # 转换为JPEG并编码为base64
|
||||||
|
# encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 80]
|
||||||
|
# _, buffer = cv2.imencode('.jpg', cv_image, encode_param)
|
||||||
|
# img_base64 = base64.b64encode(buffer).decode('utf-8')
|
||||||
|
|
||||||
|
# return {
|
||||||
|
# "type": "image",
|
||||||
|
# "format": "jpg",
|
||||||
|
# "data": img_base64,
|
||||||
|
# "width": cv_image.shape[1],
|
||||||
|
# "height": cv_image.shape[0],
|
||||||
|
# "original_encoding": image_msg.encoding,
|
||||||
|
# "timestamp": image_msg.header.stamp.to_sec()
|
||||||
|
# }
|
||||||
|
|
||||||
|
# except CvBridgeError as e:
|
||||||
|
# logger.error(f"CV桥接错误: {str(e)}")
|
||||||
|
# except Exception as e:
|
||||||
|
# logger.error(f"图像处理错误: {str(e)}")
|
||||||
|
# return None
|
||||||
|
|
||||||
def PIL_image_to_base64_sizelmt(image_path:Optional[str]=None,image:Optional[PIL_Image.Image] = None,
|
def PIL_image_to_base64_sizelmt(image_path:Optional[str]=None,image:Optional[PIL_Image.Image] = None,
|
||||||
need_size_lmt:bool= False, max_size:tuple=(800, 800), quality:float=90):
|
need_size_lmt:bool= False, max_size:tuple=(800, 800), quality:float=90):
|
||||||
|
|||||||
@@ -101,16 +101,15 @@ def set_embeddings(embedding_model_path,
|
|||||||
model_config:Optional[dict[str,Any]]):
|
model_config:Optional[dict[str,Any]]):
|
||||||
|
|
||||||
if "llamacpp_embeddings" == embedding_type:
|
if "llamacpp_embeddings" == embedding_type:
|
||||||
# 使用 .get() 方法提供默认值,确保即使配置文件中缺少某些字段也能正常工作
|
|
||||||
embeddings = set_embeddings_llamacpp(
|
embeddings = set_embeddings_llamacpp(
|
||||||
model_path=embedding_model_path,
|
model_path=embedding_model_path,
|
||||||
n_ctx=model_config.get("n_ctx", 512),
|
n_ctx=model_config["n_ctx"],
|
||||||
n_threads=model_config.get("n_threads", 4),
|
n_threads=model_config["n_threads"],
|
||||||
n_gpu_layers=model_config.get("n_gpu_layers", 0), # RTX 5090建议设20,充分利用GPU
|
n_gpu_layers=model_config["n_gpu_layers"], # RTX 5090建议设20,充分利用GPU
|
||||||
n_seq_max=model_config.get("n_seq_max", 128),
|
n_seq_max=model_config["n_seq_max"],
|
||||||
n_threads_batch=model_config.get("n_threads_batch", 4),
|
n_threads_batch = model_config["n_threads_batch"],
|
||||||
flash_attn=model_config.get("flash_attn", True),
|
flash_attn = model_config["flash_attn"],
|
||||||
verbose=model_config.get("verbose", False)
|
verbose=model_config["verbose"]
|
||||||
)
|
)
|
||||||
elif "huggingFace_embeddings" == embedding_type:
|
elif "huggingFace_embeddings" == embedding_type:
|
||||||
embeddings = set_embeddings_huggingFace(
|
embeddings = set_embeddings_huggingFace(
|
||||||
@@ -157,49 +156,19 @@ def load_vector_database(embeddings,
|
|||||||
collection_name:str):
|
collection_name:str):
|
||||||
"""加载已存在的向量数据库"""
|
"""加载已存在的向量数据库"""
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(path=persist_directory):
|
if os.path.exists(path=persist_directory):
|
||||||
logger.warning(f"向量数据库目录不存在: {persist_directory}")
|
vector_db = Chroma(
|
||||||
|
persist_directory=persist_directory,
|
||||||
|
embedding_function=embeddings,
|
||||||
|
collection_name=collection_name
|
||||||
|
)
|
||||||
|
print(f"已加载向量数据库 from {persist_directory}")
|
||||||
|
|
||||||
|
return vector_db
|
||||||
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 先检查集合是否存在
|
|
||||||
try:
|
|
||||||
import chromadb
|
|
||||||
client = chromadb.PersistentClient(path=persist_directory)
|
|
||||||
collections = client.list_collections()
|
|
||||||
collection_names = [col.name for col in collections]
|
|
||||||
|
|
||||||
if collection_name not in collection_names:
|
|
||||||
logger.warning(f"集合 '{collection_name}' 不存在于数据库中。")
|
|
||||||
logger.info(f"可用的集合: {collection_names}")
|
|
||||||
# 尝试查找有数据的集合
|
|
||||||
for col in collections:
|
|
||||||
if col.count() > 0:
|
|
||||||
logger.info(f"发现非空集合: {col.name} (count: {col.count()})")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 检查集合是否有数据
|
|
||||||
target_collection = client.get_collection(name=collection_name)
|
|
||||||
doc_count = target_collection.count()
|
|
||||||
if doc_count == 0:
|
|
||||||
logger.warning(f"集合 '{collection_name}' 存在但为空 (count: 0)")
|
|
||||||
logger.warning(f"⚠️ 该集合没有数据,RAG 检索将返回空结果")
|
|
||||||
# 仍然返回数据库对象,允许后续操作(如添加数据)
|
|
||||||
else:
|
|
||||||
logger.info(f"✅ 集合 '{collection_name}' 包含 {doc_count} 条文档")
|
|
||||||
except Exception as check_error:
|
|
||||||
logger.warning(f"检查集合时出错: {check_error},继续尝试加载...")
|
|
||||||
|
|
||||||
# 加载向量数据库
|
|
||||||
vector_db = Chroma(
|
|
||||||
persist_directory=persist_directory,
|
|
||||||
embedding_function=embeddings,
|
|
||||||
collection_name=collection_name
|
|
||||||
)
|
|
||||||
logger.info(f"✅ 成功加载向量数据库 from {persist_directory}, 集合: {collection_name}")
|
|
||||||
return vector_db
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"vector_database加载失败: {str(e)}", exc_info=True)
|
logger.error(f"vector_database加载失败: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
#设置文本分割器
|
#设置文本分割器
|
||||||
@@ -237,7 +206,7 @@ def set_document_loaders(self):
|
|||||||
def retrieve_relevant_info(vectorstore,
|
def retrieve_relevant_info(vectorstore,
|
||||||
query: str,
|
query: str,
|
||||||
k: int = 3,
|
k: int = 3,
|
||||||
score_threshold: float = None
|
score_threshold: float = 0.2
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
检索与查询相关的信息
|
检索与查询相关的信息
|
||||||
@@ -245,8 +214,7 @@ def retrieve_relevant_info(vectorstore,
|
|||||||
参数:
|
参数:
|
||||||
query: 查询文本
|
query: 查询文本
|
||||||
k: 最多返回的结果数量
|
k: 最多返回的结果数量
|
||||||
score_threshold: 相关性分数阈值。如果为 None,则使用自适应阈值(前 k 个结果中最高分数的 1.5 倍)
|
score_threshold: 相关性分数阈值
|
||||||
对于 L2 距离,分数越小越相似,阈值应该是一个较大的值
|
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
包含相关文档内容和分数的列表
|
包含相关文档内容和分数的列表
|
||||||
@@ -255,41 +223,21 @@ def retrieve_relevant_info(vectorstore,
|
|||||||
# 执行相似性搜索,返回带分数的结果
|
# 执行相似性搜索,返回带分数的结果
|
||||||
docs_and_scores = vectorstore.similarity_search_with_score(query, k=k)
|
docs_and_scores = vectorstore.similarity_search_with_score(query, k=k)
|
||||||
|
|
||||||
if not docs_and_scores:
|
|
||||||
logger.info(f"检索到 0 条结果 (k={k})")
|
|
||||||
return []
|
|
||||||
|
|
||||||
# 如果没有指定阈值,使用自适应阈值
|
|
||||||
# 对于 L2 距离,取前 k 个结果中最高的分数,然后乘以一个系数作为阈值
|
|
||||||
if score_threshold is None:
|
|
||||||
max_score = max(score for _, score in docs_and_scores)
|
|
||||||
# 使用最高分数的 1.5 倍作为阈值,确保包含所有前 k 个结果
|
|
||||||
score_threshold = max_score * 1.5
|
|
||||||
logger.debug(f"自适应阈值: {score_threshold:.2f} (基于最高分数 {max_score:.2f})")
|
|
||||||
|
|
||||||
# 过滤并格式化结果
|
# 过滤并格式化结果
|
||||||
results = []
|
results = []
|
||||||
for doc, score in docs_and_scores:
|
for doc, score in docs_and_scores:
|
||||||
# 对于 L2 距离,分数越小越相似
|
if score < score_threshold: # 分数越低表示越相似
|
||||||
# 如果阈值很大(> 1000),说明是 L2 距离,使用 < 比较
|
|
||||||
# 如果阈值很小(< 1),说明可能是余弦距离,使用 < 比较
|
|
||||||
if score < score_threshold:
|
|
||||||
results.append({
|
results.append({
|
||||||
"content": doc.page_content,
|
"content": doc.page_content,
|
||||||
"metadata": doc.metadata,
|
"metadata": doc.metadata,
|
||||||
"similarity_score": float(score)
|
"similarity_score": float(score)
|
||||||
})
|
})
|
||||||
else:
|
|
||||||
logger.debug(f"文档因分数 {score:.2f} >= 阈值 {score_threshold:.2f} 被过滤")
|
|
||||||
|
|
||||||
threshold_str = f"{score_threshold:.2f}" if score_threshold is not None else "自适应"
|
logger.info(f"检索到 {len(results)} 条相关信息 (k={k}, 阈值={score_threshold})")
|
||||||
logger.info(f"检索到 {len(results)} 条相关信息 (k={k}, 阈值={threshold_str})")
|
|
||||||
if results:
|
|
||||||
logger.debug(f"相似度分数范围: {min(r['similarity_score'] for r in results):.2f} - {max(r['similarity_score'] for r in results):.2f}")
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"检索相关信息失败: {str(e)}", exc_info=True)
|
logger.error(f"检索相关信息失败: {str(e)}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_retriever(vectorstore,search_kwargs: Dict[str, Any] = None) -> Any:
|
def get_retriever(vectorstore,search_kwargs: Dict[str, Any] = None) -> Any:
|
||||||
|
|||||||
Reference in New Issue
Block a user