返回日报条目

资料节译

RecallBuddy:本地运行的语音陪伴程序

阅读原文

**方向:**开放式创新 / 离线优先

**一句话:**为记忆力有问题的姨妈打造一位语音陪伴助手,完全运行在她家人的笔记本电脑上;她重复提问时,第四次得到的回答也和第一次一样耐心。

GitHub 链接:https://github.com/vaishud12/RecallBuddy-1

技术栈:whisper tiny + llama3.2:3b(Ollama)+ pyttsx3,全部在本地运行。MIT 许可。不让任何数据离开这台机器。

**已验证:**58 项测试 · 实时模型 3/3 · 离线证明 8/8 · 以下所有内容都在构建机器上运行(WSL2,无 GPU)。

我姨妈 Marta 71 岁,记忆力有问题。在一次谈话中,她会把同一个问题问我三四遍。

我从来没觉得这让人烦。但我开始留意到,软件是怎么回答她的。每个应用到第四次回答时,都像是在回答一个反应迟钝的人——并非刻薄,只是语气稍微平淡了些。那些出于好意的应用还会把这种态度直接说出来:

“就像我刚才提到的那样⦔

“就像我已经告诉过你的那样⦔

这样的回答会让她不再提问。而一个不再提问的人,也就不再去了解事情。

所以我的要求从来不是“好好回答”,而是:

第四次回答也要和第一次一样完整。每一次都如此。

不只是把提示词写得更友善,而是真正作出保证。

RecallBuddy 是一款在笔记本电脑上常驻运行的小程序,会聆听、思考并开口回答。它绝不会说她之前问过这个问题。

Marta      : When does the bakery open?
RecallBuddy: The bakery on Elm Street opens at 7 in the morning.
Marta      : Sorry, when does the bakery open?
RecallBuddy: The bakery on Elm Street opens at 7 in the morning.
Marta      : What time does the bakery open?
RecallBuddy: The bakery on Elm Street opens at 7 in the morning.
Marta      : Who takes the bins out?
RecallBuddy: Your neighbour Marek comes to take the bins every Wednesday.
Marta      : When do I take my tablets?
RecallBuddy: You take your blood pressure tablets at 8am and 8pm.

笔记本电脑 CPU 上每轮耗时 0.5â2 秒。它知道她的狗、她吃的药、她的垃圾桶、她的邻居 Elena——因为这些信息都保存在一个她可以自己编辑的文本文件里。

这是这项工作的核心,也是我愿意在代码审查中据理力争的部分。

使用托管 API 时,友善是你请求来的。你在系统提示词里写上“始终耐心,不要暗示用户重复问过”,然后接受返回的内容。你只能寄希望于此。而这种希望是看不见的——对于你没有得到的特性,你无法用 grep 检查回答。

**“通常有耐心”在这里远远不够。**失败的后果不是一句话说得不好,而是一个人觉得自己成了别人的负担。

自己运行推理意味着,这项特性由我拥有的代码来保障。模型和语音输出之间有 23 个正则表达式:

# companion.py
_REMINDER_REST = r"[^.!?;,]*"     # a clause, never a whole sentence
_YOU = r"\byou(?:"
        r"\s*'?(?:ve|d|re|ll|m)\b"           # you've, you'd, you're, you'll
        r"|\s+(?:will\s+have|have|had|are|were|will)\b"
        r"|\s+"                               # bare "you asked"
        r")\s*"
FORBIDDEN = [
    rf"\bas (?:i |we )?(?:already |just )?(?:said|told|mentioned|asked)\b{_REMINDER_REST}",
    rf"{_YOU}(?:mentioned|said|told me|noted|brought up)\b{_REMINDER_REST}",
    rf"{_YOU}(?:just |already )?(?:asked|said|told me)\b{_REMINDER_REST}",
    # ... 23 patterns total
]

"you already asked"无法传到她那里,因为代码会阻止它输出。

这一个字符类——[^.!?;,]*——就是删掉提醒语和删掉答案之间的全部区别:

