Skip to main content

常见问题 (FAQ)

基础使用问题

1. 为什么提示 "useAaaSPilotKit must be used within a component that provides AaaSPilotKit"?

原因provideAaaSPilotKituseAaaSPilotKit 在同一个组件中使用了。

常见错误
<!--  错误示例 -->
<script setup lang="ts">
import {provideAaaSPilotKit, useAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';

provideAaaSPilotKit(options);
const {controller} = useAaaSPilotKit(); // ❌ 错误!会报错
</script>

解决方案:拆分为父子组件

正确示例

App.vue - 父组件

<template>
<div class="app">
<PilotComponent />
</div>
</template>

<script setup lang="ts">
import {provideAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';
import PilotComponent from './PilotComponent.vue';

const options = {
token: 'your-auth-token-here',
figureId: '209337',
agentConfig: {
token: 'your-token',
robotId: 'your-robot-id'
}
};

provideAaaSPilotKit(options);
</script>

PilotComponent.vue - 子组件

<script setup lang="ts">
import {useAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';

const {controller, isReady} = useAaaSPilotKit(); // ✅ 正确
</script>

2. 数字员工形象无法显示怎么办?

原因:忘记调用 controller.mount() 挂载数字员工渲染容器。

解决方案:在父组件中挂载数字员工容器

<template>
<div class="app">
<!-- 数字员工渲染容器 -->
<div ref="digitalHumanRef" class="digital-human-container"></div>
<PilotComponent />
</div>
</template>

<script setup lang="ts">
import {ref, onMounted, onUnmounted} from 'vue';
import {provideAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';
import PilotComponent from './PilotComponent.vue';

const options = {
token: 'your-auth-token-here',
figureId: '209337',
agentConfig: {
token: 'your-token',
robotId: 'your-robot-id'
}
};

const provider = provideAaaSPilotKit(options);

// 数字员工渲染容器
const digitalHumanRef = ref<HTMLDivElement>();

// 挂载数字员工到 DOM
onMounted(() => {
if (digitalHumanRef.value) {
provider.controller.value?.mount(digitalHumanRef.value);
}
});

// 记得销毁
onUnmounted(() => {
provider.controller.value?.dispose();
});
</script>

<style scoped>
.digital-human-container {
width: 100%;
height: 400px;
position: relative;
}
</style>

3. 为什么 props 传入的 conversation 不是响应式的?

原因:直接解构 props.conversation 会丢失响应性。

解决方案:使用 toRef 包装 props

<script setup lang="ts">
import {toRef} from 'vue';
import {useConversation} from '@bdky/aaas-pilot-kit-vue3';
import type {AnyConversation} from '@bdky/aaas-pilot-kit';

const props = defineProps<{
conversation: AnyConversation | undefined;
}>();

// ✅ 正确:使用 toRef 保持响应性
const {conversationContents} = useConversation(toRef(props, 'conversation'));

// ❌ 错误:直接传 props.conversation 会丢失响应性
// const {conversationContents} = useConversation(props.conversation);
</script>

原理说明

  • defineProps 返回的 props 是响应式对象(Proxy)
  • 直接解构 props.conversation 会丢失响应性
  • toRef(props, 'conversation') 创建一个指向 props 属性的 ref,保持响应式追踪

4. 为什么 controller.valuenull

原因:控制器在初始化过程中是 null,需要等待 isReadytrue

解决方案:使用响应式状态判断

<template>
<div>
<div v-if="!isReady">初始化中...</div>
<div v-else>
<button @click="sendMessage" :disabled="!canSend">
发送消息
</button>
</div>
</div>
</template>

<script setup lang="ts">
import {computed} from 'vue';
import {useAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';

const {controller, isReady, isRendering} = useAaaSPilotKit();

// 安全的状态判断
const canSend = computed(() => {
return isReady.value && !isRendering.value && controller.value !== null;
});

const sendMessage = () => {
if (canSend.value) {
controller.value!.input('你好');
}
};
</script>

组件结构问题

5. 如何正确组织父子组件结构?

推荐的组件结构

App.vue (父组件)
├── provideAaaSPilotKit()
├── 挂载数字员工容器
└── 子组件们
├── PilotComponent.vue (使用 useAaaSPilotKit)
├── ConversationList.vue (使用 useConversationList)
└── ConversationItem.vue (使用 useConversation)

完整示例

App.vue

<template>
<div class="app">
<div ref="digitalHumanRef" class="digital-human"></div>
<ConversationList />
<ChatInput />
</div>
</template>

<script setup lang="ts">
import {ref, onMounted} from 'vue';
import {provideAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';
import ConversationList from './ConversationList.vue';
import ChatInput from './ChatInput.vue';

const provider = provideAaaSPilotKit({
token: 'your-auth-token-here',
figureId: '209337',
agentConfig: {
token: 'your-token',
robotId: 'your-robot-id'
}
});

const digitalHumanRef = ref<HTMLDivElement>();

onMounted(() => {
if (digitalHumanRef.value) {
provider.controller.value?.mount(digitalHumanRef.value);
}
});
</script>

ConversationList.vue

<template>
<div class="conversation-list">
<ConversationItem
v-for="conversation in conversationList"
:key="conversation.id"
:conversation="conversation"
/>
</div>
</template>

<script setup lang="ts">
import {useConversationList} from '@bdky/aaas-pilot-kit-vue3';
import ConversationItem from './ConversationItem.vue';

const {conversationList} = useConversationList();
</script>

ConversationItem.vue

<template>
<div class="conversation-item">
<div
v-for="item in conversationContents"
:key="item.uiId"
:class="['message', item.mod]"
>
<strong>{{ item.mod }}</strong>
<div>{{ item.content }}</div>
<span>{{ item.completed ? '✓' : '...' }}</span>
</div>
</div>
</template>

<script setup lang="ts">
import {toRef} from 'vue';
import {useConversation} from '@bdky/aaas-pilot-kit-vue3';
import type {AnyConversation} from '@bdky/aaas-pilot-kit';

const props = defineProps<{
conversation: AnyConversation | undefined;
}>();

const { conversationContents } = useConversation(toRef(props, 'conversation'));
</script>

ChatInput.vue

<template>
<div class="chat-input">
<input
v-model="inputText"
@keyup.enter="sendMessage"
:disabled="!canSend"
placeholder="输入消息..."
/>
<button @click="sendMessage" :disabled="!canSend">
发送
</button>
</div>
</template>

<script setup lang="ts">
import {ref, computed} from 'vue';
import {useAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';

const {controller, isReady, isRendering} = useAaaSPilotKit();
const inputText = ref('');

const canSend = computed(() => {
return isReady.value && !isRendering.value && inputText.value.trim();
});

const sendMessage = () => {
if (canSend.value && controller.value) {
controller.value.input(inputText.value);
inputText.value = '';
}
};
</script>

高级用法问题

6. 如何处理多个数字员工实例?

使用场景:同一应用中需要多个独立的数字员工实例(如客服 + 销售)。

解决方案:使用 useAaaSPilotKitProvider + 自定义 provide key

<template>
<div class="multi-avatar-app">
<div class="avatar-section">
<h3>客服助手</h3>
<CustomerServicePilot />
</div>
<div class="avatar-section">
<h3>销售助手</h3>
<SalesAssistantPilot />
</div>
</div>
</template>

<script setup lang="ts">
import {provide} from 'vue';
import {useAaaSPilotKitProvider} from '@bdky/aaas-pilot-kit-vue3';
import CustomerServicePilot from './CustomerServicePilot.vue';
import SalesAssistantPilot from './SalesAssistantPilot.vue';

// 为不同实例创建不同的 injection key
const customerServiceKey = Symbol('customer-service');
const salesAssistantKey = Symbol('sales-assistant');

// 创建客服机器人实例
const customerServiceProvider = useAaaSPilotKitProvider({
token: 'your-auth-token-here',
figureId: '209337',
agentConfig: {
token: 'customer-token',
robotId: 'customer-robot'
}
});

// 创建销售机器人实例
const salesAssistantProvider = useAaaSPilotKitProvider({
token: 'your-auth-token-here',
figureId: '209338',
agentConfig: {
token: 'sales-token',
robotId: 'sales-robot'
}
});

provide(customerServiceKey, customerServiceProvider);
provide(salesAssistantKey, salesAssistantProvider);
</script>

7. 如何重置会话?

<script setup lang="ts">
import {nextTick} from 'vue';
import {useAaaSPilotKit} from '@bdky/aaas-pilot-kit-vue3';

const {controller, create, dispose} = useAaaSPilotKit();

const resetSession = async () => {
// 1. 销毁当前控制器
dispose();

// 2. 等待一个 tick 确保销毁完成
await nextTick();

// 3. 创建新的控制器
create();
};
</script>

错误处理

8. 如何捕获和处理错误?

<template>
<div>
<div v-if="error" class="error-message">
{{ error }}
</div>
<!-- 正常内容 -->
</div>
</template>

<script setup lang="ts">
import {ref} from 'vue';
import {useAaaSPilotKit, useAaaSPilotKitEvents} from '@bdky/aaas-pilot-kit-vue3';

const {controller, isReady} = useAaaSPilotKit();

// 监听事件并处理错误
useAaaSPilotKitEvents({
onReady: () => {
console.log('数字员工已就绪');
},
onError: (payload) => {
// 统一的错误处理
console.error('数字员工错误:', payload);
}
});
</script>

性能优化

9. 如何优化大量对话的渲染性能?

问题:对话列表过长导致页面卡顿。

解决方案 1:分页加载

<template>
<div class="conversation-list">
<ConversationItem
v-for="conversation in visibleConversations"
:key="conversation.id"
:conversation="conversation"
/>
<button v-if="hasMore" @click="loadMore">
加载更多
</button>
</div>
</template>

<script setup lang="ts">
import {ref, computed} from 'vue';
import {useConversationList} from '@bdky/aaas-pilot-kit-vue3';

const {conversationList} = useConversationList();
const visibleCount = ref(10); // 初始显示 10 条

const visibleConversations = computed(() => {
return conversationList.value.slice(0, visibleCount.value);
});

const hasMore = computed(() => {
return conversationList.value.length > visibleCount.value;
});

const loadMore = () => {
visibleCount.value += 10; // 每次加载 10 条
};
</script>

解决方案 2:虚拟滚动(推荐用于超长列表)

使用 @tanstack/vue-virtual(更现代的选择):

npm

npm install @tanstack/vue-virtual

yarn

yarn add @tanstack/vue-virtual
<template>
<div ref="parentRef" class="list-container">
<div
:style="{
height: `${virtualizer.getTotalSize()}px`,
position: 'relative'
}"
>
<div
v-for="item in virtualizer.getVirtualItems()"
:key="item.key"
:style="{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${item.size}px`,
transform: `translateY(${item.start}px)`
}"
>
<ConversationItem :conversation="conversationList[item.index]" />
</div>
</div>
</div>
</template>

<script setup lang="ts">
import {ref, computed} from 'vue';
import {useVirtualizer} from '@tanstack/vue-virtual';
import {useConversationList} from '@bdky/aaas-pilot-kit-vue3';
import ConversationItem from './ConversationItem.vue';

const {conversationList} = useConversationList();
const parentRef = ref<HTMLElement>();

const virtualizer = useVirtualizer(
computed(() => ({
count: conversationList.value.length,
getScrollElement: () => parentRef.value,
estimateSize: () => 80, // 每项高度
overscan: 5 // 预渲染项数
}))
);
</script>

<style scoped>
.list-container {
height: 500px;
overflow: auto;
}
</style>

性能对比

  • 分页加载:适合中等规模列表(< 1000 条),实现简单
  • 虚拟滚动:适合超长列表(> 1000 条),只渲染可见区域,性能最佳

调试技巧

10. 如何在开发模式下启用详细日志?

方法:启用调试模式(推荐)

<script setup lang="ts">
import {provideAaaSPilotKit, useAaaSPilotKitEvents} from '@bdky/aaas-pilot-kit-vue3';

const isDev = import.meta.env.DEV;

const provider = provideAaaSPilotKit({
token: 'your-auth-token-here',
figureId: '209337',
agentConfig: {
token: 'your-token',
robotId: 'your-robot-id'
},
env: isDev ? 'development' : 'production',
enableDebugMode: isDev
});

// 监听错误事件获取详细错误信息
useAaaSPilotKitEvents({
onError: (error) => {
console.group('🔴 数字员工错误详情');
console.error('错误代码:', error.code);
console.error('错误信息:', error.message);
console.error('严重程度:', error.severity);
console.error('元数据:', error.metadata);
if (error.stack) {
console.error('错误堆栈:', error.stack);
}
console.groupEnd();
}
});
</script>
更多调试选项

关于 enableDebugMode 配置和完整的错误代码参考,请查看 Vanilla SDK FAQ - 如何调试问题?

麦克风设备问题

所有麦克风设备相关的问题(权限、HTTPS 要求、设备占用等)及解决方案,请参考:

👉 Vanilla JS FAQ - 麦克风设备问题排查

该章节包含:

  • 如何检测麦克风是否可用
  • 6 种常见错误的详细解决方案(错误码 3100-3105)
  • 各浏览器的权限设置指南
  • Safari 特殊注意事项
  • 完整的代码示例

所有解决方案同样适用于 Vue 版本。

更多帮助

如果遇到其他问题,请:

  1. 查看控制台错误信息 - 大部分问题都会有详细的错误提示
  2. 检查网络连接 - 确保能正常访问服务
  3. 验证配置参数 - 检查 tokenrobotIdfigureId 是否正确
  4. 版本兼容性 - 确保使用的是兼容的 Vue 3 版本(>= 3.0)

联系方式:

联系我们