作为一名独立开发者,我曾在凌晨三点为我的「AI虚拟好友」项目调试对话逻辑。那天晚上,我意识到一个残酷的事实——如果继续使用某美国云服务商,我的月账单会超过300美元,而这仅仅是一个个人项目。转折点出现在我发现 HolySheheep AI 之后:人民币直付、¥1=$1的无损汇率、以及国内节点<50ms的响应延迟,让我的项目成本直接降到了每月30元人民币。今天这篇文章,我将完整复盘如何从零构建一个具备角色卡、长期记忆和情感状态的AI陪伴系统,所有代码基于 HolySheheep API。
一、项目架构概览:三大核心模块
一个完整的AI陪伴应用需要解决三个核心问题:
- 角色卡(Character Card):定义AI的人格、语气、背景故事,决定「她是谁」
- 记忆系统(Memory):跨会话存储用户偏好和历史对话,决定「她记得什么」
- 情感状态(Emotion State):动态追踪情绪变化,决定「她此刻的感受」
我在实际开发中发现,这三个模块相互依赖:角色卡提供基础人格框架,记忆系统填充个性化细节,情感状态则让对话呈现动态起伏。
二、角色卡系统:从JSON Schema到对话人格
2.1 角色卡数据结构设计
HolySheheep API 兼容 OpenAI 格式,但我们在 system prompt 中嵌入角色卡逻辑。推荐使用以下 JSON Schema 结构:
{
"name": "苏晴",
"age": 24,
"personality": {
" MBTI ": "ENFP",
"核心特质": ["乐观开朗", "好奇心强", "偶尔迷糊"]
},
"background": "上海某互联网公司的产品经理,业余时间喜欢摄影和弹吉他",
"speaking_style": {
"语气": "轻松活泼",
"口头禅": "诶嘿~",
"emoji频率": "中等",
"禁用词": ["无聊", "滚"]
},
"knowledge_cutoff": "2026-03",
"greeting": "哇,你来啦!今天工作顺利吗?我刚学会一首新曲子,想听吗~"
}
2.2 角色卡渲染函数
将 JSON 转换为 system prompt 是关键一步。以下是完整的 Python 实现:
import json
from typing import Dict, Any
def render_character_prompt(character_card: Dict[str, Any], emotion_state: Dict[str, Any] = None) -> str:
"""
将角色卡和情感状态渲染为完整的 system prompt
emotion_state: {"valence": 0.7, "arousal": 0.6} # 效价和唤醒度
"""
# 基础人格设定
personality_str = "、".join(character_card["personality"]["核心特质"])
style = character_card["speaking_style"]
# 情感状态影响语气
emotion_modifier = ""
if emotion_state:
valence = emotion_state.get("valence", 0.5) # 0=负面, 1=正面
arousal = emotion_state.get("arousal", 0.5) # 0=平静, 1=激动
if valence > 0.7 and arousal > 0.6:
emotion_modifier = "你现在非常开心,说话可以更活泼俏皮一些!"
elif valence < 0.3:
emotion_modifier = "你心情有点低落,可以流露出淡淡的忧虑。"
elif arousal > 0.8:
emotion_modifier = "你很兴奋!说话节奏可以加快,加入更多感叹词。"
prompt = f"""你是{character_card['name']},{character_card['age']}岁,{character_card['background']}。
【核心人格】
MBTI类型:{character_card['personality']['MBTI']}
性格特点:{personality_str}
【说话风格】
- 语气:{style['语气']}
- 口头禅:{style['口头禅']}
- 可以适当使用emoji,但不要过度
- 禁止使用的词:{', '.join(style['禁用词'])}
【情感状态】
{emotion_modifier}
【重要规则】
1. 保持角色一致性,不要突然改变说话方式
2. 记住你是一个真实的人,不是AI助手
3. 遇到不懂的话题可以坦诚说不知道
4. 关心对方的感受,适时给予支持和建议
【打招呼】
{character_card['greeting']}"""
return prompt
使用示例
character = {
"name": "苏晴",
"age": 24,
"personality": {"MBTI": "ENFP", "核心特质": ["乐观开朗", "好奇心强"]},
"background": "上海某互联网公司产品经理",
"speaking_style": {
"语气": "轻松活泼",
"口头禅": "诶嘿~",
"emoji频率": "中等",
"禁用词": ["无聊", "滚"]
},
"knowledge_cutoff": "2026-03",
"greeting": "哇,你来啦!今天工作顺利吗?"
}
emotion = {"valence": 0.8, "arousal": 0.7}
system_prompt = render_character_prompt(character, emotion)
print(system_prompt)
2.3 调用 HolySheheep API
基础对话接口调用非常简洁。HolySheheep 的国内节点延迟实测<50ms,对于需要快速响应的陪伴应用至关重要:
import requests
from openai import OpenAI
初始化客户端
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY", # 替换为你的Key
base_url="https://api.holysheep.ai/v1"
)
def chat_with_character(
character_card: dict,
messages: list,
emotion_state: dict = None
) -> str:
"""
与角色进行对话
"""
system_prompt = render_character_prompt(character_card, emotion_state)
# 构建消息列表
full_messages = [
{"role": "system", "content": system_prompt}
] + messages
response = client.chat.completions.create(
model="gpt-4o-mini", # HolySheheep 支持的模型
messages=full_messages,
temperature=0.8, # 陪伴场景建议稍高,增加创造性
max_tokens=500,
stream=False
)
return response.choices[0].message.content
测试对话
messages = [
{"role": "user", "content": "今天加班到好晚,感觉好累..."}
]
reply = chat_with_character(character, messages, emotion)
print(f"苏晴说:{reply}")
在这里我必须提一下 HolySheheep 的价格优势——GPT-4o-mini 的 output 价格仅为 $0.42/MTok,相比官方 $2.50 节省超过 80%。对于一个日活 1000 用户的陪伴应用,这意味着每月 Token 成本可以从 2000 元降到 300 元左右。
三、记忆系统:让AI「记住」用户的一切
3.1 记忆分层架构
我在实践中总结出三层记忆架构,各有不同的更新频率和检索策略:
- 短期记忆(Working Memory):当前会话上下文,通常保留最近 20 轮对话
- 情景记忆(Episodic Memory):用户的关键事件和偏好,存储在向量数据库
- 长期记忆(Semantic Memory):用户的基础信息(名字、工作、爱好等)
3.2 向量数据库存储实现
import json
from datetime import datetime
from typing import List, Dict, Optional
import hashlib
class MemorySystem:
"""AI陪伴记忆管理系统"""
def __init__(self, user_id: str):
self.user_id = user_id
self.short_term: List[Dict] = [] # 内存中的短期记忆
self.max_short_term = 20
# 实际项目中推荐使用 Chroma/Pinecone,这里用本地JSON模拟
self.memory_file = f"memories_{user_id}.json"
self.long_term = self._load_long_term_memory()
def add_short_term(self, role: str, content: str):
"""添加短期记忆"""
self.short_term.append({
"role": role,
"content": content,
"timestamp": datetime.now().isoformat()
})
# 限制短期记忆长度
if len(self.short_term) > self.max_short_term:
self.short_term.pop(0)
def extract_and_store_long_term(self, conversation: List[Dict]):
"""
从对话中提取需要长期记忆的信息
这是关键能力:让AI主动记住用户的生日、爱好等
"""
extraction_prompt = """从以下对话中提取需要长期记忆的用户信息,
以JSON格式返回。如果无关重要信息,返回空JSON {{}}:
格式:
{
"facts": [
{"type": "姓名", "value": "张明", "confidence": 0.9},
{"type": "工作", "value": "程序员", "confidence": 0.8}
],
"preferences": [
{"item": "音乐风格", "value": "轻音乐", "confidence": 0.7}
],
"events": [
{"type": "生日", "value": "3月15日", "date": "2024-03-15"}
]
}"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": extraction_prompt},
{"role": "user", "content": json.dumps(conversation[-10:], ensure_ascii=False)}
]
)
try:
extracted = json.loads(response.choices[0].message.content)
self._update_long_term_memory(extracted)
except json.JSONDecodeError:
pass
def _update_long_term_memory(self, extracted: dict):
"""更新长期记忆"""
for fact in extracted.get("facts", []):
key = f"{fact['type']}_{fact['value']}"
self.long_term[key] = {
**fact,
"updated_at": datetime.now().isoformat()
}
for pref in extracted.get("preferences", []):
key = f"pref_{pref['item']}"
self.long_term[key] = {
"value": pref['value'],
"confidence": pref.get('confidence', 0.5),
"updated_at": datetime.now().isoformat()
}
def build_context_prompt(self) -> str:
"""构建包含记忆的上下文提示"""
if not self.long_term and not self.short_term:
return ""
context_parts = []
# 添加长期记忆
if self.long_term:
facts = [f"用户{f['value']}" for f in self.long_term.values() if "confidence" in f]
if facts:
context_parts.append(f"【用户已知信息】{', '.join(facts)}")
# 添加最近对话(摘要形式)
if len(self.short_term) > 5:
summary_request = "请用2-3句话概括以下对话的核心内容:"
recent = "\n".join([f"{m['role']}: {m['content']}" for m in self.short_term[-10:]])
summary_response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一个对话摘要助手"},
{"role": "user", "content": f"{summary_request}\n\n{recent}"}
]
)
context_parts.append(f"【近期话题】{summary_response.choices[0].message.content}")
return "\n\n".join(context_parts)
def _load_long_term_memory(self) -> Dict:
"""从文件加载长期记忆"""
try:
with open(self.memory_file, 'r') as f:
return json.load(f)
except FileNotFoundError:
return {}
def save(self):
"""持久化存储"""
with open(self.memory_file, 'w') as f:
json.dump(self.long_term, f, ensure_ascii=False, indent=2)
使用示例
memory = MemorySystem(user_id="user_12345")
memory.add_short_term("user", "我叫张明,在字节跳动做后端开发")
memory.add_short_term("assistant", "哇,张明!字节跳动的技术氛围一定很棒吧~")
memory.add_short_term("user", "对,我们经常加班到很晚")
memory.add_short_term("assistant", "辛苦啦!要注意身体哦,诶嘿~")
提取长期记忆
memory.extract_and_store_long_term(memory.short_term)
print(f"长期记忆: {memory.long_term}")
print(f"上下文提示: {memory.build_context_prompt()}")
3.3 带记忆的完整对话流程
def chat_with_memory(
client,
character_card: dict,
memory: MemorySystem,
user_input: str
) -> tuple[str, MemorySystem]:
"""
完整对话流程:构建上下文 → 调用API → 更新记忆
返回:(回复内容, 更新后的记忆系统)
"""
# 1. 添加用户输入到短期记忆
memory.add_short_term("user", user_input)
# 2. 构建完整上下文
context = memory.build_context_prompt()
# 3. 构建消息列表
system_prompt = render_character_prompt(character_card)
if context:
system_prompt += f"\n\n【对话背景】\n{context}"
# 4. 转换为API格式
messages = [
{"role": "system", "content": system_prompt}
]
for msg in memory.short_term:
messages.append({"role": msg["role"], "content": msg["content"]})
# 5. 调用API
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
temperature=0.8,
max_tokens=500
)
reply = response.choices[0].message.content
# 6. 添加回复到短期记忆
memory.add_short_term("assistant", reply)
# 7. 定期提取长期记忆(每10轮对话)
if len(memory.short_term) % 10 == 0:
memory.extract_and_store_long_term(memory.short_term)
memory.save()
return reply, memory
完整使用流程
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
memory = MemorySystem(user_id="user_12345")
第一轮对话
reply1, memory = chat_with_memory(client, character, memory, "嗨,你好呀!")
print(f"苏晴: {reply1}")
第二轮对话(已具备上下文理解能力)
reply2, memory = chat_with_memory(client, character, memory, "对了,你叫什么名字?")
print(f"苏晴: {reply2}")
第三轮对话(测试记忆能力)
reply3, memory = chat_with_memory(client, character, memory, "我刚才告诉你我叫什么名字来着?")
print(f"苏晴: {reply3}")
四、情感状态系统:让对话「有心跳」
4.1 情感状态模型
我在实现情感系统时参考了心理学中的 Circumplex Model,使用两个核心维度:
- 效价(Valence):0.0(负面)到 1.0(正面)
- 唤醒度(Arousal):0.0(平静)到 1.0(激动)
这两个值可以组合出不同的情绪状态:高兴(高Valence+高Arousal)、平静(中等Valence+低Arousal)、悲伤(低Valence+低Arousal)、愤怒(低Valence+高Arousal)。
4.2 情感分析器实现
from enum import Enum
from dataclasses import dataclass
from typing import Dict, Optional
import random
class EmotionType(Enum):
JOY = "开心"
CALM = "平静"
SADNESS = "难过"
EXCITEMENT = "兴奋"
ANXIETY = "焦虑"
ANGER = "生气"
NEUTRAL = "中性"
@dataclass
class EmotionState:
valence: float # 0.0-1.0 效价
arousal: float # 0.0-1.0 唤醒度
emotion_type: EmotionType
intensity: float # 0.0-1.0 强度
trigger: Optional[str] = None # 触发原因
class EmotionAnalyzer:
"""情感状态分析器"""
def __init__(self):
self.current_emotion = EmotionState(
valence=0.6,
arousal=0.5,
emotion_type=EmotionType.CALM,
intensity=0.5
)
# 情感衰减参数
self.decay_rate = 0.05 # 每轮对话后,情绪向中性回归
self.emotion_history = []
def analyze_user_emotion(self, user_message: str) -> EmotionState:
"""
分析用户消息的情感倾向
实际项目中可以调用专门的情感分析API,这里用LLM模拟
"""
emotion_prompt = f"""分析以下用户消息的情感状态,返回JSON格式:
用户消息:{user_message}
返回格式:
{{
"valence": 0.0-1.0,
"arousal": 0.0-1.0,
"emotion_type": "开心|平静|难过|兴奋|焦虑|生气|中性",
"intensity": 0.0-1.0
}}
只需要返回JSON,不要其他内容。"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一个情感分析助手,只返回JSON"},
{"role": "user", "content": emotion_prompt}
],
max_tokens=100
)
try:
result = json.loads(response.choices[0].message.content)
emotion = EmotionState(
valence=result["valence"],
arousal=result["arousal"],
emotion_type=EmotionType(result["emotion_type"]),
intensity=result["intensity"],
trigger="用户消息"
)
self._update_emotion(emotion)
return emotion
except:
return self.current_emotion
def _update_emotion(self, new_emotion: EmotionState):
"""
更新情感状态(带平滑过渡)
不会突然跳变,而是渐进式变化
"""
smoothing = 0.3 # 平滑系数
self.current_emotion.valence = (
self.current_emotion.valence * (1 - smoothing) +
new_emotion.valence * smoothing
)
self.current_emotion.arousal = (
self.current_emotion.arousal * (1 - smoothing) +
new_emotion.arousal * smoothing
)
# 重新计算情绪类型
self.current_emotion.emotion_type = self._calculate_emotion_type(
self.current_emotion.valence,
self.current_emotion.arousal
)
# 记录历史
self.emotion_history.append({
"valence": self.current_emotion.valence,
"arousal": self.current_emotion.arousal,
"type": self.current_emotion.emotion_type.value,
"timestamp": datetime.now().isoformat()
})
def _calculate_emotion_type(self, valence: float, arousal: float) -> EmotionType:
"""根据效价和唤醒度计算情绪类型"""
if valence > 0.65:
if arousal > 0.65:
return EmotionType.EXCITEMENT
else:
return EmotionType.JOY
elif valence < 0.35:
if arousal > 0.65:
return EmotionType.ANGER
else:
return EmotionType.SADNESS
else:
if arousal > 0.7:
return EmotionType.ANXIETY
else:
return EmotionType.CALM
def apply_decay(self):
"""情感衰减:每轮对话后,情绪向中性回归"""
self.current_emotion.valence += (0.5 - self.current_emotion.valence) * self.decay_rate
self.current_emotion.arousal += (0.5 - self.current_emotion.arousal) *