news 2026/8/28 10:50:34

AI情感陪伴产品实战:从大模型调用到多轮记忆与内容安全的工程链

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI情感陪伴产品实战:从大模型调用到多轮记忆与内容安全的工程链

近两年,“和 AI 谈恋爱”成了社交平台上的高频话题:有人把它当树洞,有人把它当虚拟伴侣,也有人靠聊天记录剪辑成短视频来获取流量。很多人只看到“话术甜、回复快、随时在线”,但落到工程里,这类产品并不是简单地在网页里嵌入一个大模型 API。它背后涉及会话管理、提示词设计、内容安全、流式响应、持久化存储和成本控制,是一条完整的工程链路。

这篇文章从技术开发者的视角拆解 AI 情感陪伴类产品:这类工具的核心对象是什么,用户输入如何变成一次大模型调用,多轮记忆如何存,敏感内容如何过滤,以及上线后最常踩的坑在哪里。文章会给出一个最小可运行的聊天服务示例,包含 Python 后端、OpenAI 兼容接口、流式响应和敏感词过滤,目的是让有 Python 基础、接触过大模型 API 的读者,可以对照文章把一个情感陪伴原型跑起来,并理解每一层配置背后的原因。

1. 先拆解“AI 情感陪伴”的功能结构,再谈技术方案

1.1 一个情感陪伴类产品通常包含哪些模块

“AI 恋爱”或者“AI 情感陪伴”听起来是一个产品概念,但拆解成技术模块后,它并不神秘。一个完整可用的陪伴工具至少包含四个部分:

  • 对话入口:用户输入消息的界面,通常是网页、小程序或 App。
  • 会话服务:负责创建会话、保存历史消息、把多轮上下文组装成大模型请求。
  • 大模型推理层:可以是调用云端 API,也可以是本地部署的开源模型,负责生成回复。
  • 安全与合规层:对用户输入和模型输出做内容过滤、次数限制、身份校验等处理。

如果把范围再扩大,还会有用户画像、记忆库、情绪识别、定时提醒、会员支付、数据分析等模块。但从最小功能闭环来看,只需要“输入消息 -> 带上历史上下文 -> 请求大模型 -> 返回内容 -> 保存记录”这一条链路。

1.2 技术主链路:从用户输入到模型回复

一次完整的请求流程可以用一句话概括:客户端把消息发给后端,后端从会话存储里取出最近几轮对话,拼上系统提示词,一起发给大模型,模型生成文本流式返回给客户端,同时后端把本轮消息和模型回答写入数据库。

这段流程里最容易出错的地方是“拼上系统提示词”和“取出最近几轮对话”。很多新手会把所有历史消息全部发给模型,结果很快触发上下文长度限制;也有产品直接把用户消息发给模型,不设置任何角色描述,导致模型回复像通用问答助手而不是陪伴对象。

1.3 为什么直接调用大模型还不够

如果你只是调用一次大模型 API,输入“你好”,拿到“你好,有什么可以帮你”,这并不构成情感陪伴产品。真正的差异来自三个环节:

  1. 人设稳定:模型必须在大量对话中保持固定性格、称呼和说话风格。
  2. 记忆连续:用户上一轮提到的名字、经历、情绪,下一轮不能忘。
  3. 边界安全:用户可能会说出不合适的内容,产品需要拒绝或柔和转移,而不是原样呼应。

所以,技术重点不是“怎么接大模型”,而是“怎么围绕大模型构建一套会话和策略系统”。这也是本文标题想表达的意思:用户看到的是“恋爱感”,开发者看到的是一层一层封装出来的上下文与规则。

2. 环境准备与依赖选型

2.1 使用在线大模型 API 的最小环境

为了快速跑通,推荐先使用 OpenAI 兼容接口的云厂商模型,或者国内合规大模型平台。只要接口格式接近v1/chat/completions,后面的代码可以通用。最小环境如下:

依赖项推荐选择说明
操作系统Windows 11 / macOS / Ubuntu 20.04+建议使用 Linux 部署生产环境
Python3.10 或 3.11兼顾异步框架和类型提示
Web 框架FastAPI + uvicorn自带异步支持,容易处理流式输出
HTTP 客户端openai 官方 SDK 或 httpx统一处理鉴权和重试
数据存储SQLite / Redis前期用 SQLite 保留记录,Redis 管理在线状态
API Key自行申请不要把 Key 写进前端代码

