diff --git a/sdk/app/routers/private_docs.py b/sdk/app/routers/private_docs.py index a7b721f..e7df754 100644 --- a/sdk/app/routers/private_docs.py +++ b/sdk/app/routers/private_docs.py @@ -43,13 +43,16 @@ def _envelope( @router.post("/private") async def private_qa(req: PrivateQARequest) -> dict[str, Any]: - """私有资料问答:命中/未命中 200;上游故障转错误信封。""" + """私有资料问答:命中/未命中 200(均带 ok:true 信封,BUG-PRIV-R1-01); + 上游故障转错误信封(ok:false)。""" try: - return await ask_private( + 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="私有资料检索服务不可用,请稍后重试") diff --git a/sdk/app/schemas.py b/sdk/app/schemas.py index 8094955..2b3a3ad 100644 --- a/sdk/app/schemas.py +++ b/sdk/app/schemas.py @@ -236,7 +236,7 @@ class PrivateDocBlock(BaseModel): class PrivateQARequest(BaseModel): """POST /api/qa/private 请求体(FR-PRIV-004)。""" - question: str + question: str = Field(..., min_length=1) # BUG-PRIV-R1-02:空串 422(VALIDATION 信封) session_id: str | None = None top_k: int = 8 min_score: float = 0.30 diff --git a/sdk/app/services/privateqa.py b/sdk/app/services/privateqa.py index 54adf2e..bd5866f 100644 --- a/sdk/app/services/privateqa.py +++ b/sdk/app/services/privateqa.py @@ -35,6 +35,26 @@ SYSTEM_PROMPT = ( "300 字内简体中文 markdown。" ) +# BUG-PRIV-R1-03(TL 裁决):system prompt 固定的自谦答语。LLM 返回匹配 +# 任一即改判未命中(底噪越线时不展示低质块;不调默认阈值)。 +SELF_DEPRECATING_ANSWERS = ( + "资料中未找到相关信息", + "资料中没有相关信息", + "资料中未找到相关的内容", + "资料中没有相关的内容", + "资料中找不到相关信息", + "未找到相关信息", + "没有找到相关信息", +) + + +def _is_self_deprecating(text: str) -> bool: + """确定性自谦答语判断(子串匹配)。""" + src = (text or "").strip() + if not src: + return True # 空答案视同未命中 + return any(phrase in src for phrase in SELF_DEPRECATING_ANSWERS) + def _safe_float(value: Any) -> bool: try: @@ -116,6 +136,11 @@ async def ask_private( max_tokens=PRIVATE_QA_MAX_TOKENS, timeout=PRIVATE_QA_LLM_TIMEOUT, ) + # BUG-PRIV-R1-03(TL 裁决):LLM 自谦答语 → 改判未命中。 + # vstore 检索先行行为不变(search-chunks 仍返回 items 供内部判断), + # 只是最终响应改判,前端零痕迹。 + if _is_self_deprecating(answer): + return {"hit": False, "mode": "empty", "answer": "", "items": []} elapsed_ms = int((time.perf_counter() - started) * 1000) return { "hit": True,