展示如何用 LLM 从邮件自然语言中提取日程信息并转换格式,典型的 NLP 应用案例。
我和朋友们都遇到过一个常见的问题:收到优先级为中等的可执行类邮件。这类邮件确实需要采取某种行动($\geq 最低\ 优先级$),但行动的截止期限在不远的将来($\leq 最高\ 优先级$)。我往往会草草浏览这类邮件,了解大概需要做什么,把它们标记为未读,然后承诺稍后再回复。当然,这个"稍后"只有在同一任务出现另一个紧急提醒时才会真正发生。你可以想象,如果没有任何提醒的话会怎样。因此,我想创建一个工具,当我打开邮件时,它可以和我一起阅读邮件,然后帮助我将邮件中的所有可执行项添加到我的日历中——我的生产力一站式解决方案。我觉得我们正处于聊天机器人的文艺复兴时代,这似乎是聊天机器人真正可以帮助我的地方。
我已经坦白过我对待办事项应用的热爱吗?虽然大家都吐槽这类应用,但如果你想学习任何编程语言或框架中的内部运作原理(即如何 CRUD),待办事项应用是最好的学习方式之一。它通常足够简单,你可以在一天内完成(眨眼笑脸),但又足够复杂,让你学到很多关于系统的知识。抱歉我扯远了:我们正在构建一个待办事项应用。或者说,一个 calendarGPT。我给一个朋友打电话,看他是否有兴趣,他说不感兴趣,只是无聊,我们就开始干了。三个臭皮匠,顶个诸葛亮。
既然我们都投身于邮件服务垄断——谷歌,那么 MVP 的唯一明显方式似乎就是选择它。最初,我们想在邮件顶部嵌入待办事项列表,就像他们处理日历事件一样,但谷歌不让我这样做。我们被迫转而构建一个插件。当然,没有好事不被惩罚:我不得不使用 Apps Script,经历谷歌文档的折磨,弄清楚如何创建一个简单的插件、哪些 API 还没被弃用,哪些 API 明天就会被弃用。说实话,和我五年前左右开始接触 Android 时相比,这次的体验真是好太多了。甚至还有谷歌 Colab 来帮忙。虽然大多数 API 使用的示例都不够有帮助,但经过一些挖掘,我还是弄清楚了如何构建和配置一个基本的插件。我的朋友非常有帮助,他直接跳进去设置必要的 prompt、模型和数据获取,这样当 UI 完成时,我们就可以直接把所有东西拼在一起。
最初,我们使用了 OpenAI 的 ChatGPT,因为它在函数调用等方面表现出色,而且设置的摩擦力最小。它对概念验证很有用,但我们知道最终会替换它,因为这在安全上是一场噩梦。在什么世界里我们会想把自己的邮件发送给 OpenAI 呢?尽管如此,让 ChatGPT 返回 JSON 对象的设置出乎意料地容易(真的要为他们点赞),尽管它经常会轻微修改我们的 schema,返回有效的 JSON 对象但属性名略有不同。把温度降低到约 0.001 似乎解决了这个问题。
谢天谢地,并非所有公司都是邪恶的,有些公司为我们这类工作中的开源 LLM 铺平了道路。特别是 Mistral,在这方面做了很好的工作。它的 instruct 模型被证明至少和 ChatGPT 一样有用,如果不是更有用的话。在 Ollama 的帮助下,我能够通过"威胁"它必须在任何情况下只返回 JSON 对象来配置 mistral-instruct 执行我要求的操作。我在本地机器上做了几次测试,似乎工作得相对不错,尽管它偶尔会返回有效的 JSON 对象但属性名略有不同,搞乱了我的解析。幸运的是,重新运行它就会解决这个问题,因为看起来它记得谁才是真正的主人!
我需要一种方法来确保我能调用本地的 LLM,由于我的插件运行在谷歌云上,简单的 localhost 显然不够。在学校网络上,我看到了两个选择:a) 使用 NGINX 代理到我的 localhost,直接从我的 IP 地址获取;b) 使用 ngrok 代理到我的 localhost。我选择了后者,因为有一次我配置 NGINX 配置不当,差点失去我的 VPS。这是一个宝贵的教训和最佳实践的故事。Ngrok 并不特别困难,他们的文档很直接,至少对我想要的东西是这样。注册账户,获取 API 密钥,运行 ngrok http http://localhost:port,瞧。我本该知道会有意外。由于我使用的是免费计划,我无法在上面设置任何路径。这很令人沮丧,因为 ollama 使用 http://localhost:port/api/chat 作为 llm 端点。因此,我设置了一个 express 服务器作为我的模型端点的代理,然后使用 ngrok 代理我的代理。我把 ngrok URL 添加到我的 app script 中,万岁。经过一些测试,一切都工作得很好,尽管有点慢。安全专家们,你们印象深刻吗?
构建这个项目是一个有趣、轻松的方式来试验是否可以完全依赖本地运行的 LLM 模型。我认为这是可能的,随着经验的积累和 LLM 的突破,我相信这将是未来几年的发展方向。Ollama 在让一切平稳运行方面做了非常出色的工作。我印象深刻。
与谷歌 API 一起工作,虽然比我过去的经历稍好(是谷歌变好了,还是我变好了?),但对初学者来说仍然远非愉快。尽管他们有广泛的文档,但 API 和产品的快速弃用已经成为一个瓶颈。我在尝试配置插件 UI 时陷入了许多死胡同。我试图利用 GPT,但它经常提供已弃用的 API,这些 API 根本无法工作。我相信谷歌的人在尽力而为,但开发者体验可以好得多。
时区。我从 Tom Scott 那里了解到处理时区的困难,我认为我已经做好了准备,通过一些小聪明,我能够要求 LLM 给我 UTC 时间,然后可以使用 getTimezoneOffset() 轻松将其转换为本地时间。哦天哪!
来自 LLM 的结构化输出。设置 LLM 以输出结构化数据极其困难。在我看来,这限制了我们从 LLM 作为自动化工具中提取最大价值的能力。我们越早、越高效地完成这个任务,就能越多地将 LLM 融入我们的工具链流程等等。我的前任经理曾告诉我,作为程序员,我们通过编程语言为充满混乱的世界带来秩序,从而增加价值。根据我的经验,这对 LLM 也同样成立。如果我们能以某种方式为结构化输出建立一个有效的标准,他们就能增加更多的价值。如果有人正在从事这方面的工作,或者有资源专注于这个领域,请联系我。
从事这项工作强化了一个想法,即 LLM 在自动化中具有巨大潜力。正如你所能想象的那样,向日历添加一项任务并不费力,但为多个任务、多封邮件、每天多次执行这个操作是更适合让我们的"语言向导"来完成的工作。
如果你想试用这个,你可以在这里找到代码