feat: Knowledge Base RAG 시스템 + 채팅 LLM 개선 (Phase 0~5 완료)

- KB RAG 전체 파이프라인: 업로드, 파싱(xlsx/pdf/docx/text), 임베딩, Qdrant 인덱싱
- KB 관리 UI(14번 탭): 로그인, 문서 목록, 업로드, 삭제, 재인덱스
- OllamaController: 한글 시스템 프롬프트, plant_context.md 외부 파일화, SSE tool_start/tool_result 이벤트
- 프론트: 툴 실행 카드, KB 인용 링크, 표 자동 렌더, 추천 질문 칩
- nl2sql_worker: history_table.recorded_at 사용, tag_metadata 응답 개선
- DB: KB 테이블 5개 DDL + 시드, pgcrypto 확장
This commit is contained in:
windpacer
2026-05-13 20:22:27 +09:00
parent 35136ba91e
commit 908bfe151f
32 changed files with 3202 additions and 91 deletions

View File

@@ -238,15 +238,14 @@ async def _query_pv_history(tag_names: list[str], time_from: str, time_to: str,
conn = _get_db_connection()
try:
with conn.cursor() as cur:
# TimescaleDB의 time_bucket 함수 사용
cur.execute(
"""
SELECT time_bucket('1 min', ts) AS time, tag_name, value
FROM realtime_table
WHERE tag_name = ANY(%s)
AND ts >= %s
AND ts <= %s
ORDER BY time DESC
SELECT recorded_at AS time, tagname AS tag_name, value
FROM history_table
WHERE tagname = ANY(%s)
AND recorded_at >= %s
AND recorded_at <= %s
ORDER BY recorded_at DESC, tagname
LIMIT %s
""",
(tag_names, time_from, time_to, limit),
@@ -272,17 +271,25 @@ async def _get_tag_metadata(query: str, limit: int = 10) -> str:
with conn.cursor() as cur:
cur.execute(
"""
SELECT DISTINCT tag_name, unit, description
SELECT tagname, livevalue, timestamp, node_id
FROM realtime_table
WHERE tag_name ILIKE %s
ORDER BY tag_name
WHERE tagname ILIKE %s
ORDER BY tagname
LIMIT %s
""",
(f"%{query}%", limit),
)
columns = ["tag_name", "unit", "description"]
columns = ["tag_name", "current_value", "last_updated", "node_id"]
rows = cur.fetchall()
data = [dict(zip(columns, row)) for row in rows]
data = [
{
"tag_name": r[0],
"current_value": r[1],
"last_updated": r[2].isoformat() if r[2] else None,
"node_id": r[3],
}
for r in rows
]
return {
"success": True,
"query": query,