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>
This commit is contained in:
@@ -43,13 +43,16 @@ def _envelope(
|
|||||||
|
|
||||||
@router.post("/private")
|
@router.post("/private")
|
||||||
async def private_qa(req: PrivateQARequest) -> dict[str, Any]:
|
async def private_qa(req: PrivateQARequest) -> dict[str, Any]:
|
||||||
"""私有资料问答:命中/未命中 200;上游故障转错误信封。"""
|
"""私有资料问答:命中/未命中 200(均带 ok:true 信封,BUG-PRIV-R1-01);
|
||||||
|
上游故障转错误信封(ok:false)。"""
|
||||||
try:
|
try:
|
||||||
return await ask_private(
|
result = await ask_private(
|
||||||
req.question,
|
req.question,
|
||||||
top_k=req.top_k,
|
top_k=req.top_k,
|
||||||
min_score=req.min_score,
|
min_score=req.min_score,
|
||||||
)
|
)
|
||||||
|
# BUG-PRIV-R1-01(TL 裁决):命中与未命中两态响应均包 ok:true
|
||||||
|
return {"ok": True, **result}
|
||||||
except DocgenUnavailable as exc:
|
except DocgenUnavailable as exc:
|
||||||
logger.warning("privateqa: vstore 不可达:%s", exc)
|
logger.warning("privateqa: vstore 不可达:%s", exc)
|
||||||
return _envelope("UPSTREAM_DOWN", message="私有资料检索服务不可用,请稍后重试")
|
return _envelope("UPSTREAM_DOWN", message="私有资料检索服务不可用,请稍后重试")
|
||||||
|
|||||||
+1
-1
@@ -236,7 +236,7 @@ class PrivateDocBlock(BaseModel):
|
|||||||
class PrivateQARequest(BaseModel):
|
class PrivateQARequest(BaseModel):
|
||||||
"""POST /api/qa/private 请求体(FR-PRIV-004)。"""
|
"""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
|
session_id: str | None = None
|
||||||
top_k: int = 8
|
top_k: int = 8
|
||||||
min_score: float = 0.30
|
min_score: float = 0.30
|
||||||
|
|||||||
@@ -35,6 +35,26 @@ SYSTEM_PROMPT = (
|
|||||||
"300 字内简体中文 markdown。"
|
"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:
|
def _safe_float(value: Any) -> bool:
|
||||||
try:
|
try:
|
||||||
@@ -116,6 +136,11 @@ async def ask_private(
|
|||||||
max_tokens=PRIVATE_QA_MAX_TOKENS,
|
max_tokens=PRIVATE_QA_MAX_TOKENS,
|
||||||
timeout=PRIVATE_QA_LLM_TIMEOUT,
|
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)
|
elapsed_ms = int((time.perf_counter() - started) * 1000)
|
||||||
return {
|
return {
|
||||||
"hit": True,
|
"hit": True,
|
||||||
|
|||||||
Reference in New Issue
Block a user