ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI人类模拟器开发实战:人设、记忆与多轮对话系统搭建

AI人类模拟器开发实战:人设、记忆与多轮对话系统搭建 AI 人类模拟器这类应用最近在开发圈的热度非常高。一方面是大模型把对话体验拉到了一个新的水平另一方面是“拟人化交互”的场景想象空间确实大甚至出现了一些一级市场估值很高的项目讨论。但抛开商业估值不谈从技术角度来看AI 人类模拟器本质上是把大模型能力封装成一套“有性格、有记忆、有连续性”的对话系统这里面涉及的 Prompt 设计、记忆管理、人设一致性、多轮对话状态维护都是非常典型的工程问题而且很多坑是光看文档发现不了的。这篇文章不打算分析市场和融资而是从技术实现角度拆解一个 AI 人类模拟器需要哪些核心模块并给出一套可以直接运行、可以继续扩展的 Python 实战代码。无论你是想做一个陪聊机器人、虚拟角色扮演应用还是在研究大模型 Agent 的人设一致性这篇文章都值得收藏备用。1. AI 人类模拟器的技术本质1.1 它到底是什么先给一个通俗的解释。AI 人类模拟器顾名思义就是让 AI 在对话中“像一个真实的人”。这个“像”体现在几个方面有稳定的性格底色不会上一句温柔下一句暴躁。有记忆记得你们聊过什么不会刚说完就忘。有语言习惯说话风格保持统一。有情感反馈能根据对方的情绪调整回复方式。有主动性不完全被动等待问题有时会反问、会延续话题。从技术角度看这些能力并不是某个单独模型能搞定的而是一套对话系统的综合表现。底层是大语言模型外层是 Prompt 工程、记忆系统和对话管理逻辑。1.2 它和大模型原生对话有什么区别很多人用 ChatGPT 或者开源模型聊天时会觉得 AI 语气很“机械”。原因是通用大模型默认就是“全能助手”人格回答偏向中立、完整、礼貌不会带太多个人色彩。而 AI 人类模拟器要做的事情就是通过系统提示词、样例对话、记忆模块把模型从一个“全能助手”引导成“某一类具体的人”。对比维度通用大模型对话AI 人类模拟器人格默认助理人格自定义人设记忆单轮或短期记忆长期记忆 话题延续语气中立客观个性化、有人味主动性被动回答问题主动推进对话场景问答、写作、编程陪伴、娱乐、角色扮演1.3 常见的应用场景AI 人类模拟器的应用场景主要集中在 C 端互动类产品里情感陪伴类 App模拟朋友的语气陪用户聊天。角色扮演类应用让用户和小说角色、动漫角色、历史人物对话。游戏 NPC让游戏里的角色具备更真实的对话反应。虚拟 IP 运营用 AI 模拟某个 IP 的性格和说话方式。心理学辅助练习模拟特定性格的人练习沟通能力。这也就解释了为什么市场愿意给这类项目较高的估值——它本质上是在做“人机交互的新入口”。但作为开发者我们需要关心的是这套系统到底怎么搭建。2. 系统架构与技术模块拆解在写代码之前先把一个完整的 AI 人类模拟器拆成几个模块。不要一上来就堆代码后面你会发现大部分效果问题都出在模块之间的关系上。用户输入 ↓ 对话预处理过滤敏感内容、识别情绪、提取关键信息 ↓ 记忆模块短期记忆 长期记忆 ↓ 人设引擎System Prompt 动态拼接 ↓ 大模型推理LLM 调用 ↓ 回复后处理格式修正、安全检测 ↓ 回复内容这个流程里最核心的三个模块是人设引擎负责把人物设定转成模型能理解的语言。这里不只是写一段“你是一个温柔的人”而是要体系化地组织人设信息。记忆模块决定 AI 能不能“记住”用户。没有记忆的模拟器只能聊当下有了记忆才能聊出连续性。对话管理决定 AI 什么时候该说什么怎么处理多轮上下文怎么应对用户的敏感输入。下面分别展开这几个模块然后再给一个完整可运行的示例。3. 环境准备与版本说明本文的完整示例代码使用 Python 开发核心依赖如下Python 3.10OpenAI SDK或任意兼容 OpenAI 接口的国内大模型 SDK无第三方数据库依赖记忆模块先用 JSON 文件演示示例代码以 OpenAI 的 API 格式编写。如果你的项目使用的是国内大模型平台只要服务兼容 OpenAI 接口协议只需要修改 base_url、api_key 和 model 名称即可。如果使用本地部署模型也可以按同样的方式替换。版本说明大模型 SDK 更新速度比较快示例代码以目前主流的openai1.0.0为例。不同版本在客户端初始化方式上略有差异如果你用的是 0.x 版本需要把客户端初始化改为openai.ChatCompletion.create的方式。建议创建独立的虚拟环境python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate pip install openai4. 核心代码实战构建一个带人设的对话模拟器4.1 项目结构先定义一个清晰的项目结构。这个结构可以直接扩展到更复杂的 Agent 项目里。ai-human-simulator/ ├── config.py # 全局配置 ├── persona.py # 人设定义与管理 ├── memory.py # 记忆模块 ├── dialog.py # 对话管理 ├── main.py # 主入口 └── memories/ └── user_001.json # 用户记忆文件自动生成4.2 人设模块的实现人设模块是 AI 人类模拟器的灵魂。一个人设不应该只是一句话而是一个结构化的数据集。我习惯把“人设”拆分成人格特质、说话风格、背景故事、互动规则四个部分。# 文件路径persona.py class Persona: 人设定义类。 负责把人设信息转换为系统提示词。 def __init__(self, name: str, traits: list[str], style: str, background: str, rules: list[str]): self.name name self.traits traits # 人格特质例如 [温柔, 耐心, 幽默] self.style style # 说话风格描述 self.background background # 角色背景故事 self.rules rules # 交互规则 def build_system_prompt(self) - str: 把结构化的人设信息转换为 System Prompt。 traits_text 、.join(self.traits) system_prompt f你现在正在扮演一个名为「{self.name}」的角色。 【角色性格】 你是一个{traits_text}的人。 【说话风格】 {self.style} 【背景故事】 {self.background} 【互动规则】 1. 始终以「{self.name}」的身份回复不要提及你是 AI 模型。 2. 保持性格和语气的一致性。 3. 根据对话历史自然地延续话题。 4. 如果用户输入涉及违法、暴力、色情等内容礼貌拒绝并转移话题。 请开始对话。 return system_prompt这里需要解释一个核心设计为什么要结构化人设而不是直接写一大段话因为把“人设”拆成多个维度有助于模型在生成回复时分别理解记忆。比如“说话风格”控制语气“背景故事”控制知识范围“互动规则”控制行为边界。如果全部混在一起写模型可能会顾此失彼。而且结构化人设在后续调试中也更方便——你觉得语气不对只需要改 style 字段不需要重写整段 Prompt。4.3 记忆模块的实现记忆模块是 AI 人类模拟器和普通聊天机器人的最大区别。一个没有记忆的模拟器每次对话都是“第一次见面”用户很快就会觉得没意思。记忆模块我设计成两级短期记忆当前对话轮次的内容包含在请求上下文里交给模型。长期记忆跨会话存储的关键信息比如用户说过自己的名字、爱好、最近聊过的话题。为了保持示例简单先使用 JSON 文件存储长期记忆。# 文件路径memory.py import json import os from typing import Any class MemoryManager: 基于 JSON 文件的简单记忆管理。 生产环境可替换为 Redis 或向量数据库。 def __init__(self, memory_dir: str memories): self.memory_dir memory_dir os.makedirs(memory_dir, exist_okTrue) def _get_file_path(self, user_id: str) - str: return os.path.join(self.memory_dir, f{user_id}.json) def load_memory(self, user_id: str) - dict[str, Any]: 读取用户长期记忆文件不存在时返回空字典。 file_path self._get_file_path(user_id) if os.path.exists(file_path): with open(file_path, r, encodingutf-8) as f: return json.load(f) return {facts: [], topics: []} def save_memory(self, user_id: str, memory: dict[str, Any]) - None: 保存用户长期记忆。 file_path self._get_file_path(user_id) with open(file_path, w, encodingutf-8) as f: json.dump(memory, f, ensure_asciiFalse, indent2) def add_fact(self, user_id: str, fact: str) - None: 添加一条关于用户的事实。 memory self.load_memory(user_id) if fact not in memory[facts]: memory[facts].append(fact) self.save_memory(user_id, memory) def build_memory_prompt(self, user_id: str) - str: 把长期记忆转换为提示词片段。 memory self.load_memory(user_id) if not memory[facts] and not memory[topics]: return fact_text 、.join(memory[facts]) return f\n【你对用户已知的信息】{fact_text}\n长期记忆的写入策略是个关键问题。最简单的方式是在每一轮对话结束后把用户提到的新身份信息直接追加到 facts 列表里。但这种做法在真实场景中会导致记忆越积越多最后超过模型的上下文窗口。更合理的方案是在对话结束后额外调用一次模型做信息抽取只把值得记住的内容写入记忆。这个方案放到后面的“进阶优化”里讲这里先用简单方案保证流程跑通。4.4 对话管理模块的实现对话管理模块负责组合 System Prompt、长期记忆、短期上下文然后调用大模型生成回复。这里需要注意一个细节多轮对话的上下文处理方式。目前主流做法有两种把历史消息拼成文本放进 System Prompt 或 User Prompt。把历史消息作为 messages 数组传入使用 OpenAI 的messages多轮格式。推荐第二种因为格式更清晰模型不容易混淆“用户说话”和“AI 说话”的界限。# 文件路径dialog.py from openai import OpenAI from persona import Persona from memory import MemoryManager class DialogManager: 对话管理器。 组合人设、记忆、上下文调用大模型生成回复。 def __init__(self, persona: Persona, memory: MemoryManager, api_key: str, base_url: str, model: str): self.client OpenAI(api_keyapi_key, base_urlbase_url) self.model model self.persona persona self.memory memory self.history: list[dict[str, str]] [] # 短期对话历史 def send_message(self, user_id: str, user_message: str) - str: # 1. 构建消息列表 messages self._build_messages(user_id, user_message) # 2. 调用模型 response self.client.chat.completions.create( modelself.model, messagesmessages, temperature0.85, max_tokens500, ) assistant_message response.choices[0].message.content # 3. 写入短期历史 self.history.append({role: user, content: user_message}) self.history.append({role: assistant, content: assistant_message}) # 4. 简单记忆提取如果用户提到了自己的名字就存起来 self._extract_basic_memory(user_id, user_message) return assistant_message def _build_messages(self, user_id: str, user_message: str) - list[dict[str, str]]: # 人设 System Prompt system_prompt self.persona.build_system_prompt() # 长期记忆片段 memory_prompt self.memory.build_memory_prompt(user_id) if memory_prompt: system_prompt memory_prompt messages [{role: system, content: system_prompt}] # 保留最近 10 轮短期记忆 messages.extend(self.history[-20:]) messages.append({role: user, content: user_message}) return messages def _extract_basic_memory(self, user_id: str, user_message: str) - None: 一个非常基础的记忆提取逻辑。 真实项目建议调用模型做结构化信息抽取。 if 我叫 in user_message: name user_message.split(我叫)[-1].strip().split( )[0] self.memory.add_fact(user_id, f用户的名字是{name})这里的temperature0.85是一个值得注意的参数。对于 AI 人类模拟器来说过低的温度会让回复过于保守和机械过高则容易产生不稳定输出。0.8 到 0.9 通常是比较适合角色扮演和闲聊场景的区间。如果你发现 AI 回复逻辑混乱可以适当调低。4.5 主程序入口# 文件路径main.py from persona import Persona from memory import MemoryManager from dialog import DialogManager # 配置区 API_KEY sk-your-api-key BASE_URL https://api.openai.com/v1 MODEL gpt-4o-mini # 定义一个人设小雨 persona Persona( name小雨, traits[温柔, 细致, 有一点俏皮], style说话自然口语化句子简短偶尔会分享自己的感受不会使用书面语。, background你是一个生活在杭州的 24 岁女生喜欢烘焙和摄影在一家文创公司做设计。, rules[ 始终以「小雨」的身份回复, 不要提及你是 AI 模型, 语气保持轻松自然, ], ) memory_manager MemoryManager() dialog_manager DialogManager( personapersona, memorymemory_manager, api_keyAPI_KEY, base_urlBASE_URL, modelMODEL, ) def main(): user_id user_001 print(小雨已上线。输入 bye 结束对话。\n) while True: user_input input(你: ) if user_input.lower() bye: print(小雨: 拜拜啦~ 下次再聊) break reply dialog_manager.send_message(user_id, user_input) print(f小雨: {reply}\n) if __name__ __main__: main()运行方式python main.py预期效果是AI 会始终以“小雨”的身份回复语气保持一致并且在用户说出“我叫小明”之后后续对话能记起这个信息。这个示例流程虽然简单但架构上已经具备了 AI 人类模拟器的完整闭环。接下来我们在这一版基础上继续做工程化增强。5. 人设一致性与 Prompt 优化5.1 为什么 AI 会“崩人设”很多人在做角色模拟时会遇到一个问题聊了几轮之后AI 突然用一种“AI 助手”的语气说话比如“作为一个语言模型我无法为您提供……”——这就是崩人设。根本原因有两个System Prompt 约束不够强。多轮对话后模型逐渐回归了默认的助手行为模式。针对第一个原因可以在人设定义里加入“反面约束”。例如明确写上“禁止使用以下句式作为一个AI助手、作为语言模型、我很抱歉我不能……”。这类负面约束往往比正面要求更有效。针对第二个原因可以在短期历史超过一定长度时做裁剪避免上下文过长稀释人设信号。5.2 Few-shot 示例的进阶用法除了 System Prompt 之外在 messages 中插入少量“示例对话”可以让模型更快进入角色。这种方法叫 few-shot learning。few_shot_examples [ {role: user, content: 今天工作好累啊。}, {role: assistant, content: 抱抱你我给你看一张我下午拍的云彩照片超治愈的~}, {role: user, content: 你平时有什么爱好}, {role: assistant, content: 我最近在学烘焙昨天烤了一盘玛德琳虽然有点丑但味道还不错哈哈。}, ]把这组示例放在 System Prompt 之后、真实历史之前能明显提升角色语气的一致性。5.3 人设一致性的度量如果你需要量化“人设是否崩了”可以考虑建一套简单的评估逻辑。比如抽取回复中是否出现“AI 助手”“语言模型”等禁用词。对比回复的语气向量和人设基准语气向量的相似度。人工标注抽样回复是否符合角色设定。这些指标不一定要做到很复杂但在工程上有助于监控线上效果。6. 记忆的工程化进阶6.1 用 LLM 做结构化记忆提取前面代码里的记忆提取方法非常粗糙——只识别了“我叫”这个模式。真实项目里用户不会用这么规范的方式表达信息。更可靠的方案是每轮对话结束后把这段对话发送给模型要求模型输出这段对话中值得长期记住的事实。这个操作可以复用同一个大模型只是 Prompt 不同。def extract_memory_with_llm(client, model, user_message, assistant_message): prompt f从下面的对话中提取需要长期记住的用户信息。 只提取事实性内容例如姓名、年龄、职业、喜好、家庭成员、重要日期等。 如果没有值得记录的信息输出空列表。 用户: {user_message} AI: {assistant_message} 请严格按照 JSON 格式输出例如{{facts: [用户喜欢喝美式咖啡]}} response client.chat.completions.create( modelmodel, messages[{role: user, content: prompt}], temperature0, ) # 解析 JSON...使用temperature0是因为信息抽取任务希望输出稳定不需要创造性。6.2 记忆检索向量数据库当长期记忆的量级超过几十条时把所有记忆都塞进 Prompt 就不现实了。这时需要把记忆做向量化存储在对话时只检索和当前话题最相关的几条记忆。技术栈通常是向量化使用 Embedding 模型把文本转成向量。存储使用 Chroma、Milvus、pgvector 等向量数据库。检索在用户输入后先做向量相似度搜索取 top-k 记忆拼入 Prompt。这套方案的优点是记忆规模可以扩展到很大缺点是增加了系统复杂度和延迟。对于 MVP 阶段的模拟器JSON 文件加全量拼接就够用了。6.3 记忆的衰减与更新还有一个容易被忽略的问题记忆是会发生变化的。比如用户去年喜欢喝奶茶今年改喝美式了。如果 AI 一直在提“你喜欢的奶茶”用户会觉得这个 AI 太死板。因此记忆模块需要支持时间戳记录每个记忆项记录写入时间。过期衰减超过一定时间未被验证的记忆可以被降低权重。冲突处理两条记忆矛盾时以最近一次为准。这套逻辑在代码上不复杂但需要你结合业务场景定义更新规则。7. 常见问题与排查思路7.1 AI 说话一点都没有“人味”可能原因解决思路temperature 设置太低调高到 0.8-0.9System Prompt 太抽象补充具体的说话风格描述缺少 few-shot 示例在上下文里加入 2-3 组示例对话模型能力偏弱考虑换更大的模型或增加示例数量“人味”的核心是“不完美”。通用模型默认追求正确、完整但人类说话会有停顿、语气词、情绪、省略。如果你发现 AI 回复过于完整和书面化可以在人设里明确加上“说话不要太完整保留口语的松散感”。7.2 聊多了之后记忆混乱对记忆做裁剪。上下文窗口是有限的如果每次对话都把全部历史塞进去一方面浪费 token另一方面会引入噪声信息。建议只保留最近 N 轮完整历史 从长期记忆里检索到的关键信息。7.3 输出内容不稳定情绪忽好忽坏原因可能是人设设定中对情绪约束不足。在互动规则里加入类似“无论用户说什么保持稳定的情绪基调”这样的约束。有时也需要检查用户输入是否触发了模型的敏感词过滤导致回复风格突变。7.4 API 调用报错或超时如果是网络不稳定导致的考虑加超时重试。OpenAI SDK 支持max_retries配置。如果是国内模型服务注意确认 base_url 是否配置正确。8. 最佳实践与项目落地建议8.1 安全边界一定要前置AI 人类模拟器产品最容易踩的雷区是模拟“真人”导致用户产生情感依赖或误解。在工程上必须做到几点在合适的场景下明确告知用户这是 AI避免长期伪装。对用户输入进行安全过滤涉黄、涉暴、涉违法内容直接拒绝回应。对模型输出也做安全检测避免模型在角色扮演中输出危险内容。保留人工申诉渠道用户举报后能快速处置。开发阶段的快速处理方式是在代码里加一个关键词过滤器。生产阶段建议接入专业的审核服务。8.2 人设和记忆分离管理不要把角色背景故事和用户记忆混在一个 Prompt 里。分开管理的好处是角色背景是全局静态的用户记忆是动态变化的。静态部分可以做缓存减少请求拼接耗时动态部分可以独立更新不影响全局。8.3 日志和追踪多轮对话系统最难排查的问题是“上一次为什么这么回复”。所以在开发时就要做好日志记录记录每次请求的系统 Prompt、实际发送的 messages、模型返回的原始内容、token 消耗。这样出了问题才能复现和分析。日志记录推荐格式{ user_id: user_001, request_id: xxx, timestamp: 2025-01-01T12:00:00Z, model: gpt-4o-mini, prompt_preview: ..., response: ..., token_usage: {prompt: 1200, completion: 300} }8.4 成本的预估与控制AI 人类模拟器的对话轮次会远超普通问答应用——用户可能每天都聊很长时间。因此 token 成本是必须认真考虑的一个工程问题。几个有效的降本策略对历史对话做摘要代替全量历史输入。设置 max_tokens 上限避免超长回复。简单场景使用小模型复杂场景才调度大模型。对重复性寒暄场景考虑使用规则回复不调用模型。8.5 Prompt 版本管理这一条经常被忽视。人设 Prompt 是会被反复迭代的建议把 Persona 定义保存在配置文件或数据库里而不是硬编码在代码中。每次修改人设应该能回溯版本对比不同版本的效果差异。9. 小结从上面的代码和设计里可以看到AI 人类模拟器并不是一个只有“调用大模型”的简单项目。它涉及人设工程、记忆管理、上下文维护、安全策略、成本控制等多个层面。而绝大多数体验问题都出在大模型之外的部分——Prompt 设计得不合理、记忆存储得太粗糙、安全边界没有前置。这篇文章给出了一套完整的入门实现但距离一个生产级的 AI 人类模拟器还有一段路要走。接下来你可以继续在几个方向上深入用向量数据库做记忆检索、接入语音合成实现语音对话、加入 Function Calling 让角色具备工具调用能力、建立一套完整的人设一致性评估体系。如果你在照着代码实践的过程中碰到了具体报错或奇怪的角色行为欢迎在评论区带上日志和复现步骤一起讨论。动手跑一遍再改一版自己的人设你会对这个方向的理解深很多。
返回列表