feat(knowledge): 支持文件导入沉淀知识,知识图谱可视化随主题切换

- 后端新增 /api/knowledge/ingest/file:上传 md/txt/pdf/word/ppt/excel,
  转 Markdown 后走 wiki-capture LLM 蒸馏为陈述式知识笔记,失败回退逐字草稿
- service.capture_file + 文档专用蒸馏提示词,按主题拆分 1~4 页
- 知识图谱导出 HTML 支持 ?theme=light|dark,布局稳定后冻结,
  按度数缩放节点,悬停/点击聚焦邻居
- 前端知识库页新增「导入文件」对话框与 ingestKnowledgeFile API
- 智能体问答页与简介侧边栏 UI 优化;批量人物蒸馏脚本提取正文逻辑修正

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.8 committed 2026-06-11 10:25:27 +08:00
1 parent 17bb3715d1
commit 3845d5090b
14 files changed
+1040 -352

No files matched your search

+10 -2
View File
@@ -4,13 +4,17 @@ import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { useTheme } from "@/contexts/ThemeContext";
import { knowledgeExportFileUrl } from "@/core/knowledge/api";
import { useKnowledgeActions } from "@/core/knowledge/hooks";
export default function KnowledgeGraphPage() {
const navigate = useNavigate();
const { theme } = useTheme();
const { exportGraph } = useKnowledgeActions();
const [src, setSrc] = useState<string | null>(null);
// Keep the served base URL (without the theme) so a theme toggle can re-skin
// the iframe without regenerating the graph on the backend.
const [baseSrc, setBaseSrc] = useState<string | null>(null);
const [stats, setStats] = useState<{ nodes: number; edges: number } | null>(null);
const regenerate = useCallback(async () => {
@@ -18,7 +22,7 @@ export default function KnowledgeGraphPage() {
const result = await exportGraph.mutateAsync();
setStats(result.stats);
// Cache-bust the iframe so it reflects the freshly generated graph.
setSrc(`${knowledgeExportFileUrl("graph.html")}?t=${Date.now()}`);
setBaseSrc(`${knowledgeExportFileUrl("graph.html")}?t=${Date.now()}`);
} catch (error) {
toast.error(error instanceof Error ? error.message : "生成图谱失败");
}
@@ -29,6 +33,10 @@ export default function KnowledgeGraphPage() {
void regenerate();
}, [regenerate]);
// The graph HTML lives in an iframe and can't read the app's CSS theme, so we
// pass the active day/night mode via a query param; toggling re-renders it.
const src = baseSrc ? `${baseSrc}&theme=${theme}` : null;
return (
<div className="flex size-full min-h-0 flex-col">
<header className="flex items-center gap-2 border-b p-3">