拟人化自动发言 Agent 架构理解
1. 项目目标
目标不是构造一个一问一答的聊天机器人,而是构造一个会在真实聊天流中自动出现、沉默、插话、连发、跳话题的拟人化 agent。
传统聊天机器人逻辑是:
用户输入 -> 模型回复本项目需要的是:
消息流 + 时间流 + agent 内部状态 -> 是否发言 -> 何时发言 -> 发几条 -> 每条说什么因此,agent 的输出不一定是回复,也可能是沉默。沉默本身就是拟人化行为的一部分。
2. 核心判断
拟人化能力不能全部依赖模型内在能力,尤其不能指望小模型仅靠微调学会完整的人类聊天行为。
合理分工是:
外部 Agent / 统计行为模型:控制什么时候说、说几条、沉默多久
LoRA 内容模型:控制这一条消息具体怎么说
RAG / 记忆系统:提供历史场景和相关记忆也就是说:
行为节奏靠 Agent
语言风格靠 LoRA
历史记忆靠 RAG模型可以学习局部表达风格,但不适合作为第一版的全权行为调度器。
3. 数据建模原则
最小单位必须是单条消息,而不是一轮回复。
真实聊天不是严格的一问一答,两个人可能随机离散发言,也可能各说各的。不能假设一条消息天然是在回复上一条消息。
因此,初级数据结构应保持原子消息:
{
"msg_id": 12345,
"platform": "platform_a",
"time": "2024-01-01 12:00:00",
"sender": "target",
"type": "text",
"text": "...",
"gap_prev_sec": 8,
"gap_next_sec": 42
}所有会话切分都只判断相邻两条消息之间是否应该断开:
message_i | message_i+1不做以下假设:
用户消息 -> 目标人格回复
连续同一发送者消息 -> 必须合并
两个平台消息 -> 天然属于同一条时间线4. 会话断点策略
第一层使用时间断点,按平台分别处理,不跨平台混合。
推荐断点层级:
gap > 60min:强断点
gap > 30min:候选断点
gap > 15min:弱候选断点第一步先用 60 分钟切出高置信自然大段。随后在大段内部标记 30 分钟和 15 分钟候选断点。
后续再借助 LLM 判断候选断点附近是否发生了自然聊天场景切换。
LLM 不应判断“谁回复谁”,只判断:
这两条相邻消息之间,是否像一个聊天场景的结束与另一个聊天场景的开始。推荐给 LLM 的上下文形式:
左侧若干条消息
---
候选断点:间隔 N 分钟
---
右侧若干条消息输出结构:
{
"is_boundary": true,
"confidence": 0.82,
"reason": "前后话题和聊天场景明显变化"
}5. 行为调度模型
agent 运行时不应该每次收到用户消息就回复。用户消息只是事件,不是命令。
事件来源包括:
用户发出一条消息
用户连续发出多条消息
当前会话已经沉默一段时间
当前话题与目标人格高兴趣主题相关
agent 刚刚发过一条消息
当前处于连续发言状态
当前处于低活跃时间段
RAG 检索到相似历史场景调度器周期性计算发言概率:
speak_score =
话题兴趣
+ 是否被明显 cue 到
+ 当前聊天热度
+ 目标人格自身表达冲动
+ 随机扰动
- 冷却时间
- 离线/低活跃状态超过阈值或抽样命中后,agent 才发言。否则保持沉默。
6. 输出不是回复,而是事件序列
agent 每次决策的输出可以是空,也可以是一组带延迟的原子消息:
[]表示沉默。
[
{"delay_sec": 18, "text": "确实"},
{"delay_sec": 6, "text": "这波有点离谱"},
{"delay_sec": 42, "text": "不过也正常"}
]表示延迟后连续发出多条消息。
因此运行时结构应是:
Message Stream
↓
Session State
↓
Behavior Scheduler
↓
是否发言
↓
LoRA Generator
↓
Single Message
↓
Burst Controller
↓
是否继续发下一条7. LoRA 训练策略
不建议把多年数据按年份顺序覆盖式继续训练成一个 LoRA:
year_1 -> year_2 -> year_3 -> year_4这种方式容易导致后面的年份覆盖前面的年份,产生灾难性遗忘。最终 adapter 更像近期数据,而不是稳定人格。
更推荐将 LoRA 组件化:
base model
+ global_lora
+ recent_lora其中:
global_lora:使用全部数据或均衡采样数据,学习长期稳定语言风格
recent_lora:使用最近半年到两年的数据,学习近期话题和当前表达状态推理时可用不同 scale 混合:
global_lora scale = 0.6 ~ 0.8
recent_lora scale = 0.2 ~ 0.5如果需要研究时间变化,可以单独训练年度 LoRA:
lora_year_1
lora_year_2
lora_year_3
lora_year_4但年度 LoRA 更适合分析和对比,不建议作为第一版主路径。
8. 训练样本构造方向
LoRA 内容训练不应构造成传统问答对,而应构造成:
最近 N 条原子消息 + 时间间隔信息 -> 目标人格下一条原子消息对每一条目标人格真实发出的消息,可构造一个正样本:
context = 这条消息之前的若干条消息
target = 目标人格这一条消息注意 target 仍然是单条消息,不合并连续消息。
行为调度器则从全量消息流中统计:
目标人格在他人发言后多久出现
目标人格发一条后继续发下一条的概率
连续发言长度分布
不同时间段活跃概率
session 开始后活跃度变化
沉默多久后基本视为自然结束
哪些话题提高发言概率9. 推荐阶段路线
第一阶段:
按平台保留原子消息
计算 gap_prev / gap_next
60min 强切自然 session
30/15min 标记候选断点
统计基础行为分布第二阶段:
用 LLM 辅助判断候选断点
生成最终自然 message segment
构造 LoRA 内容样本
构造行为统计样本第三阶段:
训练 global_lora
训练 recent_lora
实现 Behavior Scheduler
实现单条消息生成
实现连续发言控制第四阶段:
加入 RAG 记忆
加入话题兴趣判断
加入长期状态和短期情绪
调试发言频率、沉默概率和连发概率10. 最终系统形态
最终系统应类似一个常驻在线人格进程:
监听消息流
维护 session 状态
维护短期上下文
维护长期记忆
周期性 tick
统计调度器决定是否发言
LoRA 生成单条消息
burst controller 决定是否继续发
必要时调用 RAG 或工具这不是一个被动回答器,而是一个由统计行为模型驱动、由 LoRA 表达风格、由 RAG 提供记忆的拟人化自动发言 agent。