商业产业 Hacker News AI 2026-10-05T22:05 正文 ok 1282 words

为你的 AI Agent 打造领域特定语言(DSL)

英文标题:🔶 Give Your AI Agent a Domain-Specific Language

大语言模型(LLM)目前普遍面临缺乏持久记忆与受限上下文窗口两大工程瓶颈,若强行让模型处理底层繁琐的执行细节,极易导致错误率上升与性能下滑。财务建模平台 Modeloptic 提出,通过为 Agent 封装一套领域特定语言(DSL)及高级抽象工具,能将复杂的逐单元格操作压缩为单次语义函数调用,彻底剥离与业务本质无关的“附带复杂度”。该实践表明,将确定性流程交由传统软件执行、仅将 LLM 用于高层逻辑判断,不仅能大幅提升响应速度与准确率,还能原生支持结构化变更日志。这一基于 DSL 的高阶抽象设计,为垂直行业构建高可靠、高复杂度的 AI Agent 系统提供了极具价值的落地范式。
打开原文相关 3 · 机会 1
media2.2 KB media21.6 KB media44.4 KB media492.1 KB media515.8 KB media495.8 KB media261.2 KB

中文

AI 驱动的财务建模终于可行了。使我们做到这一点的关键要素之一,就是为我们使用的 LLM 提供了一门可供其操作的领域特定语言(Domain-Specific Language,简称 DSL)。如果你正在将 AI 用于需要反复执行的复杂任务,却遇到了有限或不稳定的效果,那么 DSL 可能会帮助你解锁所需的能力。

为什么需要 DSL?

LLM 目前存在两大主要约束:

  • 它们有“健忘症”: 除非你在特定领域对模型进行微调训练,否则每次与它交互时,你都必须重新灌入任何你希望提供给它的领域相关信息。
  • 它们受到上下文限制: 它们在同一时间只能处理有限的信息量。工作记忆中的信息越多,表现就越差。“信息”还包括用于思考推理所消耗的 Token。

因此,“上下文工程”(Context Engineering)已成为激发 LLM 性能的关键实践。Agent 需要应对的复杂度越低,我们在准确性和速度方面的预期表现就越好。

由此可以得出结论:我们应当尽可能从 LLM 的职责范围内剔除“附带复杂度”(incidental complexity,即并非源自所要解决问题本身的额外难度、开销或摩擦)。 为你的 Agent 构建一套 DSL,正是实现这一目标的策略之一。

案例研究:财务建模

财务建模是 AI 工具的热门领域。财务模型通常在 Excel 中构建,因此将 AI 引入该问题的第一步很自然地就是以插件的形式在 Excel 上“撒一点 AI”。

我们认为这是一条行不通的路线,因为除了其他原因之外,它迫使 LLM 必须以 Excel 逐个单元格的数据格式来表达财务模型逻辑,从而不得不面对过度且不必要的复杂度。

相反,我们开发了一套用于描述财务模型逻辑的 DSL,使我们的 Agent 能够在更高的层级上运行,完全无需操心 Excel 的底层机制。以一个具体示例来说明,下面是一种极其常见的财务建模模式,称为“硬编码顺延”(hardcode carry-forward):

Excel 中包含 3Q26 和 3Q27 硬编码的“硬编码顺延”行

其核心逻辑是:你在任意期间输入一个硬编码假设值,该数值就会平推(顺延)到后续期间,直到输入新的硬编码假设值,新的假设值又会继续顺延,直至达到预测范围的末尾。在上述示例中,我们在 2026 年第 3 季度(3Q26)和 2027 年第 3 季度(3Q27)输入了硬编码数值,并设有对各年份季度求和的全年汇总列。

对比一下在 Excel 中运行的 LLM 要写入这样一行需要做些什么,以及在 Modeloptic 中运行的 LLM 需要做什么:

__通过 Excel 插件写入该行(9 个步骤)

  1. 确定要写入的工作表(Sheet)
  2. 确定该行应写入的行号
  3. 确定写入该行标签的列,并写入标签
  4. 确定哪些列对应哪些期间,并记住它们之间的映射关系(例如记住 1Q26 是 C 列,2Q26 是 D 列等)。正确识别出 G 列和 M 列是年度汇总,而 H 列是空白间隔列
  5. 识别出这是一种“硬编码顺延”模式,并开始将该知识映射到当前特定场景
  6. 判定 E4 单元格应写入硬编码的 25,K4 单元格应写入硬编码的 50;在其知识库中检索到硬编码输入应使用蓝色字体,并将这些值写入这两个单元格
  7. 判定 1Q26 和 2Q26 都应包含硬编码的 0 且同样应为蓝色字体,确定对应的是 C4 和 D4 单元格,并写入它们
  8. 判定其余季度列应引用前一季度,为每个单元格构建正确的公式地址;记住在跨年份边界时,引用的单元格并非紧邻,而是相隔 3 个单元格,然后将它们全部写入(F4 写入 "=E4",I4 写入 "=F4" 等)
  9. 记起 G 列和 M 列是年度汇总,推断出这意味着它们应分别对该行前 4 个单元格求和;回忆并决定应使用“sum”函数,构建两个公式(G4 写入 "=SUM(C4:F4)" 等),并写入它们

