feat(knowledge): Phase D 统一搜索 + 种子知识冷启动
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

- search/recommend API 返回 UnifiedSearchResult (文档+结构化双通道)
- POST /api/v1/knowledge/seed 种子知识冷启动 (幂等, admin权限)
- seed_knowledge service: 按标题+行业查重, source=distillation
- SearchRequest 扩展: search_structured/search_documents/industry_id
This commit is contained in:
iven
2026-04-12 20:46:43 +08:00
parent f8c5a76ce6
commit 640df9937f
4 changed files with 167 additions and 16 deletions

View File

@@ -374,21 +374,17 @@ pub async fn rollback_version(
// === 检索 ===
/// POST /api/v1/knowledge/search — 语义搜索
/// POST /api/v1/knowledge/search — 统一搜索(双通道:文档 + 结构化)
pub async fn search(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
Json(req): Json<SearchRequest>,
) -> SaasResult<Json<Vec<SearchResult>>> {
) -> SaasResult<Json<UnifiedSearchResult>> {
check_permission(&ctx, "knowledge:search")?;
let limit = req.limit.unwrap_or(5).min(10);
let min_score = req.min_score.unwrap_or(0.5);
let results = service::search(
let results = service::unified_search(
&state.db,
&req.query,
req.category_id.as_deref(),
limit,
min_score,
&req,
Some(&ctx.account_id),
).await?;
Ok(Json(results))
}
@@ -398,15 +394,15 @@ pub async fn recommend(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
Json(req): Json<SearchRequest>,
) -> SaasResult<Json<Vec<SearchResult>>> {
) -> SaasResult<Json<UnifiedSearchResult>> {
check_permission(&ctx, "knowledge:search")?;
let limit = req.limit.unwrap_or(5).min(10);
let results = service::search(
let mut req = req;
req.min_score = Some(0.3);
req.search_structured = req.search_structured.or(Some(true));
let results = service::unified_search(
&state.db,
&req.query,
req.category_id.as_deref(),
limit,
0.3,
&req,
Some(&ctx.account_id),
).await?;
Ok(Json(results))
}
@@ -885,3 +881,34 @@ async fn handle_structured_upload(
_ => Err(SaasError::InvalidInput("意外的处理结果".into())),
}
}
// === 种子知识冷启动 ===
/// POST /api/v1/knowledge/seed — 触发种子知识冷启动
///
/// 需要 admin 权限,幂等(按标题+行业查重)
pub async fn seed_knowledge(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
Json(req): Json<SeedKnowledgeRequest>,
) -> SaasResult<Json<serde_json::Value>> {
check_permission(&ctx, "knowledge:admin")?;
if req.items.len() > 100 {
return Err(SaasError::InvalidInput("单次种子不能超过 100 条".into()));
}
let created = service::seed_knowledge(
&state.db,
&req.industry_id,
req.category_id.as_deref().unwrap_or("seed"),
&req.items.iter().map(|i| (i.title.clone(), i.content.clone(), i.keywords.clone().unwrap_or_default())).collect::<Vec<_>>(),
&ctx.account_id,
).await?;
Ok(Json(serde_json::json!({
"industry_id": req.industry_id,
"created_count": created,
"total_submitted": req.items.len(),
})))
}