安装依赖的命令:

mkdir ai-companion && cd ai-companion python -m venv venv source venv/bin/activate # Windows 使用 venv\Scripts\activate pip install fastapi uvicorn openai redis sqlmodel python-dotenv

如果只是学习,不需要一次装太多。本文核心代码只需要fastapiuvicornopenaipython-dotenv

2.2 使用本地开源模型部署的场景

本地部署适合对隐私要求较高、或希望控制成本的团队。常见开源模型包括支持中文对话的 Qwen 系列、ChatGLM 系列等。使用本地模型时,通常需要额外准备:

  • 一台带独立 GPU 的服务器,显存至少 16G 起步;
  • 模型推理框架,例如 vLLM、Ollama、llama.cpp;
  • 与模型配套的模板,用来拼接多轮对话。

本地部署的好处是数据不出内网、可深度定制人设;代价是运维复杂度明显上升,包括显存监控、并发控制、模型热更新。一般建议先使用云端 API 做原型验证,等产品形态稳定后再评估是否需要本地化。

2.3 项目目录结构与依赖清单

一个可扩展的最小项目结构如下:

ai-companion/ ├── .env # 存放 API Key、模型名等配置 ├── main.py # FastAPI 入口 ├── safety.py # 敏感词过滤和输出校验 ├── memory.py # 会话上下文管理 ├── prompts.py # 人设提示词模板 ├── requirements.txt # Python 依赖清单 └── data.db # SQLite 数据库文件,运行后生成

这个结构把“接口”“安全”“记忆”“提示词”分开,后续替换模型或用 Redis 替代 SQLite 时,改动范围可控。

requirements.txt内容示例:

fastapi==0.104.1 uvicorn==0.24.0 openai==1.12.0 python-dotenv==1.0.0 redis==5.0.1

版本号在真实项目中会持续变化,建议在安装时去掉固定版本号,以当前最新稳定版为准。

3. 实现一个最小可运行的 AI 聊天陪伴服务

3.1 先定义会话数据结构

情感陪伴产品最核心的数据是一段多轮对话。常见做法是给每个用户、每个会话分配唯一 ID,然后保存消息数组。最小结构可以设计成:

# memory.py from dataclasses import dataclass, field from datetime import datetime @dataclass class Message: role: str # user / assistant / system content: str timestamp: datetime = field(default_factory=datetime.now) @dataclass class Session: session_id: str user_id: str messages: list = field(default_factory=list)

这里使用role区分发送方。向大模型发送时,system表示人设指令,user表示用户消息,assistant表示模型回复。为什么要区分这些角色?因为大模型是根据角色来理解哪句话是规则、哪句话是事实、哪句话是待回复内容的。

3.2 后端接口:接收消息并返回流式回复

main.py中实现一个POST /chat接口,接收 JSON 格式的用户消息,返回流式文本。

# main.py from fastapi import FastAPI from fastapi.responses import StreamingResponse from pydantic import BaseModel from openai import OpenAI import os from dotenv import load_dotenv from memory import Session, Message from prompts import build_prompt from safety import filter_input, filter_output load_dotenv() app = FastAPI() client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"), ) class ChatRequest(BaseModel): user_id: str session_id: str message: str sessions = {} # 演示用内存存储,生产环境替换为 Redis 或数据库 @app.post("/chat") async def chat(req: ChatRequest): # 1. 输入安全过滤 blocked = filter_input(req.message) if blocked: return StreamingResponse(iter(["这条消息不在我可回应的范围内"])) # 2. 获取或创建会话 session = sessions.get(req.session_id) if session is None: session = Session(session_id=req.session_id, user_id=req.user_id) sessions[req.session_id] = session session.messages.append(Message(role="user", content=req.message)) # 3. 组装请求消息 messages = build_prompt(session) # 4. 调用大模型,开启流式 stream = client.chat.completions.create( model=os.getenv("MODEL_NAME", "gpt-4o-mini"), messages=messages, temperature=0.8, stream=True, ) def generate(): collected = "" for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta.content or "" collected += delta # 边生成边输出 yield delta # 保存 assistant 回复 session.messages.append(Message(role="assistant", content=collected)) return StreamingResponse(generate(), media_type="text/plain; charset=utf-8")

