ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

让启元 T1 回答自己的项目资料:资料问答 Skill 开发实战

让启元 T1 回答自己的项目资料:资料问答 Skill 开发实战

1. 前言

做完一个机器人项目,往往会留下几份开发笔记:接入方式记在一处,运行顺序放在另一处,不同版本的说明也常常同时保存。再查“这个版本要不要唤醒”“录制前需要准备什么”,仍得重新翻文件。如果能直接问身边的 T1,并在电脑上看到它查到的原文,查资料会方便一些。

这次做的就是一个资料问答 Skill。这里的 Skill 是运行在机器人上的小程序,负责收音、提示音和播报;电脑端负责把语音识别成文字,从自己的笔记里查找内容,再请 DeepSeek 组织回答。项目在已有语音能力上增加资料导入、版本查询和原文查看,真机交互以语音为主,不需要机器人移动。

下面使用已经完成真机问答的同一组资料,从实际效果讲起,再展开环境准备、资料查询和原生 Skill 的实现。最后给出完整运行顺序,并补充网页操作与真机问答演示。

2. 先看资料问答怎样使用

2.1 给 T1 准备什么资料

演示资料来自前一个技术面试官项目,包括两份开发笔记和一份录制检查表,全部随源码保存在 sample_docs 目录。先看两份笔记里与本次提问直接有关的原句:

资料

笔记中的内容

技术面试官开发笔记 V2

机器人完整播完问题并响两声提示音后,使用者直接回答,不需要再次叫唤醒名字。

技术面试官开发笔记 V1

使用者需要先唤醒机器人,再说出回答。

V1、V2 是这两份笔记的版本,分别描述技术面试官项目早期和后来的接法。它们不是 T1 固件版本。选择 V2,就是让程序按 V2 笔记回答;问旧版,则应重新查 V1,不能把两份笔记的句子混在一起。

第三份 recording-guide-v2.txt 记录录制准备和使用顺序,归在 V2 下。它和 V2 开发笔记可以共同参与一次查询。这样既能问“要不要唤醒”这种具体问题,也能查询同版本资料中其他已记下的步骤。

2.2 问完整问题,再接着追问

真机运行时,先选择 V2 并建立会话。听到两声提示音后,说:

第二版需要唤醒机器人吗?

T1 当时的实际回答是:“不需要。机器人完整播完问题并响两声提示音后,直接回答即可,不用再叫唤醒名字。”电脑页面同时保留 V2 标签和所引用的原句。点击“查看原文”,可以展开相应笔记,直接看到回答依据。

等它答完并再次响提示音,再问“那旧版呢?”。这一轮回答变成:“需要。资料提到早期方案中,使用者要先唤醒机器人,再说出回答。”程序沿用上一句“需要唤醒机器人吗”这个问题,改查 V1;使用者不用把整句话再说一遍。

这两轮来自同一次真机连续问答,原始识别文字与采用的问题一致,没有人工改字,现场也确认了完整播报和没有原来的闲聊插话。后面还会单独演示“机器人官方售价是多少”:示例资料没写价格,程序就说明资料不足,不补一个数值。这个问题在另一场 V2 会话中完成,不与前两轮拼成一次历史运行。

2.3 一句话经过哪些步骤

以“第二版需要唤醒机器人吗”为例,声音先从 T1 麦克风进入程序,在电脑上识别成文字。程序把“第二版”对应到 V2,再从 V2 笔记中找与唤醒有关的段落。DeepSeek 根据这些段落生成回答和引用,最后由 T1 读出回答,网页展示原文。

开发时可以把它分成两部分。先让电脑能够根据资料回答文字问题,再把真机收音和播报接进来。两边共用同一个问答核心:网页输入和麦克风识别出的文字,最后都会进入 Knowledge.ask()。这样排查时也有顺序,资料查错了就先看查询结果,声音没收完整则检查语音采集,不必同时改动所有环节。

这里没有把模型和资料库都塞进机器人。T1 保留现场交互,开发电脑运行识别模型、文本检索和网页;当前问题、检索到的片段及同版本少量问答历史会发送到 DeepSeek。自己的资料是否适合发给文本服务,需要在导入之前确定。

