优化案例查询和方案思考过程

This commit is contained in:
kx_guo 2026-09-18 11:36:55 +08:00
parent 736a6653de
commit 4a8f4e03d3
3 changed files with 64 additions and 0 deletions

View File

@ -14,6 +14,7 @@ from app.schemas.conversation import (
ConversationData,
ConversationMessageCreateRequest,
ConversationSummary,
DirectQueryRequest,
)
from app.services.conversations import ConversationService
@ -39,6 +40,15 @@ async def create_conversation(
return ApiResponse(data=await ConversationService(session).create(current_user, payload.title))
@router.post("/direct-query", response_model=ApiResponse[dict])
async def direct_query(
payload: DirectQueryRequest,
session: DatabaseSession,
_current_user: Annotated[UserAccount, Depends(require_permission("risk.assess", operate=True))],
) -> ApiResponse[dict]:
return ApiResponse(data=await ConversationService(session).direct_query(payload.kind, payload.query))
@router.get("/{conversation_id}", response_model=ApiResponse[ConversationData])
async def get_conversation(
conversation_id: int,

View File

@ -19,6 +19,11 @@ class ConversationMessageCreateRequest(ApiSchema):
message_type: Literal["text", "image", "file", "audio"] = "text"
class DirectQueryRequest(ApiSchema):
kind: Literal["supplier", "material", "cases", "solution"]
query: str = Field(min_length=1, max_length=2000)
class ConversationListQuery(ApiSchema):
limit: int = Field(default=50, ge=1, le=100)

View File

@ -128,6 +128,49 @@ class ConversationService:
raise AppException("对话创建失败", code=500, status_code=500)
return self._data(persisted, messages=[], total=0, offset=0)
async def direct_query(self, kind: str, query: str) -> dict[str, Any]:
"""Build a shortcut result without creating a conversation message."""
text = query.strip()
if not text:
raise AppException("查询内容不能为空", code=422, status_code=422)
if kind == "supplier":
return await self._supplier_card(text, {"supplier": text})
if kind == "material":
return await self._material_card(text, {"packaging": text})
if kind == "solution":
semantics = self._normalize_semantics(
text,
await self.llm.extract_semantics(text, history=[]),
)
return await self._solution_card(text, semantics)
results = await self._similar_cases(
text,
category=None,
material=None,
supplier=None,
factory=None,
stage=None,
anomaly_type=None,
)
top_cases = list(results[:3])
# Keep the same similarity signal used by the risk assessment card.
# Vector retrieval populates it in RAG; lexical fallback still gets a
# deterministic score so the shortcut card never loses that field.
for item in top_cases:
if item.case.similarity is None:
item.case.similarity = round(max(0.0, min(1.0, item.score / 100)), 3)
return {
"type": "cases",
"title": "案例检索",
"query": text,
"cases": [item.case.model_dump(mode="json", by_alias=True) for item in top_cases],
"matchedCount": len(top_cases),
"suggestions": [
"同类问题优先复用历史方案,再按判定标准复测验证",
"如已出货,准备客诉应对预案并对同批次抽检",
],
}
async def delete(self, conversation_id: int, user: UserAccount) -> None:
"""Delete one user-owned conversation and retain an audit trail."""
@ -876,6 +919,12 @@ class ConversationService:
"type": "solution",
"title": "AI 案例推理生成",
"confidence": "中",
"thinkingSummary": [
f"识别产品场景:{category},提取包装、供应链和风险问题等关键信息",
f"检索历史异常案例,匹配到 {len(similar_cases)} 条相似案例并进行相似度排序",
f"关联判定标准库 {len(standards)} 条规则,完成规则引擎兜底校验",
f"综合历史案例与标准规则,整理 {len(suggestions)} 条可执行建议",
],
"analysisBase": (
f"目标品类「{category}」当前命中历史异常 {direct_count} 条;"
"方案结合已导入异常案例和标准规则生成。"