---
title: "RecallBuddy：本地运行的语音陪伴程序"
canonical: "https://siyuanailab.com/curation/translations/2a96ff7be1ac68515b187946e9026205-55b408e7c2dc3831dbcee35621af477dbc6769707895c233b6ca25de137820bc/"
dateModified: "2026-10-05"
datePublished: "2026-10-05"
---

[返回日报条目](https://siyuanailab.com/curation/#entry/2a96ff7be1ac68515b187946e9026205)

 

资料节译

 

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

 

[阅读原文](https://dev.to/vaishud12/recallbuddy-hackathon-submission-594i)

 

现有资料无法确认原文完整，本页只翻译已取得的材料。

 

现有原件无法确认图片是否齐全。

 

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

 

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

 

GitHub 链接：[https://github.com/vaishud12/RecallBuddy-1](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 许可。

 

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

 

AI 翻译；具体表述请与原文对照。
