Files
adminandClaude Fable 5 04c08e8339 fix(priv): R1 缺陷修复——信封包 ok 键 + 空 question 422 + 自谦答语确定性改判 hit:false
- private_docs.py: 命中/未命中两态响应包 ok:true(错误信封 ok:false 不变)
- schemas.py: PrivateQARequest.question 加 min_length=1
- privateqa.py: LLM 自谦答语(资料中未找到相关信息+等价变体)确定性改判
  {hit:false,mode:empty},不调默认阈值(底噪 0.3-0.37 与真实命中 0.42-0.73
  有重叠)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 23:07:19 +08:00

65 lines
2.4 KiB
Python
Raw Permalink 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.
"""routers/private_docs.py —— 私有资料问答 BFF 端点(FR-PRIV-004,A1)。
- POST /api/qa/private body={question, session_id?, top_k?, min_score?}
三态契约(FR-PRIV 假设 8):
- 命中:200 {ok:true, hit:true, mode:"vector", answer, items, elapsed_ms}
- 未命中:200 {ok:true, hit:false, mode:"empty", answer:"", items:[]}
- vstore 不可达:503 UPSTREAM_DOWN;LLM 失败:502 UPSTREAM_ERROR
(信封复用 app.upstream.envelope:{ok:false, error:{code, message, degraded}})
技术纪律:无生成器/无 SSE(Cython 安全);服务层只用 dict/stdlib。
"""
from __future__ import annotations
import logging
from typing import Any
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from ..schemas import PrivateQARequest
from ..services.docgen.client import DocgenUnavailable
from ..services.docgen.llm import LLMError
from ..services.privateqa import ask_private
from ..upstream import envelope
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/qa", tags=["private_docs"])
def _envelope(
code: str,
message: str | None = None,
degraded: bool | None = None,
status: int | None = None,
) -> JSONResponse:
if degraded is None:
degraded = code == "UPSTREAM_DOWN"
body, http = envelope(code, message=message, degraded=degraded, status=status)
return JSONResponse(status_code=http, content=body)
@router.post("/private")
async def private_qa(req: PrivateQARequest) -> dict[str, Any]:
"""私有资料问答:命中/未命中 200(均带 ok:true 信封,BUG-PRIV-R1-01);
上游故障转错误信封(ok:false)。"""
try:
result = await ask_private(
req.question,
top_k=req.top_k,
min_score=req.min_score,
)
# BUG-PRIV-R1-01(TL 裁决):命中与未命中两态响应均包 ok:true
return {"ok": True, **result}
except DocgenUnavailable as exc:
logger.warning("privateqa: vstore 不可达:%s", exc)
return _envelope("UPSTREAM_DOWN", message="私有资料检索服务不可用,请稍后重试")
except LLMError as exc:
logger.warning("privateqa: LLM 失败:%s", exc)
return _envelope("UPSTREAM_ERROR", message="私有资料回答生成失败,请稍后重试")
except Exception as exc:
logger.exception("privateqa: 未预期异常")
return _envelope("INTERNAL", message=f"私有资料问答失败:{exc}")