一个真实痛点:厨房里没法用 AI

事情是这样的。上周末我尝试做一道糖醋排骨,油热到七成准备下排骨的时候,突然忘了——是先放糖还是先放醋?

手上全是油,手机在客厅充电,平板挂在墙上但不想用油手去戳屏幕。那一刻我特别希望厨房墙上挂的屏幕里有个"人",我喊一声它就回答,不用碰任何设备。

回来之后我就在思考:能不能搭建一套厨房烹饪助手具身交互智能体,部署在厨房壁挂屏幕;直接语音提问,即可解答烹饪步骤、支持多轮追问、烹饪定时提醒,还可以结合冰箱现有食材自动推荐菜谱。

这个场景看似简单,对 Agent 提出了很高的要求:具备嘈杂环境语音识别能力、会话记忆能力、基于食材做菜谱推理的认知能力,同时还需要一套可视化、可语音播报、可输出手势反馈的具身交互表达界面。

这恰好就是Agent 认知与具身交互智能的交汇点:Agent 作为认知大脑解决 “懂不懂做菜”,具身交互智能解决 “如何在厨房这种特殊环境下自然使用 AI”。纯文本 Agent 在厨房场景几乎无法落地 —— 颠勺做菜的时候,根本腾不出双手敲击键盘、触摸屏幕。

为什么 Agent 需要一个「身体」?

先说清楚我的核心判断:**Agent 缺的不是大脑,**缺少的是面向真实终端的具身交互表达界面。

过去一年我折腾过不少 Agent 框架——LangChain、AutoGPT、Dify,能力都很强。但它们有一个共同问题:交互界面是聊天框。你在客厅坐着用没问题,但在厨房、在车间、在展厅、在门店——任何双手被占用的场景里,聊天框就废了。

Agent 的认知能力(理解、推理、记忆、工具调用)已经足够成熟,Qwen、DeepSeek 这些大模型的推理水平完全能支撑烹饪指导这种场景。但如果输出只能通过文字,它就永远被困在"需要你主动去用"的工具定位里。

