作为一名独立开发者,我曾在凌晨三点为我的「AI虚拟好友」项目调试对话逻辑。那天晚上,我意识到一个残酷的事实——如果继续使用某美国云服务商,我的月账单会超过300美元,而这仅仅是一个个人项目。转折点出现在我发现 HolySheheep AI 之后:人民币直付、¥1=$1的无损汇率、以及国内节点<50ms的响应延迟,让我的项目成本直接降到了每月30元人民币。今天这篇文章,我将完整复盘如何从零构建一个具备角色卡、长期记忆和情感状态的AI陪伴系统,所有代码基于 HolySheheep API。

一、项目架构概览:三大核心模块

一个完整的AI陪伴应用需要解决三个核心问题:

我在实际开发中发现,这三个模块相互依赖:角色卡提供基础人格框架,记忆系统填充个性化细节,情感状态则让对话呈现动态起伏。

二、角色卡系统:从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 记忆分层架构

我在实践中总结出三层记忆架构,各有不同的更新频率和检索策略:

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+高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) *