3. 准备开发环境与项目资料

3.1 先分清使用哪一种开发方式

启元提供的资料里,有 ROS2 SDK,也有原生 Skill 的开发与调试方式。两者的运行入口不同,环境不能混着装。T1 ROS2 SDK 快速入门推荐 Ubuntu 22.04 与 ROS2 Humble;本文使用的是机器人机内已有的 Lumina 运行时,让它执行 Skill 包中的 Python 程序,电脑另外运行资料服务。

位置

本文使用的环境与职责

开发电脑

Windows、Python 3.12.14,运行资料服务、中文识别、DeepSeek 请求与网页

T1 机器人

机内 Python 3.10.12、已有 Lumina 服务,执行原生 Skill 并访问机器人音频能力

连接

电脑与 T1 局域网互通,通过已核验主机指纹的 SSH 建立开发连接

PrimeHub Dev Kit 手册介绍了原生 Skill 结构与 robot 对象;官方 PrimeConsole 下载页可获取调试工作台及资料包。本文按已经使用的源码调试入口安装并启动任务。机器人语音接管部分还适配了本台设备的机内对象和消息类型,换固件时需先核对这一部分,不能把它当作所有 T1 都相同的公开方法。

3.2 安装电脑端依赖

取得项目源码后,进入 projects/02-private-knowledge。下面的命令在 Windows CMD 中执行,先建立独立环境,再安装项目依赖:

暂时无法在飞书文档外展示此内容

激活后,命令提示符前会出现 (.venv),随后使用的 python 就是这个环境里的解释器。--quiet 只是减少安装日志,最后的 pip check 用于检查依赖关系。本次安装后显示 No broken requirements found,同时核对了 Paramiko、Vosk、FunASR ONNX 和 ONNX Runtime 的版本。

电脑还需要准备三份本地模型。Paraformer 用来把中文录音转为文字;FSMN VAD 判断一句话是否已经说完;Vosk 保留为同段录音的识别对照。按 README 的文件清单下载后,放成下面的目录,或在启动命令里换成自己的路径:

暂时无法在飞书文档外展示此内容

Paraformer 和 FSMN 目录需要相应的 model_quant.onnx、配置及辅助文件,不能只有一个空文件夹。具体文件名及模型链接见 README 的运行前准备。DeepSeek Key 可保存在项目的 private/deepseek.key,也可以使用当前终端的 DEEPSEEK_API_KEY 环境变量;它由电脑读取,不写进机器人技能包。

3.3 启动资料控制台

仍在这个 CMD 窗口执行:

暂时无法在飞书文档外展示此内容

18882 是本机网页端口。vad 表示说完并停顿后结束收音,20 是一次收音窗口的上限,review-seconds 0 表示默认直接采用识别文字。命令中的 ^ 是 CMD 的续行符,不要把它换成 PowerShell 的写法。

终端出现本地地址后,浏览器打开 http://127.0.0.1:18882/。先确认页面能够加载、识别模型就绪、资料列表里有三份示例文本,再进行下一步。

此时打开的是开发电脑上的页面,127.0.0.1 不是 T1 的设备地址。浏览器能显示控制台,说明电脑服务可以访问;后面部署命令的 --host 才填写机器人在局域网中的地址。把两个地址分清,可以避免拿网页端口去测试机器人是否在线。

控制台的版本选择决定本轮从哪组资料查找内容,“新建问答”建立一次会话,问题与回答显示在当前会话中。真机尚未启动时,也可以先用文字输入核对资料查询。文字输入和麦克风输入会分别记录,方便后面确认一个答案究竟来自哪次操作。

3.4 导入自己的 Markdown 或 TXT

示例文本由 sample_docs/index.json 自动载入,复现本文时不必重新添加。换成自己的笔记时,展开“添加自己的资料”,填写资料 ID、标题、版本,再选择文件或粘贴正文。

资料 ID 用来关联同一份笔记,例如两版开发笔记都叫 interview-guide;标题用于在回答旁边辨认出处;版本用于限定查询范围。初次导入前把这三个字段定好,后面引用就能找到确定的一份正文。

