☰
用Cursor 1小时搭建带长期记忆的情感陪伴智能体:TaoToken统一Key接入实战
2026/10/11 5:38:33 网站建设 项目流程

1. 从零构建情感陪伴智能体,为什么长期记忆是绕不开的坎

很多人第一次做情感陪伴类 AI-Agent,都会掉进同一个坑:聊三句还行,聊到第十句它就开始失忆,前面说过的名字、喜好、情绪状态全忘了。这不是模型不行,而是你没给它装"记忆"。情感陪伴和普通问答最大的区别在于,它需要跨会话记住"你是谁、你最近在烦什么、你上次说想养猫"。没有长期记忆,它就是个复读机。

我这次用 Cursor 从零搭了一个带长期记忆的情感陪伴智能体,核心链路是:Cursor 负责写代码和调试,TaoToken 统一 Key 负责模型接入,向量库负责记忆存储。整个流程实测下来 1 小时能跑通端到端对话。适合谁?适合会一点 Python、想快速验证 AI-Agent 想法、但不想在鉴权和多模型切换上耗时间的开发者。

为什么强调"统一 Key"?因为情感陪伴场景经常要换模型——便宜的模型跑日常闲聊,强一点的模型处理情绪危机,如果每个模型都单独配 Key、单独改 Base URL,代码里会到处是硬编码。TaoToken 的价值就在于一个 Key、一个 API 通道,切换模型只改一个 Model ID 字符串。下面我把完整过程拆开讲,包括 Cursor 里怎么配、记忆结构怎么设计、请求怎么验证、报错怎么排。

2. TaoToken 前置准备:一个 Key 打通多模型接入

在动手写代码前,先把模型通道准备好。这一步不做,后面 Cursor 生成的代码跑起来必然 401。TaoToken 在这里扮演的是统一模型服务入口,你不需要为每个模型单独申请账号、单独管理密钥。

先注册并拿到 API Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 完成账号注册,然后进控制台创建密钥。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 页面点新建,复制那串 sk- 开头的字符串。这个 Key 就是你后面所有模型调用的唯一凭证。

API 的基础地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接作为 Base URL 使用。很多新手会把带 UTM 的官网地址误填进代码里,结果请求 404,这是第一个高频坑。

关于模型选择,情感陪伴场景我建议至少准备两个 Model ID:一个轻量模型跑高频闲聊,一个能力更强的模型处理深度情绪对话。TaoToken 支持在同一个 Key 下切换不同模型,你只需要在请求体里改 model 字段。具体有哪些模型可用,可以在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 里先手动试聊几句,确认模型 ID 拼写正确再写进代码。

如果你后续要做长期编码或者更复杂的 Agent 编排,可以考虑 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它更适合持续性的开发任务。但本篇的 MVP 阶段,一个普通 Key 就够了。

这里要提醒一句:Key 不要硬编码进 Git 仓库。我习惯用 .env 文件管理,Cursor 生成代码时也会默认读环境变量。下面配置片段里我会用 os.getenv 的方式读取,你照着做就不会把密钥泄露出去。

3. 可复制配置:Cursor 项目结构与记忆存储设计

这一节是核心,直接给你能复制的配置和代码结构。先在 Cursor 里新建一个空项目文件夹,比如 emotional-agent,然后用 Cursor 打开。我建议的项目结构是这样的:

emotional-agent/ ├── .env ├── requirements.txt ├── config.py ├── memory.py ├── agent.py └── main.py

先写 .env,把 Key 和 Base URL 放进去:

TAOTOKEN_API_KEY=sk-你的密钥 TAOTOKEN_BASE_URL=https://taotoken.net/api CHAT_MODEL=你的轻量模型ID DEEP_MODEL=你的深度模型ID

然后是 config.py,统一读取配置,避免到处硬编码:

import os from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("TAOTOKEN_API_KEY") BASE_URL = os.getenv("TAOTOKEN_BASE_URL") CHAT_MODEL = os.getenv("CHAT_MODEL") DEEP_MODEL = os.getenv("DEEP_MODEL") # 记忆检索阈值:相似度低于此值不触发长期记忆 MEMORY_THRESHOLD = 0.75 # 每次注入对话的历史记忆条数 MEMORY_TOP_K = 3

接下来是记忆存储结构。情感陪伴的记忆分两层:短期记忆(当前会话的对话历史)和长期记忆(跨会话的用户画像与关键事件)。短期记忆直接用列表存,长期记忆用向量库存。我用 chromadb 做演示,轻量且本地可跑。memory.py 的核心结构:

import chromadb from config import MEMORY_THRESHOLD, MEMORY_TOP_K client = chromadb.PersistentClient(path="./memory_db") collection = client.get_or_create_collection(name="user_memory") def save_memory(user_id: str, text: str, metadata: dict): """把一条关键信息写入长期记忆""" collection.add( documents=[text], metadatas=[metadata], ids=[f"{user_id}-{metadata['ts']}"] ) def recall_memory(user_id: str, query: str): """根据当前输入检索相关长期记忆""" results = collection.query( query_texts=[query], n_results=MEMORY_TOP_K, where={"user_id": user_id} ) docs = results.get("documents", [[]])[0] return docs

这里有个关键设计:不是每句话都写进长期记忆,否则向量库会被"嗯""好的"这种废话塞满,检索质量暴跌。我的做法是让模型在回复时顺带判断"这句话是否包含值得长期记住的信息",如果是,才调用 save_memory。这个判断逻辑放在 agent.py 里。

agent.py 负责组装请求,把短期记忆 + 检索到的长期记忆一起塞进 messages:

from openai import OpenAI from config import API_KEY, BASE_URL, CHAT_MODEL, DEEP_MODEL from memory import recall_memory, save_memory client = OpenAI(api_key=API_KEY, base_url=BASE_URL) SYSTEM_PROMPT = """你是一个有温度的情感陪伴助手。 你会收到用户的历史记忆片段,请自然地融入对话,不要生硬复述。 如果用户表达了值得长期记住的信息(如喜好、重要事件、情绪困扰), 在回复末尾用 [MEMORY] 标记该信息。""" def build_messages(user_id: str, user_input: str, history: list): memories = recall_memory(user_id, user_input) memory_text = "\n".join(memories) if memories else "暂无历史记忆" messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "system", "content": f"用户历史记忆:\n{memory_text}"} ] messages.extend(history[-10:]) # 短期记忆保留最近10轮 messages.append({"role": "user", "content": user_input}) return messages def chat(user_id: str, user_input: str, history: list): messages = build_messages(user_id, user_input, history) resp = client.chat.completions.create( model=CHAT_MODEL, messages=messages, temperature=0.8 ) reply = resp.choices[0].message.content # 解析是否需要写入长期记忆 if "[MEMORY]" in reply: clean = reply.replace("[MEMORY]", "").strip() save_memory(user_id, clean, {"user_id": user_id, "ts": str(len(history))}) return clean return reply

注意 build_messages 里我把长期记忆作为独立的 system 消息注入,而不是拼进用户输入。这样做的好处是模型能区分"这是背景记忆"和"这是用户当前说的话",回复更自然。Cursor 在生成这段时我特意让它把记忆注入和用户输入分开,实测对话质量明显更好。

requirements.txt 内容:

openai chromadb python-dotenv

装依赖:pip install -r requirements.txt。到这里配置部分就齐了,下面验证请求。

4. 端到端验证:跑通第一轮带记忆的对话

配置写完,先别急着写复杂逻辑,用最小请求验证通道是否通。main.py 写一个交互循环:

from agent import chat def main(): user_id = "test_user_001" history = [] print("情感陪伴智能体已启动,输入 exit 退出") while True: user_input = input("你:") if user_input.lower() == "exit": break reply = chat(user_id, user_input, history) print(f"AI:{reply}") history.append({"role": "user", "content": user_input}) history.append({"role": "assistant", "content": reply}) if __name__ == "__main__": main()

运行python main.py。第一轮你输入"我最近工作压力好大,晚上总失眠",模型会正常回复。这时候长期记忆还没触发,因为第一轮没有历史可检索。

关键验证在第二轮和第三轮。你继续说"我养了只猫叫豆豆,它晚上会趴我枕头边",如果模型回复里带了 [MEMORY] 标记,说明它识别出这是值得记住的信息,save_memory 被调用了。然后你退出程序,重新运行 main.py,输入"我家猫最近怎么样",这时候 recall_memory 会检索到"豆豆"这条记忆,模型应该能说出猫的名字。

我实测下来,第一次跑通这个链路大概花了 40 分钟,其中 20 分钟在调记忆阈值。默认 0.75 有时候太严,聊好几轮才触发一次长期记忆,你可以先把 MEMORY_THRESHOLD 调到 0.6 试试,观察检索命中率。如果发现检索出来的记忆和当前话题不相关,再往上调。

验证成功的标志有三个:一是控制台没有报错,二是第二轮对话出现了 [MEMORY] 标记,三是重启程序后模型能回忆起之前的信息。三个都满足,说明端到端链路通了。