__在 Modeloptic 中写入该行(3 个步骤)

  1. 识别出这是一种“硬编码顺延”模式
  2. 在其系统提示词(System Prompt)中回忆起它拥有一个可用于写入该模式的工具,并阅读工具描述以了解如何使用它
  3. 携带正确的参数调用该工具,大致格式如下:

text operation="add logic row" forecast-option="hardcode carry-forward" label="# of New Subscribers" values="3Q26:25, 3Q27:50"

在 Excel 中,仅为了写入单一简单的数据行,就需要管理海量细节。而在我们的 DSL 中,只需一次函数调用,其余工作全部由 LLM 职责范围之外的代码处理。通过缩短“硬编码顺延”这一业务概念与其在数据中具体表示的执行路径之间的距离,我们能够让 LLM 发挥出更出色的性能。Agent 不必把有限的上下文浪费在单元格地址和低级 Excel 机制上,而是可以更多地在概念层面进行操作。

为你的业务领域打造 DSL

为你的 Agent 提供 DSL 的核心原因,是让它的工作尽可能简单。 它的工作越轻松,做对的概率就越高,执行速度也会越快。

DSL 可以通过减少 Agent 的思考负担,并将其职责范围内的繁琐工作彻底剥离,从而让它的工作更轻松。用软件代码去执行任务中确定性的部分;仅在需要主观判断时才使用 LLM。不要让执行细节拖垮你的 Agent;让它专注于更高层级的概念。

需要明确说明的是,我们这里讨论的不仅仅是给你的 Agent 增加更多可调用的工具(尽管工具可能是让 Agent 与你的 DSL 交互的最佳方式)。我们谈论的是找到恰当的抽象层,使思维到结果之间的距离尽可能缩短。 从这个意义上说,这种实践在概念上与优秀的软件工程或产品开发并无二致。

这里有几个简单的思路:

  • 假设你想让 Agent 在特定情况下通过电子邮件向客户发送账单。不要让 Agent 直接去操作 SMTP(底层邮件发送协议),而是给它一个 send_invoice 工具
  • 假设你想让 Agent 读取销售人员的收件箱,并将遗漏的信息更新到 CRM 中。不要让 Agent 直接与 SQL 数据库打交道,而是给它一个 update_status 工具
  • 假设你的 Agent 负责撰写定制化的客户合同。不要让它每次都输出自由格式的文本,而是给它一个 insert_section 工具,让它能够直接调用预先编写好的特定条款

在战术层面上,你可以通过以下方式向 Agent 暴露你的 DSL:

  1. 定义它可以调用的工具
  2. 通过工具描述(Tool Descriptions)、系统提示词(System Prompt)和技能(Skills)的某种组合,为这些工具的参数提供文档说明

具体哪种方式最理想取决于你的应用场景,但某项工具的使用频率越低,将其文档化为技能(Skill)的理由就越充分。

除了降低复杂度之外,编写合理的 DSL 还能带来另一个显著优势:结构化变更日志(Structured Change Log)。如果你让 Agent 通过一系列预定义的操作列表与系统交互,并由你的底层代码负责执行,你就能为用户提供一份 Agent 代其执行的所有操作的完整清单。相比之下,让 Agent 直接随意直接编辑数据则无法做到这一点。

每个业务领域都有其独特的细微差异;希望本文能为你解决自己领域的问题带来一些启发。

English / 原文

AI-powered financial modeling finally works now. One of the key ingredients that made that possible for us is we gave the LLMs we use a domain-specific language (DSL) in which to operate. If you're using AI for something complicated that needs to be done repeatedly and are seeing limited or inconsistent success, a DSL might help you unlock the capability you seek.

Why a DSL?

LLMs currently have two major constraints:

  • They have amnesia: Unless you train your own model on your particular domain, it must re-ingest any information you want to give it related to your domain every time you talk to it.
  • They are context-constrained: They can only manage so much information at one time. The more information they have in working memory, the worse they perform. "Information" includes tokens spent on thinking.

As such, "context engineering" has become a vital practice to extract performance out of LLMs. The less complexity the agent needs to deal with, the better we can expect it to perform, both in terms of accuracy and speed.

It therefore follows that we should remove as much "incidental complexity" (the extra difficulty, overhead, or friction that does not come from the actual problem being solved) from an LLM's purview as we can. Creating a DSL for your agent to use is one strategy for accomplishing this.

Case Study: Financial Modeling

Financial modeling is a hot area for AI tools. Financial models are typically built in Excel, so naturally the first step to bringing AI to bear on the problem has been to sprinkle some AI onto Excel in the form of plug-ins.

