Repository navigation
feat: 增加三级记忆召回 #355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 增加三级记忆召回 #355
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| import os | ||
| import sys | ||
| from pathlib import Path | ||
| from uuid import uuid4 | ||
|
|
||
| from dotenv import load_dotenv | ||
| from trpc_agent_sdk.context import AgentContext | ||
|
|
@@ -26,6 +27,21 @@ | |
| load_dotenv() | ||
| sys.path.append(str(Path(__file__).parent)) | ||
|
|
||
| _FINAL_DATE_MARKERS = ("11月6", "11/6", "2026-11-06") | ||
| _FINAL_ROOM_MARKER = "3b" | ||
|
|
||
|
|
||
| def _office_messages(run_marker: str) -> tuple[str, ...]: | ||
| return ( | ||
| f"{run_marker} 的 Q4 发版评审会原定于 11 月 5 日下午 3 点,会议室 3A。", | ||
| "我这周负责修 pay-service 的订单超时 bug,已经在 TAPD 建了单 TAPD-88231。", | ||
| "以后给我写 commit message 都用英文,标题不超过 72 字符,正文用 bullet 列改动。", | ||
| "刚定位到根因是连接池 maxIdle 配成 2 太小,改成 20 后本地复现不出来了。", | ||
| f"{run_marker} 的评审会最终改到 11 月 6 日上午 10 点,会议室 3B;" | ||
| "原定的 11 月 5 日下午 3 点、会议室 3A 作废。", | ||
| "发版前必须跑一遍全量回归,这是我定的硬规矩,别跳过。", | ||
| ) | ||
|
|
||
|
|
||
| def _required_env(name: str) -> str: | ||
| value = os.getenv(name, "").strip() | ||
|
|
@@ -73,39 +89,109 @@ async def _run_turn( | |
| print() | ||
|
|
||
|
|
||
| async def _print_recall_verification( | ||
| memory_service: TencentDBMemoryService, | ||
| *, | ||
| user_id: str, | ||
| run_marker: str, | ||
| recall_query: str, | ||
| ) -> None: | ||
| """Print the public recall result and its inferred memory layers.""" | ||
| result = await memory_service.search_memory( | ||
| key=user_id, | ||
| query=recall_query, | ||
| limit=10, | ||
| ) | ||
| found_layers: set[str] = set() | ||
| current_run_l1_found = False | ||
| current_run_final_found = False | ||
| normalized_marker = "".join(run_marker.split()).lower() | ||
| print("\nTencentDB recall verification:") | ||
| for index, memory in enumerate(result.memories): | ||
| if memory.author == "scenario": | ||
| layer = "L2" | ||
| elif memory.author == "core": | ||
| layer = "L3" | ||
| elif memory.author in {"user", "assistant", "message"}: | ||
| layer = "L0" | ||
| else: | ||
| layer = "L1" | ||
| found_layers.add(layer) | ||
| text = "".join(part.text or "" for part in memory.content.parts) | ||
| normalized = "".join(text.split()).lower() | ||
| belongs_to_current_run = layer == "L1" and normalized_marker in normalized | ||
| current_run_l1_found = current_run_l1_found or belongs_to_current_run | ||
| if (belongs_to_current_run and any(marker in normalized for marker in _FINAL_DATE_MARKERS) | ||
| and _FINAL_ROOM_MARKER in normalized): | ||
| current_run_final_found = True | ||
| preview = text if len(text) <= 500 else f"{text[:500]}...[truncated]" | ||
| print(f" [{index}] {layer}:{memory.author} {preview}") | ||
| layers = ",".join(sorted(found_layers)) if found_layers else "none" | ||
| print( | ||
| "TencentDB recall verification summary: " | ||
| f"layers={layers}, memories={len(result.memories)}, " | ||
| f"current_run_l1_found={current_run_l1_found}, " | ||
| f"current_run_final_found={current_run_final_found}", ) | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| """Write a fact in one session and recall it from another.""" | ||
| """Write office facts, wait for extraction, then recall across sessions.""" | ||
| from agent.agent import create_agent | ||
| from agent.agent import root_agent | ||
|
|
||
| memory_service = create_memory_service() | ||
| runner = Runner( | ||
| session_service = InMemorySessionService() | ||
| write_runner = Runner( | ||
| app_name="tencentdb_memory_demo", | ||
| agent=create_agent(recall_enabled=False), | ||
| session_service=session_service, | ||
| memory_service=memory_service, | ||
| close_session_service_on_close=False, | ||
| close_memory_service_on_close=False, | ||
| ) | ||
| recall_runner = Runner( | ||
| app_name="tencentdb_memory_demo", | ||
| agent=root_agent, | ||
| session_service=InMemorySessionService(), | ||
| session_service=session_service, | ||
| memory_service=memory_service, | ||
| ) | ||
| user_id = "alice" | ||
| user_id = _required_env("TENCENTDB_MEMORY_USER_ID") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 问题: 触发条件: 用户按 README 第 4 节执行 实际影响: 示例按文档操作必然无法运行且无任何文档提示该新必需项,示例可用性被破坏。 修正方向: 在 |
||
| run_id = uuid4().hex[:8] | ||
| run_marker = f"验证项目-{run_id}" | ||
| recall_query = f"{run_marker} 的 Q4 发版评审会最终安排在什么时间和会议室?" | ||
| write_session_id = f"office-write-{run_id}" | ||
| recall_session_id = f"office-recall-{run_id}" | ||
| print(f"TencentDB verification marker: {run_marker}") | ||
|
|
||
| try: | ||
| await _run_turn( | ||
| runner, | ||
| user_id=user_id, | ||
| session_id="session-write", | ||
| text="My favorite color is blue. Please remember it.", | ||
| ) | ||
|
|
||
| wait_seconds = float(os.getenv("TENCENTDB_MEMORY_PIPELINE_WAIT_SECONDS", "5"), ) | ||
| for text in _office_messages(run_marker): | ||
| await _run_turn( | ||
| write_runner, | ||
| user_id=user_id, | ||
| session_id=write_session_id, | ||
| text=text, | ||
| ) | ||
|
|
||
| wait_seconds = float(os.getenv("TENCENTDB_MEMORY_PIPELINE_WAIT_SECONDS", "10"), ) | ||
| print(f"Waiting {wait_seconds:g}s for asynchronous memory extraction...", ) | ||
| await asyncio.sleep(wait_seconds) | ||
|
|
||
| await _print_recall_verification( | ||
| memory_service, | ||
| user_id=user_id, | ||
| run_marker=run_marker, | ||
| recall_query=recall_query, | ||
| ) | ||
|
|
||
| await _run_turn( | ||
| runner, | ||
| recall_runner, | ||
| user_id=user_id, | ||
| session_id="session-recall", | ||
| text="What is my favorite color?", | ||
| session_id=recall_session_id, | ||
| text=recall_query, | ||
| ) | ||
| finally: | ||
| await runner.close() | ||
| await write_runner.close() | ||
| await recall_runner.close() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
问题: 本变更将示例内容从"喜欢的颜色"(chat 模式)改为"工程任务和发版评审"并要求服务端使用
MEMORY_PROMPT_MODE=code(第 86 行),但 README 故障排查段落仍残留旧结论:日志显示promptMode=code且提取数为 0 时"设置MEMORY_PROMPT_MODE=chat并重新执行"(第 312 行),与示例新要求直接矛盾。触发条件: 用户按新要求以 code 模式部署后遇到 L1 提取为空,按 README 指示切换为 chat 模式——与新示例内容(工程任务)所需模式相反,提取依然为空或错误提取。
实际影响: 故障排查指引引导用户做出与示例要求矛盾的配置,误导排查方向;同类残留还出现在第 199 行引用块("MEMORY_PROMPT_MODE=chat 应配置在服务端")。
修正方向: 将故障排查段和相关引用同步为 code 模式语义(例如"确认服务端已使用 code 模式而非 chat")。