[魔珐星云](https://xingyun3d.com/)补齐的正是这一层。

它不是虚拟形象制作工具,也不是简单 Agent 套壳组件;它实现 AI 多模态感知、环境理解,依托语音、表情、语义手势、实时响应能力完成自然人机沟通。

大模型解决 AI 的「大脑」,魔珐星云补齐 AI 的「身体、表达和交互」。

技术层面,魔珐星云把端到端全链路封装为 SDK 一站式接入:多模态感知层(听见·看见·理解)× 大模型智能体层(理解·决策·调度)× 表达引擎层(3D 大模型·端侧渲染·百元芯片可跑·千路并发)。

选 Qwen Plus 做大脑,是因为它中文理解力强、菜谱和烹饪知识覆盖面广。AI Coding 工具用 Trae,之前用它搭过一个 Vue 项目体验不错,这次试试更复杂的服务层架构。

项目结构:Trae 搭建,四级服务分层

用 Trae 创建项目时,我给了它一段提示词:

创建一个 Vue3 + TypeScript 项目,实现厨房烹饪数字人助手。
要求:
1. components 下放数字人 UI 组件
2. services 下分四个模块:llm(大模型菜谱问答)、rag(检索增强)、knowledge-base(烹饪知识库)、xingyun(星云SDK)
3. 主入口 KitchenAssistant.vue,集成数字人渲染和服务调用

数字人核心功能:
- 🍳 菜谱推荐 :支持按食材、口味、难度搜索菜谱,点击菜谱可查看详细步骤并由数字人口播指导
- 🥬 食材知识 :查询食材的分类、口感、烹饪方式和技巧
- 💬 智能对话 :基于大模型的烹饪问答,结合RAG增强提供更精准的菜谱推荐
- 🎭 数字人互动 :支持多种数字人动作(打招呼、讲解、指导、推荐等),口播菜谱步骤

完整的项目骨架:

docs/
  魔珐星云数字人搭建指南.md
src/
  components/
    KitchenAssistant.vue       ← 厨房助手主界面
    CustomerService.vue         ← 预留客服场景扩展
  services/
    llm.service.js             ← Qwen 大模型对接(菜谱问答)
    rag.service.js             ← 检索增强(匹配菜谱步骤)
    knowledge-base.service.js  ← 本地烹饪知识库(食材、口味偏好)
    xingyun.service.js         ← 魔珐星云 SDK 封装
  App.vue
  main.ts
index.html

这个分层的关键在于:认知逻辑和具身表达解耦。`services/` 负责大脑——菜谱推理、步骤检索、知识记忆;`components/` 负责身体——数字人渲染、语音驱动、动作触发。两者通过事件通信,改一个不影响另一个。

下面逐模块拆解。

让厨师长「站」到厨房屏幕上

这是地基。魔珐星云具身驱动 SDK 封装在此,负责数字人渲染、语音合成和动作驱动。

先在魔珐星云平台创建驱动应用。厨房场景选了一个穿厨师服的男性形象,音色选了热情爽朗的风格——做菜嘛,总得有点烟火气。平台有 5000+ 形象可选,也支持上传照片定制。拿到 App IDApp Secret 后开始写代码。

在这里插入图片描述

// xingyun.service.js - 魔珐星云 SDK 封装
let sdkInstance = null;

export function initXingyun(containerId, appId, appSecret) {
  sdkInstance = new XmovAvatar({
    containerId,
    appId,
    appSecret,
    gatewayServer: 'https://nebula-agent.xingyun3d.com/user/v1/ttsa/session',
    hardwareAcceleration: 'prefer-hardware',
    onMessage: (msg) => console.log('[星云]', msg),
  });

  sdkInstance.init({
    onDownloadProgress: (p) => console.log(`[星云] 资源加载: ${p}%`),
  });

  return sdkInstance;
}

// 流式驱动:is_start / is_end 控制播报节奏
export function speak(text, isStart, isEnd) {
  if (!sdkInstance) return;
  sdkInstance.speak(text, isStart, isEnd);
}

// 打断当前播报,切回倾听状态——厨房场景刚需
export function interactiveIdle() {
  if (!sdkInstance) return;
  sdkInstance.interactiveidle();
}

// SSML 驱动:说话 + 语义化动作
export function speakWithAction(text, action) {
  const ssml = `
<speak>
  <ue4event>
    <type>ka</type>
    <data><action_semantic>${action}</action_semantic></data>
  </ue4event>
  ${text}
</speak>`;
  speak(ssml, true, true);
}

export function destroy() {
  if (sdkInstance) sdkInstance.destroy();
}

两个坑提醒:SDK 只支持 localhosthttps 访问,直接用 IP 会报 VideoDecoder is not defined;频繁刷新会触发 10005 房间限流,靠 beforeunloaddestroy() 释放连接。

参数流路线在厨房场景的价值很直接——厨房屏幕通常是便宜的安卓平板或盒子,百元级芯片就能跑 AI 端渲和端侧解算,不需要高配 GPU。端到端约 500ms 响应意味着我在灶台前喊一声"下一步该干嘛",数字人几乎秒回,不会让我等着油烧过头。

knowledge-base.service.js:给厨师长装「记忆」

厨房助手需要记住三类信息:用户的口味偏好(不吃香菜、少油少盐)、当前菜谱的进度(做到第几步了)、常备食材清单(冰箱里有什么)。

// knowledge-base.service.js - 烹饪知识库管理
const STORAGE_KEY = 'kitchen_assistant_kb';

function loadKB() {
  const raw = localStorage.getItem(STORAGE_KEY);
  if (!raw) return { preferences: [], ingredients: [], progress: {} };
  try {
    return JSON.parse(raw);
  } catch {
    return { preferences: [], ingredients: [], progress: {} };
  }
}

function saveKB(kb) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(kb));
}

// 保存口味偏好
export function savePreference(preference) {
  const kb = loadKB();
  kb.preferences.push({
    id: Date.now(),
    content: preference, // "不吃香菜" "少油少盐"
    createdAt: new Date().toISOString(),
  });
  saveKB(kb);
}

// 更新冰箱食材清单
export function updateIngredients(ingredients) {
  const kb = loadKB();
  kb.ingredients = ingredients; // ["鸡蛋","番茄","牛肉","土豆"]
  saveKB(kb);
}

// 记录当前菜谱进度
export function saveProgress(dish, step) {
  const kb = loadKB();
  kb.progress = { dish, step, updatedAt: new Date().toISOString() };
  saveKB(kb);
}