"You mentioned earlier it was 7am, so she goes there in the morning."
                            â  stop at the comma
"So she goes there in the morning."

如果字符类里没有逗号,模式就会一直匹配到句号,回复会变成 ".",pyttsx3 读出来时就会停顿。我一开始写错了,后来在一次实时运行中发现了这个问题。

1 · 按分句匹配的模式——如上所示。删掉提醒语,保留答案。

**2 · 运行两遍。**过滤器会在整理前和整理后各运行一次。这不是过度防备,而是一个真实的漏洞:"I already said that."在清理时会因末尾的that被当作悬空词语而剥除,结果变成 "I already said."——而它本身就是禁用短语。这个保护机制竟然制造出了它要防止的内容。现在通过过滤两遍修复了。

**3 · 独立、直接的审查。**用词汇检查,不用正则,也刻意不复用上面的模式。

这件事很重要,因为它让我白白花了一小时。我的实时测试通过对回复运行 FORBIDDEN_RE 来断言“回复里没有禁用短语”——也就是用过滤器本身来测试过滤器。它高高兴兴地通过了测试,但记录里确实漏出了一句:

You've mentioned that before, but I'll always be happy to remind you.

每个模式都要求在you后面紧跟一个空格。"You've"里有撇号。所以 23 个模式全都漏掉了,测试也漏掉了,因为测试用的就是这同一组 23 个模式。现在审查改用一个独立、判断更直接的工具,能够发现过滤器漏掉的内容。

**4 · 再问一次。**如果保护机制处理后没有留下任何可用内容,它就会设置 bailed 标志,应用会用平实的语气再问一次。如果我们也不再回答问题,那光有善意还不够。

这种审查方法简单直接,但也可能误判。

我第一个版本把once和twice都当成提醒用语。一次实时会话中,它删掉了这句话:

“早上吃一次,晚上再吃一次。”

â¦并用一句没有任何实质内容的备用回复替换了它。对于一位因血压药而需要这个应用的女性来说,这是可能出现的最糟糕的故障——比说“就像我刚才提到的”还糟,因为现在她什么都没得到。

修复方法是根据某个词是否可能无害地出现在事实性回答中,将审查拆成两类:

AUDIT_TERMS_STRICT = {"already", "mentioned", "repeated", "looping"}
AUDIT_TERMS_SOFT   = {"before", "again", "said", "keep", "remind", ...}

严格词项会触发中止。柔性词项只会发出警告,因为*“商店六点关门,所以四点前去”和“把它放在前门旁边”*都是完全正常的英语表达。

**“这个词在事实性回答中是否有无害的用法”是合适的判断标准吗?**这个办法可行,但感觉像是手工调出来的。蕴含检查、轻量分类器,或者不必二选一的更聪明的备用方案,都会更好。我很想听听大家会怎么做。

verify_offline.sh并不是让我关掉 Wi-Fi,然后相信输出结果。它会创建一个只包含回环接口的 Linux 网络命名空间,在其中启动 Ollama,并在那里运行整个流程:

1. Is there an internet?
  PASS  cannot reach https://example.com
  PASS  cannot reach https://api.openai.com
  PASS  cannot reach https://huggingface.co
       iface: lo:
2. Start Ollama inside the namespace
  PASS  Ollama is up on loopback with llama3.2:3b
3. Whisper weights, read from disk (no download)
  PASS  whisper tiny.pt already cached (73M)
4. Full loop: hear -> think -> speak, with no internet
  PASS  the pipeline ran to completion
  PASS  3 repeat questions detected and all answered in full
  PASS  no reply tripped the pattern filter or the audit
  SKIP  speech not exercised here
RESULT: 8 passed, 0 failed

在那里通过的一切都不可能打电话回家,因为根本没有可以用来打回家的外部路由。

唯一一次 SKIP 是真实情况,我如实报告,没有隐藏:WSL2 没有 PortAudio,也没有 espeak,所以这台机器上从未测试过麦克风和扬声器。Windows 上两者都有。除此之外的一切,包括完整的 AI 循环,都经过了验证。

