- 人工智能
- 语音
- 交互助手
- 智能硬件
【免费下载链接】mycroft-core
Mycroft Core, the Mycroft Artificial Intelligence platform.
本篇技术指南以 mycroft-core 官方 API 文档 doc/source/mycroft.util.format.rst 为骨架,深入讲解语音助手技能(Skill)开发中最常用的格式化工具模块mycroft.util.format。该模块的核心使命是把原始数字、秒数、时间戳转化为"人话"——既能拼读成 TTS 语音,也能输出为屏幕显示文本,并完整支持多语言本地化。读完本文,你将掌握 9 个公开 API 的完整签名、参数语义、输出示例与源码级行为细节,能够直接在技能中输出地道的自然语言内容。
模块定位:为什么 Mycroft 需要一套"人性化"格式化工具
语音交互与屏幕文本有一个根本差异:机器看到的数值(163秒、13:22、1.435634)与人类听/读习惯的表达("two minutes forty three seconds"、"one twenty two"、"1.436")完全不同。mycroft.util.format正是为解决这一问题而存在。
从 mycroft/util/format.py 顶部的模块 docstring 可以看到官方定位:
The mycroft.util.format module provides various formatting functions for things like numbers, times, etc. The focus of these formatting functions is to create human digestible content either as speech or in display form. It is also enables localization.
也就是说,该模块有两条设计主线:
- 双输出形态:几乎所有函数都提供
speech开关,True时输出可供 TTS 朗读的自然语言(如"one minute one second"),False时输出适合屏幕/日志的紧凑文本(如"1:01"); - 本地化优先:全部函数通过
lang参数接受 BCP-47 语言代码,默认语言来自 lingua-franca 的默认设置(详见下文"语言与本地化"小节)。
与 lingua-franca 的分工
理解该模块的关键在于其分层架构:mycroft/util/format.py 中明确注释了来源——绝大多数格式化逻辑来自独立的 lingua-franca),Mycroft 层只是做包装(wrap)与聚合:
# These are the main functions we are using lingua franca to provide from lingua_franca import get_default_loc from lingua_franca.format import ( join_list, nice_date, nice_date_time, nice_number, nice_time, nice_year, pronounce_number )其中nice_duration/nice_duration_dt是仅有的两个在 Mycroft 侧自行实现的时长格式化函数(源码中的TODO 21.08 - move nice_duration methods to Lingua Franca注释表明它们计划在未来迁移回 lingua-franca)。因此从本项目视角看,整个模块的公开 API 由 9 个函数加 1 个枚举组成:
| API | 功能 | 实现来源 |
|---|---|---|
join_list | 用连接词拼接列表(and/or) | lingua-franca |
nice_date | 日期人性化 | lingua-franca |
nice_date_time | 日期+时间人性化 | lingua-franca |
nice_duration | 秒数/timedelta → 时长表达 | Mycroft 自实现 |
nice_duration_dt | 两个 datetime 的差值 → 时长表达 | Mycroft 自实现 |
nice_number | 数字 → 人性化数字 | lingua-franca |
nice_time | 时间人性化 | lingua-franca |
nice_year | 年份人性化 | lingua-franca |
pronounce_number | 数字 → 完整拼读 | lingua-franca |
TimeResolution | 时长输出精度枚举 | Mycroft 自实现 |
说明:
mycroft.util.format还会额外导出expand_options(来自 lingua-franca 的date_time_format模块),它被 mycroft/dialog/dialog.py 和 mycroft/skills/skill_data.py 用于展开.dialog/.voc文件中的(a|b)可选分支语法,属于技能资源渲染的基础设施。
数字格式化:nice_number 与 pronounce_number
pronounce_number:把数字"读"出来
pronounce_number是语音输出最核心的工具,签名(按 lingua-franca 0.4.x 及测试用例确认的参数)为:
pronounce_number(number, lang=None, places=2, short_scale=True, scientific=False)number:整数、浮点数或可转换为数字的字符串;lang:BCP-47 语言代码,默认取 lingua-franca 默认语言;places:小数保留位数(语音读法按位拼读);short_scale:是否使用短级数制(short scale,如 billion/trillion),False时使用长级数制;scientific:是否以科学计数法朗读。
从 test/unittests/util/test_format.py 的测试夹具可以归纳出完整行为:
基础整数:0 → "zero",1 → "one",15 → "fifteen",27 → "twenty seven",33 → "thirty three"。
负数:统一加"minus "前缀,-1 → "minus one",-33 → "minus thirty three"。
小数(按位拼读):1.234 → "one point two three";places参数控制拼读位数——21.234在places=1/0/3/4/5下分别输出"twenty one point two"、"twenty one"、"twenty one point two three four"(注意places超过实际小数位不会报错,会按实际位数截断)。
大数(长/短级数制):1456 → "fourteen fifty six"(百位以上按口语习惯分段);95505896639631893在short_scale=True时读作"ninety five quadrillion, five hundred and five trillion, ...",而short_scale=False时切换为长级数制"ninety five thousand five hundred and five billion, ..."。
科学计数法:scientific=True时,33 → "three point three times ten to the power of one",299792458 → "two point nine nine times ten to the power of eight";配合places=6可控制有效数字位数。该能力对技能朗读光速、阿伏伽德罗常数等场景非常实用。
nice_number:保留可读性的"口语化数字"
nice_number与pronounce_number的分工在于:它保留数字本身的可读性,只在合适时切换为分数表达。其行为可用 test/unittests/util/test_format.py 中的NUMBERS_FIXTURE_EN夹具完整说明:
| 输入 | speech=True 输出 | 说明 |
|---|---|---|
1.435634 | '1.436' | 默认保留 3 位小数 |
2/5.0 | '2'/'5' | 整数值去尾零 |
0.5 | 'a half' | 二分之一用口语 |
1.333 | '1 and a third' | 三分之一 |
2.666 | '2 and 2 thirds' | 三分之二 |
0.25 | 'a forth' | 四分之一(注意 lingua-franca 的拼写为 forth) |
0.75 | '3 forths' | 四分之三 |
3.1 | '3 and a tenth' | 十分之一 |
0.05 | 'a twentyith' | 二十分之一 |
核心参数:
denominators:允许使用的分母列表,例如nice_number(5.5, denominators=[1, 2, 3])输出'5 and a half';若传入的分母列表无法表达该小数(如nice_number(2.333, denominators=[1, 2])),则退回普通小数'2.333';speech=False时输出显示形态,如nice_number(6.777, speech=False) → '6 7/9',nice_number(6.0, speech=False) → '6';lang传不支持的代码(如'as-fd')时安全降级为原数字字符串'5.5',不会抛异常。
在 mycroft/util/init.py 中,nice_number被提升为mycroft.util包的顶层导出,技能中可直接from mycroft.util import nice_number使用。
时间与日期格式化:nice_time、nice_date、nice_date_time、nice_year
这四个函数均直接包装 lingua-franca 的同名实现,负责把datetime.datetime对象转成符合人类语言习惯的表达。
nice_time:口语化报时
nice_time(dt, lang=None, speech=True, use_24hour=False, use_ampm=False)use_24hour控制 24 小时制,use_ampm控制 AM/PM 后缀,两者可独立组合。以 test/unittests/util/test_format.py 的用例为例,datetime(2017, 1, 31, 13, 22, 3)的输出矩阵:
| 参数组合 | 输出 |
|---|---|
| 默认(speech) | 'one twenty two' |
use_ampm=True | 'one twenty two p.m.' |
speech=False | '1:22' |
speech=False, use_ampm=True | '1:22 PM' |
speech=False, use_24hour=True | '13:22' |
use_24hour=True | 'thirteen twenty two' |
值得注意的语言学细节:
- 整点:
13:00 → 'one o'clock'(口语)/'1:00'(显示);24 小时制整点读'thirteen hundred'; - 零分钟:
13:02 → 'one oh two'(分钟个位前补 "oh"),24 小时制下则读'thirteen zero two'; - 午夜:
00:02 → 'twelve oh two a.m.',显示为'12:02 AM',24 小时制显示'00:02'; - 特殊时刻:
12:15 → 'quarter past twelve'(一刻过),5:30 → 'half past five a.m.'(半点),1:45 → 'quarter to two'(差一刻)。
这些规则说明该函数不是简单翻译数字,而是遵循英语报时的惯用表达。
nice_date / nice_date_time / nice_year
nice_date(dt, lang=None, now=None) nice_date_time(dt, lang=None, now=None, use_24hour=False, use_ampm=False) nice_year(dt, lang=None, bc=False)now参数用于相对日期表达(如"今天/昨天"),传入参考时间点;测试中通过now=None表示以当下为锚点;nice_date_time额外接收use_24hour/use_ampm,透传给内部的nice_time;nice_year的bc参数控制公元前年份表达。
这些函数的正确性由多语言测试夹具驱动:test/unittests/util/test_format.py 会读取date_time_format.config_path下每种语言的date_time_test.json文件,逐一断言nice_date、nice_date_time、nice_year的输出,并额外做全量扫描——遍历一年中 368 天、以及 0~9999 年间的所有年份,确保任何语言下都不会产生空输出。这套"数据驱动 + 全量扫描"的双重测试策略,是这四个函数可靠性的直接证据。
join_list:用连接词拼列表
join_list(list_to_join, connector, sep=None, lang=None)把列表元素用connector(and/or)连接为自然语言序列,sep可自定义元素间分隔符(默认逗号)。测试用例(test/unittests/util/test_format.py)展示:
join_list(["a", "b"], "and") → 'a and b'join_list(["a", "b", "c"], "or") → 'a, b or c'(牛津逗号)join_list(["a", "b", "c"], "or", ";") → 'a; b or c'- 空输入
None/[]返回空字符串
技能中一个典型用法出现在 mycroft/skills/mycroft_skill/mycroft_skill.py 的ask_yesno实现:它用join_list(options, "or", lang=self.lang) + "?"拼接候选选项向用户发问,用pronounce_number(idx + 1, self.lang)朗读编号——这正是两个格式化 API 在真实技能代码中的协同实例。
时长格式化:nice_duration 与 nice_duration_dt
这是本模块中唯一由 Mycroft 侧完整自研实现的部分(mycroft/util/format.py),逻辑最复杂、参数最丰富,也是技能中计算等待时间、倒计时、播放时长时的首选工具。
TimeResolution:输出精度的"下限"
两个时长函数都接受resolution参数,其类型为本模块定义的枚举(mycroft/util/format.py):
class TimeResolution(Enum): YEARS = 1 DAYS = 2 HOURS = 3 MINUTES = 4 SECONDS = 5 MILLISECONDS = 6语义是"输出精度的下限":例如resolution=TimeResolution.HOURS时只输出小时及以上单位;resolution=TimeResolution.MINUTES时小于 1 分钟的结果会显示为'under a minute'(语音)或'0m'(显示)。文档明确提示两点限制:
nice_duration默认不会产生毫秒,只有显式传入TimeResolution.MILLISECONDS才会输出毫秒;nice_duration_dt不支持TimeResolution.MILLISECONDS,传入时会静默回退到TimeResolution.SECONDS。
nice_duration:把秒数变成"人话"
nice_duration(duration, lang=None, speech=True, use_years=True, clock=False, resolution=TimeResolution.SECONDS)duration:秒数(int/float)或datetime.timedelta;speech=True输出口语('one minute one second'),speech=False输出M:SS/H:MM:SS/Dd H:MM:SS时钟格式('1:01');use_years=True时按年/天拆分(如366 天 → '1 year 1 day'),False时输出总天数;clock=True强制输出数字时钟样式,且只对显示输出生效、对 DAYS/YEARS 精度无影响。
核心输出示例(均来自 test/unittests/util/test_format.py):
| 输入 | speech=True | speech=False |
|---|---|---|
1 | 'one second' | '0:01' |
61 | 'one minute one second' | '1:01' |
3600 | 'one hour' | '1h' |
5000 | 'one hour twenty three minutes and twenty seconds' | '1:23:20' |
500000 | 'five days eighteen hours fifty three minutes and twenty seconds' | '5d 18:53:20' |
0 | 'zero seconds' | '0:00' |
clock=True与resolution的组合效果:nice_duration(60, resolution=TimeResolution.HOURS, clock=True, speech=False) → '0:01:00',nice_duration(1, resolution=TimeResolution.MINUTES, clock=True, speech=False) → '0:01',即补齐了原本会被省略的前导零位。
毫秒处理是内部实现的一大亮点(mycroft/util/format.py):_duration_handler会把 float 秒数的小数部分拆出作为毫秒——语音模式下保留 2 位(0.25s → 'zero point two five seconds'),显示模式下保留 3 位并右补零(nice_duration(0.25, speech=False, resolution=TimeResolution.MILLISECONDS) → '0:00.250')。
nice_duration_dt:两个时刻之间的时长
nice_duration_dt(date1, date2, lang=None, speech=True, use_years=True, clock=False, resolution=TimeResolution.SECONDS)接受两个datetime.datetime,内部先取max/min保证顺序(mycroft/util/format.py),再交给共享的_duration_handler。典型结果:
nice_duration_dt(datetime(2019, 1, 1), date2=datetime(2018, 1, 1)) → 'one year'nice_duration_dt(..., use_years=False) → 'three hundred and sixty five days'nice_duration_dt(datetime(2019, 12, 25, 20, 30), date2=datetime(2019, 10, 31, 8, 00), speech=False) → '55d 12h 30m'
该函数有两个值得注意的边界行为(mycroft/util/format.py):
- 类型必须一致:
date1与date2类型不同会抛出Exception(测试test_nice_duration_dt用pytest.raises(Exception)验证); - 非 datetime 的第二参数被忽略:传入数值型第二参数时发出
UserWarning并忽略之(测试用pytest.warns(UserWarning)验证); - 自动降低精度:当两个 datetime 的秒、分、时均为 0 时,会逐级把
resolution上调为 MINUTES/HOURS/DAYS,避免输出一长串无意义的00。
此外,两个 datetime 相差整年时还会通过calendar.leapdays校正闰年影响(mycroft/util/format.py),例如 2018-01-01 到 2019-01-01 在use_years=True下输出'one year'而非'365 days'。
语言与本地化:lang 参数与默认语言设置
所有格式化函数都接受lang(BCP-47 语言代码,如'en-us'、'es-mx'),不传时使用get_default_loc()的返回值(mycroft/util/format.py)。默认语言由 lingua-franca 的全局状态决定,Mycroft 通过两个途径维护它:
- 配置启动:mycroft/skills/main.py 在技能进程启动时读取
Configuration中的lang字段(默认'en-us')并调用load_languages([lang_code, "en-us"])加载对应语言包; - 运行期切换:mycroft/configuration/locale.py 提供
set_default_lf_lang(lang_code),例如 mycroft/skills/intent_service.py 在收到消息时会按消息携带的语言调用它,实现多语言会话的动态切换。
因此,在技能代码中你可以不传lang(跟随全局),也可以显式传入self.lang(技能实例的语言)保证与技能当前语言一致——mycroft/skills/mycroft_skill/mycroft_skill.py 正是这样做的。
技能开发实战:格式化 API 的典型组合
结合上文,一个"播放进度播报"类的技能可以这样组合使用:
from datetime import datetime, timedelta from mycroft.util.format import nice_duration, nice_time, join_list # 1) 把播放进度(秒)播报成口语时长 elapsed = nice_duration(5000) # 'one hour twenty three minutes and twenty seconds' remaining = nice_duration(timedelta(seconds=3660), speech=False) # '1:01:00' # 2) 报时 now_str = nice_time(datetime.now(), use_ampm=True) # 'one twenty two p.m.' # 3) 用连接词拼装候选列表(对应 ask_yesno 的实现方式) choices = join_list(["volume up", "volume down", "stop"], "or", lang="en-us") # 'volume up, volume down or stop'判断"要不要用"的简单经验法则:
- 需要朗读数字 →
pronounce_number/nice_number(speech=True); - 需要显示数字 →
nice_number(speech=False)(分数形式)或pronounce_number(speech 场景除外); - 秒数转时长 →
nice_duration;两个时间点差 →nice_duration_dt; - 报时/报日期/报年份 →
nice_time/nice_date/nice_date_time/nice_year; - 拼接选项列表 →
join_list。
质量保障:测试如何锁定格式化行为
mycroft.util.format的全部行为都被 test/unittests/util/test_format.py 以单元测试形式固化,是阅读与验证本文所有示例的第一手依据:
TestNiceNumberFormat:用NUMBERS_FIXTURE_EN字典(20 余组输入/期望输出对)覆盖nice_number的分数口语化、denominators过滤与speech=False形态,并验证未知语言降级;TestPronounceNumber:覆盖整数、负数、小数按位拼读、places截断、长/短级数制、科学计数法,甚至包含95505896639631893量级的超大数据;TestNiceDateFormat:以 lingua-franca 各语言的date_time_test.json驱动nice_date/nice_date_time/nice_year的多语言断言,并对一年 368 天、0~9999 年做全量空输出扫描;TestNiceDurationFuncs:系统验证nice_duration/nice_duration_dt的 speech/clock/resolution/use_years 组合矩阵,以及类型错误抛异常、第二参数被忽略告警等边界行为;TestJoinList:验证连接词、分隔符、空输入等场景。
这些测试与 mycroft/util/format.py 的实现一一对应,如果你要扩展该模块或迁移到 lingua-franca,这套用例就是现成的回归保障。
小结
mycroft.util.format是 Mycroft 技能开发中"把数据变成语言"的标准化出口:9 个公开函数覆盖数字拼读、口语化数字、报时、报日期、报年份、时长表达与列表拼接,统一遵循speech双形态输出与lang本地化两条设计主线。其中nice_duration/nice_duration_dt由 Mycroft 侧基于TimeResolution精度枚举自主实现,毫秒处理、闰年校正、精度自动降级等细节值得深入阅读 mycroft/util/format.py;其余函数包装自 lingua-franca,其行为已被 test/unittests/util/test_format.py 的多语言数据驱动测试完整锁定。技能作者只需要记住一条准则:凡是最终要"说"或"显示"给用户的数字与时间,都交给本模块处理。
- 人工智能
- 语音
- 交互助手
- 智能硬件
【免费下载链接】mycroft-core
Mycroft Core, the Mycroft Artificial Intelligence platform.
相关推荐
BitCPM4-CANN-8B量化感知训练(QAT)技术详解:仅5%训练开销的奥秘
BitCPM4 CANN 8B量化感知训练 QAT 技术详解:仅5%训练开销的奥秘 你是否想过,能否在几乎不损失性能的前提下,将大语言模型的存储和计算需求降低6
Leptos数字格式化:数字、日期、时间格式化
Leptos数字格式化:数字、日期、时间格式化 引言 在现代Web应用开发中,数据格式化是一个至关重要的环节。无论是展示货币金额、日期时间,还是处理用户输入的数
前端后端Web框架SSR旧Mac还能装新版macOS吗?OpenCore Legacy Patcher 三阶段完整指南
旧Mac还能装新版macOS吗?OpenCore Legacy Patcher 三阶段完整指南 升级弹窗弹出"这台 Mac 太旧,无法安装此版本 macOS"时
操作系统固件驱动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考