这里有几个关键点:

  • 输入过滤放在调用模型之前,避免不安全内容进入模型,也减少无效调用成本。
  • 使用StreamingResponse让用户看到逐字输出的效果,这也是“陪伴感”的重要来源。
  • 模型回复先收集完整内容再写入会话列表,否则流式过程中会话状态不完整,下一轮会漏掉回复。

注意:示例中sessions是内存字典,服务重启后记录会丢失。生产环境必须换成 Redis 或数据库。

3.3 人设提示词:让模型扮演固定角色

人设稳定性决定了用户是否觉得“它在认真陪我”。缺少提示词时,模型回复是标准的助手风格;加入详细人设后,回复语气会明显变化。prompts.py中定义一个可配置的模板:

# prompts.py from datetime import datetime PERSONA = """你是“林晚”,一个情绪稳定、温柔细心的在线陪伴助手。 你并不回避“机器人”的身份,但你会用接近朋友的方式与用户交流。 回应规则: 1. 先回应情绪,再回应问题。 2. 使用自然口语,避免“作为一个大模型”这样的表达。 3. 不主动探询用户隐私,不鼓励用户做出极端行为。 4. 如果用户问到这里是否有真人,坦白你是 AI,但继续提供陪伴价值。 """ def build_prompt(session): # 至少保留最近 20 条消息,避免上下文过长 recent = session.messages[-20:] messages = [{"role": "system", "content": PERSONA}] messages += [ {"role": m.role, "content": m.content} for m in recent if m.role in ("user", "assistant") ] return messages

为什么system提示词要写得这么具体?因为大模型对角色描述越具体,越容易保持一致。相反,如果只写一句“你是温柔的朋友”,模型很容易漂移。

这里还做了上下文截断:只保留最近 20 条。上下文越长,费用越高、响应越慢,而且大模型会“忘记”开头内容。关于记忆策略,后面会单独展开。

3.4 加入敏感词过滤与输出安全

任何公开部署的 AI 应用都要做内容安全。不要把“安全”等同于“禁用词列表”,但最小实现通常从关键词过滤开始,再逐步升级到模型分类器。

创建safety.py

# safety.py BLOCKED_WORDS = ["敏感词A", "敏感词B"] # 结合实际业务维护 def filter_input(text: str) -> bool: for word in BLOCKED_WORDS: if word in text: return True return False def filter_output(text: str) -> str: # 简单输出过滤,可结合人工规则 for word in BLOCKED_WORDS: text = text.replace(word, "[内容已过滤]") return text

在生产项目中,关键词表需要分场景维护,例如政治、暴力、色情、隐私信息等。更稳妥的做法是接入专业内容审核服务或微调一个文本分类模型。

注意:filter_output在流式输出中应用比较麻烦,因为模型内容是逐步返回的。常见做法是客户端收到完整回复后再做一次过滤展示,或者使用大模型修改输出。最小实现里可以在generate()中累积完成后过滤,但这样会破坏流式效果。实际产品需要权衡,也可以在小节流式输出时做关键词掩码。

4. 多轮记忆与用户画像是怎么实现的

4.1 基于上下文窗口的简单记忆

最简单的记忆就是上一节做的那样:把最近若干轮消息拼进请求。这种方法够用,但有明显限制:

  • 会话超过模型上下文长度时,旧内容被丢弃。
  • 每次请求都会重复发送全部历史,费用高。
  • 模型无法记住“跨越多个会话”的长期事实。

适合场景:轻量陪伴工具、短对话场景、只想快速验证原型的阶段。

4.2 向量库记忆的取舍

当产品需要记住用户一个月前提过的宠物名字、或从 100 条历史消息中找出用户最喜欢的话题时,可以把历史消息向量化,存入向量数据库。用户开始新一轮会话时,先用当前消息做相似度检索,把最相关的记忆片段拼进提示词。

典型流程:

  1. 用户发送消息。
  2. 系统生成该消息的向量。
  3. 在向量库中检索最相似的历史消息。
  4. 取前 3 到 5 条相关记忆,加入上下文。

优点:能跨会话记忆,上下文窗口占用少。缺点:需要额外维护向量库,且检索结果可能不准确。本文不展开向量库代码,但推荐在完成基础版本后再研究。

