优化案例查询和方案思考过程
This commit is contained in:
parent
736a6653de
commit
4a8f4e03d3
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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} 条;"
|
||||
"方案结合已导入异常案例和标准规则生成。"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user