您可以利用面板小程序开发构建出一个基于 Ray 框架的 AI 录音转写总结设备面板。
详见 面板小程序 > 搭建环境。
首先需要创建一个产品,定义产品有哪些功能点,然后再在面板中一一实现这些功能点。
注册登录 涂鸦开发者平台,并在平台创建产品:
面板小程序的开发在 小程序开发者 平台上进行操作,首先请前往 小程序开发者平台 完成平台的注册登录。
详细操作步骤可以参考 面板小程序 > 创建面板小程序。
打开 IDE 创建一个基于 AI录音转写总结模版 的面板小程序项目,需要在 Tuya MiniApp IDE 上进行操作。
详细操作步骤可以参考 面板小程序 > 初始化项目工程。
在项目内配置您在前面 创建产品 > 步骤 11 中复制的智能体 ID,模版代码 src/pages/Home/index
:
// 智能体 ID,修改为产品配置的智能体 ID
const AGENT_ID = "xxx";
同时,需要提前配置音频的语言 Code 参数,或者实现语言 Code 选择的交互,模版代码 src/pages/Home/index
;语言 Code 格式如: en-US、zh-CN、fr-FR、ja-JP 等。
// App 启动回调
startAIAudioTranscriptionTask({
devId,
businessCode: AGENT_ID,
key: storageKey.current,
language: "zh-CN", // 录音音频语言
duration: Math.floor(duration / 1000),
template: "default", // 当前仅支持 default,不可更改
});
这样就完成了 AI 录音转写总结面板的基础配置。
录音通过 音频 API-InnerAudioContext 实现。
// 音频文件格式 固定mp3
const AUDIO_FILE_SUFFIX = "mp3";
const recorderManagerRef = useRef(null);
const [isRecording, setIsRecording] = useState(false);
const [tempAudioFilePath, setTempAudioFilePath] = useState("");
const [duration, setDuration] = useState(0); // 录音文件时长,毫秒
const tempPathRef = useRef(null); // 录音文件临时路径
useEffect(() => {
recorderManagerRef.current = ty.getRecorderManager();
}, []);
// 开始录音
const handleStart = () => {
try {
recorderManagerRef.current.start({
frameSize: undefined,
sampleRate: 16000,
numberOfChannels: 1,
format: AUDIO_FILE_SUFFIX,
success: (d) => {
tempPathRef.current = d.tempFilePath;
setIsRecording(true);
},
fail: (err) => {},
});
} catch (error) {}
};
// 结束录音
const handleStop = () => {
try {
recorderManagerRef.current.stop({
success: (d) => {
setTimeout(() => {
if (tempPathRef.current) {
setTempAudioFilePath(tempPathRef.current);
}
ty.getAudioFileDuration({
path: tempPathRef.current,
success: (res) => {
if (res.duration) {
setDuration(res.duration);
}
},
fail: (err) => {},
});
setIsRecording(false);
}, 1000);
},
});
} catch (error) {}
};
录音结束,获取录音临时文件路径后,进行文件上传并发起转录。
import {
getAIAudioTranscriptionStorageConfig,
startAIAudioTranscriptionTask,
} from "@ray-js/ray";
// 音频文件格式 固定为 MP3
const AUDIO_FILE_SUFFIX = "mp3";
// 0-未开始 1-文件上传中 2-文件上传成功 3-转录中 4-转录完成
const [transferStatus, setTransferStatus] = useState(0);
// 音频文件Key
const storageKey = useRef();
const handleStartTransfer = async () => {
try {
ty.showLoading({ title: "" });
setTransferStatus(1);
// 请求云存储授权 Token
const storageConfig = await getAIAudioTranscriptionStorageConfig({
devId,
name: `audio_${Math.round(Math.random() * 100)}_${new Date().getTime()}`,
businessCode: AGENT_ID,
suffix: AUDIO_FILE_SUFFIX,
});
const { headers, key, url } = storageConfig as any;
storageKey.current = key;
// 上传文件
const task = ty.uploadFile({
url,
filePath: tempAudioFilePath,
name: key,
header: headers,
success: (res) => {
setTransferStatus(2);
startAIAudioTranscriptionTask({
devId,
businessCode: AGENT_ID,
key: storageKey.current,
language: "zh-CN", // 录音语言
duration: Math.floor(duration / 1000),
template: "default",
})
.then(() => {
setTransferStatus(3);
})
.catch((e) => {
ty.showToast({ title: "文件转录失败" });
});
},
fail: (err) => {
console.log("Upload fail", err);
ty.showToast({ title: "文件上传失败" });
setTransferStatus(0);
},
});
task.onProgressUpdate((res) => {
console.log("Upload progress", res.progress);
});
task.onHeadersReceived((res) => {
console.log("Upload headers", res);
});
ty.hideLoading();
} catch (error) {
ty.hideLoading();
}
};
发起转录任务且接口返回成功后,需要轮询转录状态接口查询任务进行状态
import { getAIAudioTranscriptionStatus } from "@ray-js/ray";
const intervalId = useRef(null);
const getTransferProcessStatus = useCallback(async () => {
try {
// 对已上传且转录中的录音进行轮询
const transferStatusList = await getAIAudioTranscriptionStatus({
devId,
keys: storageKey.current,
businessCode: AGENT_ID,
});
// 1 - 已上传
// 2 - 转录中
// 9 - 已完成
// 100 - 错误
if (transferStatusList?.[0].status === 9) {
clearInterval(intervalId.current);
setTransferStatus(4);
} else if (transferStatusList?.[0].status === 100) {
clearInterval(intervalId.current);
setTransferStatus(0);
ty.showToast({ title: "转录失败 code:100" });
}
} catch (error) {
console.log(error);
clearInterval(intervalId.current);
}
}, []);
当轮询转录状态接口返回结果 status=9 时,表示转录、总结完成,此时可以调用接口分别查询转录、总结结果。
import { getAIAudioTranscriptionSttText, getAIAudioTranscriptionSummary } from "@ray-js/ray";
getAIAudioTranscriptionSttText({
devId,
key: storageKey.current,
businessCode: AGENT_ID,
}).then((d: any) => {
if (d?.length) {
setSttList(d);
}
});
getAIAudioTranscriptionSummary({
devId,
key: storageKey.current,
businessCode: AGENT_ID,
}).then((d: any) => {
if (d?.summary) {
setSummary(d?.summary);
}
});