1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
| import warnings import torch import os import json import numpy as np import time from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uvicorn from sentence_transformers import SentenceTransformer
warnings.filterwarnings("ignore") os.environ["CUDA_VISIBLE_DEVICES"] = "" torch.cuda.is_available = lambda: False torch.set_num_threads(4)
MODEL_PATH = "local_model/bge-small-zh-v1.5"
CONFIG_PATH = "config/command_list.json"
CONFIG_CACHE = {} CMD_EMBEDDINGS_CACHE = None
def load_config(): """从静态JSON文件加载配置""" global CONFIG_CACHE, CMD_EMBEDDINGS_CACHE try: print("📄 从静态JSON文件加载配置...") with open(CONFIG_PATH, "r", encoding="utf-8") as f: CONFIG_CACHE = json.load(f)
if not isinstance(CONFIG_CACHE.get("command_list"), list) or len(CONFIG_CACHE["command_list"]) == 0: raise ValueError("command_list必须是非空列表") if not isinstance(CONFIG_CACHE.get("threshold"), (int, float)): raise ValueError("threshold必须是数字") command_list = CONFIG_CACHE["command_list"] threshold = CONFIG_CACHE["threshold"] CMD_EMBEDDINGS_CACHE = model.encode( command_list, convert_to_numpy=True, normalize_embeddings=True ) print(f"✅ 配置加载成功:指令库{len(command_list)}条,阈值{threshold}") return CONFIG_CACHE except Exception as e: raise RuntimeError(f"加载配置失败:{e}")
def save_config(new_config): """保存新配置到静态JSON文件""" global CONFIG_CACHE, CMD_EMBEDDINGS_CACHE try: print("📄 保存配置到静态JSON文件...") if not isinstance(new_config.get("command_list"), list) or len(new_config["command_list"]) == 0: raise ValueError("command_list必须是非空列表") if not isinstance(new_config.get("threshold"), (int, float)) or new_config["threshold"] < 0 or new_config["threshold"] > 1: raise ValueError("threshold必须是0-1之间的数字")
with open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(new_config, f, ensure_ascii=False, indent=2)
load_config() return True except Exception as e: raise ValueError(f"保存配置失败:{e}")
print("🔍 加载模型...") try: model = SentenceTransformer(MODEL_PATH, device="cpu") load_config() except Exception as e: raise RuntimeError(f"服务启动失败:{e}")
app = FastAPI(title="船舶指令推理API", version="1.0")
class UpdateConfigRequest(BaseModel): command_list: list[str] threshold: float = 0.75
class InferRequest(BaseModel): asr_text: str
@app.get("/get_config", summary="查询当前配置(指令库/阈值)") async def get_config(): try: config = load_config()
return { "code": 200, "msg": "查询成功", "data": { "command_list": config.get("command_list", []), "threshold": config.get("threshold", 0.75), "update_time": int(time.time()) } } except Exception as e: raise HTTPException(status_code=500, detail=f"查询配置失败:{e}")
@app.post("/update_config", summary="更新配置(静态JSON数据源)") async def update_config(request: UpdateConfigRequest): try: new_config = { "command_list": request.command_list, "threshold": request.threshold } save_config(new_config) return { "code": 200, "msg": "配置更新成功", "data": new_config } except ValueError as e: raise HTTPException(status_code=400, detail=f"参数错误:{e}") except Exception as e: raise HTTPException(status_code=500, detail=f"配置更新失败:{e}")
@app.post("/infer", summary="船舶指令推理(根据配置读取数据源)") async def infer(request: InferRequest): try: start_time = time.time()
if not CONFIG_CACHE or CMD_EMBEDDINGS_CACHE is None: load_config()
if not CONFIG_CACHE or CMD_EMBEDDINGS_CACHE is None: raise RuntimeError("配置加载失败,请检查数据库连接和配置")
asr_embedding = model.encode( [request.asr_text], convert_to_numpy=True, normalize_embeddings=True )
similarity = np.dot(asr_embedding, CMD_EMBEDDINGS_CACHE.T)[0] top_idx = np.argmax(similarity) matched_command = CONFIG_CACHE["command_list"][top_idx] similarity_score = round(float(similarity[top_idx]), 3) is_execute = similarity_score >= CONFIG_CACHE["threshold"]
infer_time = round((time.time() - start_time) * 1000, 2)
return { "code": 200, "msg": "推理成功", "data": { "asr_text": request.asr_text, "matched_command": matched_command, "similarity_score": similarity_score, "threshold": CONFIG_CACHE["threshold"], "is_execute": is_execute, "infer_time_ms": infer_time } } except Exception as e: raise HTTPException(status_code=500, detail=f"推理失败:{e}")
@app.get("/health", summary="服务健康检查") async def health_check(): return { "code": 200, "msg": "服务运行中", "data": { "model_loaded": True if 'model' in locals() else False, "config_loaded": True if CONFIG_CACHE else False, "command_count": len(CONFIG_CACHE.get("command_list", [])), "threshold": CONFIG_CACHE.get("threshold"), "cpu_only": True, "timestamp": int(time.time()) } }
if __name__ == "__main__": uvicorn.run( app, host="0.0.0.0", port=9529, workers=1, log_level="error" )
|