feat: V1 - DAG编排/告警系统/Agent循环/知识库RAG/Webhook + 审核管理富上下文修复
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""Knowledge base service: document CRUD + simple RAG search."""
|
||||
import json
|
||||
import re
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from app.models.knowledge import KnowledgeDoc
|
||||
|
||||
|
||||
class KnowledgeService:
|
||||
@staticmethod
|
||||
def list_docs(db, tenant_id, project_id=None):
|
||||
q = db.query(KnowledgeDoc).filter(KnowledgeDoc.tenant_id == tenant_id)
|
||||
if project_id:
|
||||
q = q.filter(
|
||||
(KnowledgeDoc.project_id == project_id) | (KnowledgeDoc.project_id == None)
|
||||
)
|
||||
return q.order_by(KnowledgeDoc.created_at.desc()).all()
|
||||
|
||||
@staticmethod
|
||||
def get_doc(db, tenant_id, doc_id):
|
||||
return db.query(KnowledgeDoc).filter(
|
||||
KnowledgeDoc.id == doc_id, KnowledgeDoc.tenant_id == tenant_id
|
||||
).first()
|
||||
|
||||
@staticmethod
|
||||
def create_doc(db, tenant_id, req):
|
||||
keywords = KnowledgeService._extract_keywords(req.content + " " + req.title + " " + req.tags)
|
||||
doc = KnowledgeDoc(
|
||||
tenant_id=tenant_id, project_id=req.project_id,
|
||||
title=req.title, content=req.content,
|
||||
doc_type=req.doc_type, source=req.source,
|
||||
tags=req.tags, keywords=json.dumps(keywords, ensure_ascii=False),
|
||||
)
|
||||
db.add(doc)
|
||||
db.commit()
|
||||
db.refresh(doc)
|
||||
return doc
|
||||
|
||||
@staticmethod
|
||||
def update_doc(db, tenant_id, doc_id, content=None, title=None):
|
||||
doc = KnowledgeService.get_doc(db, tenant_id, doc_id)
|
||||
if not doc:
|
||||
return None
|
||||
if content is not None:
|
||||
doc.content = content
|
||||
if title is not None:
|
||||
doc.title = title
|
||||
doc.keywords = json.dumps(
|
||||
KnowledgeService._extract_keywords(doc.content + " " + doc.title),
|
||||
ensure_ascii=False,
|
||||
)
|
||||
db.commit()
|
||||
db.refresh(doc)
|
||||
return doc
|
||||
|
||||
@staticmethod
|
||||
def delete_doc(db, tenant_id, doc_id):
|
||||
doc = KnowledgeService.get_doc(db, tenant_id, doc_id)
|
||||
if not doc:
|
||||
return False
|
||||
db.delete(doc)
|
||||
db.commit()
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def search(db, tenant_id, query, project_id=None, limit=5):
|
||||
"""Simple keyword-based search (MVP RAG without vector DB)."""
|
||||
docs = KnowledgeService.list_docs(db, tenant_id, project_id)
|
||||
query_lower = query.lower()
|
||||
query_terms = set(re.findall(r'\w+', query_lower))
|
||||
|
||||
scored = []
|
||||
for doc in docs:
|
||||
content_lower = doc.content.lower()
|
||||
title_lower = doc.title.lower()
|
||||
keywords = set(json.loads(doc.keywords or "[]"))
|
||||
|
||||
score = 0.0
|
||||
# Title match (high weight)
|
||||
for term in query_terms:
|
||||
if term in title_lower:
|
||||
score += 3.0
|
||||
if term in keywords:
|
||||
score += 2.0
|
||||
count = content_lower.count(term)
|
||||
score += count * 0.5
|
||||
|
||||
if score > 0:
|
||||
snippet = doc.content[:200] + "..." if len(doc.content) > 200 else doc.content
|
||||
scored.append({
|
||||
"doc_id": doc.id,
|
||||
"title": doc.title,
|
||||
"snippet": snippet,
|
||||
"score": round(score, 2),
|
||||
})
|
||||
|
||||
scored.sort(key=lambda x: x["score"], reverse=True)
|
||||
return scored[:limit]
|
||||
|
||||
@staticmethod
|
||||
def _extract_keywords(text):
|
||||
"""Extract keywords from text (simple: Chinese 2-4 char segments + English words)."""
|
||||
# English words
|
||||
en_words = re.findall(r'[a-zA-Z]{2,20}', text)
|
||||
# Chinese segments (2-4 chars)
|
||||
cn_segs = re.findall(r'[\u4e00-\u9fff]{2,4}', text)
|
||||
# Deduplicate and limit
|
||||
all_kw = list(set(en_words + cn_segs))[:20]
|
||||
return all_kw
|
||||
Reference in New Issue
Block a user