用 Claude API 从零搭一个记住你习惯的私人 AI 助手
本文最后更新于 2026-05-23,文章内容可能已经过时。
用 Claude API 从零搭一个记住你习惯的私人 AI 助手
你有没有算过,你这辈子跟 AI 说了多少次"我不喜欢用'首先其次最后'"?
我数了一下,大概是 47 次。每次让 Claude 或 ChatGPT 帮我写东西,都要在开头贴一段"说明书":我是做产品的、偏好简洁直接的语气、段落不要超过三行、禁止用任何排比句……说完这段话,AI 才算真正"认识了我"。
然后对话窗口一关,下次又是陌生人。
这不是 AI 的问题,是所有 AI 产品的架构通病——每个对话都是独立的,没有跨会话的记忆。ChatGPT 的 Memory 功能是个例外,但它是黑箱,你不知道它记了什么、怎么用的。
今天这篇文章要做的事很简单:用不到 100 行 Python,给 Claude 装上一个透明可控的记忆系统,让它真正记住你是谁、你喜欢什么、你的工作习惯是什么。
不需要懂机器学习,不需要搭数据库,只需要一个 Claude API Key 和一点点耐心。
为什么 AI 总是"失忆"?
先花两分钟搞清楚原理,后面动手会顺很多。
大语言模型的工作方式是这样的:你给它一段文字(叫做 context),它预测下一个词。它没有持久化存储,每次对话的"记忆"完全依赖你传给它的 context 内容。
这意味着,如果你想让 AI 记住你的偏好,本质上就是一件事:在每次对话开始时,把你的偏好信息塞进 context 里。
具体有三种实现路径:
| 方案 | 优点 | 缺点 | 适用场景 | | System Prompt 注入 | 最简单,零依赖 | 只能存少量结构化信息 | 个人使用,偏好记忆 | | 外部文件存储 | 可持久化,可编辑 | 需要手动维护 | 本文选择方案 | | 向量数据库检索 | 可存海量内容,语义检索 | 搭建成本高 | 企业级/内容记忆场景 |本文选择 "结构化 JSON 文件 + 动态 System Prompt 拼接" 这条路。理由很实在:零额外依赖、完全透明可控、你随时可以打开 JSON 文件直接编辑自己的画像。
💡 Claude 的 context window 上限是 200K tokens,相当于一本 30 万字的中文小说。对于存储个人偏好画像这种场景,完全绰绰有余——一份写得很详细的用户画像,也不过几千个 token。
数据流长这样:
用户输入
↓
读取本地 profile.json(用户画像)
↓
将画像拼接进 System Prompt
↓
发送给 Claude API
↓
Claude 生成回复
↓
判断:这次对话有没有透露新的偏好信息?
↓
有 → 更新 profile.json → 写回本地
没有 → 直接结束
整个系统的核心就是这个循环。接下来我们一步步把它实现出来。
动手实操:30 分钟搭出来
第一步:获取 Claude API 密钥 + 环境配置
💡 国内用户获取 Claude API 的最快路径
>
直接访问 [api.884819.xyz](https://api.884819.xyz) 注册,无需解决网络问题,支持调用 Claude 全系列模型(包括claude-opus-4和claude-sonnet-4-5),本文所有代码均在该平台测试通过。新用户注册即送体验 token,跑完本教程的额度完全够用。
拿到 API Key 之后,安装依赖:
pip install anthropic
就这一个包,没有其他依赖。
第二步:建立你的用户画像 JSON
在项目目录下创建 profile.json,这是整个系统的"记忆核心":
{
"name": "Alex",
"profession": "产品经理",
"writing_style": {
"tone": "简洁直接,不废话",
"paragraph_length": "每段不超过3行",
"forbidden_phrases": ["首先", "其次", "最后", "综上所述", "不难看出"],
"preferred_format": "短句为主,必要时用列表"
},
"language_preference": "中文,偶尔用英文术语",
"work_context": {
"industry": "互联网,To B SaaS",
"common_tasks": ["写周报", "整理会议纪要", "分析竞品", "写PRD"],
"team_size": "10人左右的产品团队"
},
"personal_preferences": {
"likes": ["数据驱动的分析", "有结论的建议", "类比举例"],
"dislikes": ["套话", "模糊的建议", "过度正面的语气"]
},
"last_updated": "2025-01-01"
}
这个 JSON 就是你的"AI 身份证"。字段可以按需增减,关键是把你真正在乎的偏好写进去。
第三步:动态拼接 System Prompt
# prompt_builder.py
import json
from datetime import date
def load_profile(path="profile.json") -> dict:
"""加载用户画像,文件不存在时返回空画像"""
try:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
return {}
def build_system_prompt(profile: dict) -> str:
"""将用户画像转换为 System Prompt"""
if not profile:
return "你是一个通用 AI 助手。"
# 核心:把结构化数据转成自然语言指令
prompt = f"""你是 {profile.get('name', '用户')} 的私人 AI 助手。
【关于用户】
- 职业:{profile.get('profession', '未知')}
- 工作背景:{profile.get('work_context', {}).get('industry', '未知')}
- 常用语言:{profile.get('language_preference', '中文')}
【写作风格要求】(每次回复都必须遵守)
- 语气:{profile.get('writing_style', {}).get('tone', '正常')}
- 段落长度:{profile.get('writing_style', {}).get('paragraph_length', '适中')}
- 禁止使用的词汇:{', '.join(profile.get('writing_style', {}).get('forbidden_phrases', []))}
- 格式偏好:{profile.get('writing_style', {}).get('preferred_format', '正常')}
【用户偏好】
- 喜欢:{', '.join(profile.get('personal_preferences', {}).get('likes', []))}
- 不喜欢:{', '.join(profile.get('personal_preferences', {}).get('dislikes', []))}
请始终以符合上述偏好的方式回答,不需要在回复中提及这些设定。"""
return prompt
这段代码只做一件事:把 JSON 里的结构化数据,翻译成 Claude 能理解的自然语言指令。
第四步:主对话循环
# main.py
import json
import anthropic
from datetime import date
from prompt_builder import load_profile, build_system_prompt
client = anthropic.Anthropic(
api_key="你的API密钥",
base_url="https://api.884819.xyz" # 使用8848AI平台
)
def save_profile(profile: dict, path="profile.json"):
"""将更新后的画像写回文件"""
profile["last_updated"] = str(date.today())
with open(path, "w", encoding="utf-8") as f:
json.dump(profile, f, ensure_ascii=False, indent=2)
def check_and_update_profile(user_input: str, profile: dict) -> dict:
"""
让 Claude 判断用户输入是否包含新的偏好信息
如果有,返回更新后的画像;否则返回原画像
"""
update_prompt = f"""分析以下用户输入,判断是否包含用户偏好、习惯或背景信息的更新。
用户输入:"{user_input}"
当前用户画像:
{json.dumps(profile, ensure_ascii=False, indent=2)}
如果用户输入包含新的偏好信息(比如"我不喜欢..."、"以后帮我..."、"我是做...的"等),
请返回更新后的完整 JSON 画像。
如果没有新信息,只返回字符串 "NO_UPDATE"。
只返回 JSON 或 "NO_UPDATE",不要有其他内容。"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1000,
messages=[{"role": "user", "content": update_prompt}]
)
result = response.content[0].text.strip()
if result == "NO_UPDATE":
return profile
try:
updated_profile = json.loads(result)
print("✅ 检测到偏好更新,已自动保存到画像")
return updated_profile
except json.JSONDecodeError:
return profile # 解析失败时保留原画像
def chat():
"""主对话循环"""
profile = load_profile()
conversation_history = []
name = profile.get('name', '你')
print(f"你好,{name}!我已加载你的个人画像,开始对话吧。输入 'quit' 退出。\n")
while True:
user_input = input("你:").strip()
if user_input.lower() == 'quit':
print("再见!你的偏好已自动保存。")
break
if not user_input:
continue
# 每次对话前重新加载画像(支持实时更新)
profile = load_profile()
system_prompt = build_system_prompt(profile)
# 将用户输入加入对话历史
conversation_history.append({
"role": "user",
"content": user_input
})
# 发送请求
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2000,
system=system_prompt,
messages=conversation_history
)
assistant_reply = response.content[0].text
conversation_history.append({
"role": "assistant",
"content": assistant_reply
})
print(f"\nClaude:{assistant_reply}\n")
# 异步判断是否需要更新画像
updated_profile = check_and_update_profile(user_input, profile)
if updated_profile != profile:
save_profile(updated_profile)
if __name__ == "__main__":
chat()
运行这段代码,当 Claude 第一次用你偏好的语气、叫着你的名字回答你时——那个感觉,真的有点不一样。
进阶玩法:让记忆更聪明
基础版已经能用了,但还有三个方向可以继续打磨。
1. 按场景分类记忆
一个人在工作和生活中的语气需求完全不同。可以在 profile.json 里加一个 modes 字段:
{
"modes": {
"work": {
"tone": "专业简洁",
"forbidden_phrases": ["哈哈", "感觉", "好像"]
},
"life": {
"tone": "轻松随意",
"forbidden_phrases": ["综上所述", "数据显示"]
}
},
"current_mode": "work"
}
在 build_system_prompt 里根据 current_mode 动态选择对应的风格配置,对话中输入 /work 或 /life 切换模式。
2. 记忆的"遗忘机制"
画像积累久了会越来越乱,互相矛盾的偏好会让 AI 无所适从。可以加一个定期清理函数:
def clean_profile(profile: dict) -> dict:
"""让 Claude 整理并精简画像,去除冗余和矛盾项"""
clean_prompt = f"""以下是用户的 AI 助手画像,请帮我整理:
1. 合并重复或相似的条目
2. 删除互相矛盾的设定(保留更新的那条)
3. 精简措辞,保持简洁
原始画像:
{json.dumps(profile, ensure_ascii=False, indent=2)}
返回整理后的完整 JSON,不要有其他内容。"""
# ... 调用 API 并解析返回结果
建议每隔 30 次对话自动触发一次,或者在 profile.json 里记录 conversation_count 来追踪。
3. 对话摘要压缩
当 conversation_history 积累很长时,可以定期让 Claude 把历史对话压缩成摘要,避免 context 过长:
def compress_history(history: list) -> list:
"""将超过20轮的对话历史压缩为摘要"""
if len(history) <= 20:
return history
# 保留最近10轮,把更早的压缩成摘要
old_history = history[:-10]
recent_history = history[-10:]
# 让 Claude 总结旧对话
summary = summarize_conversation(old_history)
return [{"role": "user", "content": f"[对话摘要] {summary}"},
{"role": "assistant", "content": "好的,我已了解之前的对话背景。"}] + recent_history
踩坑记录:这三个报错你大概率会遇到
报错 1:AuthenticationError
anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}}
原因:API Key 填错了,或者忘记设置 base_url。
解决:检查 Key 是否完整复制(注意不要有空格),并确认 base_url 指向正确的平台地址。
报错 2:JSON 解析失败
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
原因:check_and_update_profile 函数里,Claude 有时会在 JSON 前后加上 markdown 代码块标记( `json 和 ` ),导致直接 json.loads() 失败。
解决:在解析前清理一下:
result = result.strip()
if result.startswith("
"):
result = result.split("``")[1]
if result.startswith("json"):
result = result[4:]
result = result.strip()
check_and_update_profile报错 3:画像更新死循环
现象:每次对话都触发画像更新,哪怕你只是说了句"好的"。 原因:的判断提示词写得太宽泛,Claude 过于积极地"发现"新偏好。 解决:在提示词里加一条约束:
只有当用户明确表达偏好(使用"我喜欢/不喜欢/以后/帮我记住"等明确意图词)时才更新。
日常对话、问答、闲聊一律返回 "NO_UPDATE"。
``这套方案的边界在哪里
说实话,这个方案是有局限的。
JSON 文件适合存储结构化的偏好信息:语气风格、职业背景、禁忌词汇……这些东西几百个字段就能描述清楚。
但如果你想让 AI 记住的是非结构化的内容——你写过的所有文章、读过的所有资料、过去一年的会议记录——JSON 就力不从心了。你不可能把一年的文档都塞进 System Prompt,context window 再大也有上限,而且每次都全量传输,成本和延迟都不可接受。
这套方案今天能解决 80% 的个人使用场景。剩下那 20%,需要另一套武器。文中所有代码可在 [api.884819.xyz](https://api.884819.xyz) 申请密钥后直接运行,新用户注册后有免费额度可以完整跑完本教程。
下一篇预告 👇
>
本文的记忆方案用 JSON 文件存储,适合记住几十条偏好。
但如果你想让 AI 记住的不是几条习惯,而是你过去一年写过的所有文档、读过的所有文章——
JSON 就不够用了。
>
下一篇我们会把这套系统升级:接入向量数据库,让 Claude 拥有真正的长期语义记忆,实现"你随口一提,它就能翻出三个月前你说过的那句话"。
>
关注我,不要错过。本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。
#AI教程 #Claude #Python #AI助手 #Prompt技巧 #8848AI #人工智能 #开发者工具