以下是修改后的代码,实现了根据用户输入动态更新数据库中长期记忆的功能。核心逻辑:读取已有画像 → 从用户消息中提取新爱好 → 如果不同则更新数据库。
importosimportsysimportasyncioimportrefromdotenvimportload_dotenvfromlangchain_openaiimportChatOpenAIfromlanggraph.graphimportStateGraph,MessagesState,STARTfromlanggraph.checkpoint.memoryimportInMemorySaverfromlanggraph.store.postgresimportAsyncPostgresStore# 解决 Windows 上 psycopg 与 ProactorEventLoop 不兼容的问题ifsys.platform=='win32':asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())load_dotenv()# ========== 1. 数据库配置 ==========DB_URI="postgresql://postgres:root@localhost:5432/langgraph_db"# ========== 2. 初始化 LLM ==========llm=ChatOpenAI(model="qwen-plus",temperature=0.7,api_key=os.getenv("DASHSCOPE_API_KEY"),base_url=os.getenv("DASHSCOPE_BASE_URL"),model_kwargs={"extra_body":{"enable_thinking":False}})checkpointer=InMemorySaver()defcall_agent(state:MessagesState):response=llm.invoke(state["messages"])return{"messages":[response]}defextract_hobby_from_text(text:str)->str|None:"""从用户消息中提取爱好关键词(简单规则)"""patterns=[r"喜欢(养)?(猫|狗|鸟|鱼|乌龟|兔子|摄影|画画|唱歌|跳舞|游泳|跑步|读书)",r"爱好是(.{2,6})",r"现在喜欢(养)?(.{2,6})",]forpatterninpatterns:match=re.search(pattern,text)ifmatch:# 提取捕获组中的关键词hobby=match.group(2)iflen(match.groups())>=2elsematch.group(1)ifhobby:returnhobby.strip()returnNoneasyncdefmain():asyncwithAsyncPostgresStore.from_conn_string(DB_URI)asstore:awaitstore.setup()print("✅ PostgreSQL 长期存储已连接")builder=StateGraph(MessagesState)builder.add_node("call_agent",call_agent)builder.add_edge(START,"call_agent")graph=builder.compile(checkpointer=checkpointer,store=store)user_id="user_001"config={"configurable":{"thread_id":f"{user_id}_thread"}}namespace=("profiles",user_id)# 1. 读取已有记忆existing=awaitstore.aget(namespace,"user_info")ifnotexisting:default_profile={"name":"张明","hobby":"摄影","preferences":{"language":"zh-CN","style":"detailed"}}awaitstore.aput(namespace,"user_info",default_profile)print("💾 首次写入默认用户画像")profile=default_profileelse:profile=existing.valueprint(f"📋 读取现有画像:{profile}")# 2. 模拟用户输入(可从控制台输入或硬编码)user_input=input("\n请输入您的消息: ")# 例如:"我现在喜欢养猫"print(f"用户:{user_input}")# 3. 提取新爱好并比较new_hobby=extract_hobby_from_text(user_input)ifnew_hobbyandnew_hobby!=profile.get("hobby"):print(f"🔄 检测到爱好变化:{profile['hobby']}→{new_hobby}")profile["hobby"]=new_hobbyawaitstore.aput(namespace,"user_info",profile)print("💾 用户画像已更新到 PostgreSQL")# 重新读取以获取最新(仅用于后续显示)existing=awaitstore.aget(namespace,"user_info")profile=existing.value# 4. 构造带记忆的对话context=f"用户偏好:{profile}。请基于此回答。"messages=[("system",context),("user",user_input)]# 5. 执行图foreventingraph.stream({"messages":messages},config=config,stream_mode="values"):event["messages"][-1].pretty_print()if__name__=="__main__":asyncio.run(main())主要修改点说明
添加爱好提取函数
extract_hobby_from_text
使用简单的正则表达式从用户消息中提取“喜欢xxx”或“爱好是xxx”的内容。你可以根据实际场景扩展规则。交互式输入
将硬编码的用户消息改为input(),方便测试不同输入。动态更新逻辑
- 读取现有画像 → 从新消息中提取爱好 → 如果不同则调用
store.aput覆盖数据库。 - 更新后重新读取
profile,确保后续对话使用最新画像。
- 读取现有画像 → 从新消息中提取爱好 → 如果不同则调用
保持回复使用最新记忆
构造系统提示时使用更新后的profile,让 LLM 基于最新爱好回答。
测试效果
- 首次运行:存储默认
hobby: "摄影"。 - 第二次运行,输入
"我现在喜欢养猫":- 程序提取出新爱好
"猫",发现与"摄影"不同。 - 执行
UPDATE store SET value = ...,数据库中的hobby变为"养猫"。 - 对话中的系统提示变为
"用户偏好:{... 'hobby': '养猫' ...}"。
- 程序提取出新爱好
- 第三次运行,输入
"我喜欢拍风景照片"(未提及爱好变化),不会更新数据库,但模型回答仍会基于"养猫"给出个性化回复(如果上下文合理)。
如何进一步优化
- 使用 LLM 判断意图:可调用一次轻量模型来判断用户是否表达了偏好改变,而不依赖正则。
- 支持多字段更新:不仅爱好,还可更新姓名、偏好风格等。
- 增加确认机制:更新前询问用户“确认将爱好从XX改为YY吗?”
这样,你就拥有了一个能够动态演化的长期记忆系统,数据库中的信息会随着对话而自动更新。