版本标签也需要在同一批资料里保持一致。例如开发笔记和录制检查表都属于当前方案,就一起归到 V2。程序不会按文件修改日期自动推断最新版,也不会把只有 V1 的段落补进 V2。整理资料时先定版本,再提问,结果才容易核对。

目前支持 UTF-8 Markdown 和 TXT,单份正文不超过 64 KiB。同一资料、同一版本和相同正文重复导入,会保持原记录;内容变了则应使用新版本。这样昨天回答所引用的那句话,今天还能查回原来的内容。PDF 或网页资料需要先整理成文本,本项目没有把文档提取一并实现。

4. 实现“按资料回答”的核心逻辑

4.1 先把笔记整理成可检索的段落

阅读源码时,先找到这几个文件,不必从语音底层一路追起:

文件

本例中负责的工作

knowledge_core.py

导入资料、查询版本、检索原文与生成回答

server.py

给网页和原生 Skill 提供同一套问答接口

buffered_asr.py

在电脑上完成整段中文识别与句尾检测

skill/src/main.py

串起提示音、收音、请求答案和播报

run_robot.py

打包、建立开发连接、安装及启动原生任务

先读资料核心,再看原生入口如何调用它,能够把问题分开:即使暂时没有机器人,仍可检查资料是否正确导入、同一问题在两版笔记里找到什么;接上 T1 后,再检查声音和播报是否完整。

资料由 knowledge_core.py 中的 Knowledge.import_document() 接收。导入时先检查文件和正文,再调用 split_chunks(),按照 Markdown 标题与段落拆分内容。每段同时保存标题路径、原文行号和版本,后面既能检索,也能定位出处。

这里没有逐句随意切开正文。例如 V2 笔记中的“提示音后直接回答”带有使用条件,应该与同一段的上下文一起交给模型。保留段落,可以减少只拿到“不需要”三个字、却不知道在什么条件下不需要的问题。较长段落会继续按长度拆分,完整文档仍单独保存,方便页面展开。

资料并不是发一次问题就重新从文件读一遍。电脑将规范化文本和段落存进本地 SQLite 数据库,后续查询在这份已导入内容上进行。新增版本时保留旧文本,因此一次问答始终能对应到当时使用的资料。

4.2 在选定版本里找相关内容

Knowledge.retrieve() 接收问题和资料版本,先限定 V2,再给 V2 下的段落计算相关程度。本项目资料量小,采用中文相邻双字片段与英文标识符的词法匹配,没有额外部署向量数据库。返回的是最多四段值得进一步查看的原文,不是最终答案。

可以先运行下面的独立示例,看看“需要唤醒机器人吗”会找到哪些内容。它使用临时数据库,只查本地文本,不请求模型,也不连接机器人:

暂时无法在飞书文档外展示此内容

输出里应同时看文件名、版本和正文。即使结果里出现两段不同资料,只要它们都属于 V2,也可能共同说明同一个操作;但某段只提到“机器人”却没有回答唤醒问题,就还不能据此下结论。若没有找到预期原句,先检查导入正文和所选版本,再调整问法。

检索的作用是缩小阅读范围。如果问题与“技术面试官”有关,就先找到这个项目的相关段落;段落里是否真的回答了问题,还要继续判断。因此,代码不会把得分第一的原文直接当作答案读出。具体特征、分数和阈值可以在调试时查看,正常使用先关注找到的原文是否相关。

4.3 把问题和原文交给 DeepSeek

网页和语音入口最后都调用 Knowledge.ask()。它先检查会话是否仍在进行,再进入 _ask_snapshot():识别问题中的版本,补全短追问,调用检索,最后请求模型。

第一次说“第二版需要唤醒机器人吗”,程序得到 V2 和“需要唤醒机器人吗”。下一次只说“那旧版呢”,就沿用刚才的实质问题,改用 V1 重新检索。这个补全来自会话保存的上一条问题,不是把上一句答案里的“不需要”改成“需要”。