// 检索
export function search(query) {
  const kb = loadKB();
  const results = [];

  // 匹配偏好
  kb.preferences.forEach(p => {
    if (query.includes(p.content.slice(0, 2))) results.push({ ...p, type: 'preference' });
  });

  // 匹配进度
  if (kb.progress.dish && query.includes(kb.progress.dish)) {
    results.push({ ...kb.progress, type: 'progress', content: `当前在做${kb.progress.dish},第${kb.progress.step}步` });
  }

  // 返回食材清单
  if (query.includes('食材') || query.includes('冰箱')) {
    results.push({ content: `冰箱里有:${kb.ingredients.join('、')}`, type: 'ingredients' });
  }

  return results;
}

export function getKB() {
  return loadKB();
}

实际用起来是这样的:我第一次跟数字人说"记住,我不吃香菜",之后它推荐菜谱时会自动避开含香菜的菜。做到糖醋排骨第三步时我说"记住进度",之后我问"我做到哪了",它能回答"你在做糖醋排骨,做到第三步收汁"。

这种跨会话的持久记忆,是传统聊天框 Agent 做不到的——ChatGPT 关了窗口就忘了你是谁,但厨房助手得记住你的口味。
在这里插入图片描述

rag.service.js:检索增强,让回答「言之有据」

光有记忆不够,Agent 回答烹饪问题时得能从知识库里检索菜谱信息,注入到大模型上下文里,避免瞎编菜谱。

// rag.service.js - 检索增强生成
import { search as searchKB } from './knowledge-base.service.js';
import { streamChat } from './llm.service.js';

export async function ragQuery(question, onChunk) {
  // 1. 从知识库检索相关信息
  const relevant = searchKB(question);
  const context = relevant
    .map(e => `[${e.type}] ${e.content}`)
    .join('\n');

  // 2. 构造增强 prompt
  let enhancedPrompt = question;
  if (context) {
    enhancedPrompt = `参考以下已知信息回答用户问题。
已知信息:
${context}

用户问题:${question}`;
  }

  // 3. 流式调用大模型
  await streamChat(enhancedPrompt, onChunk);
}

RAG 在厨房场景的价值很实际。有一次我问"冰箱里有鸡蛋和番茄能做什么",RAG 先检索到食材清单,再交给 Qwen 推理菜谱,数字人回答"番茄炒蛋、番茄蛋汤、鸡蛋番茄面都可以做,你想做哪个"。如果没有 RAG,大模型不知道你冰箱里有什么,推荐出来的菜你可能没食材做。

llm.service.js:Qwen 做菜谱推理

// llm.service.js - Qwen 大模型对接
const LLM_API = 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
const LLM_KEY = 'your_dashscope_api_key';

const SYSTEM_PROMPT = `你是厨房AI助手"大厨",帮用户解决烹饪问题。
你的特点:
1. 回答简洁,2-3句话,因为用户在做饭,手忙脚乱没空听长篇大论
2. 步骤清晰,用"先...再...最后..."的句式
3. 记住用户的口味偏好(通过RAG注入),推荐菜谱时自动规避忌口食材
4. 涉及油温、火候给具体描述,比如"筷子放进去周围冒密集小泡就是七成热"
5. 用户问到做不出来的菜时,给出替代方案
6. 语气热情,像厨房里的大厨在教你做菜`;