如果你想在 Cursor 里直接调试,可以在 chat 函数里打断点,看 messages 数组里长期记忆有没有正确注入。Cursor 的调试体验比传统 IDE 顺滑很多,变量面板能直接展开看 messages 内容,这点对排查记忆注入问题特别有用。

5. 常见报错排查:401、local proxy failed 与记忆不触发

跑不通的时候,90% 的问题集中在这几类。我按真实报错逐个说。

401 Unauthorized。这是最常见的。原因通常是 Key 没读到或者 Base URL 填错。先检查 .env 文件是否在项目根目录,load_dotenv 是否能找到。然后在 config.py 里加一行print(API_KEY[:8])确认 Key 读进来了。如果 Key 正常,检查 BASE_URL 是不是写成了带 UTM 的官网地址——必须是 https://taotoken.net/api ,不能带任何查询参数。还有一个隐蔽情况:Key 复制时带了空格,用 strip() 清一下。

local proxy failed / connection error。这类报错通常是网络层的问题,不是 Key 的问题。先确认你的运行环境能正常访问外网 API。如果你在公司内网,可能有防火墙拦截,换个网络环境试试。另外检查是不是本地开了什么网络工具导致请求被劫持,关掉再试。这个报错和 Key 无关,别去反复重置密钥。

reading 'choices' 报错,提示 NoneType 没有 choices 属性。这说明请求发出去了但返回体结构不对,通常是 resp 为 None 或者返回了错误信息。在 chat 函数里加异常捕获,把原始返回打出来:

try: resp = client.chat.completions.create(...) except Exception as e: print("请求异常:", e) return "抱歉,我这边出了点问题"

打印出来你大概率会看到模型 ID 拼写错误,或者该模型当前不可用。去模型对话页面确认一下 Model ID 的准确拼写。

OAuth 相关报错。如果你在 Cursor 里配置了某些插件的 OAuth 登录,可能会和 API Key 鉴权冲突。情感陪伴项目不需要 OAuth,确保你用的是纯 API Key 方式。Cursor 的 settings 里如果有残留的 OAuth 配置,清掉。

记忆不触发。程序不报错,但聊了很多轮长期记忆始终是空的。三个排查方向:一是 MEMORY_THRESHOLD 太高,检索永远不命中,调低到 0.6;二是模型没有按格式输出 [MEMORY] 标记,检查 SYSTEM_PROMPT 是否完整;三是 save_memory 的 metadata 里 user_id 和 recall 时的 where 条件不一致,导致写进去查不出来。我踩过的坑是第三条,user_id 一个用了字符串一个用了变量,查了半天。

Codex auth.json 相关。如果你同时用 Codex 类工具,注意它的 auth.json 和本项目的 .env 是两套独立配置,不要混用。本项目只需要 Base URL、Key、Model ID 三件套,分别对应 TAOTOKEN_BASE_URL、TAOTOKEN_API_KEY、CHAT_MODEL。

排障的核心思路是:先确认 Key 和 Base URL 正确,再确认模型 ID 存在,最后确认记忆读写用的是同一个 user_id。按这个顺序查,基本都能定位。

6. 继续迭代:把 MVP 变成能长期用的陪伴 Agent

跑通之后,这个 Agent 还有很多可以打磨的地方。我列几个我实际加过的优化,你可以按需跟进。

第一是记忆的衰减和合并。向量库会越存越多,时间久了很多旧记忆不再相关。可以给每条记忆加时间戳,检索时对近期记忆加权。或者定期让模型把相似记忆合并成一条摘要,减少冗余。

第二是情绪识别分流。日常闲聊用轻量模型,一旦检测到用户情绪低落或提到危机信号,自动切到 DEEP_MODEL。切换只需要改 chat 函数里的 model 参数,因为 TaoToken 是统一 Key,不用换客户端。

第三是记忆的主动召回。现在是用户说话才检索,可以改成每次对话开始前,先根据用户画像主动召回几条核心记忆,让模型开场就能接上上次的话题,陪伴感更强。

如果你要把这个 Agent 做成长期运行的服务,建议看看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它在持续调用和 Agent 编排上更省心。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到参数细节可以直接查。API Keys 管理页面还是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,需要新建或轮换密钥时去那里操作。

最后说个实用技巧:Cursor 生成代码后,别急着全盘接受。让它先解释每一段在干什么,尤其是记忆读写部分,确认 user_id 传递链路完整再运行。我一开始就是没检查,save 和 recall 用了不同的 user_id 变量,白白 debug 了半小时。把记忆链路的 user_id 统一成一个常量,能省掉很多麻烦。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询