本文作者为 360 奇舞团前端开发工程师
你是否想过拥有一个可以自定义行为的桌面虚拟角色?本文将带你从零开始,打造一个基于 Live2D 的桌面女友,并赋予她可定制的工作流能力。通过 Electron + LangGraph 的组合,我们可以创建一个既可爱又智能的桌面应用。
通过本项目,你能得到什么:
一位常驻桌面的虚拟女友:使用 Live2D 技术打造的可爱角色,可以常驻桌面,陪伴你的工作时光
可定制的工作流系统:通过 LangGraph 构建的工作流引擎,让虚拟角色能够执行各种自定义任务和逻辑
模板管理系统:可以保存、复用和分享工作流模板,提高工作效率
高度可扩展的架构:模块化设计,可以轻松添加新的步骤类型、集成 AI 对话、文件操作等功能
项目概览
在开始之前,让我们先了解一下整个项目的架构:
┌─────────────────────────────────────────────────────────┐
│ 用户交互层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Live2D 模型 │ │ 工作流管理 │ │
│ │ (桌面女友) │ │ 界面 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────┐
│ Electron 应用层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Express 服务 │ │ BrowserWindow│ │
│ │ (静态资源) │ │ (工作流UI) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────┐
│ 工作流服务层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ FastAPI │ │ LangGraph │ │
│ │ (REST API) │ │ (工作流引擎) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘整个系统分为三个层次:
用户交互层:Live2D 模型交互和工作流管理界面
Electron 应用层:桌面应用框架和资源服务
工作流服务层:后端 API 和工作流执行引擎
第一步:获取 Live2D 免费模型
Live2D 官方提供了丰富的免费示例模型,我们可以直接下载使用。
访问下载页面
打开 Live2D 官方示例数据集页面:https://www.live2d.com/zh-CHS/learn/sample/
在这个页面上,你可以看到多个免费模型,包括:
Haru:一个可爱的女孩角色
Nito:二头身卡通角色
下载模型文件
选择一个你喜欢的模型(推荐从 Haru 开始),下载 FREE 版本。下载后的文件结构通常如下:
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/ 目录下,例如:
electron/
└── live2d/
└── model/
└── haru/ # 你下载的模型文件夹
├── index.json
└── ...第二步:初始化 Electron + Live2D 项目
项目结构
首先,我们需要创建一个 Electron 项目,并集成 Live2D 显示功能。这里使用 live2d-widget 项目的构建工程集成 Live2D。
添加构建工程
将下载 live2d-widget 工程放到以下目录:
electron/
└── live2d/
└── live2d-widget/安装依赖
在 electron 目录下创建 package.json:
{
"name": "wenko",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"dependencies": {
"electron": "^latest",
"express": "^4.18.0"
}
}然后运行:
npm install创建主窗口
创建 index.html 文件,用于显示 Live2D 模型:
<!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 服务器:
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();
});工作流程
┌─────────────────┐
│ 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(如果还没有安装):
curl -LsSf https://astral.sh/uv/install.sh | sh然后初始化项目:
mkdir workflow
cd workflow
# 使用 uv 初始化项目
uv init
# 添加依赖
uv add langgraph langgraph-cli fastapi uvicorn httpx pydantic python-dotenv项目结构
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 基类:
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:设置变量
{ "type": "SetVar", "params": { "key": "greeting", "value": "Hello, World!" } }GetVar:获取变量
{ "type": "GetVar", "params": { "key": "greeting" } }EchoInput:回显输入
{ "type": "EchoInput", "params": { "input_key": "user_input", "output_key": "response" } }
步骤注册表:
所有步骤都注册在 STEP_REGISTRY 中,方便动态创建:
STEP_REGISTRY = {
'EchoInput': EchoInputStep,
'SetVar': SetVarStep,
'GetVar': GetVarStep,
# ... 更多步骤类型
}2. executor.py - 工作流执行器
executor.py 负责执行工作流步骤序列:
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
}执行流程:
┌─────────────────┐
│ 工作流请求 │
│ { │
│ 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 /run@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 调用示例:
# 执行工作流 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": {} }'启动服务
cd workflow uv run python main.py服务启动后,访问
http://localhost:8002/docs可以看到自动生成的 API 文档。第五步:构建工作流管理界面
最后,我们需要一个友善的界面来管理模板和执行工作流。
修改 main.js 添加工作流窗口
在
electron/main.js中添加快捷键监听,打开工作流管理窗口: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. 工作流执行标签页
┌─────────────────────────────────────┐ │ 工作流执行 │ ├─────────────────────────────────────┤ │ 工作流步骤 (JSON) │ │ ┌───────────────────────────────┐ │ │ │ [ │ │ │ │ {"type": "SetVar", ...} │ │ │ │ ] │ │ │ └───────────────────────────────┘ │ │ │ │ 初始上下文 (JSON, 可选) │ │ ┌───────────────────────────────┐ │ │ │ {"key": "value"} │ │ │ └───────────────────────────────┘ │ │ │ │ ☑ 调试模式 │ │ │ │ [执行工作流] [加载示例] [清空] │ └─────────────────────────────────────┘2. 模板管理标签页
┌─────────────────────────────────────┐ │ 模板管理 │ ├─────────────────────────────────────┤ │ [+ 创建新模板] │ │ │ │ ┌───────────────────────────────┐ │ │ │ 模板名称 │ │ │ │ 描述:这是一个示例模板... │ │ │ │ 标签:[基础] [示例] │ │ │ │ [查看] [执行] [编辑] [删除] │ │ │ └───────────────────────────────┘ │ └─────────────────────────────────────┘3. 步骤注册表标签页
显示所有可用的步骤类型:
┌─────────────────────────────────────┐ │ 步骤注册表 │ ├─────────────────────────────────────┤ │ [刷新步骤列表] │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │EchoInput│ │SetVar│ │GetVar│ │ │ └──────┘ └──────┘ └──────┘ │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │FetchURL│ │ParseJSON│ │...│ │ │ └──────┘ └──────┘ └──────┘ │ └─────────────────────────────────────┘模板执行流程
当用户点击"执行"按钮时:
┌─────────────────┐ │ 用户点击执行 │ └────────┬────────┘ │ v ┌─────────────────┐ │ 弹出对话框 │ │ 让用户输入上下文│ └────────┬────────┘ │ v ┌─────────────────┐ │ 调用 API │ │ POST /templates/│ │ {id}/execute │ └────────┬────────┘ │ v ┌─────────────────┐ │ 显示执行结果 │ └─────────────────┘完整系统架构
现在,让我们看看整个系统是如何协作的:
┌─────────────────────────────────────────────────────────────┐ │ 用户操作流程 │ └─────────────────────────────────────────────────────────────┘ │ 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:简单的问候工作流
{ "steps": [ { "type": "SetVar", "params": { "key": "name", "value": "Wenko" } }, { "type": "TemplateReplace", "params": { "template": "你好,{{name}}!今天过得怎么样?", "template_key": null, "output_key": "greeting" } } ], "initial_context": {} }执行结果:
{ "success": true, "result": { "name": "Wenko", "greeting": "你好,Wenko!今天过得怎么样?" } }总结
通过本文,我们完成了一个完整的可定制工作流桌面女友系统:
✅ Live2D 模型集成:从官网下载免费模型并集成到 Electron
✅ 静态资源服务:使用 Express 提供模型文件访问
✅ 工作流引擎:基于 LangGraph 构建可扩展的工作流系统
✅ REST API:提供完整的模板管理和执行接口
✅ 管理界面:友好的 Web 界面用于模板管理
这个系统具有很强的扩展性:
可以添加更多步骤类型(如 AI 对话、文件操作等)
可以集成更多 Live2D 模型和动作
可以添加数据库持久化模板
可以实现工作流与 Live2D 模型的联动(根据工作流结果触发模型动作)
希望这篇文章能帮助你打造属于自己的智能桌面女友!🚀
参考资源
Live2D 官方示例数据集 https://www.live2d.com/zh-CHS/learn/sample/
live2d-widget 项目 https://github.com/stevenjoezhang/live2d-widget
LangGraph 官方文档 https://langchain-ai.github.io/langgraph/
FastAPI 官方文档 https://fastapi.tiangolo.com/
Electron 官方文档 https://www.electronjs.org/
本项目仓库 https://github.com/daijinru/wenko-END -
如果您关注前端+AI 相关领域可以扫码进群交流

添加小编微信进群😊
关于奇舞团
奇舞团是 360 集团最大的大前端团队,非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

564

被折叠的 条评论
为什么被折叠?



