From bf34b7df4ee57b02e7bbbdda594ea8e168c35655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Tue, 19 May 2026 17:22:27 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Memory=20V2=20?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=AF=BC=E8=87=B4=E7=9A=84=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit master 的 Memory V2 重构删除了 memory/updater.py,admin_users.py 仍从中导入 get_memory_data 导致启动 ImportError。改用 BuiltinFileProvider 读取 V2 的 USER.md / 各 agent MEMORY.md;前端 MemoryBucketView 同步改为渲染 V2 条目列表。 Co-Authored-By: Claude Opus 4.7 --- .../src/pages/AdminUserActivityPage.tsx | 73 ++++++------------- .../app/gateway/routers/admin_users.py | 26 +++++-- 2 files changed, 43 insertions(+), 56 deletions(-) diff --git a/frontend-web/src/pages/AdminUserActivityPage.tsx b/frontend-web/src/pages/AdminUserActivityPage.tsx index 6fe20519..ac22c7d2 100644 --- a/frontend-web/src/pages/AdminUserActivityPage.tsx +++ b/frontend-web/src/pages/AdminUserActivityPage.tsx @@ -71,67 +71,38 @@ function MessageRow({ message }: { message: AdminMessage }) { ); } -/** Render one memory bucket: context summaries + facts. */ +/** Render one Memory V2 bucket: a flat list of USER.md / MEMORY.md entries. */ function MemoryBucketView({ bucket }: { bucket: AdminMemoryBucket }) { - const data = bucket.data as { - user?: Record; - history?: Record; - facts?: Array<{ content?: string; category?: string; confidence?: number }>; - }; - const sections: Array<[string, string]> = []; - const labels: Record = { - workContext: "工作背景", - personalContext: "个人背景", - topOfMind: "近期关注", - recentMonths: "近几个月", - earlierContext: "更早记录", - longTermBackground: "长期背景", - }; - for (const group of [data.user, data.history]) { - for (const [key, value] of Object.entries(group ?? {})) { - const summary = value?.summary?.trim(); - if (summary) sections.push([labels[key] ?? key, summary]); - } - } - const facts = (data.facts ?? []).filter((f) => f.content?.trim()); + const raw = (bucket.data as { entries?: unknown }).entries; + const entries = Array.isArray(raw) + ? raw.filter( + (e): e is string => typeof e === "string" && e.trim().length > 0, + ) + : []; return (
- {bucket.name === "global" ? "全局记忆" : `Agent: ${bucket.name}`} + {bucket.name === "global" ? "用户画像" : `Agent: ${bucket.name}`} + {entries.length > 0 && ( + + {entries.length} 条 + + )}
- {sections.length === 0 && facts.length === 0 && ( + {entries.length === 0 ? (

该记忆库暂无内容。

- )} - {sections.length > 0 && ( -
- {sections.map(([label, summary]) => ( -
- {label} -

{summary}

-
+ ) : ( +
    + {entries.map((entry, i) => ( +
  • + · + {entry} +
  • ))} -
- )} - {facts.length > 0 && ( -
- 事实记录({facts.length}) -
    - {facts.map((fact, i) => ( -
  • - · - {fact.content} - {fact.category && ( - - {fact.category} - - )} -
  • - ))} -
-
+ )}
); diff --git a/offline-backend-20260512/backend/app/gateway/routers/admin_users.py b/offline-backend-20260512/backend/app/gateway/routers/admin_users.py index 68e80aca..9e022a37 100644 --- a/offline-backend-20260512/backend/app/gateway/routers/admin_users.py +++ b/offline-backend-20260512/backend/app/gateway/routers/admin_users.py @@ -23,7 +23,7 @@ from app.gateway.deps import ( get_current_user_from_request, get_thread_store, ) -from deerflow.agents.memory.updater import get_memory_data +from deerflow.agents.memory import BuiltinFileProvider from deerflow.config.agents_config import AGENT_NAME_PATTERN from deerflow.config.paths import get_paths from deerflow.runtime import serialize_channel_values @@ -136,24 +136,40 @@ class AdminMemoryResponse(BaseModel): buckets: list[AdminMemoryBucket] = Field(default_factory=list) +def _read_memory_bucket(user_id: str, agent_id: str | None) -> dict: + """Read one Memory V2 file into a JSON-able ``{"entries": [...]}`` dict. + + ``agent_id is None`` -> the user's ``USER.md`` (profile, cross-agent); + otherwise that agent's ``MEMORY.md`` (per-agent working notes). + """ + provider = BuiltinFileProvider() + provider.initialize(user_id=user_id, agent_id=agent_id) + target = "user" if agent_id is None else "memory" + return {"entries": provider.read(target)} + + @router.get("/{user_id}/memory", response_model=AdminMemoryResponse) async def get_user_memory(user_id: str, request: Request) -> AdminMemoryResponse: - """Return every memory bucket (global + per-agent) for one user.""" + """Return every memory bucket for one user. + + Memory V2 layout: the ``global`` bucket is ``USER.md`` (user profile); + each per-agent bucket is that agent's ``MEMORY.md`` working notes. + """ await _require_admin(request) buckets: list[AdminMemoryBucket] = [ - AdminMemoryBucket(name="global", data=get_memory_data(None, user_id=user_id)) + AdminMemoryBucket(name="global", data=_read_memory_bucket(user_id, None)) ] agents_root = get_paths().user_dir(user_id) / "agents" if agents_root.is_dir(): for entry in sorted(agents_root.iterdir(), key=lambda p: p.name.lower()): - if not entry.is_dir() or not (entry / "memory.json").exists(): + if not entry.is_dir() or not (entry / "MEMORY.md").exists(): continue if not AGENT_NAME_PATTERN.match(entry.name): continue try: - data = get_memory_data(entry.name, user_id=user_id) + data = _read_memory_bucket(user_id, entry.name) except Exception: logger.warning("Failed to read memory bucket %s for user", entry.name) continue