feat: 管理面板补齐(RAG导入/记忆添加/会话接history/诚实表述) + design/16 §4.6 IM多会话隔离+第三方身份+黑白名单
agent-98 面板批次:
- 会话管理面板改接 history.ListSessions(主web+次级) + 新建次级会话按钮
- 记忆面板手动添加入口 POST /admin/memories(owner)
- 知识库真正实现 IndexDocument(分块+落projectindex) + POST /admin/rag 导入端点
- 三面板空态诚实表述(去除'后端M10+提供')
- RAG嵌入降级到keyword/line(无直属embedder,不伪造语义,符合红线)
agent-99 design/16 §4.6:
- model.Message.SenderName + 迁移0006 sender_name 列
- 第三方身份边界: session.ResolveActor判定(非LLM猜) + [第三方·昵称]前缀(不改源content)
- 群聊/陌生人落独立sess-xxx(persistIsolated),不写主会话,不广播
- 黑/白名单(allowGroups/denyGroups/allowUsers/denyUsers)最前拦截,不进LLM
- 多账号 CHANNELS_JSON 配置 + 每平台多实例
- 管理只读 GET /admin/channels/{platform}/messages(该平台非主人消息流)
实机验证(JWT): 会话管理返回主会话web / 记忆添加成功 / RAG导入documents非空+Cyrene命中; 后端build/vet/test全绿 + 前端build绿
This commit is contained in:
1 parent
37e98ec584
commit
9c2a37c4ac
32 files changed
+1768
-136
No files matched your search
@@ -1,13 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { useAsyncData } from "../../lib/useAsyncData";
|
||||
import { adminDeleteRag, adminListRag, adminSearchRag } from "../../lib/api";
|
||||
import { adminDeleteRag, adminIndexRag, adminListRag, adminSearchRag } from "../../lib/api";
|
||||
import type { AdminRagDoc, AdminRagHit } from "../../lib/types";
|
||||
import { useAuditStore } from "../audit";
|
||||
import { Notice, PageHeader } from "../components";
|
||||
import { Button, Surface, Tag } from "../../components/primitives";
|
||||
|
||||
const WRITE_M10 = "上传/分块/嵌入为后端 M10+ 提供;当前只读列文档与检索,删除接口在 M10 落地。";
|
||||
|
||||
export function RagModule(): React.ReactNode {
|
||||
const log = useAuditStore((s) => s.log);
|
||||
const { data, loading, error, reload } = useAsyncData(() => adminListRag(), []);
|
||||
@@ -15,6 +13,11 @@ export function RagModule(): React.ReactNode {
|
||||
const [hits, setHits] = useState<AdminRagHit[]>([]);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [notice, setNotice] = useState<string | null>(null);
|
||||
// Manual "导入文档" form fields.
|
||||
const [docName, setDocName] = useState("");
|
||||
const [docDesc, setDocDesc] = useState("");
|
||||
const [docContent, setDocContent] = useState("");
|
||||
const [importing, setImporting] = useState(false);
|
||||
|
||||
const docs: AdminRagDoc[] = data?.documents ?? [];
|
||||
|
||||
@@ -40,7 +43,34 @@ export function RagModule(): React.ReactNode {
|
||||
try {
|
||||
await adminDeleteRag(d.id);
|
||||
} catch (err: unknown) {
|
||||
setNotice(err instanceof Error ? err.message : WRITE_M10);
|
||||
setNotice(err instanceof Error ? err.message : "文档删除为后端 M10 写操作");
|
||||
}
|
||||
};
|
||||
|
||||
const importDoc = async () => {
|
||||
if (!docName.trim() || !docContent.trim()) {
|
||||
setNotice("文档名称与内容不能为空");
|
||||
return;
|
||||
}
|
||||
setImporting(true);
|
||||
setNotice(null);
|
||||
try {
|
||||
await adminIndexRag({
|
||||
name: docName.trim(),
|
||||
desc: docDesc.trim() || undefined,
|
||||
content: docContent.trim(),
|
||||
});
|
||||
log("rag.index", "ok");
|
||||
setDocName("");
|
||||
setDocDesc("");
|
||||
setDocContent("");
|
||||
reload();
|
||||
setNotice("文档已导入,可按知识库检索。");
|
||||
} catch (err: unknown) {
|
||||
log("rag.index", "error");
|
||||
setNotice(err instanceof Error ? err.message : "导入文档失败");
|
||||
} finally {
|
||||
setImporting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,21 +78,43 @@ export function RagModule(): React.ReactNode {
|
||||
<div className="space-y-5">
|
||||
<PageHeader
|
||||
title="知识库 RAG"
|
||||
subtitle="GET /api/v1/admin/rag · 文档列表(分块/嵌入状态) + 检索测试"
|
||||
subtitle="GET /api/v1/admin/rag · 文档列表(分块/嵌入状态) + POST /rag 导入 + 检索测试"
|
||||
actions={
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
log("rag.upload", "ok");
|
||||
setNotice(WRITE_M10);
|
||||
}}
|
||||
>
|
||||
上传文档
|
||||
<Button variant="soft" onClick={reload} disabled={importing}>
|
||||
刷新
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
{notice ? <Notice tone="warn">{notice}</Notice> : null}
|
||||
{notice ? <Notice tone={notice.includes("失败") || notice.includes("不能为空") ? "err" : "warn"}>{notice}</Notice> : null}
|
||||
|
||||
<Surface className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">导入文档(写入项目索引,可分块、可检索)</h3>
|
||||
<input
|
||||
value={docName}
|
||||
onChange={(e) => setDocName(e.target.value)}
|
||||
placeholder="文档名称(如:内部知识库.md)…"
|
||||
className="w-full rounded-[var(--mu-radius)] bg-[var(--mu-surface-2)] px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-[var(--mu-accent)]"
|
||||
/>
|
||||
<input
|
||||
value={docDesc}
|
||||
onChange={(e) => setDocDesc(e.target.value)}
|
||||
placeholder="描述(可选,作为项目描述)…"
|
||||
className="w-full rounded-[var(--mu-radius)] bg-[var(--mu-surface-2)] px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-[var(--mu-accent)]"
|
||||
/>
|
||||
<textarea
|
||||
value={docContent}
|
||||
onChange={(e) => setDocContent(e.target.value)}
|
||||
placeholder="粘贴文档正文,空行分段,后端自动分块并落到项目索引(关键词/行上下文可检索)…"
|
||||
rows={6}
|
||||
className="w-full resize-y rounded-[var(--mu-radius)] bg-[var(--mu-surface-2)] px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-[var(--mu-accent)]"
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<Button variant="primary" disabled={importing} onClick={() => void importDoc()}>
|
||||
{importing ? "导入中…" : "导入文档"}
|
||||
</Button>
|
||||
</div>
|
||||
</Surface>
|
||||
|
||||
<Surface className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -76,7 +128,7 @@ export function RagModule(): React.ReactNode {
|
||||
) : error ? (
|
||||
<p className="text-xs text-[var(--mu-text-lo)]">{error}</p>
|
||||
) : docs.length === 0 ? (
|
||||
<p className="text-xs text-[var(--mu-text-lo)]">暂无知识文档(项目索引为空)。</p>
|
||||
<p className="text-xs text-[var(--mu-text-lo)]">当前无文档:点『导入文档』导入资料后可按知识库检索。</p>
|
||||
) : (
|
||||
docs.map((d) => (
|
||||
<div
|
||||
@@ -151,7 +203,7 @@ export function RagModule(): React.ReactNode {
|
||||
|
||||
<Notice>
|
||||
分块/嵌入状态与语义检索:配置了 pgvector / SQLite 向量增强后,后端经真实语义/向量路径返回(非空壳);
|
||||
未配置时走项目索引关键词 + 行上下文。上传/删除为后端 M10 写操作。
|
||||
未配置时走项目索引关键词 + 行上下文(导入文档即时可查)。文档删除为后端 M10 写操作。
|
||||
</Notice>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user