Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/9701sunsets_aiot-intelligent-env-assistant.json
T
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
9.6 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "9701sunsets/aiot-intelligent-env-assistant", "files": {"backend_server/app/ai/llm_service.py": {"2c18a914a457ef41bc23bd7fc060d85134441956": {"t": 1786067530.3270874, "n": 8336, "h": "d9ed9bbcf6f7f950", "c": "import os\nimport json\nimport requests\nimport logging\nimport math\ntry:\n import openai\nexcept ImportError:\n openai = None\n\nlogger = logging.getLogger(__name__)\n\nclass LLMService:\n \"\"\"\n 封装与语言模型交互的逻辑,提供统一接口\n generate_advice(data) -> {\"advice\": str, \"level\": \"ok\"|\"warning\"|\"critical\"}\n \"\"\"\n def __init__(self, openai_model=\"gpt-3.5-turbo\", deepseek_endpoint=None, deepseek_key=None):\n self.model = openai_model\n self.api_key = os.environ.get(\"OPENAI_API_KEY\")\n # DeepSeek 配置:优先从构造参数,再从环境变量读取\n self.deepseek_endpoint = deepseek_endpoint or os.environ.get(\"DEEPSEEK_ENDPOINT\", \"https://api.deepseek.com\")\n self.deepseek_key = deepseek_key or os.environ.get(\"DEEPSEEK_API_KEY\")\n if self.api_key and openai:\n openai.api_key = self.api_key\n\n def generate_advice(self, data):\n \"\"\"\n data: dict with keys device_id, temperature, humidity, light\n 返回 dict: {\"advice\": \"...\", \"level\": \"...\"}\n \"\"\"\n\n def _is_valid_number(v):\n try:\n f = float(v)\n return math.isfinite(f)\n except Exception:\n return False\n\n is_chat = \"question\" in data and data.get(\"question\")\n if not is_chat:\n if not (_is_valid_number(data.get(\"temperature\")) and _is_valid_number(data.get(\"humidity\")) and _is_valid_number(data.get(\"light\"))):\n logger.warning(\"Invalid sensor inputs for device %s: %s\", data.get(\"device_id\"), data)\n return {\"advice\": \"传感器数据缺失,请检查设备连接。\", \"level\": \"critical\"}\n\n # 若配置了 deepseek key,优先调用 DeepSeek\n logger.info(\"generate_advice: deepseek_key=%s\", bool(self.deepseek_key))\n if self.deepseek_key:\n try:\n ds = self._call_deepseek(data, mode=\"chat\" if is_chat else \"advice\")\n if isinstance(ds, dict):\n if is_chat and ds.get(\"answer\"):\n return ds\n if ds.get(\"advice\") and ds.get(\"level\"):\n return ds\n except Exception as e:\n # 出错则回退到 OpenAI 或规则引擎\n logger.error(\"Error occurred while calling DeepSeek: %s\", e)\n logger.info(\"generate_advice: deepseek_key=%s\", bool(self.deepseek_key))\n\n # 之后尝试 OpenAI(原有逻辑)\n if self.api_key and openai:\n try:\n # ... existing openai call code ...\n pass\n except Exception as e:\n logger.error(\"Error occurred while calling OpenAI: %s\", e)\n\n return self._rule_engine_advice(data)\n\n def _rule_engine_advice(self, d):\n try:\n t = float(d.get(\"temperature\", 0))\n except Exception as e:\n logger.error(\"Error occurred while parsing temperature value: %s\", e)\n t = 0.0\n try:\n h = float(d.get(\"humidity\", 0))\n except Exception as e:\n logger.error(\"Error occurred while parsing humidity value: %s\", e)\n h = 0.0\n try:\n l = float(d.get(\"light\", 0))\n except Exception as e:\n logger.error(\"Error occurred while parsing light value: %s\", e)\n l = 0.0\n\n # 简单阈值策略(可按需调整)\n if (t >= 35) or (h >= 90):\n return {\"advice\": \"室内温度/湿度极高,请立即处理(通风/制冷/除湿)。\", \"level\": \"critical\"}\n if (t >= 30 and h >= 75) or (h >= 80):\n return {\"advice\": \"室内较闷热潮湿,建议开启空调除湿或通风。\", \"level\": \"warning\"}\n if (t >= 28) or (h >= 70):\n return {\"advice\": \"室内偏热潮,建议检查通风或降低温度。\", \"level\": \"warning\"}\n return {\"advice\": \"环境良好,无需特别操作。\", \"level\": \"ok\"}\n \n def _call_deepseek(self, data, mode=\"advice\"):\n \"\"\"\n 使用 DeepSeek 的 OpenAI-compatible SDK 调用 chat completion。\n 要求:安装 openai 包 (pip install openai),并确保 self.deepseek_endpoint 为 base_url(如 \"https://api.deepseek.com\")。\n 返回 dict {\"advice\": str, \"level\": \"ok\"|\"warning\"|\"critical\"}\n \"\"\"\n\n try:\n from openai import OpenAI\n except Exception as e:\n raise RuntimeError(\"OpenAI SDK not installed. Install with: pip install openai\") from e\n\n base_url = (self.deepseek_endpoint or \"\").rstrip('/')\n if base_url.endswith(\"/v1\"):\n base_url = base_url.rstrip(\"/v1\").rstrip('/')\n\n client = OpenAI(api_key=self.deepseek_key, base_url=base_url)\n model_name = \"deepseek-v4-pro\"\n\n # 构造消息\n if mode == \"chat\":\n system_msg = (\n \"你是一名专业的物联网环境陪伴助手,使用中文回答用户问题。\"\n \"给定传感器读数与用户提问,返回严格的 JSON:{\\\"answer\\\": \\\"...\\\"},不要输出其它内容。\"\n )\n user_payload = {\n \"device_id\": data.get(\"device_id\"),\n \"temperature\": data.get(\"temperature\"),\n \"humidity\": data.get(\"humidity\"),\n \"light\": data.get(\"light\"),\n \"question\": data.get(\"question\")\n }\n user_msg = json.dumps(user_payload, ensure_ascii=False)\n messages = [\n {\"role\": \"system\", \"content\": system_msg},\n {\"role\": \"user\", \"content\": f\"Sensor+Question: {user_msg}\\nRespond with JSON only.\"}\n ]\n else:\n # 以前的 advice 模式(保持原有 prompt)\n system_msg = (\n \"你是一名专业的物联网环境分析师,根据传感器读数,\"\n \"return a JSON object {\\\"advice\\\": str, \\\"level\\\": \\\"ok\\\"|\\\"warning\\\"|\\\"critical\\\"}.\"\n \" Do not output any extra text.\"\n )\n user_msg = json.dumps({\n \"device_id\": data.get(\"device_id\"),\n \"temperature\": data.get(\"temperature\"),\n \"humidity\": data.get(\"humidity\"),\n \"light\": data.get(\"light\")\n }, ensure_ascii=False)\n messages = [\n {\"role\": \"system\", \"content\": system_msg},\n {\"role\": \"user\", \"content\": f\"Sensor data: {user_msg}\\nRespond with JSON only.\"}\n ]\n\n text = \"\"\n try:\n resp = client.chat.completions.create(\n model=model_name,\n messages=messages,\n stream=False,\n reasoning_effort=\"high\",\n extra_body={\"thinking\": {\"type\": \"enabled\"}}\n )\n # Safe extraction of response text\n try:\n text = resp.choices[0].message.content\n except Exception:\n # fallback for different response shapes\n try:\n choice0 = resp.choices[0] if resp.choices else None\n msg = getattr(choice0, \"message\", None) or (choice0.get(\"message\") if isinstance(choice0, dict) else None)\n if isinstance(msg, dict):\n text = msg.get(\"content\", \"\") or \"\"\n else:\n text = str(msg or \"\")\n except Exception:\n text = \"\"\n except Exception:\n logger.exception(\"DeepSeek SDK request failed\")\n raise\n\n text = (text or \"\").strip()\n logger.info(\"DeepSeek SDK reply (truncated): %s\", text[:1000])\n\n # 尝试解析 JSON;若失败尝试抽取 {...} 子串\n parsed = None\n try:\n parsed = json.loads(text)\n except Exception:\n start = text.find('{')\n end = text.rfind('}')\n if start != -1 and end != -1 and end > start:\n try:\n parsed = json.loads(text[start:end+1])\n except Exception:\n parsed = None\n\n if isinstance(parsed, dict):\n if mode == \"chat\" and parsed.get(\"answer\"):\n return {\"answer\": parsed.get(\"answer\", \"\").strip()}\n if parsed.get(\"advice\") and parsed.get(\"level\"):\n return {\"advice\": parsed.get(\"advice\", \"\").strip(), \"level\": parsed.get(\"level\", \"ok\")}\n\n # plain text handling: 根据关键词提升严重等级\n lower = text.lower()\n critical_keywords = [\"缺失\", \"异常\", \"请检查设备\", \"sensor error\", \"no data\"]\n if any(k in lower for k in critical_keywords):\n if mode == \"chat\":\n return {\"answer\": text, \"level\": \"critical\"}\n return {\"advice\": text, \"level\": \"critical\"}\n\n # 兜底返回\n if mode == \"chat\":\n return {\"answer\": text, \"level\": \"ok\"}\n return {\"advice\": text, \"level\": \"ok\"}"}}}}