演示 LLM 作为交互界面的实可行性,用自然语言完成复杂任务,提供产品设计新思路。
TL;DR 我们为一个开源的美国税务场景库构建了一个 GPT 接口,它在某些时候相当不错,但对用户的要求也不少。
显然这个博客会探讨 Andrej Karpathy 观察的含义。这次我们在思考他的一个评论:LLM 可能会被理解为一种更高阶的操作系统——不是万能的,而是充当连接其他组件(如数据存储、特定领域库和用户界面)的粘合剂。Cam Pedersen 写了一篇很好的文章,更全面地阐述了这些想法。
我认为构建一个具体的数据产品来探索这个"LLM 即操作系统"的想法会很有趣,并将其与现有技术进行对比。这个想法源于:早前我写了一个叫 tenforty 的 Python 库,它反过来使用了一个优秀的开源包 Open Tax Solver,来探索我们当时处理的一些税务场景。我一直没有写过相关文章,但想有朝一日能从中构建一个 webapp 来分享。这似乎是一个很好的机会来尝试构建一种新式的 LLM-OS 应用:一个 AI 税务顾问。
tenforty 将税表计算转换为 Python 函数调用,这使得评估一份退税或许多假设退税变得容易。后者的"如果"方面是我创建该库的主要初衷:如果我们在两年内而不是一年内出售这只股票会怎样?如果我们最大化 401K 会怎样?在 TurboTax 中回答这些问题感觉像西西弗斯的任务;记下税额,往回退-往回退-往回退,调整输入,往前进-往前进-往前进,记下税额……1
在 LLM 出现之前,要为这些"假设"构建一个 webapp,我可能会研究 tenforty 能帮助的、超出我们自己的常见税务场景,并构建某种计算器应用来涵盖那些情况,可能类似于 SmartAsset 的税收概览页面。
在 2024 年,如果我们让 LLM 充当 UX,那么构建应用变成了更少的工作、不同的工作。某种意义上,用户只需带来他们自己的场景,应用就不需要预期这些情况。以下是步骤:
构建一个小型 web 服务,将 tenforty 的主要函数暴露为端点。我们使用了 FastAPI。
在 OpenAI 上配置自定义 GPT:
如果你有付费的 ChatGPT+ 账户,你可以在这里试用 Tax Driver。隐私政策从 GPT 链接,也在这里;要点是支持的 web 服务设置为纯计算器,仅记录哪个端点被调用,以及何时被 OpenAI 的服务器调用。注意,独立于 ChatGPT+,你也可以使用包含的 Colab notebook 直接尝试 tenforty。
Tax Driver 在很多方面表现得很好:
从最少的输入——如这里的示例截图所示——在适当的时候,它通常会调用 Action 来评估场景。我们在过去一年左右已经习惯了 GPT-4 的自然语言能力,但它们确实令人惊叹。(注意:点击/轻触图像可以放大。)
它愿意至少尝试评估任何你可能遇到的税务场景,结合其知识库与 tenforty 启用的特定计算。我永远无法用一套预设的场景计算器达到这样的广度。这是另一个相当不错的例子——如果一个人把 AMT 理解为"由于 AMT 计算而超出常规税款的超额税",如有时非正式讨论的那样。
它可以生成很好地总结结果的表格/图形:
被要求时,它可靠地生成恰当的 Python 代码,使用 tenforty,你可以将其放进笔记本中从那里继续。
其中一些能力就是来自未来的,而有些对我或一个团队来说本可以实现,只是需要多得多的工作。而构建这个"应用"花费的时间是构建其前 LLM 时代类似物所需时间的一小部分。
但它也可能令人沮丧:
它可能会完全错过一个给定场景请求的要点,以 GPT-4 那种欢快而自信的方式,就像一个政治家在市政厅会议上回避问题。在这个例子中,我们要求它查看如果所有事情都在一年内行使 vs 分散在两到三年内的税收负担,Tax Driver 却完全没抓住要点:
它有一套从 prompt 中要遵循的特定指令,它经常遵循,但有时也违抗,特别是涉及图表生成的指令。例如,这是一个案例,我们明确要求一个阴影面积图,此外 prompt 中还有指示在此类图中倾向于阴影面积图,但它声称已经绘制了一个,实际上却没有:
它可能不稳定。运行相同的 prompt 两次,一次你会得到合理的输出,第二次它会无法成功运行一个 Action 或处理 Action 的结果。未显示示例。
它有时会混淆 Action 调用(即 web 请求)和对 tenforty 的 Python 调用。未显示示例;输出是生成的 Python 代码,本应调用 Action 并用响应做某事。
这些限制对我们现在来说是熟悉的。这种缺乏元认知是为什么这第一代 LLM 基础产品——ChatGPT、Stable Diffusion、GitHub Copilot——被表达为 copilot,而不是说自主助手,可以被依赖来正确执行任务。我们在之前关于用 LLM 进行科学文献元分析的文章中遇到了同样的问题。
更广泛地说,尽管创建这个应用很容易,通用的聊天界面是众所周知的"万金油,不精通任何事"。它对用户承担了很多负担,要求他们知道自己想要什么,仔细写出请求,并在解释结果和迭代时谨慎。Simon Willison 在最近与 Outerbounds 的一个播客中提出了相关要点:通用聊天界面在可发现性方面留下很多待改进之处,并对吓跑非爱好者潜在用户的风险很高。
结果:Tax Driver 是一个有能力但易变的助手。用我们自己的实际税务场景来说,只要有一点耐心和一些指导,它产生了扎实的分析。我发现特别有帮助的是要求它生成 tenforty 代码,我可以在这些对话之一的最后自己尝试。相比必须自己做所有事情,这似乎更有用,但它需要一个相当宽容的用户,并且不如人们理想地希望从税务顾问那里得到的那样可靠。
衷心感谢 Sarah Laskey 改进了早期草稿。所有错误都是我的!
tenforty 所依赖的 Open Tax Solver(OTS)包是互联网的这些稀有珍宝之一。OTS 团队已经连续发布了他们的软件超过二十年。它提供了美国 1040 税表计算的实现,以及许多州的表格,包括加州和纽约州。
你可以使用 OTS 提供的 GUI 准备税务申报表,或通过简单的文本文件格式。无论哪种情况,OTS 都将结果申报表输出到另一个文本文件,并可以将输出插入到官方税表 PDF。因此它是一个 DIY、开源的 TurboTax,改进了手工计算。非常出色。
tenforty 通过将 OTS 版本中的 C 语言源代码合并到一个单一的大源文件中,然后将该源文件包装为 cython 扩展来工作。由于 OTS 从根本上是围绕读写文本文件(包含税务申报信息)而设计的,tenforty 实际上在幕后的临时目录中写入和读取文本文件,尽管库用户只看到 Python dict 和 pandas dataframe。
OTS 的文本文件接口以税表行号为单位工作,例如 W-2 收入在美国 1040 的第 1 行报告。由于只有会计师和非常执着的人才会知道行号,OTS 在其 GUI 中用自然名称如"W-2 Income"来注释它们。为了解决同样的问题,我为 tenforty 添加了一个更高级的接口,提供熟悉的税表项目的自然名称,例如 w2_income。所有可用选项都在 repo 的 README 中列出。
这个更高级的接口对某些人来说会有限制性,因为我只实现了相当常见的项目。例如,如果你有小企业,会有一些缺失的输入。有两种方法来解决这个问题:
tenforty 目前有一个较低级别的接口,你可以在其中提供任何行级输入作为 Python 字典,评估申报表,并得到一个包含所有行级输出的字典。
tenforty 是开源的,鼓励人们用改进提交 PR。
DEVELOP.md 文档详细说明了该包的工作原理。
尽管 OTS 可以追溯到 Bush II 政府时代,我只在 tenforty 中包括了 2018 到 2023 年度税,并且我只包装了加州(我们的所在地)、马萨诸塞州和纽约州,尽管 OTS 支持更多州。更多年份/州/等可以通过 PR 添加。
已经有一些基本的单元测试,然后是使用 hypothesis 库的一些基于属性的测试,通过生成各种输入并检查某些属性是否成立来运行 tenforty/OTS,例如"如果你的 W-2 收入增加,其他所有条件不变,你的总税款必须总是相同或更高。"
我进一步验证了我们过去几年外部准备的申报表通过 tenforty 给出相同的答案。样本量不大,但原则上可以构建一个更大的参考申报表计算测试集。
这是说,有真诚的努力来测试这个包,但测试方法肯定可以改进。欢迎 PR。:)
也许 TurboTax 不是最好的,但用 Open Tax Solver 的 GUI 做假设怎么样?OTS 的 GUI 也不是真正为假设设计的,所以这个过程会相当费力,尽管比 TurboTax 少一些。OTS FAQ 预见并为这个用例提供了一些指导(参考):
Q: 我可以使用 OTS 在全年进行快速的"假设"吗?例如,我想理解出售一些共同基金、额外工作时间或延迟 401K 或 IRA 收入的税务后果?
A: 可以。你可以在任何行中输入暂定值,保存到反映你实验的文件名,如"1040_more_hours.txt"。运行求解器并比较你税务改变前后。更好的是,你可以编写一个脚本扫描某些值,如收入或资本利得或损失,并在每种情况下运行求解器。然后你可以绘制结果。(你可能无法用任何商业包来做这件事,或者根本无法做到。)你可能会发现你的税务情况相当非线性。在全年期间意识到临界点是有帮助的,当你仍然可以对其采取措施的时候。
在某种意义上,tenforty 的目标是使编写这样的值扫描脚本变得更容易。↩︎
我们最近重新看了《浪子回头》……↩︎