你是否想过拥有一个可以自定义行为的桌面虚拟角色?本文将带你从零开始,打造一个基于 Live2D 的桌面女友,并赋予她可定制的工作流能力。通过 Electron + LangGraph 的组合,我们可以创建一个既可爱又智能的桌面应用。
通过本项目,你能得到什么:
- 一位常驻桌面的虚拟女友:使用 Live2D 技术打造的可爱角色,可以常驻桌面,陪伴你的工作时光
- 可定制的工作流系统:通过 LangGraph 构建的工作流引擎,让虚拟角色能够执行各种自定义任务和逻辑
- 模板管理系统:可以保存、复用和分享工作流模板,提高工作效率
- 高度可扩展的架构:模块化设计,可以轻松添加新的步骤类型、集成 AI 对话、文件操作等功能
项目概览
在开始之前,让我们先了解一下整个项目的架构:
scss
┌─────────────────────────────────────────────────────────┐
│ 用户交互层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Live2D 模型 │ │ 工作流管理 │ │
│ │ (桌面女友) │ │ 界面 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────┐
│ Electron 应用层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Express 服务 │ │ BrowserWindow│ │
│ │ (静态资源) │ │ (工作流UI) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────┐
│ 工作流服务层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ FastAPI │ │ LangGraph │ │
│ │ (REST API) │ │ (工作流引擎) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
整个系统分为三个层次:
- 用户交互层:Live2D 模型交互和工作流管理界面
- Electron 应用层:桌面应用框架和资源服务
- 工作流服务层:后端 API 和工作流执行引擎
第一步:获取 Live2D 免费模型
Live2D 官方提供了丰富的免费示例模型,我们可以直接下载使用。
访问下载页面
打开 Live2D 官方示例数据集页面:www.live2d.com/zh-CHS/lear...
在这个页面上,你可以看到多个免费模型,包括:
- Haru:一个可爱的女孩角色
- Nito:二头身卡通角色
下载模型文件
选择一个你喜欢的模型(推荐从 Haru 开始),下载 FREE 版本。下载后的文件结构通常如下:
bash
haru/
├── haru_greeter_t05.moc3 # 模型数据文件
├── haru_greeter_t05.physics3.json # 物理效果配置
├── haru_greeter_t05.pose3.json # 姿势配置
├── haru_greeter_t05.cdi3.json # 显示辅助文件
├── index.json # 模型索引文件
├── texture_00.png # 纹理贴图
├── texture_01.png # 纹理贴图
└── motion/ # 动作文件夹
├── haru_g_idle.motion3.json # 待机动作
└── ... # 其他动作文件
放置模型文件
将下载的模型文件夹放到项目的 electron/live2d/model/ 目录下,例如:
bash
electron/
└── live2d/
└── model/
└── haru/ # 你下载的模型文件夹
├── index.json
└── ...
第二步:初始化 Electron + Live2D 项目
项目结构
首先,我们需要创建一个 Electron 项目,并集成 Live2D 显示功能。这里使用 live2d-widget github.com/stevenjoezh... 项目的构建工程集成 Live2D。
添加构建工程
将下载 live2d-widget 工程放到以下目录:
markdown
electron/
└── live2d/
└── live2d-widget/
安装依赖
在 electron 目录下创建 package.json:
json
{
"name": "wenko",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"dependencies": {
"electron": "^latest",
"express": "^4.18.0"
}
}
然后运行:
bash
npm install
创建主窗口
创建 index.html 文件,用于显示 Live2D 模型:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Wenko</title>
</head>
<body>
<div id="wenko_wifu"></div>
<script src="http://localhost:8080/live2d/live2d-widget/dist/autoload.js"></script>
</body>
</html>
这里我们通过 localhost:8080 来加载 Live2D 的资源文件,这个服务我们会在下一步创建。
live2d-widget 项目和 Live2D 模型有什么关系?
live2d-widget 项目不包括任何模型,仅负责在网页中加载和运行 Live2D 模型。具体逻辑可参考 autoload.js 。
第三步:配置 Express 静态资源服务
为了让 Live2D 模型文件能够正常加载,我们需要在 Electron 主进程中启动一个 Express 服务器来提供静态文件服务。
修改 main.js
打开 electron/main.js,添加 Express 服务器:
javascript
const { app, BrowserWindow } = require('electron');
const path = require('path');
const express = require('express');
// 创建 Express 服务器提供 live2d 静态文件访问
function createStaticServer() {
const expressApp = express();
const port = 8080;
// 提供 live2d 目录的静态文件访问
expressApp.use('/live2d', express.static(path.join(__dirname, 'live2d')));
expressApp.listen(port, () => {
console.log(`Static server running on http://localhost:${port}`);
});
}
// 启动静态文件服务器
createStaticServer();
// 创建 Electron 窗口
function createWindow() {
const mainWindow = new BrowserWindow({
width: 350,
height: 400,
frame: false,
transparent: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
webSecurity: false
}
});
mainWindow.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
});
工作流程
bash
┌─────────────────┐
│ Electron 启动 │
└────────┬─────────┘
│
├──> 启动 Express 服务器 (端口 8080)
│ └──> 提供 /live2d/* 静态资源
│
└──> 创建 BrowserWindow
└──> 加载 index.html
└──> 通过 http://localhost:8080/live2d/... 加载模型
为什么需要 Express 服务器?
Live2D 的模型文件(.moc3、.json、.png 等)需要通过 HTTP 协议加载。虽然 Electron 支持 file:// 协议,但某些情况下会遇到跨域问题。使用 Express 提供 HTTP 服务可以:
- 避免跨域问题:统一使用 HTTP 协议
- 便于调试:可以在浏览器中直接访问资源
- 灵活配置:可以添加缓存、压缩等中间件
第四步:构建工作流系统
现在,让我们为桌面女友添加"大脑"------一个可定制的工作流系统。
初始化 LangGraph 项目
在项目根目录下创建 workflow 目录,并初始化 Python 项目:
首先安装 uv(如果还没有安装):
bash
curl -LsSf https://astral.sh/uv/install.sh | sh
然后初始化项目:
bash
mkdir workflow
cd workflow
# 使用 uv 初始化项目
uv init
# 添加依赖
uv add langgraph langgraph-cli fastapi uvicorn httpx pydantic python-dotenv
项目结构
bash
workflow/
├── main.py # FastAPI 应用入口
├── executor.py # 工作流执行器
├── steps.py # 步骤定义
├── graph.py # LangGraph 图定义
├── control_steps.py # 控制流步骤(If/Then/Else)
└── pyproject.toml # 项目配置
核心组件解析
1. steps.py - 步骤定义
steps.py 定义了所有可用的工作流步骤。每个步骤都是一个类,继承自 Step 基类:
python
class Step(ABC):
"""步骤基类"""
def __init__(self, step_type: str, params: Dict[str, Any]):
self.step_type = step_type
self.params = params
@abstractmethod
def execute(self, context: StepContext) -> Any:
"""执行步骤"""
pass
步骤类型示例:
-
SetVar:设置变量
python{ "type": "SetVar", "params": { "key": "greeting", "value": "Hello, World!" } } -
GetVar:获取变量
python{ "type": "GetVar", "params": { "key": "greeting" } } -
EchoInput:回显输入
python{ "type": "EchoInput", "params": { "input_key": "user_input", "output_key": "response" } }
步骤注册表:
所有步骤都注册在 STEP_REGISTRY 中,方便动态创建:
python
STEP_REGISTRY = {
'EchoInput': EchoInputStep,
'SetVar': SetVarStep,
'GetVar': GetVarStep,
# ... 更多步骤类型
}
2. executor.py - 工作流执行器
executor.py 负责执行工作流步骤序列:
python
class WorkflowExecutor:
"""工作流执行器"""
def execute(self, steps: List[Dict], initial_context: Dict = None):
# 创建上下文
context = StepContext(initial_context or {})
# 依次执行每个步骤
for step_config in steps:
step_type = step_config.get('type')
step_params = step_config.get('params', {})
# 从注册表创建步骤实例
step = create_step(step_type, step_params)
# 执行步骤
result = step.execute(context)
return {
'success': True,
'result': context.variables
}
执行流程:
yaml
┌─────────────────┐
│ 工作流请求 │
│ { │
│ steps: [...], │
│ context: {} │
│ } │
└────────┬─────────┘
│
v
┌─────────────────┐
│ WorkflowExecutor│
└────────┬─────────┘
│
v
┌─────────────────┐ ┌──────────────┐
│ StepContext │<─────│ 变量存储 │
│ (上下文) │ │ variables │
└────────┬────────┘ └──────────────┘
│
v
┌─────────────────────────────────────┐
│ 遍历步骤列表 │
│ ┌──────────┐ ┌──────────┐ │
│ │ Step 1 │→ │ Step 2 │→ ... │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────┘
│
v
┌─────────────────┐
│ 返回执行结果 │
│ { │
│ success: true,│
│ result: {...} │
│ } │
└─────────────────┘
3. main.py - REST API 接口
main.py 使用 FastAPI 提供 RESTful API:
核心接口:
-
执行工作流 -
POST /runpython@app.post("/run", response_model=WorkflowResponse) async def run_workflow(request: WorkflowRequest): executor = WorkflowExecutor() result = executor.execute( steps=request.steps, initial_context=request.initial_context or {} ) return result -
模板管理接口:
POST /templates- 创建模板GET /templates- 列出所有模板GET /templates/{id}- 获取模板详情PUT /templates/{id}- 更新模板DELETE /templates/{id}- 删除模板GET /templates/search/{query}- 搜索模板POST /templates/{id}/execute- 执行模板
-
系统接口:
GET /health- 健康检查GET /steps- 获取步骤注册表
API 调用示例:
bash
# 执行工作流
curl -X POST http://localhost:8002/run \
-H "Content-Type: application/json" \
-d '{
"steps": [
{
"type": "SetVar",
"params": {"key": "name", "value": "Wenko"}
},
{
"type": "EchoInput",
"params": {"input_key": "name", "output_key": "greeting"}
}
],
"initial_context": {}
}'
启动服务
bash
cd workflow
uv run python main.py
服务启动后,访问 http://localhost:8002/docs 可以看到自动生成的 API 文档。
第五步:构建工作流管理界面
最后,我们需要一个友善的界面来管理模板和执行工作流。
修改 main.js 添加工作流窗口
在 electron/main.js 中添加快捷键监听,打开工作流管理窗口:
javascript
ipcMain.on('wenko_shortcut', async (event, data) => {
// 处理快捷键事件: action: open
const { action } = data;
if (action === 'open') {
const shortcutWindow = new BrowserWindow({
width: 1200,
height: 800,
title: 'Workflow API 测试工具',
icon: path.join(__dirname, 'assets', 'favicon.ico'),
frame: true,
transparent: false,
alwaysOnTop: false,
resizable: true,
hasShadow: true,
fullscreenable: true,
skipTaskbar: false, // 在任务栏显示
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
webSecurity: false // 关闭同源策略和 CSP 检查,方便开发加载任意脚本
}
});
shortcutWindow.loadFile('workflow.html');
} else {
console.warn('Unknown action:', action);
}
});
workflow.html 功能模块
workflow.html 使用 React + Ant Design 构建,包含以下功能:
1. 工作流执行标签页
scss
┌─────────────────────────────────────┐
│ 工作流执行 │
├─────────────────────────────────────┤
│ 工作流步骤 (JSON) │
│ ┌───────────────────────────────┐ │
│ │ [ │ ││ │ {"type": "SetVar", ...} │ ││ │ ] │ │
│ └───────────────────────────────┘ │
│ │
│ 初始上下文 (JSON, 可选) │
│ ┌───────────────────────────────┐ │
│ │ {"key": "value"} │ │
│ └───────────────────────────────┘ │
│ │
│ ☑ 调试模式 │
│ │
│ [执行工作流] [加载示例] [清空] │
└─────────────────────────────────────┘
2. 模板管理标签页
css
┌─────────────────────────────────────┐
│ 模板管理 │
├─────────────────────────────────────┤
│ [+ 创建新模板] │
│ │
│ ┌───────────────────────────────┐ │
│ │ 模板名称 │ │
│ │ 描述:这是一个示例模板... │ │
│ │ 标签:[基础] [示例] │ │
│ │ [查看] [执行] [编辑] [删除] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
3. 步骤注册表标签页
显示所有可用的步骤类型:
css
┌─────────────────────────────────────┐
│ 步骤注册表 │
├─────────────────────────────────────┤
│ [刷新步骤列表] │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │EchoInput│ │SetVar│ │GetVar│ │
│ └──────┘ └──────┘ └──────┘ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │FetchURL│ │ParseJSON│ │...│ │
│ └──────┘ └──────┘ └──────┘ │
└─────────────────────────────────────┘
模板执行流程
当用户点击"执行"按钮时:
bash
┌─────────────────┐
│ 用户点击执行 │
└────────┬────────┘
│
v
┌─────────────────┐
│ 弹出对话框 │
│ 让用户输入上下文│
└────────┬────────┘
│
v
┌─────────────────┐
│ 调用 API │
│ POST /templates/│
│ {id}/execute │
└────────┬────────┘
│
v
┌─────────────────┐
│ 显示执行结果 │
└─────────────────┘
完整系统架构
现在,让我们看看整个系统是如何协作的:
javascript
┌─────────────────────────────────────────────────────────────┐
│ 用户操作流程 │
└─────────────────────────────────────────────────────────────┘
│
v
┌───────────────────────────────────┐
│ 1. 启动 Electron 应用 │
│ - 显示 Live2D 模型 │
│ - 启动 Express 静态服务 │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 2. 与 Live2D 模型交互打开管理界面 │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 3. 在工作流界面中: │
│ - 创建/编辑模板 │
│ - 执行工作流 │
│ - 查看步骤注册表 │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 4. 前端调用 FastAPI │
│ http://localhost:8002/... │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 5. FastAPI 处理请求 │
│ - 解析 JSON │
│ - 调用 WorkflowExecutor │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 6. WorkflowExecutor 执行步骤 │
│ - 创建 StepContext │
│ - 遍历步骤列表 │
│ - 执行每个步骤 │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 7. 返回执行结果 │
│ - 成功/失败状态 │
│ - 上下文变量 │
└───────────────┬─────────────────────┘
│
v
┌───────────────────────────────────┐
│ 8. 前端显示结果 │
│ - 成功提示 │
│ - 结果 JSON 展示 │
└───────────────────────────────────┘
使用示例
示例 1:简单的问候工作流
json
{
"steps": [
{
"type": "SetVar",
"params": {
"key": "name",
"value": "Wenko"
}
},
{
"type": "TemplateReplace",
"params": {
"template": "你好,{{name}}!今天过得怎么样?",
"template_key": null,
"output_key": "greeting"
}
}
],
"initial_context": {}
}
执行结果:
json
{
"success": true,
"result": {
"name": "Wenko",
"greeting": "你好,Wenko!今天过得怎么样?"
}
}
总结
通过本文,我们完成了一个完整的可定制工作流桌面女友系统:
- ✅ Live2D 模型集成:从官网下载免费模型并集成到 Electron
- ✅ 静态资源服务:使用 Express 提供模型文件访问
- ✅ 工作流引擎:基于 LangGraph 构建可扩展的工作流系统
- ✅ REST API:提供完整的模板管理和执行接口
- ✅ 管理界面:友好的 Web 界面用于模板管理
这个系统具有很强的扩展性:
- 可以添加更多步骤类型(如 AI 对话、文件操作等)
- 可以集成更多 Live2D 模型和动作
- 可以添加数据库持久化模板
- 可以实现工作流与 Live2D 模型的联动(根据工作流结果触发模型动作)
希望这篇文章能帮助你打造属于自己的智能桌面女友!🚀
参考资源
- Live2D 官方示例数据集 www.live2d.com/zh-CHS/lear...
- live2d-widget 项目 github.com/stevenjoezh...
- LangGraph 官方文档 langchain-ai.github.io/langgraph/
- FastAPI 官方文档 fastapi.tiangolo.com/
- Electron 官方文档 www.electronjs.org/
- 本项目仓库 github.com/daijinru/we...