We think this is a losing approach because it forces the LLM to deal with excessive levels of unnecessary complexity by forcing it to express financial model logic in Excel's cell-by-cell data format, among other reasons.

Instead, we made a DSL for specifying financial model logic so our agent can operate at a higher level, and not worry about Excel mechanics at all. To walk through a specific example, below is an extremely common financial modeling pattern called a "hardcode carry-forward":

A hardcode carry-forward row in Excel with hardcodes in 3Q26 and 3Q27

The idea is that you enter a hardcoded assumption in any period, and that value is flatlined (carried forward) into subsequent periods until a new hardcoded assumption is entered, which itself is carried forward, until we reach the end of the forecast range. In the above example, we have hardcodes entered in 3Q26 and 3Q27, and full year total columns that sum up the quarters of each year.

Consider what an LLM operating in Excel needs to do to write such a line, compared with what an LLM has to do using Modeloptic:

__Writing the row via an Excel plug-in 9 steps

  1. Identify the sheet on which to write
  2. Identify the row number in which this row should be written
  3. Identify the column in which to write the row's label, and write it
  4. Identify which columns correspond to which periods, and remember the mapping between them (eg remember that 1Q26 is column C, 2Q26 is column D, etc). Correctly identify that columns G and M are year totals and column H is a blank spacer column
  5. Identify this as a hardcode carry-forward pattern, and begin mapping that knowledge to this particular situation
  6. Decide that a hardcoded 25 belongs in E4, a hardcoded 50 belongs in K4, find in its knowledge that hardcoded inputs should carry blue font, and write the values to the two cells
  7. Decide that 1Q26 and 2Q26 should both have hardcoded zeroes that should also be blue font, identify that those are cells C4 and D4, and write them
  8. Decide that the remaining quarterly columns should reference the prior quarter, construct the proper formula addresses for each, remember that when crossing a year boundary the cell to reference is not immediately adjacent but is instead 3 cells over, and write them all (F4 writes "=E4", I4 writes "=F4", etc)
  9. Recall that columns G and M are year totals, figure out that this means they should each sum the 4 preceding cells in the row, recall and decide that the "sum" function should be used, construct the two formulas (G4 writes "=SUM(C4:F4)", etc), and write them

__Writing the row in Modeloptic 3 steps

  1. Identify this as a hardcode carry-forward pattern
  2. Recall in its system prompt that it has a tool available that will write this pattern, and read the tool description to figure out how to use it
  3. Call the tool with the proper arguments, which is roughly:

operation="add logic row" forecast-option="hardcode carry-forward" label="# of New Subscribers" values="3Q26:25, 3Q27:50"

In Excel, that's a ton to manage to write a single simple row. In our DSL, it's one function call, and the rest is handled by code outside of the LLM's purview. By shortening the distance between the "hardcode carry-forward" concept and the actual execution of representing it in the data, we are able to get much better performance out of the LLM. Instead of wasting finite context on cell addresses and low level Excel mechanics, the agent can operate much more in the realm of concepts.

Making a DSL for Your Domain

The main reason to give your agent a DSL is to make its job as easy as possible. The easier its job is, the more likely it is to do it correctly, and the faster it will perform.

A DSL can make your agent's job easier by giving it less to think about, and by removing work from its purview altogether. Execute the deterministic parts of the task with software; use the LLM only when judgment is needed. Don't bog down your agent with execution details; let it focus on the higher level concepts.

To make one thing clear, we're not just talking about giving your agent more tools that it can call (although tools are probably the best way to allow your agent to interact with your DSL). We're talking about finding the right abstractions to make the distance between thought and result as short as possible. In that sense, this exercise is not all that different conceptually from doing good software engineering or product development.

Here are a few simple ideas:

  • Say you want your agent to send customer invoices via email under certain circumstances. Don't make the agent interact with SMTP (the raw email sending protocol), give it a send_invoice tool
  • Say you want your agent to read salespeople's inboxes and update the CRM with anything missing. Don't make the agent interact with a SQL database directly, give it an update_status tool
  • Say your agent writes tailored customer contracts. Don't just have it output free form text every time, give it an insert_section tool that lets it call up specific pre-written provisions

Tactically, you can expose your DSL to your agent by:

  1. Defining tools it can call
  2. Documenting the arguments to those tools in some combination of the tool descriptions, the system prompt, and skills

Which is optimal will depend on your use case, but the less frequently a tool will be used, the stronger the case for documenting it in a skill.

Beyond reducing complexity, there's another big advantage that a properly written DSL can help enable: A structured change log. If you have your agent interact with your system via a defined list of operations that your code then executes, you can provide your users with a full list of every action the agent prescribed on their behalf. Contrast that with an agent that does its own haphazard editing of data directly.

Every domain is going to have its own nuances; I hope this post has helped spur some ideas for addressing your own.