找到段落后,发送给 DeepSeek 的数据包含当前问题、所选版本、相关原文,以及同版本最近少量已回答内容。提示要求只依据这些资料回答;片段没有提供被问事实时,就说明资料不足。模型负责把原文组织成可播报的回答,不负责替程序选一个未经查询的资料版本。

历史问答也要跟随当前资料版本。上一轮问 V2、下一轮改查 V1 时,不能把 V2 的答案继续当作 V1 的事实。这里保留的是“上一轮问了什么”,让短句能够补全;用于回答的原文仍要从本轮版本重新取得。这个区别很小,却决定了连续问答是在继续查资料,还是只在改写上一句话。

项目通过 DeepSeek JSON Output接收结构化结果。其中 answer 是要读出来的话,citations 保存答案使用的原句和片段编号,status 区分正常回答与资料不足。先把这几项分开,网页就能同时显示回答和出处,机器人只播报适合听的正文。

4.4 给回答留下可以打开的出处

模型返回内容后,Knowledge.validate_answer() 做三项检查:引用的片段确实来自本次检索;引用版本与当前选择一致;引用文字逐字存在于原文。有答案却没有引用,或者引用了一段不存在的文字,都不能直接保存为正常回答。

通过检查后,程序从原始候选中补上资料标题、文件名和行号。模型只负责指出用了哪一段、哪一句,不自由填写一个看似可信的来源。网页点击“查看原文”时,再通过 get_document() 取回同一版本的完整正文,并高亮对应文字。

回答旁边的信息

读者用它核对什么

资料标题与版本

回答使用的是哪份笔记

原文引句

结论直接依据哪一句话

行号与完整正文

引句前后有没有适用条件

这些检查能防止部分错引文,仍需要人阅读原文判断结论有没有概括过头。查询重要内容时,应核对原句讨论的是不是同一个对象,而不是只看回答说得顺不顺。更细的引用测试与检索分析放在 FAQ,不影响这里的使用流程。

模型服务出错时,程序也要与“资料没写”分开处理。前者需要检查接口或重新尝试,后者需要补充资料;如果都回复“资料不足”,使用者就分不清是笔记缺内容,还是服务根本没有正常工作。本项目在生成请求失败时保留错误,不把这一轮保存为正常回答。

5. 将问答程序接入 T1 Skill

5.1 技能包里装什么

原生 Skill 的最小结构是 cfg/pkg.yaml 和 src/main.py。Lumina 启动入口脚本时,会注入全局对象 robot;脚本通过它使用播报和音频能力,不在入口里自行 import robot。这个约定可对照 Dev Kit 运行时契约。

本项目把语音流程、采集器和模式适配分别维护在 skill/src。run_robot.py 构建时将这些模块合并成入口,连同包配置写入 RPK。机内程序只使用标准库和现有运行时,Paraformer、检索数据库及 DeepSeek Key 留在电脑。

暂时无法在飞书文档外展示此内容

先在第二个 CMD 窗口进入同一项目并激活环境,检查仓库附带的包和部署参数:

暂时无法在飞书文档外展示此内容

仓库附带的包在 artifacts;截图检查的是本机构建目录 dist 下的同一份包。压缩包检查与 --help 都只在电脑执行。真正安装、启动任务和收音播报,还要在连接 T1 后分别完成。RPK 结构与打包方式可参考工具包的 pack.py。

5.2 把文字回答变成机器人播报

问答服务返回后,原生入口从结果中取出 answer,把这段文字交给播报函数。机器人发声用到的是 Lumina 注入的 robot 对象,关键调用都在 skill/src/main.py 中:

实际调用

在本例中的作用

robot.play_tts(text)

提交要说的文字,检查请求是否被接受

robot.get_audio_play_state()

读取播放状态与所属技能,确认本轮确实开始播放并进入稳定空闲

robot.play_audio_file(path, pkg_name=PACKAGE, priority=6)

播放两声本地 WAV 提示音,告诉使用者可以提问

speak() 先提交文字,再在循环里读取音频状态。本项目使用的运行时中,状态值 1 表示正在播放,2 表示空闲;程序先观察本技能的播放,再等待连续空闲。这样才能判断这一句已经播完,随后安排提示音和收音。若播报被其他语音抢占,或者没有观察到完成,程序会记录原因并停止本轮。