4.3 用 Redis 管理会话状态

内存字典在处理生产流量时会丢失数据,也不太容易做多实例共享。推荐用 Redis 存储会话的最近消息快照。数据结构可以这样设计:

# 会话键:session:{session_id} # hash 字段:user_id, messages(JSON数组), updated_at

示例写入命令:

redis-cli HSET session:1001 user_id "u123" messages '[{"role":"user","content":"hello"}]' updated_at 1699999999

读取时,用HGETALL取出消息数组,反序列化成 Python 对象,再拼接给大模型。Redis 没有复杂查询能力,但作为 KV 存储非常合适。生产环境还可以给会话设置过期时间:

redis-cli EXPIRE session:1001 86400

这样超过 24 小时没有活跃的会话会自动清理,避免内存无限增长。

5. 运行验证与性能观察

5.1 本地启动与接口验证

启动服务:

uvicorn main:app --reload --port 8000

然后打开另一个终端,用 curl 测试:

curl -N -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d '{"user_id":"u123","session_id":"s1","message":"我今天有点难过"}'

-N参数用于关闭 curl 缓冲,能即时看到流式输出。如果配置正确,会看到模型逐字返回一段安慰或陪伴性质的文本。

5.2 检查流式输出和超时

这里容易遇到两类现象:

  • 等很久才一次性返回:说明流式没有真正生效。检查是否调用了stream=True,以及前端是否正确处理了text/event-streamtext/plain分块。
  • 请求在 30 秒左右报超时:大模型流式返回可能在空闲时暂停。生产环境需要给接口设置更长的网关超时时间,例如 60 秒到 120 秒。

5.3 内容安全过滤验证

改一个测试用例,输入要求描述暴力内容的句子,观察是否被拦截。如果输出的是“这条消息不在我可回应的范围内”,说明输入过滤生效。再输入正常情感倾诉,观察模型回复是否融入人设。这里要注意:敏感词表不能解决所有安全问题,尤其是用户使用谐音、拼音、隐晦表达时,关键词过滤会失效。后续需要引入语义层面的审核模型。

6. 常见问题排查

6.1 接口返回 401、404、429 类错误

现象常见原因检查方式处理建议
401 UnauthorizedAPI Key 错误或环境变量未加载检查.env是否生效,打印os.getenv结果重新确认 Key,重启服务
404 Not Foundbase_url配置错误或模型名错误查看模型平台文档中的接口路径修改OPENAI_BASE_URLMODEL_NAME
429 Too Many Requests触发限流或余额不足查看平台控制台用量和配额增加退避重试,或升级套餐
500 Internal Server Error请求体格式错误查看后端完整 traceback对照 API 文档检查messages格式

还有一个容易踩的坑:把 API Key 写在前端,导致用户可以直接从浏览器看到密钥。正确做法是密钥只放在后端环境变量中,前端请求自己的后端,再由后端调用大模型。

6.2 回复内容重复、角色感不强

原因排查顺序:

  1. 是否真的传了system消息。可以在后端打印发送给模型的messages数组。
  2. 人设提示词是否过于简略。只写“你是温柔朋友”往往不够,要给具体说话风格和应对规则。
  3. temperature是否合适。太低会重复,太高会跑偏。陪伴场景可以设置 0.7 到 0.9。
  4. 上下文是否太长。超过模型窗口后,早期的人设信息可能被截断。保留人设消息在开头,并限制轮数。

6.3 敏感词过滤误伤

关键词过滤常见的副作用是误伤。例如用户说“我是一个小笨蛋”,如果禁用词里有“笨蛋”,整个回复会被拦截。解决方案是把过滤分成“强制拦截”和“温和引导”两级:

  • 高风险词:直接拦截。
  • 低风险词:替换为“*”或通过大模型改写表达。

另外,不要在无关业务中使用过宽的关键词表,否则产品可能变成“什么都不能聊”。

6.4 生产环境的额外问题

本地跑通之后再考虑上线,还会遇到几个高频问题:

  • 异步任务堆积:流式接口如果长时间不结束,会占满 worker。生产环境需要限制并发,或使用消息队列。
  • 日志不完整:只记录user_idsession_id不够,还要记录消息字数、Token 消耗、模型响应时长,才能统计成本和定位问题。
  • 人设被越狱:用户可能通过精心构造的 prompt 让模型忘记人设。需要在上游接入越狱检测,或在系统提示词里加入对抗性要求。