export async function streamChat(userMessage, onChunk, context = []) {
  const messages = [
    { role: 'system', content: SYSTEM_PROMPT },
    ...context.slice(-6),
    { role: 'user', content: userMessage },
  ];

  const response = await fetch(LLM_API, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${LLM_KEY}`,
    },
    body: JSON.stringify({
      model: 'qwen-plus',
      messages,
      stream: true,
    }),
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop() || '';

    for (const line of lines) {
      if (!line.startsWith('data: ') || line === 'data: [DONE]') continue;
      try {
        const json = JSON.parse(line.slice(6));
        const content = json.choices?.[0]?.delta?.content;
        if (content) onChunk(content);
      } catch (e) { /* 忽略解析异常 */ }
    }
  }
}

System Prompt 里特意写了"油温给具体描述"——大模型容易说"七成热油",但很多人不知道七成热是多热。加了这条后,Qwen 回答变成了"筷子放进去周围冒密集小泡就是七成热",实用多了。
在这里插入图片描述

KitchenAssistant.vue:把大脑和身体缝起来

这是最关键的一步——把四个 service 的输出缝到数字人表达上。核心链路:语音输入 → RAG 检索 + Qwen 推理 → 流式文本 → 星云 SDK 参数流驱动 → 数字人说话。

<!-- KitchenAssistant.vue -->
<template>
  <div class="kitchen-container">
    <div id="avatar-sdk"></div>
    <div id="subtitle" class="subtitle"></div>
    <div class="status-bar">
      <span v-if="cooking">正在做:{{ currentDish }} · 第{{ currentStep }}步</span>
      <span v-else class="idle-hint">说"大厨"唤醒我</span>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { initXingyun, speak, interactiveIdle, speakWithAction, destroy }
  from '../services/xingyun.service.js';
import { ragQuery } from '../services/rag.service.js';
import { savePreference, saveProgress, search, getKB }
  from '../services/knowledge-base.service.js';

const cooking = ref(false);
const currentDish = ref('');
const currentStep = ref(0);
let pendingText = null;

// 语音状态管理
function handleVoiceState(res) {
  const state = (typeof res === 'string') ? res : (res.state || res.data);
  if ((state === 'end' || state === 'idle') && pendingText) {
    speak(pendingText, true, true);
    pendingText = null;
  }
}

// 打断
function interruptAndSpeak(text) {
  pendingText = text;
  interactiveIdle();
}

// 主对话流程
async function processCommand(msg) {
  // 记忆指令
  if (msg.startsWith('记住') || msg.startsWith('我不吃')) {
    const pref = msg.replace(/^(记住|我不吃)/, '').trim();
    savePreference(pref || msg);
    speakWithAction(`记下了,以后做菜不放${pref || msg}。`, 'ThumbsUp');
    return;
  }

  // 进度指令
  if (msg.includes('记住进度')) {
    saveProgress(currentDish.value, currentStep.value);
    speakWithAction('进度已记住,随时可以问我在做到哪一步。', 'ThumbsUp');
    return;
  }

  // RAG 检索 + LLM 推理 → 流式驱动
  let buf = '';
  let count = 0;
  await ragQuery(msg, (chunk) => {
    buf += chunk;
    count++;
    if (count >= 6) {
      speak(buf, buf === chunk, false);
      buf = '';
      count = 0;
    }
  });
  if (buf) speak(buf, false, true);
}

onMounted(() => {
  initXingyun('#avatar-sdk', 'your_appid', 'your_appsecret');

  // 开场白——根据时间问候
  const hour = new Date().getHours();
  let greeting = '我准备好啦,今天想做什么菜?';
  if (hour < 10) greeting = '早上好,想做点早餐吗?煎蛋还是下面条?';
  else if (hour > 16) greeting = '该准备晚饭啦,有什么想吃的吗?';

  // 检查有无口味偏好,个性化问候
  const kb = getKB();
  if (kb.preferences.length > 0) {
    greeting += `我记着你不吃${kb.preferences[0].content}呢。`;
  }

  speakWithAction(greeting, 'Hello');
});

onBeforeUnmount(() => {
  destroy();
});
</script>

<style scoped>
.kitchen-container {
  width: 100%;
  height: 100%;
  position: relative;
}
#avatar-sdk {
  width: 100%;
  height: 100%;
}
.subtitle {
  position: absolute;
  bottom: 60px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  padding: 8px 20px;
  border-radius: 20px;
  font-size: 16px;
  max-width: 80%;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.subtitle.show { opacity: 1; }
.status-bar {
  position: absolute;
  top: 12px;
  left: 12px;
  background: rgba(255, 255, 255, 0.9);
  padding: 4px 12px;
  border-radius: 12px;
  font-size: 13px;
  color: #333;
}
.idle-hint {
  color: #999;
}
</style>

在这里插入图片描述

定时提醒:烹饪场景的刚需

做菜最怕忘时间——红烧肉炖久了肉烂、蒸蛋蒸久了有孔。我在 KitchenAssistant.vue 里加了定时任务,让数字人主动开口提醒:

在这里插入图片描述

// 定时提醒模块
const timers = new Map();

function setCookingTimer(dish, step, minutes) {
  const timerId = setTimeout(() => {
    speakWithAction(
      `${dish}的${step}已经${minutes}分钟了,该看看了!`,
      'Guide'
    );
  }, minutes * 60 * 1000);

  timers.set(`${dish}-${step}`, timerId);
}

// 语音触发:"大厨,设个5分钟的定时"
if (msg.includes('定时') || msg.includes('设个')) {
  const match = msg.match(/(\d+)分钟/);
  if (match) {
    const mins = parseInt(match[1]);
    setCookingTimer(currentDish.value || '当前菜', currentStep.value || '这步', mins);
    speakWithAction(`好,${mins}分钟后提醒你。`, 'ThumbsUp');
    return;
  }
}

通过 SSML 的 KA 指令,数字人提醒时会配合动作——指引手势表示"该看看锅了",比手机闹钟有效太多。有一次我在客厅等红烧肉炖好,数字人从厨房屏幕喊"红烧肉已经炖了 30 分钟了,该收汁了",声音穿过客厅都能听到——这种主动交互是纯文本 Agent 完全做不到的。

在这里插入图片描述

实际用了一周的感受

认知层的表现。 RAG + 知识库的组合让数字人真正有了"记忆"。第一次跟它说"记住我不吃香菜",之后推荐菜谱全部自动规避香菜。做到一半问"我到哪一步了",它能从进度记录里回答。这种跨会话的持久认知,比堆 context 强太多。

具身层的体验。 过去拿手机查阅菜谱,手上沾满油污还要小心触碰屏幕;如今壁挂屏幕部署具身交互智能体,纯语音即可提问,无需触摸设备。原生支持交互打断:播报烹饪步骤途中,临时提问 “盐放多少”,会立刻终止当前播报,应答完成后继续讲解;端到端约 500ms 响应,真正做到 “开口即可获得反馈”。

参数流在厨房场景的价值。 厨房屏幕就是个普通安卓平板,百元级芯片跑 AI 端渲毫无压力。如果是视频流方案,还得在厨房里放台高配主机——先不说成本,厨房那油烟环境,机器活不过三个月。参数流方案让数字人在低性能终端上稳定跑,这在厨房场景是硬要求。

主动播报。 定时提醒功能用了一周就离不开了。以前用手机定闹钟,响了还得去看是哪个菜的时间到了。现在数字人直接说"红烧肉该收汁了",不用看不用记,比闹钟有效太多。

Qwen 的表现。 Qwen Plus 在烹饪问答上表现不错,菜谱步骤清晰,油温火候的描述加了具体判断方法后很实用。有一次我问"糖醋排骨的糖醋比例",它回答"排骨和糖醋的比例是 1:1:1,也就是一斤排骨配一两糖一两醋",精准到位。

Trae 的开发效率。 项目从零到跑通大约一天。Trae 生成的服务层分层很干净,四个 service 模块职责清晰。中文注释理解能力比预期好,写"检索知识库并注入上下文"这种注释它直接帮你生成 RAG 框架代码。整体改单个模块不影响其他,后续迭代很方便。

写在最后

完成这个 Demo 之前,我曾经以为 Agent 只需要把推理、记忆、工具调用这类 “大脑能力” 做到足够强就可以。搭建完这套厨房项目之后我的认知发生改变:Agent 的认知大脑解决 “能不能思考”,具身交互智能解决 “能不能被真实场景所使用”。厨房、车间、展厅这类双手无法自由操作屏幕的场景,如果缺少完整的具身交互表达能力,Agent 即便再聪明,也属于无法落地的工具。

魔珐星云依托AI 端侧解算 + 自研参数流架构,为各类 Agent 补齐完整的具身交互智能体系。它不去解决 “Agent 懂不懂烹饪” 这类认知问题,而是解决核心落地难题:Agent 如何在厨房环境下实现自然的人机交互。具身交互 Agent 会成为下一代人机交互主流入口,本项目的厨房实战充分印证该判断:壁挂屏幕部署具身交互智能体,语音提问、主动定时提醒收汁,整套交互体验远优于翻阅手机查阅菜谱。

大模型让 AI 学会了思考,下一步是让 AI 进入终端,以具身的方式与人交互。厨房只是其中一个场景,但它是最高频、最日常的那个——毕竟,人天天都要吃饭。

数字人体验

Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