为了让你在 5 分钟内把 Python SDK 的完整链路跑通,下面给出一段可直接复制运行的端到端 demo 脚本。
脚本模拟一个普通 C 端用户「小磊」与虚构的 AI 健康助手「小番茄」之间的 10 轮日常对话,自动完成三件事:
1. 把 10 轮(共 20 条消息)的对话调用
add_conversation 写入 L0 原始对话。2. 默认等待 5 分钟,留给 Memory 内核异步沉淀出 L1 原子记忆 / L2 场景文件 / L3 核心人格画像。
3. 自动回查 L0 / L1 / L2 / L3 四层数据并打印摘要,方便你对照控制台「记忆数据」页确认沉淀效果。
运行方式
1. 下载并本地安装 Python SDK:从
tencentdb_agent_memory_sdk_python-0.1.0-py3-none-any.whl 下载安装包到本地,然后执行 pip install ./tencentdb_agent_memory_sdk_python-0.1.0-py3-none-any.whl 完成本地安装(暂未发布到公网 PyPI,必须走本地 whl 安装)。2. 复制下方完整脚本:把下文「完整脚本」小节里的代码整段复制保存为本地
.py 文件(例如 quickstart.py)。3. 填入实例连接信息:在脚本顶部「用户需要修改的配置区域」把
ENDPOINT / API_KEY / SERVICE_ID 三个字符串替换为真实值。4. 运行脚本:执行
python quickstart.py(替换为你保存的实际文件名),默认会写入 L0 → 等待 5 分钟 → 回查 L0/L1/L2/L3 并打印摘要。命令行参数
参数 | 说明 |
--wait-seconds N | 自定义等待秒数(默认 300,即 5 分钟) |
--no-wait | 写完立即查看(仅冒烟用,通常看不到 L1/L2/L3) |
--skip-write | 跳过写入,仅观察当前 session 已沉淀的数据 |
--session-id <ID> | 自定义 session_id(默认 demo-pm-quickstart-001) |
完整脚本
下面是 demo 的完整源码,可以直接复制保存为本地 .py 文件运行。脚本分为三步:① 构造一段 10 轮的示例对话并写入;② 等待几分钟让内核完成异步沉淀;③ 回查四层记忆数据,打印每一层的概况。
"""Memory Python SDK 快速上手 Demo —— C 端 AI 助手对话这个脚本演示如何用 ``tencentdb-agent-memory-sdk-python`` 把一段普通用户与AI 健康助手"小番茄"的日常对话(约 10 轮、覆盖 2~3 个生活话题)快速写入Memory 实例的 L0 层,等待几分钟后再回查 L0/L1/L2/L3 四层记忆数据。> 注:「小番茄」是一个虚构的 C 端 AI 健康助手产品,定位类似大众熟悉的> AI 减脂教练 / 饮食记录助手,仅作为示例对话场景使用。"""from __future__ import annotationsimport argparseimport sysimport timefrom datetime import datetime, timedelta, timezonefrom typing import Anyfrom tencentdb_agent_memory import MemoryClient, TDAMError# ============================================================================# 👇 用户需要修改的配置区域 👇# ============================================================================# 把下面三个字符串替换为你自己实例的真实值即可。# ============================================================================# Memory 数据面网关地址(控制台 → 实例详情 → 连接信息),形如 https://memory.<region>.example.comENDPOINT = "请替换为你的 Memory 实例 Endpoint"# 数据面 API Key(控制台 → 实例详情 → 连接信息 中获取)API_KEY = "请替换为你的 API Key"# Memory 实例 ID,形如 mem-xxxxxxxxSERVICE_ID = "请替换为你的实例 ID"# 默认 session_id:同一段对话归属的会话标识,重复运行会"追加"到同一会话DEFAULT_SESSION_ID = "demo-pm-quickstart-001"# ============================================================================# 👆 用户需要修改的配置区域 👆# ============================================================================# ---------------------------------------------------------------------------- ## 1. 构造 C 端用户和"小番茄" AI 健康助手的 10 轮对话# (覆盖 3 个生活话题:减脂目标 / 饮食运动偏好 / 使用习惯)# ---------------------------------------------------------------------------- #def build_dialogue() -> list[dict]:"""生成 10 轮日常对话,共 20 条消息(user + assistant 各 10 条)。"""base_time = datetime(2026, 5, 25, 20, 0, 0, tzinfo=timezone.utc)# 每个元素是一轮对话:{"user": ..., "assistant": ...},一共 10 轮rounds: list[dict] = [# —— 话题 1:减脂目标与计划(4 轮)——{"user": "我现在 78 公斤,身高 175,想在 3 个月内减到 70 公斤,帮我看看靠不靠谱?","assistant": "你好,按当前数据 BMI 约 25.5,属于轻度超重;3 个月减 8 公斤、平均每周 0.65 公斤,属于健康的减脂速度,是可以达成的目标。我可以帮你制定饮食和训练计划。",},{"user": "OK,那就定 3 个月减到 70 公斤这个目标。我每周能锻炼 4 次,每次 1 小时左右。","assistant": "已记下你的目标:3 个月内从 78 公斤减到 70 公斤,每周训练 4 次、每次 60 分钟。建议安排成 2 次力量训练 + 2 次有氧训练,我会帮你跟进进度。",},{"user": "我比较喜欢跑步和动感单车,不太喜欢撸铁,力量训练能不能换成自重训练?","assistant": "完全可以。你的训练计划调整为:每周 2 次有氧(跑步 / 动感单车任选)+ 2 次自重训练(深蹲、俯卧撑、平板支撑这一类),不安排器械力量。",},{"user": "对了,每周一早上帮我称一次体重,记录下来做趋势图。","assistant": "好的,每周一早上 08:00 我会推送称重提醒,你拍照或手动输入后我会自动汇总成周度体重曲线,方便你看趋势。",},# —— 话题 2:饮食和运动偏好(3 轮)——{"user": "我对牛奶过敏,乳糖不耐受那种,喝了会拉肚子,以后推荐食谱别给我加牛奶和奶酪。","assistant": "已记下饮食禁忌:牛奶、奶酪等乳制品(乳糖不耐受)。后续给你的食谱会用无乳糖牛奶或豆浆替代,奶酪类食物也不再推荐。",},{"user": "另外我不吃香菜,看到就反胃,外卖推荐的时候帮我自动过滤掉含香菜的菜。","assistant": "收到,已加入忌口清单:香菜。今后给你推荐外卖菜品时会自动过滤包含香菜的选项。",},{"user": "我习惯早上 6:30 出门跑步,晚上不想再做运动了,安排训练时尽量都放上午。","assistant": "明白,你的训练时间偏好已记下:所有训练统一安排在上午(默认起跑时间 06:30),晚上不再排任何运动课程。",},# —— 话题 3:使用偏好(3 轮)——{"user": "App 里所有提醒推送,都改成晚上 10 点之前发,再晚就别推了,会影响我睡觉。","assistant": "好的,推送时段已限定为每日 22:00 之前,22:00~次日 06:30 之间不再推送任何消息。",},{"user": "重量单位我习惯用公斤,距离用公里,别给我用英制的磅和英里。","assistant": "已设置度量单位偏好:体重 / 重量统一用公斤 (kg),距离统一用公里 (km),不再出现 lb / mile 等英制单位。",},{"user": "你跟我说话别太正式,像朋友一样就行,不用每句话都'您好''请问',听着累。","assistant": "明白,以后我会用更轻松的口吻和你聊,去掉敬语和客套话,像朋友那样直接给你建议。",},]# 把每轮 {user, assistant} 展开成扁平的消息列表,并加上时间戳messages: list[dict] = []for idx, turn in enumerate(rounds):ts_user = base_time + timedelta(minutes=idx * 2) # 模拟用户每 2 分钟说一句ts_ai = ts_user + timedelta(seconds=20) # AI 在 20s 后回复messages.append({"role": "user","content": turn["user"],"timestamp": ts_user.strftime("%Y-%m-%dT%H:%M:%SZ"),})messages.append({"role": "assistant","content": turn["assistant"],"timestamp": ts_ai.strftime("%Y-%m-%dT%H:%M:%SZ"),})return messages# ---------------------------------------------------------------------------- ## 2. 一些控制台输出的小工具,让 stdout 看起来更友好# ---------------------------------------------------------------------------- #def section(title: str) -> None:bar = "─" * 70print(f"\\n{bar}\\n {title}\\n{bar}")def info(msg: str) -> None:print(f" · {msg}")def ok(msg: str) -> None:print(f" ✓ {msg}")def warn(msg: str) -> None:print(f" ! {msg}")def fail(msg: str) -> None:print(f" ✗ {msg}")def safe_call(label: str, fn) -> Any:"""统一封装 SDK 调用:成功返回数据、失败返回 None 并友好提示,不抛异常。"""try:result = fn()ok(f"{label} 调用成功")return resultexcept TDAMError as exc:warn(f"{label} 业务返回 code={exc.code}:{exc.message}"+ (f"(request_id={exc.request_id})" if exc.request_id else ""))return Noneexcept Exception as exc:fail(f"{label} 异常:{type(exc).__name__}: {exc}")return None# ---------------------------------------------------------------------------- ## 3. 把 10 轮对话写入 L0# ---------------------------------------------------------------------------- #def step_write_l0(client: MemoryClient, session_id: str) -> int:section(f"Step 1/3 写入 L0 原始对话(session_id = {session_id})")messages = build_dialogue()info(f"准备写入 {len(messages) // 2} 轮 / 共 {len(messages)} 条消息")info("话题覆盖:① 减脂目标与训练计划 ② 饮食运动偏好 ③ 使用习惯偏好")data = safe_call("L0 add_conversation",lambda: client.add_conversation(session_id, messages),)if not data:fail("写入失败,后续步骤无法进行。请检查 ENDPOINT / API_KEY / SERVICE_ID 配置。")sys.exit(1)accepted = data.get("accepted_ids") or []total = data.get("total_count")info(f"服务端受理:total_count = {total}")info(f"前 3 个 message_id:{accepted[:3]}")return total or len(accepted)# ---------------------------------------------------------------------------- ## 4. 等待内核异步沉淀(L0 → L1 → L2/L3)# ---------------------------------------------------------------------------- #def step_wait(seconds: int) -> None:section(f"Step 2/3 等待内核异步沉淀({seconds} 秒)")if seconds <= 0:info("已选择不等待,直接进入查看阶段(注意:可能还看不到 L1/L2/L3)")returninfo("Memory 内核会异步把 L0 对话蒸馏成 L1 原子记忆、L2 场景文件、L3 核心人格画像。")info("一般 3~5 分钟内可以看到结果,本脚本默认等 5 分钟。")end_at = time.time() + secondswhile True:remain = int(end_at - time.time())if remain <= 0:breakmm, ss = divmod(remain, 60)print(f" ⏳ 剩余 {mm:02d}:{ss:02d} ...", end="\\r", flush=True)time.sleep(min(30, remain))print(" " * 40, end="\\r") # 清掉最后一行倒计时ok("等待结束,开始查看四层记忆数据")# ---------------------------------------------------------------------------- ## 5. 回查 L0 / L1 / L2 / L3 四层记忆# ---------------------------------------------------------------------------- #def step_inspect(client: MemoryClient, session_id: str) -> None:section("Step 3/3 查看 L0 / L1 / L2 / L3 四层记忆数据")# ---- L0:原始对话回查 ----print("\\n[L0] 原始对话")l0 = safe_call("L0 query_conversation",lambda: client.query_conversation(session_id=session_id, limit=100, offset=0),)if l0:# 注:v2 /conversation/query 的列表字段是 messages(不是 items)messages = l0.get("messages") or l0.get("items") or []info(f"会话 {session_id} 中累计 {len(messages)} 条消息")if messages:sample = messages[0]preview = (sample.get("content") or "")[:50]info(f"首条预览:[{sample.get('role')}] {preview}...")# ---- L1:原子记忆(按三种 type 分别拉一次)----print("\\n[L1] 原子记忆(atomic notes)")l1_total = 0type_summary: dict[str, int] = {}for atom_type in ("episodic", "persona", "instruction"):data = safe_call(f"L1 query_atomic type={atom_type}",lambda t=atom_type: client.query_atomic(type=t, limit=50, offset=0),)cnt = len(data.get("items") or []) if data else 0type_summary[atom_type] = cntl1_total += cntinfo(f"L1 抽取统计:episodic={type_summary.get('episodic', 0)} / "f"persona={type_summary.get('persona', 0)} / "f"instruction={type_summary.get('instruction', 0)}(共 {l1_total} 条)")if l1_total == 0:warn("L1 暂无数据,可能内核还在沉淀中,可以稍后用 --skip-write 再跑一次只观察")# ---- L2:场景文件 ----print("\\n[L2] 场景文件(scenario files)")l2 = safe_call("L2 list_scenarios (root)", lambda: client.list_scenarios(path_prefix=""))if l2:files = l2.get("items") or l2.get("files") or []info(f"根目录下共 {len(files)} 个场景文件")for f in files[:5]:name = f.get("path") or f.get("name") or f.get("key") or "<unknown>"info(f" - {name}")# ---- L3:核心人格画像 ----print("\\n[L3] 核心人格画像(core profile)")l3 = safe_call("L3 read_core", lambda: client.read_core())if l3:content = l3.get("content") or l3.get("text") or ""if content:preview = content[:200].replace("\\n", " ")info(f"L3 已生成,长度 {len(content)} 字,前 200 字预览:")print(f" {preview}{'...' if len(content) > 200 else ''}")else:warn("L3 read 成功但内容为空,可能仍在沉淀中")# 注:L3 在实例还没沉淀时常常是 404/业务错误,safe_call 会友好提示并继续,不会中断。# ---------------------------------------------------------------------------- ## main# ---------------------------------------------------------------------------- #def main() -> None:parser = argparse.ArgumentParser(description="Memory Python SDK 快速上手 Demo(C 端 AI 助手 10 轮对话)")parser.add_argument("--wait-seconds", type=int, default=300,help="写入后等待秒数,默认 300(5 分钟)")parser.add_argument("--no-wait", action="store_true",help="写完立即查看(冒烟用,不推荐生产观察)")parser.add_argument("--skip-write", action="store_true",help="跳过写入步骤,直接观察当前 session 的沉淀结果")parser.add_argument("--session-id", default=DEFAULT_SESSION_ID,help=f"自定义 session_id(默认 {DEFAULT_SESSION_ID})")args = parser.parse_args()# 配置兜底检查:避免用户忘了改默认占位符if "请替换" in ENDPOINT or "请替换" in API_KEY or "请替换" in SERVICE_ID:fail("检测到 ENDPOINT / API_KEY / SERVICE_ID 还是占位符,请先在脚本顶部填入真实值再运行。")sys.exit(2)section("Memory Python SDK 快速上手 Demo")info(f"实例 ID :{SERVICE_ID}")info(f"网关地址 :{ENDPOINT}")info(f"会话 ID :{args.session_id}")# MemoryClient 支持 with 语法,会自动关闭底层 httpx 连接with MemoryClient(endpoint=ENDPOINT,api_key=API_KEY,service_id=SERVICE_ID,timeout=30,verify=False, # test 网关用自签证书;正式环境删掉这行或改 True) as client:# 1) 写入 L0if not args.skip_write:step_write_l0(client, args.session_id)else:section("Step 1/3 跳过写入(--skip-write)")info("仅观察当前 session 已有沉淀")# 2) 等待内核沉淀wait = 0 if args.no_wait else args.wait_secondsstep_wait(wait)# 3) 回查四层step_inspect(client, args.session_id)section("Demo 执行完毕 ✅")print(" 下一步建议:")print(" 1. 打开 Memory 控制台 → 实例详情 → 记忆数据,可以可视化看到 L0/L1/L2/L3 全貌")print(" 2. 如果 L1/L2/L3 还为空,几分钟后再跑一次:")print(f" python {sys.argv[0]} --skip-write --no-wait")print(" 3. 若要换一个会话演示,加 --session-id <自定义ID> 即可\\n")if __name__ == "__main__":main()
演示对话内容
脚本里
build_dialogue() 会构造 10 轮(共 20 条)模拟对话,覆盖三个典型生活话题,让内核有足够的素材沉淀出有意义的 L1/L2/L3:话题 | 轮次 | 要点 |
① 减脂目标与训练计划 | 第 1~4 轮 | 3 个月减重 8kg、每周 4 次训练、自重训练为主、周一称重 |
② 饮食与运动偏好 | 第 5~7 轮 | 乳糖不耐受(忌牛奶/奶酪)、不吃香菜、训练只排上午 |
③ 使用习惯偏好 | 第 8~10 轮 | 22:00 后不推送、公制单位、轻松口吻 |
每条消息都会带一个递增的 ISO 8601 时间戳,方便在控制台 L0 列表中看到自然的对话时序。
典型输出
脚本结束时会打印类似下面的摘要,可以快速判断沉淀链路是否健康:
──────────────────────────────────────────────────────────────────────Step 3/3 查看 L0 / L1 / L2 / L3 四层记忆数据──────────────────────────────────────────────────────────────────────[L0] 原始对话✓ L0 query_conversation 调用成功· 会话 demo-pm-quickstart-001 中累计 20 条消息· 首条预览:[user] 我现在 78 公斤,身高 175,想在 3 个月内减到...[L1] 原子记忆(atomic notes)· L1 抽取统计:episodic=6 / persona=4 / instruction=5(共 15 条)[L2] 场景文件(scenario files)· 根目录下共 3 个场景文件[L3] 核心人格画像(core profile)· L3 已生成,长度 412 字,前 200 字预览:用户小磊,男性上班族,目标在 3 个月内由 78kg 减至 70kg ...
常见问题
Q1:跑完发现 L1 / L2 / L3 还是空的?
多半是内核还在沉淀。不需要重复写入,等几分钟后用下面这条命令再观察一次即可(把
quickstart.py 换成你保存的实际文件名):python quickstart.py --skip-write --no-wait
Q2:L3 read_core 报 404?
正常现象。L3 核心人格画像在实例首次还没攒够素材时就是 404,脚本里的
safe_call 会捕获 TDAMError 并友好打印,不会中断流程。等下次沉淀完成后再查即可。Q3:想自己定制对话内容?
直接改
build_dialogue() 里的 rounds 列表即可:每个元素是 {"user": ..., "assistant": ...} 一轮对话,脚本会自动展开成扁平消息列表并打上时间戳。Q4:想换个会话不污染原来的 demo session?
加
--session-id 即可:python quickstart.py --session-id my-test-001