From 85cb2b45dccb9abbac1b332ea5aec5ce223a8ad9 Mon Sep 17 00:00:00 2001 From: DawnARC Date: Wed, 6 May 2026 01:03:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(A=5Fmemorix)&fix(Docker):=20=E8=AE=B0?= =?UTF-8?q?=E5=BF=86=E6=90=9C=E7=B4=A2=E6=94=AF=E6=8C=81=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?/=E7=94=A8=E6=88=B7=E7=AD=9B=E9=80=89=E5=8F=8A=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增平台和用户账号维度的筛选能力,并对记忆/画像功能进行交互体验优化 前端:MemoryEpisodeManager 与 MemoryProfileManager 组件增加了平台和用户 ID 输入项,人员 ID 调整为可折叠的“高级”入口,新增原始 JSON 切换、人员名称展示,优化了标签文案和列表模式;搜索触发逻辑现已支持基于账号的查询和画像搜索结果的展示。 API客户端:统一记忆 API 的基础路径,请求携带凭证,增加 HTML 回退页面的检测和本地备用地址的重试机制,优化了错误提示信息。 服务端:记忆路由在返回片段和画像时,补充了来自数据库的人员名称字段,新增 /profiles/search 及兼容性接口,并在片段/画像列表接口中支持传入 platform 和 user_id 参数 其他优化:防止 SPA 路由劫持 /api 路径,入口脚本改用 venv 中的 Python,Dockerfile 中将 venv 加入 PATH 并调整了 uv sync 相关参数。 --- Dockerfile | 3 +- .../memory/MemoryEpisodeManager.tsx | 169 ++++++++++++---- .../memory/MemoryProfileManager.tsx | 142 ++++++++++--- dashboard/src/lib/memory-api.ts | 129 ++++++++++-- .../knowledge-base/tabs/FeedbackTab.tsx | 2 +- docker-entrypoint.sh | 2 +- src/webui/app.py | 3 + src/webui/routers/memory.py | 190 +++++++++++++++++- 8 files changed, 545 insertions(+), 95 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6904aafc..570702af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ WORKDIR /MaiMBot ENV MAIBOT_LEGACY_0X_UPGRADE_CONFIRMED=1 +ENV PATH="/MaiMBot/.venv/bin:${PATH}" # Copy dependency metadata COPY pyproject.toml uv.lock ./ @@ -13,7 +14,7 @@ COPY pyproject.toml uv.lock ./ RUN apt-get update && apt-get install -y git # Install runtime dependencies -RUN uv sync --frozen --no-dev --system --no-install-project +RUN uv sync --frozen --no-dev --no-install-project # Copy project source COPY . . diff --git a/dashboard/src/components/memory/MemoryEpisodeManager.tsx b/dashboard/src/components/memory/MemoryEpisodeManager.tsx index d52e09a5..1f392f39 100644 --- a/dashboard/src/components/memory/MemoryEpisodeManager.tsx +++ b/dashboard/src/components/memory/MemoryEpisodeManager.tsx @@ -1,10 +1,11 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Loader2, Play, RefreshCw, RotateCcw, Search } from 'lucide-react' +import { ChevronDown, Loader2, Play, RefreshCw, RotateCcw, Search } from 'lucide-react' import { Alert, AlertDescription } from '@/components/ui/alert' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { ScrollArea } from '@/components/ui/scroll-area' @@ -88,7 +89,11 @@ export function MemoryEpisodeManager() { const { toast } = useToast() const [query, setQuery] = useState('') const [source, setSource] = useState('') + const [platform, setPlatform] = useState('') + const [userId, setUserId] = useState('') const [personId, setPersonId] = useState('') + const [showAdvancedPersonId, setShowAdvancedPersonId] = useState(false) + const [showRawEpisodePayload, setShowRawEpisodePayload] = useState(false) const [timeStart, setTimeStart] = useState('') const [timeEnd, setTimeEnd] = useState('') const [limit, setLimit] = useState('20') @@ -118,11 +123,14 @@ export function MemoryEpisodeManager() { const loadEpisodes = useCallback(async () => { setLoading(true) try { + const directPersonId = showAdvancedPersonId ? personId.trim() : '' const [listPayload] = await Promise.all([ getMemoryEpisodes({ - query, - source, - personId, + query: query.trim(), + source: source.trim(), + platform: platform.trim(), + userId: userId.trim(), + personId: directPersonId, limit: parsePositiveInt(limit, 20), timeStart: parseOptionalNumber(timeStart), timeEnd: parseOptionalNumber(timeEnd), @@ -143,7 +151,7 @@ export function MemoryEpisodeManager() { } finally { setLoading(false) } - }, [limit, loadStatus, personId, query, selectedId, source, timeEnd, timeStart, toast]) + }, [limit, loadStatus, personId, platform, query, selectedId, showAdvancedPersonId, source, timeEnd, timeStart, toast, userId]) const loadDetail = useCallback(async (episodeId: string) => { if (!episodeId) { @@ -260,10 +268,23 @@ export function MemoryEpisodeManager() { Episode 查询 - 按来源、人物和时间范围查看情节记忆构建结果。 + 按平台账号、来源和时间范围查看情节记忆构建结果;person_id 查询放在高级入口。
+
+ + setPlatform(event.target.value)} + placeholder="例如 qq、telegram、webui" + /> +
+
+ + setUserId(event.target.value)} placeholder="输入平台侧 user_id" /> +
setQuery(event.target.value)} placeholder="搜索摘要或内容" /> @@ -272,10 +293,6 @@ export function MemoryEpisodeManager() { setSource(event.target.value)} placeholder="chat_summary:..." />
-
- - setPersonId(event.target.value)} placeholder="person_id" /> -
setLimit(event.target.value)} /> @@ -289,6 +306,25 @@ export function MemoryEpisodeManager() { setTimeEnd(event.target.value)} placeholder="可选" />
+ + + + + + + + setPersonId(event.target.value)} + placeholder="调试或后台管理时直接输入" + /> + + +