diff --git a/web/agent_api.py b/web/agent_api.py index d8d7169..4781f81 100644 --- a/web/agent_api.py +++ b/web/agent_api.py @@ -286,38 +286,44 @@ def _ensure_audit_table(): def _review_one_exp(cfg, prompt, recipe, tool_seq, hits): - """调模型评审单条经验,返回 (verdict, score, reason);失败保守返回保留。""" - try: - import httpx - body = { - "model": cfg.get("model") or "deepseek-v4-flash-vision-exp", - "messages": [{"role": "user", - "content": _AUDIT_PROMPT.format( - prompt=(prompt or "")[:400], - recipe=(recipe or "")[:600], - tool_seq=(tool_seq or "")[:600], - hits=hits or 0)}], - "max_tokens": 200, - } - headers = {"Authorization": f"Bearer {cfg.get('api_key', '')}", - "Content-Type": "application/json"} - r = httpx.post(f"{(cfg.get('api_base') or 'https://api.deepseek.com').rstrip('/')}/chat/completions", - json=body, headers=headers, timeout=20) - if r.status_code != 200: - raise RuntimeError(f"HTTP {r.status_code}") - text = (((r.json() or {}).get("choices") or [{}])[0] - .get("message") or {}).get("content") or "" - m = re.search(r"\{.*\}", text, re.S) - if not m: - raise RuntimeError("响应无 JSON") - j = json.loads(m.group(0)) - keep = bool(j.get("keep", True)) - score = max(0, min(10, int(j.get("score", 5) or 5))) - reason = str(j.get("reason", ""))[:200] - return ("keep" if keep else "delete"), score, reason - except Exception as e: - _log.warning(f"经验评审失败(按保留处理): {e}") - return "keep", 0, f"评审失败自动保留: {str(e)[:80]}" + """调模型评审单条经验,返回 (verdict, score, reason)。 + + 模型偶尔输出解释文字不带 JSON——重试一次(强调只输出 JSON), + 仍失败则保守返回保留。 + """ + import httpx + base_prompt = _AUDIT_PROMPT.format(prompt=(prompt or "")[:400], + recipe=(recipe or "")[:600], + tool_seq=(tool_seq or "")[:600], + hits=hits or 0) + for attempt in (1, 2): + try: + content = base_prompt if attempt == 1 else ( + base_prompt + "\n注意:只输出一个 JSON 对象,不要输出任何其它文字、解释或代码块标记。") + body = { + "model": cfg.get("model") or "deepseek-v4-flash-vision-exp", + "messages": [{"role": "user", "content": content}], + "max_tokens": 200, + } + headers = {"Authorization": f"Bearer {cfg.get('api_key', '')}", + "Content-Type": "application/json"} + r = httpx.post(f"{(cfg.get('api_base') or 'https://api.deepseek.com').rstrip('/')}/chat/completions", + json=body, headers=headers, timeout=20) + if r.status_code != 200: + raise RuntimeError(f"HTTP {r.status_code}") + text = (((r.json() or {}).get("choices") or [{}])[0] + .get("message") or {}).get("content") or "" + m = re.search(r"\{.*\}", text, re.S) + if not m: + raise RuntimeError("响应无 JSON") + j = json.loads(m.group(0)) + keep = bool(j.get("keep", True)) + score = max(0, min(10, int(j.get("score", 5) or 5))) + reason = str(j.get("reason", ""))[:200] + return ("keep" if keep else "delete"), score, reason + except Exception as e: + _log.warning(f"经验评审第 {attempt} 次失败: {e}") + return "keep", 0, "评审失败自动保留(两次尝试均无有效 JSON)" def run_experience_audit():