长答案由 speak_answer() 按句子边界分成不超过 100 字的片段,再依次交给 speak()。这个长度是本项目的分段策略。完整逻辑可直接查看原生入口源码,资料服务、播报函数和入口代码分别负责查询、发声和流程控制。

下面这段实际入口把两端接在一起:向电脑的 /api/ask 请求资料答案,取回回答与引用,最后调用 speak_answer(result['answer'])。前面第 4 章的资料查询,到这里才变成 T1 扬声器里的声音。

两声提示音同样需要检查播放状态。完整播报、提示音、收音依次进行,使用者听到提示后再说一句。音频能力可对照官方 SDK 的接口说明和 Dev Kit 运行时契约;本文执行的具体方法名来自后者的 robot 对象。

5.3 麦克风音频怎样送到电脑

采集部分参考官方 SDK 的 record_audio.py,调用机内已有的 parec 工具,从机器人本机 PulseAudio Unix socket 读取内置麦克风。本次设备是八通道、48 kHz,代码混合第 5、6 通道并转换为 16 kHz 单声道,再通过开发连接发送给电脑识别。

SdkMicrophone 管理这一段采集,电脑端 BufferedAsr 判断句尾并识别完整录音。识别完成后,原生入口将文字交给 /api/ask;电脑返回答案,入口再调用上一节的播报函数。底层音频与资料查询因此可以分开检查:前者要拿到完整问题,后者要找对版本和原文。

为了避免原来的聊天助手同时抢答,程序在交互阶段管理临时语音接管,并在采集收尾时恢复、读回状态。当前 only_voice 适配写在 voice_mode.py,依赖本台机内服务;它与资料检索逻辑分开。语音底座的前期接入过程可参照技术面试官项目,本篇不再重复整套收音调试。

6. 在真机上运行同一组问答

6.1 建立连接并启动原生任务

先确认 T1 已开机联网,电脑能够通过局域网访问它。手机 App 显示蓝牙连接,不能代替这里的开发连接。先取得本台 T1 可用的 SSH 登录资料,并核验主机指纹,再运行部署器:

暂时无法在飞书文档外展示此内容

大写参数换成自己的设备信息。--known-hosts 指向已核验的主机指纹文件,用于确认连接对象;密码由终端交互读取,使用 SSH 私钥时增加 --key-file。部署器通过 SSH 访问机器人本机的 Lumina 服务,向 /packages/script 装入主 Skill 与恢复 Skill,并建立回环转发,让机器人能访问电脑的 18882 服务。它采用的源码调试方式可对照 deploy_test.py。

--prepare-only 会安装技能,但暂不开始问答。看到 ready: true 后,先在网页选 V2、点击“新建问答”,再回到部署终端输入 run。这时程序向 /tasks 发起任务,机器人开始执行原生入口。后面要分别看是否听到开场、提示音、回答及结束提示,而不只看安装返回值。

本文完成的是这条开发入口的安装和真机运行,没有把 PrimeConsole 图形导入或 PrimeHub 技能市场上架算作已经完成。

机器人脚本里的 127.0.0.1 和浏览器里的本地地址原本指向不同设备。本项目通过 SSH 回环转发,把机器人发到 18882 的请求送回电脑端服务,所以原生入口可以调用同一套问答接口。连接中断后,这条转发也随之停止;运行期间保留控制台和部署终端,等任务实际结束再关闭连接。

6.2 完成两轮连续提问并查看原文

第一段按以下顺序操作,保留机器人声音、使用者提问和电脑页面之间的对应关系:

顺序

实际操作

需要看到或听到的结果

1

在 V2 新会话中启动原生任务

开场完整,随后响两声提示音

2

提示音后问“第二版需要唤醒机器人吗”

回答不需要;页面显示 V2 和原文引用

3

等回答结束及下一次提示音后问“那旧版呢”

回答早期方案需要唤醒;本轮资料变为 V1

4

第二答播完后,在页面点击“结束问答”

等任务结束、robot.active_session 为空并确认语音模式恢复

5

展开这两轮的“查看原文”