| 能离线运行吗? | 能。在没有外部路由的命名空间中验证。8/8。 | | 数据会留在服务器之外吗? | 会。请求路径中没有 URL,而且有测试强制检查这一点。她的声音、她说的话和她的用药时间表都不会离开这台机器。 | | 可以换模型吗? | 可以。llama3.2:3b âllama3.1:8b âqwen2.5。保护机制与模型无关,也保持不变。 | | 可以改变行为吗? | 可以——编辑文本文件即可。 | | 每次对话的成本? | 零。永远没有按 token 计费的账单。 |

如果用闭源方案,原始模型质量会更高,但它会在这里每一项重要指标上落败。

隐私也不是点缀。这个软件是为一位女性开发的,她的认知衰退是她最私密的状况,而她的家人已经为她忧心不已。为了修复礼貌问题,就把她的家庭情况和用药时间表交给第三方,这笔交易并不划算。

.txt

## Routines and facts worth holding on to
- Her dog is named Pepper. Pepper is 12 and sleeps on the blue blanket.
- She takes her tablets at 8am and 8pm. The blue ones are for blood pressure.
## Things to be careful about
- Do not bring up the car. She is still sad about it.

刻意不使用向量数据库:

她可以编辑它——用记事本即可,不需要工具,也不必重建索引。 2. 她可以阅读它——她能准确看到机器认为自己知道她的哪些事情。这一点比听起来更重要。 3. 它装得下——25 条事实可以放进 3B 模型的提示词,不会把它撑爆。

python -m unittest discover -s tests     # 52 fast, no model needed
RB_SLOW=1 python -m unittest discover     # + 6 against the real local model
./verify_offline.sh                        # the namespace proof

58 项测试。对30 种刻意刁钻的措辞逐一断言:提醒语会被删掉,答案会保留下来,而且结果绝不会变成空内容。另外还有一组平行测试,确认无害的回复能原封不动地通过——会吞掉答案的保护机制只是更悄无声息的失败,所以它也要接受和保护机制本身同样严格的测试。

实时运行:3/3 均正常。离线证明:8/8。

因为一个夸大自身能力的陪伴助手,只是换了种形式带来同样的问题。

**这是一个 3B 模型,局限显而易见。**它会编造细节。有一次它说 Marek 在“面包店”收垃圾。有一次,银行说是周二和周四,它却回答“每天”。它也曾在同一场会话中自相矛盾。事实库很小,也是真实的;模型对它的补充则不够可靠,不能用于任何重要事情。

第三人称的说法还是会漏出来,尽管提示词要求避免——它会说“她去那里”,而不是“你去那里”。我选择不在代码中改写代词,因为“她”很多时候是正确的(比如邻居 Elena),盲目替换造成的问题会比偶尔说错更严重。

**Whisper tiny**在安静房间里处理年长者的声音时表现较弱。base明显更好,而且在 CPU 上运行仍然很快。

**这不是医疗器械。**提示词会拒绝回答医疗、法律和财务问题。

**Marta 实际上还没有用过它。**目前只在我的机器上通过模拟她的问题进行了验证。她还没有亲自体验过。

我的环境中没有 DevRelay,所以没有可嵌入的已保存智能体会话。这里改用测试和对话记录展示工作成果。

倒数第二项是我坦承的局限,也是我接下来要着手解决的事。

ollama pull llama3.2:3b
python -m venv .venv
.venv/bin/pip install torch --index-url https://download.pytorch.org/whl/cpu
.venv/bin/pip install -r requirements.txt
.venv/bin/python app.py --name Marta

Windows:双击 run_windows.bat。

app.py         wiring, CLI, repeat detection, re-ask
companion.py   the patient prompt, the guard, the audit
audio_io.py    microphone, WAV encode/decode, Whisper
brain.py       Ollama client
speech.py      pyttsx3
memory.py      knowledge-bank parser

MIT 许可。

我尤其欢迎大家批评保护机制的设计,特别是严格/柔性审查的划分。