这是以下挑战的参赛作品:Hacktoberfest 开源 AI 挑战赛第 1 周:Touch Grass
简短版
你正要出门时,家里有人连珠炮似的给你列了一串:
“买 1 升牛奶。别买面包——家里还有。把包裹送到邮局。再买三个笔记本。”
大多数任务类应用会让收到这条消息的人承担所有工作:停下来、输入、拆分、记住,并正确理解取消项。
Nikalll 把这个杂乱的家庭请求变成一张经过复核的口袋卡片。你可以说出或粘贴请求,让开源权重的 Gemma 模型提出待办事项,逐项复核,确认目的地,然后把最终卡片带出门。
重要的不是 AI 生成了一份清单。重要的是已取消的项目仍然可见,数量仍然保留,不确定性仍然可见,并且由人——而不是模型——批准最终带出门的内容。
我构建了什么
Nikal 是一个语音优先的家庭跑腿交接工具,面向那些通常以嘈杂语音消息或印地语/印地英语/英语混合消息形式出现的、真实世界中的小任务。
它面向的是正要离家、需要记住别人托付自己购买、取回或送达什么的人。
流程被刻意设计得很短:
- 采集——录制一段简短语音或粘贴原始消息。
- 转写——ElevenLabs Scribe v2 将语音转为可编辑文本。
- 提取——通过 Backboard 调用的 Gemma 提出结构化待办事项、数量、取消项、目的地提示和澄清问题。
- 复核与分配——原始请求保持可见,同时用户可以编辑、取消、推迟或分配每一项。
- 口袋卡片——Nikal 按目的地对已批准的任务进行分组。
- 出门后——用户将每一项标记为已完成、无法获取、推迟或未解决。
Nikal 不是自主购物智能体。它不购买任何东西、不给任何人发消息、不查找商店、不追踪 GPS,也不假装模型输出的合法 JSON 就意味着请求已被理解。
这个设计要防止的失败
最危险的失败不是崩溃。而是一份看起来合理、却悄悄丢掉了关键部分的清单:
- “别买面包”变成了“买面包”。
- “两升”丢失了数量。
- “把这个放到邮局”丢失了它的目的地。
- 一个不明确的商店变成了一个自信的猜测。
- 一个无法获得的物品被悄悄计为已完成。
Nikal 将这些视为产品状态,而非边缘情况。
卡片所保留的内容
- 已包含的差事。
- 已明确取消的差事。
- 已推迟或未解决的差事。
- 数量和单位。
- 用户的原始源文本。
- 目的地分配和手动更正。
- 诚实的完成结果。
准备好的工作区保存在带版本控制的浏览器存储中。AI 请求是在线且托管的;准备好的本地卡片不需要再次调用模型来标记结果。我不声称这个原型是一个完全离线的 AI 应用或经过验证的可安装 PWA。
演示
在线演示: nikalll.onrender.com
视频演示:
视频演示展示的是完整的故事,而非功能导览:
- 从一个混乱的家庭请求开始。
- 保持取消和数量可见。
- 让 Gemma 生成一份草稿。
- 在审核步骤中更正或确认目的地。
- 准备口袋卡片。
- 将一项任务标记为完成,并让一个无法获得/推迟的状态保持可见。
如果托管提供商不可用,应用会保留源文本并提供手动路径,而不是伪造一个成功的 AI 结果。
代码
🚶 Nikalll (WalkRecall)
将混乱的门口语音笔记转化为结构化、可离线使用的差事卡片。
出门前说一次,几秒钟内审核完毕,然后带着一份可离线完全使用的、一目了然的邻里清单出门。
💡 问题:“门口语音笔记”的混乱
每次你走出家门,你的母亲、室友或伴侣都会连珠炮似地喊出一串混乱的差事:
“喂,听好了!你要出门的话,就从蔬菜市场买 1 公斤土豆和香菜……但别买西红柿,家里已经有了……然后从杂货店买 2 升牛奶和酸奶,但面包千万别买……再从药店买降压糖浆,别买创可贴,那东西在抽屉里!”
为什么传统工具会失败:
- 待办应用太慢:没有人有时间或耐心手动输入 8 个单独的项目,还要……
该项目是一个 Next.js + TypeScript 应用,领域契约、提供商边界、测试和安装说明与 UI 一同提交。
最相关的区域包括:
-
app/page.tsx——捕获、审核和口袋卡片流程。 -
app/api/extract/route.ts——有边界、经用户同意的文本提取边界。 -
app/api/transcribe/route.ts——有边界的音频上传边界。 -
lib/server/backboard.ts——显式的 Backboard 提供商/模型请求。 -
lib/server/elevenlabs.ts——ElevenLabs 语音转文本适配器。 -
lib/domain/——schema 与确定性的卡片/证据逻辑。 -
tests/——领域与提供商归一化的回归测试。
我是如何构建它的
开源权重 AI 是核心,而非装饰
Gemma 承担核心的解读任务:将对话式、混合语言的 household 指令转化为有边界的提取草稿。
浏览器从不选择提供商或模型。服务器从环境配置中选择它们,并发送带有以下内容的固定提示词:
- 显式的提供商和模型名称;
- 非流式输出;
- 请求 JSON 输出作为格式化辅助;
- 禁用记忆;
- 禁用网络搜索;
- 无工具或自主操作。
默认仓库配置通过 Backboard 中的openrouter提供商使用 Gemma。具体模型可由服务器配置;仓库默认值为google/gemma-3-27b-it。不会向客户端发送 API 密钥。
Gemma 的输出不会仅因为它是 JSON 就被信任。Zod 会校验响应,服务器会检查每条证据引文是否逐字出现在原始来源中。模型无法返回受信任的目标 ID、应用 ID、时间戳、完成状态或审批决定。
ElevenLabs 是一个聚焦的输入边界
语音之所以有用,是因为原始问题常常始于口头表达。ElevenLabs Scribe v2 负责转写;随后 Nikal 会在提取前将转写文本显示为可编辑文本。
音频上传有大小限制,仅通过服务器路由接受,且不作为应用数据存储。UI 始终可以回退到文本输入。
确定性代码负责信任敏感决策
普通 TypeScript——而非模型——负责:
- schema 校验;
- 证据匹配;
- 目标别名匹配;
- 纳入/取消/推迟状态;
- 卡片准备规则;
- 本地持久化;
- 结果跟踪。
一个已包含的条目如果没有确认的去向,就不能进入已准备好的卡片。未解决的条目必须被解决或明确延后。不可用的条目绝不会被悄悄标记为完成。
技术栈
- Next.js 16 App Router
- React 19 和严格 TypeScript
- Tailwind CSS 4,沿用现有的 shadcn/Base UI 配置
- Hugeicons
- Zod 用于运行时契约
- 通过 Backboard 调用 Gemma 进行提取
- ElevenLabs Scribe v2 用于转录
- Render 用于公开部署
我从第一次尝试中学到了什么
我之前的黑客松项目教会我,精致的界面和冗长的功能列表并不能证明核心问题已被解决。
所以这个项目有意做得更窄。演示只有一个完整的故事:一个混乱的请求、一份带来源链接的 AI 草稿、一次人工审核、一张口袋卡片,以及一个诚实的结果。我保留了原始文本可见,因为看起来正确的提取仍可能是错的。我把取消和不确定性保留为一等状态,因为隐藏它们比展示一个不完整的结果更糟。
我还删除了自己无法证明的说法:
- 托管的 Gemma 不被描述为离线 AI;
- 浏览器持久化不被描述为完全离线的 PWA;
- 模型的 JSON 不被描述为批准;
- 去向标签不被描述为实时商店发现;
- 没有经过标注的留出评估,就不声称任何准确率百分比;
- 不可用的跑腿任务不会被转换成成功状态。
最有用的工程决策是让提供方的输出穿过一个小而无聊的边界:验证它,对照原始消息核验其证据,然后让确定性的应用代码决定用户可以批准什么。
为什么开放创新很重要?
开放模型在这里很有用,因为 Nikal 并不是试图用一个不透明的助手取代人。它试图让混乱的人工交接变得可检查、可纠正。
以开放权重模型作为提取步骤的核心:
- 模型和提供方可以在不重写产品工作流的情况下更换;
- 提取提示词和 JSON 契约可以被检查;
- 失败可以与人工基线对比,而不是隐藏在聊天界面后面;
- 应用可以把批准、证据和完成状态保留在模型之外;
- 未来自托管的 Gemma 部署可以缩小敏感家庭消息的托管数据边界;
- 同一份安全契约可以验证不同的开放模型。
Backboard 为应用提供了一个明确的模型边界,而不是浏览器端的供应商依赖。我可以在服务器上选择 Gemma 的提供商/模型,关闭记忆和网页搜索,并让应用的其余部分独立于这一选择。
ElevenLabs 的范围刻意更小:它解决语音采集问题,然后把可编辑文本交给同一条审核流水线。它不会成为第二个决策者。
有一个重要的注意事项:当前的公开部署使用托管服务。开放创新让模型层可替换、可检查;它并不会神奇地让托管推理变得私密。当请求被发送用于转录或提取时,Nikal 会告知用户。
我的智能体会话
我用以下工具构建了 NikalOmniRush.ai作为编码和审查伙伴。该智能体在架构、领域契约、提供商边界、故障处理和测试用例方面提供了帮助;我做出产品决策,并将最终范围聚焦于一次物理世界的交接。
我没有在这份草稿中嵌入 DevRelay 会话链接,因为我没有可链接的公开已保存会话 URL。本节可以在发布前删除,或者在创建了 DevRelay URL 后替换为该 URL。
奖项类别
- Gemma 最佳使用—— Gemma 负责核心的解读问题:嘈杂家庭语言中的数量、否定、取消、目的地提示和澄清候选。它不是装饰性的聊天。
- Backboard 最佳使用—— Backboard 是明确的服务器端模型边界。请求固定提供商/模型,禁用记忆和网页搜索,请求结构化输出,并在结果到达审核 UI 之前对其进行验证。
- ElevenLabs 最佳使用—— ElevenLabs Scribe v2 处理短语音笔记转录路径,并在提取之前返回可编辑文本。
- Render 最佳使用—— 可运行的 Next.js 应用及其由提供商支持的路由已公开部署在nikalll.onrender.com。
构建者@taqui。
如果这是团队提交,队友应在发布前在此处用他们的 DEV 用户名署名。