一、项目概述
AI桌面伴侣是一个基于 LangGraph 框架构建的智能虚拟角色系统,集成了大语言模型、语音识别、语音合成、3D角色渲染等技术,为用户提供自然、生动的交互体验。本文将从架构设计、核心模块、数据流转三个维度进行深度剖析。
核心技术栈:
- 智能体框架: LangGraph (ReAct状态机)
- 前端: Unity 2023 LTS + VRM + uLipSync
- 后端: Python FastAPI + LangChain
- LLM: GPT-4o / DeepSeek-V3
- 语音: ChatTTS (TTS) + Whisper (ASR)
二、LangGraph ReAct 状态机架构
2.1 架构设计理念
传统AI对话系统采用"输入→处理→输出"的线性模式,而AI桌面伴侣采用 ReAct (Reasoning + Acting) 模式,将推理与行动交织,实现复杂的任务规划和执行能力。
为什么选择LangGraph?
| 特性 | 传统方案 | LangGraph方案 |
|---|---|---|
| ReAct循环 | 需自行实现 | 框架内置 |
| 状态管理 | 需自行实现 | Checkpointer内置 |
| 流式输出 | 需自行实现 | stream() API |
| 人机协同 | 需自行实现 | interrupt()支持 |
| 状态持久化 | 需自行实现 | 内置SQLite/PostgreSQL |
2.2 状态机核心流程
上图展示了LangGraph ReAct状态机的核心架构,包含四个关键节点:
1. THINK节点(推理)
THINK节点是ReAct循环的起点,负责分析当前状态并决定下一步行动:
async def _think_node(self, state: AgentState) -> dict:
# 1. 分析用户输入
# 2. 结合上下文和记忆
# 3. 决定下一步行动
# 4. 输出thought
return {
"thought": parsed["thought"],
"action_type": parsed["action_type"],
"tool_call": parsed.get("tool_call"),
}
输入参数包括用户消息、历史对话、可用工具列表。输出为推理过程(thought)和行动类型(action_type)。
2. ACT节点(行动决策)
ACT节点根据THINK节点的输出执行具体行动,支持三种action_type:
- tool_call: 调用外部工具(如web_search、file_read)
- respond: 生成对话内容和行为数据
- ask_clarification: 向用户请求更多信息
async def _act_node(self, state: AgentState) -> dict:
action_type = state["action_type"]
if action_type == "tool_call":
tool_result = await self._execute_tool(state["tool_call"])
return {"observation": tool_result}
elif action_type == "respond":
response = await self._generate_response(state)
return {
"dialog": response["dialog"],
"behavior": response["behavior"],
"task_complete": True
}
3. OBSERVE节点(观察)
OBSERVE节点分析执行结果,判断任务是否完成:
async def _observe_node(self, state: AgentState) -> dict:
if state["task_complete"]:
return {"should_continue": False}
return {
"observation": state["observation"],
"should_continue": state["iteration"] < self.max_iterations
}
4. should_continue条件判断
条件判断节点控制循环流程:
- 任务完成 → 进入END状态
- 达到最大迭代次数(5次) → 进入END状态
- 未完成 → 返回THINK节点继续推理
2.3 状态持久化机制
LangGraph的Checkpointer机制提供了开箱即用的状态持久化能力:
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
# 创建SQLite检查点
checkpointer = AsyncSqliteSaver.from_conn_string("./data/checkpoints.db")
# 编译图时绑定检查点
graph = workflow.compile(checkpointer=checkpointer)
# 使用thread_id隔离会话
config = {"configurable": {"thread_id": "conversation-001"}}
result = await graph.ainvoke(initial_state, config)
2.4 人机协同审批
对于敏感工具(如file_delete、system_command),系统通过interrupt()实现人机协同:
from langgraph.types import interrupt
async def approve_node(self, state: AgentState) -> dict:
tool_call = state["tool_call"]
if tool_call["name"] in SENSITIVE_TOOLS:
approval = interrupt({
"type": "tool_approval",
"tool_name": tool_call["name"],
"tool_args": tool_call["arguments"],
"message": f"工具 '{tool_call['name']}' 需要您的确认"
})
if not approval.get("approved"):
return {"observation": "用户拒绝了工具执行"}
return state
三、四轴并行动画控制系统
3.1 四轴架构设计
AI桌面伴侣的核心特性之一是"智能体自身驱动"——所有动画行为由AI实时生成,而非预设脚本。系统采用四轴并行同步架构:
四轴定义:
| 轴线 | 控制内容 | 技术实现 | 参数范围 |
|---|---|---|---|
| Head Pose | 头部姿态 | 骨骼旋转 | rotation_x: [-30,30], rotation_y: [-45,45] |
| Facial Expression | 面部表情 | BlendShape | intensity: [0.0,1.0] |
| Eye Gaze | 眼神追踪 | LookAt IK | target: user/up_thinking/down_remembering |
| Body Action | 身体动作 | Mecanim FSM | action_type: idle/nod/tilt_head/lean_forward |
3.2 TimelineController统一调度
四轴动画通过TimelineController实现精确同步:
public class TimelineController : MonoBehaviour
{
[SerializeField] private HeadController headController;
[SerializeField] private ExpressionController expressionController;
[SerializeField] private EyeGazeController eyeGazeController;
[SerializeField] private BodyActionController bodyActionController;
public async Task ExecuteBehavior(BehaviorData behavior)
{
var startTime = Time.time;
// 并行执行四轴动画
var tasks = new Task[]
{
headController.Animate(behavior.HeadPose, startTime),
expressionController.Animate(behavior.FacialExpression, startTime),
eyeGazeController.Animate(behavior.EyeGaze, startTime),
bodyActionController.Animate(behavior.BodyAction, startTime),
};
await Task.WhenAll(tasks);
}
}
关键技术点:
- 统一时间轴:所有轴线使用相同的startTime,确保同步执行
- 并行执行:使用C# async/await实现四轴并行,避免串行延迟
- 过渡插值:每个控制器内部使用SmoothStep或Slerp实现平滑过渡
3.3 各轴线详细实现
头部姿态控制 (HeadController)
public class HeadController : MonoBehaviour
{
[SerializeField] private Transform headBone;
public async Task Animate(HeadPose pose, float startTime)
{
var targetRotation = Quaternion.Euler(
pose.RotationX,
pose.RotationY,
pose.RotationZ
);
var startRotation = headBone.localRotation;
var elapsed = 0f;
while (elapsed < pose.TransitionTime)
{
elapsed += Time.deltaTime;
var t = Mathf.SmoothStep(0, 1, elapsed / pose.TransitionTime);
headBone.localRotation = Quaternion.Slerp(startRotation, targetRotation, t);
await Task.Yield();
}
}
}
面部表情控制 (ExpressionController)
public class ExpressionController : MonoBehaviour
{
[SerializeField] private VRMBlendShapeProxy blendShapeProxy;
private static readonly Dictionary<ExpressionType, BlendShapePreset>
ExpressionMap = new()
{
{ ExpressionType.Happy, BlendShapePreset.Joy },
{ ExpressionType.Sad, BlendShapePreset.Sorrow },
{ ExpressionType.Angry, BlendShapePreset.Angry },
{ ExpressionType.Surprised, BlendShapePreset.Surprised },
};
public async Task Animate(FacialExpression expression)
{
var preset = ExpressionMap[expression.ExpressionType];
blendShapeProxy.ImmediatelySetValue(preset, expression.Intensity);
await Task.CompletedTask;
}
}
眼神追踪控制 (EyeGazeController)
public class EyeGazeController : MonoBehaviour
{
[SerializeField] private Transform lookAtTarget;
public async Task Animate(EyeGaze gaze)
{
var targetPosition = gaze.Target switch
{
EyeGazeTarget.User => Camera.main.transform.position,
EyeGazeTarget.UpThinking => transform.position + Vector3.up * 2f,
EyeGazeTarget.DownRemembering => transform.position + Vector3.down * 1f,
_ => Camera.main.transform.position,
};
lookAtTarget.position = targetPosition;
SetBlinkRate(gaze.BlinkRate);
await Task.CompletedTask;
}
}
3.4 物理一致性验证
为了避免AI生成的动画参数出现物理上不合理的情况,系统实现了物理一致性验证器:
验证规则:
- 头眼一致性:头部向右转时(rotation_y > 20),眼睛不能直视用户
- 思考眼神:facial_expression为thinking时,eye_gaze应设置为up_thinking
- 情感一致性:expression_type为sad时,mouth_corner_up应小于0.3
- 时间约束:transition_time必须在[0.1, 3.0]秒范围内
def validate_behavior(behavior: Behavior) -> tuple[bool, list[str]]:
errors = []
# 头眼一致性
if behavior.head_pose.rotation_y > 20:
if behavior.eye_gaze.target == EyeGazeTarget.USER:
errors.append("头部向右转时,眼睛不能直视用户")
# 思考时眼神
if behavior.facial_expression.expression_type == ExpressionType.THINKING:
if behavior.eye_gaze.target == EyeGazeTarget.USER:
errors.append("思考时眼睛不应直视用户")
# 表情一致性
if behavior.facial_expression.expression_type == ExpressionType.SAD:
if behavior.facial_expression.mouth_corner_up > 0.3:
errors.append("悲伤表情时,不应有笑容")
return len(errors) == 0, errors
3.5 LipSync音频驱动嘴型
嘴型动画由TTS音频实时驱动,而非AI直接控制:
AI输出 → TTS合成 → 音频数据 → uLipSync → 嘴型动画(jaw_open)
设计原理:
- AI不输出jaw_open参数
- TTS生成的音频实时分析phoneme
- uLipSync根据phoneme驱动BlendShape
- 音频播放与嘴型动画同步
四、端到端数据流转流程
4.1 完整流程概览
上图展示了从用户语音输入到Unity渲染输出的完整14步流程:
阶段一:输入处理 (步骤1-3)
- 用户语音输入 → 用户说出"今天天气怎么样?"
- ASR语音识别 → Whisper将语音转为文字
- WebSocket传输 → 前端通过WebSocket发送user_input消息
阶段二:LangGraph处理 (步骤4-9) 4. THINK节点 → AI分析意图,决定调用搜索工具
- ACT节点 → 执行tool_call,调用web_search
- OBSERVE节点 → 获取搜索结果,判断需继续推理
- THINK节点(第2轮) → 分析结果,决定直接回复
- ACT节点 → 生成dialog和behavior的JSON
- 流式消息推送 → 通过WebSocket推送各阶段消息
阶段三:前端渲染 (步骤10-14) 10. 内容解析层 → 解析JSON,分离dialog和四轴参数
- TTS语音合成 → ChatTTS将dialog转为音频
- 四轴并行动画执行 → TimelineController协调四轴
- LipSync嘴型同步 → 音频驱动jaw_open
- Unity渲染输出 → VRM角色呈现给用户
4.2 延迟优化分析
目标总延迟: ~2秒
| 阶段 | 耗时 | 优化手段 |
|---|---|---|
| ASR识别 | ~50ms | 流式识别,边说边传 |
| 网络传输 | ~30ms | WebSocket长连接 |
| LLM推理 | ~800ms | 流式输出,首字<500ms |
| TTS合成 | ~1000ms | 流式合成,边生成边播放 |
| 动画渲染 | ~实时 | 60fps渲染 |
流式处理策略:
async def stream_response(agent, user_input):
async for event in agent.astream({"messages": [user_input]}):
if "think" in event:
yield {"type": "thought_update", "content": event["think"]["thought"]}
elif "act" in event:
if event["act"].get("tool_call"):
yield {"type": "action_update", "tool": event["act"]["tool_call"]["name"]}
elif event["act"].get("dialog"):
yield {"type": "agent_response", "dialog": event["act"]["dialog"]}
五、WebSocket通信时序
5.1 带工具调用的对话流程
上图展示了用户查询天气(需要工具调用)的完整时序:
ReAct循环第1轮:工具调用
用户 → Unity: 语音输入
Unity → Unity: ASR识别
Unity → 后端: user_input消息
后端 → LLM: 调用THINK
LLM → 后端: 返回thought_update
后端 → Unity: 推送thought_update
后端 → LLM: 调用ACT
后端 → Unity: 推送action_update (tool_call)
后端 → 工具: 执行web_search
工具 → 后端: 返回搜索结果
后端 → Unity: 推送observation_update
ReAct循环第2轮:生成回复
后端 → LLM: 第2轮THINK
LLM → 后端: 返回处理结果
后端 → LLM: 生成回复
LLM → 后端: 返回agent_response (dialog+behavior)
后端 → Unity: 推送agent_response + tts_audio
Unity → 用户: 播放音频 + 渲染动画
5.2 消息类型定义
客户端 → 服务端:
{
"type": "user_input",
"content": "你好,今天天气怎么样?",
"conversation_id": "conv_001"
}
服务端 → 客户端:
// thought_update
{
"type": "thought_update",
"thought": "用户想了解天气,需要搜索网络获取信息",
"iteration": 1
}
// action_update
{
"type": "action_update",
"action_type": "tool_call",
"tool_name": "web_search"
}
// agent_response
{
"type": "agent_response",
"dialog": "今天天气晴朗,温度在18到25度之间!",
"behavior": {
"head_pose": {"rotation_x": 5.0, "rotation_y": 0.0},
"facial_expression": {"expression_type": "happy", "intensity": 0.7},
"eye_gaze": {"target": "user"},
"body_action": {"action_type": "nod"}
}
}
六、结构化输出Schema
6.1 统一输出格式
AI桌面伴侣采用统一的JSON Schema作为LLM输出格式:
class AgentOutput(BaseModel):
thought: str # 推理过程
action_type: ActionType # respond/tool_call/ask_clarification
tool_call: Optional[ToolCall] # 工具调用参数
dialog: Optional[str] # 对话内容
behavior: Optional[Behavior] # 行为数据(四轴)
iteration: int # 迭代次数
class Behavior(BaseModel):
head_pose: HeadPose
facial_expression: FacialExpression
eye_gaze: EyeGaze
body_action: BodyAction
6.2 提示词工程
系统提示词设计遵循ReAct模式:
你是一个AI桌面伴侣,使用ReAct(Reasoning + Acting)模式:
1. Thought(思考):分析用户输入,推理当前状态
2. Action(行动):执行工具调用或生成回复
3. Observation(观察):观察执行结果
4. 循环:如任务未完成,返回Thought继续(最多5轮)
输出格式要求:
{
"thought": "你的推理过程",
"action_type": "respond | tool_call | ask_clarification",
"tool_call": {...}, // action_type为tool_call时
"dialog": "...", // action_type为respond时
"behavior": {...} // action_type为respond时
}
七、总结
AI桌面伴侣的技术架构围绕LangGraph ReAct状态机展开,实现了以下核心能力:
- 智能体驱动:所有行为由AI实时生成,无需预设动画脚本
- 四轴并行同步:头部、表情、眼神、身体四轴线精确协调
- 音频驱动嘴型:TTS音频实时驱动LipSync,实现自然的口型同步
- 人机协同:敏感工具操作需用户确认,保障安全性
- 状态持久化:支持会话恢复,实现长期记忆
技术亮点:
- 利用LangGraph框架大幅简化ReAct实现
- 四轴并行架构确保动画同步性
- 流式处理优化用户体验
- 物理一致性验证避免不合理的动画组合
图表文件说明:
01-langgraph-react-architecture.png- LangGraph ReAct状态机架构02-four-axis-animation-system.png- 四轴并行动画控制系统03-complete-data-flow-with-arrows.png- 完整数据流转流程04-websocket-sequence-v2.png- WebSocket通信时序图
详细了解请查看项目地址: 雪玲AI
另外提一句的是当前项目还处于早起开发阶段,很多细节需要天下英雄一起编写,如果对项目感兴趣的话没有技术的请资金上支持,如果有技术的话请联系我。