分别看到 V2 第 9 行和 V1 第 7 行对应的句子

6

在部署终端输入 close

关闭本次开发连接,保留技能和已保存的问答

每次原生任务最多使用四个收音窗口。没有选版本时,程序可能先问要查哪一版,这段澄清也会占一个窗口;复现上述两问时先选 V2,流程更直接。每个窗口只说一句,等下一次提示音再继续。

第二答后先结束问答,再查原文,可以避免翻阅时第三个收音窗口等待超时。结束会话会保留当前页面记录;查完后再新建下一场会话。回答措辞可能随模型返回变化,核对重点是版本、核心事实和引文,不要求逐字重演历史播报。

6.3 另开一轮查询资料没有提供的内容

结束前一段后,重新建立开发连接,在页面选择 V2 并新建问答,再启动一次原生任务。提示音后问“机器人官方售价是多少”。这组笔记只记录项目接入,没有售价;保存的真机结果为资料不足,引用列表为空。

这个例子检查的是资料范围。给机器人导入自己的笔记,并不意味着它就有了与项目有关的全部事实。想让它回答价格,应先加入确实包含价格及适用条件的资料,再进行新的验证。已有问答仍保留当时使用的版本和原文,不随新增资料改写。

6.4 网页操作与真机演示

这次选择“技术面试官更新后的开发笔记”,把问题上限设为 1。开始真机问答后,直接说出“模型密钥应该保存到哪里”。T1 根据笔记回答:密钥留在电脑本地,不放进机器人 Skill,也不放进公开仓库;网页同步保存答案、原文引用和本次知识整理。

网页操作与结果

资料专家_网页操作与结果

真机语音演示

资料专家_真机语音演示

7. 联调中遇到的问题

7.1 原来的聊天助手也在回答怎么办

早期直接使用原生聊天转写时,原有助手也会处理同一份语音,出现插话或抢占播放。当前方案将原生 Skill 的采集与播报作为一个受控交互过程,临时管理语音接管,采集结束后检查恢复状态。资料问答只接受这一轮送来的文字,不另拼原聊天结果。

排查时先看问题有没有完整收到,再看问答结果,最后确认播放有没有被其他语音打断。不能因为电脑已经生成答案,就忽略现场只听到半句的情况。第一篇与本篇也要串行使用,不能同时让两个 Skill 占用相同的语音状态。

7.2 说完一句为什么还要等很久

最初的固定录音时间加上文字确认,会让短问题也等待较长时间。现在默认采用句尾检测,说完并停顿约 800 毫秒后结束本轮,上限 20 秒;识别完成后直接采用文字。需要人工核对技术词时,再打开页面的十秒确认选项。

提示音前的等待则通过提前准备麦克风、确认上一句播完后直接播放提示音来减少。收音等待、识别耗时和模型请求是不同环节,调试时分别看;具体测量与对照保存在等待时间记录,不把少数轮次当作普遍时延保证。

7.3 当前录制前还需要核对什么

完整问题、旧版追问和资料不足已有真机记录。短结束口令曾经漏收,当前代码增加了录音流低延迟请求,但这项修改后的完整“结束问答”尚未完成真机复测。录制当前案例可以先用页面按钮结束,并等待任务结束和语音恢复,不能把安装成功当作短口令已经修好。

引用检查、版本处理和页面接口另有本地受控测试,详细范围见真机验证记录与 FAQ。遇到问题时按实际发生的环节查看,不需要先读完全部日志和测试脚本才能使用这个 Skill。

8. 总结

这次给 T1 增加的能力,是把自己已有的项目笔记接进语音问答。电脑保存资料,T1 接收问题并播报回答;问到另一版,就重新查对应版本;想确认答案,则在页面打开原文。资料查询、现场交互和结果核对由此能连成一次实际操作。

工程目前适合小型 Markdown、TXT 资料集,运行时需要电脑参与识别和文本问答。后续替换成自己的笔记,可以先用文字问题检查内容,再用同样的问题接真机验证。完整源码、示例资料、技能包和运行说明均保存在本项目目录,前面的操作顺序也可用于替换自己的资料后复现问答。

返回列表