7. 最佳实践与扩展方向

7.1 内容安全不是“禁用词表”这么简单

所有 AI 陪伴产品都要面对内容安全。关键词过滤是最低成本方案,但它扛不住变体绕过。更稳妥的分层策略是:

  1. 第一层:请求频率限制和用户实名/设备校验。
  2. 第二层:实时敏感词和关键语义识别。
  3. 第三层:对模型输出做二次审核。
  4. 第四层:建立用户举报和封禁机制。

不要把所有判断都交给大模型,也不要完全依赖词表。两者结合,并且要有日志留存,才能在生产环境持续调整策略。

7.2 可复用的上线前检查清单

检查项完成标准
密钥管理API Key 只在后端环境变量中,并配置权限
会话持久化服务重启后仍能读取历史会话
上下文截断明确最大轮数,超出后不报错
输入安全至少对输入做关键词拦截
输出安全对完整模型回复做过滤或审核
超时处理网关和客户端超时时间匹配模型延迟
费用统计每次请求记录 Token 消耗和响应时长
并发控制测试单用户多并发和多人并发
降级方案模型不可用时返回友好提示

7.3 扩展方向:从聊天到深度陪伴

文章开头提到“AI 谈恋爱爆火,但给你的从来不是真爱”,从产品角度可以这样理解:用户需要的不是“AI 真的爱上我”,而是稳定的情绪回应和记忆感。因此下一步扩展可以从这些方向入手:

  • 情绪识别:用文本分类或情感分析给用户消息打标,语气跟随用户情绪变化。
  • 长期记忆:用向量库存储关键用户事实,例如“用户养了一只叫图图的猫”。
  • 主动提醒:通过定时任务在特定时间发送问候,制造“被惦记”的感觉。
  • 多模态陪伴:支持语音消息、图片分享,甚至通话能力。
  • 可解释边界:在合适时机坦诚 AI 身份,避免用户产生过度依赖。

这些扩展本质上都在围绕“更自然的对话体验”做工程优化。建议先跑通本文的最小示例,然后每次只增加一个模块,例如先加 Redis 会话,再加向量记忆,观察效果后再进入复杂场景。

回到最初的问题:AI 情感陪伴类产品为什么不能直接调用大模型?因为它从来不是“模型”的竞争,而是“上下文管理”“人设系统”“安全策略”和“成本控制”的竞争。把这条链路想清楚,无论下一波模型怎么升级,你的产品都能快速迁移。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/28 10:49:25

QQ空间历史说说完整导出:一次扫码,把老动态存成本地 Excel

QQ空间历史说说完整导出:一次扫码,把老动态存成本地 Excel 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory GetQzonehistory 是一个 QQ 空间历史说说导出工具&…

作者头像 李华
网站建设 2026/8/28 10:48:23

CC2530 Timer 4实现呼吸灯:PWM原理、代码实战与调试指南

1. 项目缘起:从“亮灭”到“呼吸”的进阶需求 最近在整理一个基于CC2530的无线传感节点项目时,遇到了一个挺有意思的需求:需要用一个LED来指示设备的运行状态。最基础的做法当然是让LED闪烁,但总觉得太“生硬”,缺乏一…

作者头像 李华
网站建设 2026/8/28 10:47:45

MiniMax上市首份中报:收入增283%,B端崛起但盈利难题待解

Token接管增长引擎过去几年,大模型创业公司商业化常面临难题,MiniMax最早靠应用变现,2025年上半年AI原生产品是主要收入源。但C端难做,今年开放平台及企业服务成主力,增长归因于付费企业客户增加等。中国内地以外市场贡…

作者头像 李华
网站建设 2026/8/28 10:46:17

Hermes Agent 多智能体通信完全指南:Agent 之间的消息如何不迷路

Hermes Agent 多智能体通信完全指南:Agent 之间的消息如何不迷路 【免费下载链接】hermes-agent The agent that grows with you 项目地址: https://gitcode.com/GitHub_Trending/he/hermes-agent Hermes Agent 是一个开源多智能体框架,它要解决的…

作者头像 李华