我们如何构建 Linear Agent

HN I Built AI Coding 2026-08-11T12:22:11.321893

Abstract line illustration of overlapping solid and dashed ellipses within a rectangular grid, suggesting multiple possible paths constrained within a defined structure.

Abstract line illustration of overlapping solid and dashed ellipses within a rectangular grid, suggesting multiple possible paths constrained within a defined structure.

大多数软件的设计目标都是行为一致。在传统观念中,好的工程往往意味着不断缩小可能结果的取值范围,直到同一个操作总能稳定地产出相同的结果。

AI 至少在某种程度上颠覆了这套逻辑。Linear Agent 的很大一部分价值,在于它能以我们从未明确定义的方式,完成我们未曾预料到的工作;如果把它行为脚本定得太死,反而会削弱这种让它变得有用的灵活性。所以,我们没有为 Linear Agent 设计一条固定的执行路径,而是划定了一些边界,让它在边界内自主探索。

这些边界体现在几个方面:agent 的系统提示词(system prompt)、工具的设计、agent 对 Linear 产品的理解模型、每一次运行的范围,以及底层的自定义运行框架(harness)。

系统提示词

我们把 Linear Agent 的提示词聚焦在少量核心要素上。

沟通方式

agent 的沟通要清晰自然,既不要太商务腔,也不要太随意。它还应该根据所处平台调整语气;比如,在 Slack 里可以更口语化一些,而在 Loop 中则不必。

硬性边界

agent 不应对政治、宗教、个人关系等敏感话题发表意见。如果用户的请求范围被大幅扩展,agent 也必须先获得确认,而不能擅自行动。

对 Linear 特有概念的解释

agent 需要理解产品的分类体系(taxonomy),才能正确解析用户请求,并代表用户采取合适的操作。

关于某些功能用法的默认建议

用户提示往往非常简短,因此智能体需要具备敏锐的判断力:什么时候该揣测用户的意图,什么时候该追问澄清。

像这样一个高层次的系统提示,既为智能体指明了大方向,又给它留出了临场发挥的空间。比如在 Slack 里,Linear Agent 会捕捉对话的氛围,适时插一句恰到好处的玩笑;同时,提示词里划定的边界又能确保它在玩笑涉及敏感话题时不轻易介入。

工具设计:把约束写进工具本身

我们发现,与其把约束一条条写进提示词,不如直接设计进 Linear Agent 的工具里,效果更好。我们精心打磨每个工具,让它的参数易于理解,也让无效操作难以执行。这个思路与写出好的代码抽象或用户界面如出一辙——直观的行为不需要额外解释。

这样做还给智能体留下了成长空间:随着模型能力的提升,它可以变得更强。因为动作空间没有被过度脚本化,工具之间可以灵活组合,更聪明的模型就能协调这些工具,应对越来越复杂的任务。

智能体眼中的 Linear

要让智能体自由发挥,前提是它得理解自己身处的环境。对 Linear Agent 来说,这意味着要教会它产品的结构和语义——这个挑战和构建编程智能体(coding agent)截然不同。

编程智能体只握有少数几个深度工具,比如 read_filewrite_filerun_command,它所做的几乎每一件事,都是这些基础操作组合的结果。代码和 shell 命令在模型的训练数据中占比很高,所以这类智能体即便面对陌生情况,使用这些工具的直觉也往往很好。

Linear Agent 的设计思路正好相反:它配备了大量相对轻量的工具,每个工具都对应一个具体的产品操作,比如创建 issue(可以理解为任务/工作项)或修改文档。要正确使用这些工具,还需要理解一些产品概念,而模型在现有知识里对这类概念覆盖得并不好——Linear 也许出现在训练数据中,但和代码、shell 相比,占比几乎可以忽略不计。

因此,要让 Linear Agent 能操作产品的任意一部分,需要三样东西:

以「客户请求」(Customer Requests)功能为例。它对应的系统技能为 agent 提供了创建、更新和列出请求的工具,可以在 issue、项目和客户之间灵活操作;同时,技能会解释每条请求记录的是某位客户的具体反馈,通常关联到一个 issue 或项目,并概括了客户提出了什么需求。此外,技能里还内置了使用该功能的最佳实践——比如在起草项目规格(project spec)时,它会引导 agent 去汇总多条客户请求中的共性模式。

单次运行的覆盖范围

下一个挑战是:如何把这些上下文和工具交给 agent,又不至于让它不堪重负。由于大多数用户请求只涉及 Linear 产品功能的一小部分,我们引入了「系统技能」(system skills),作为 agent 的组装单元。

每个系统技能打包了三样东西:一些基础元数据、一小段系统提示词(system prompt)和一组工具。合在一起,它们代表一组独立的能力,可以在需要时按需渐进地开放给 agent。(这和用户自定义技能是两回事——后者是用户为了自己的工作流自行编写的可复用指令,构建在 agent 之上,不属于这里介绍的内置系统技能。)

系统技能在后台加载,要么在代理开始处理任务之前,要么在任务展开过程中按需加载。每次运行前,Linear Agent 会根据用户提示及调用它的上下文推断哪些技能可能相关。例如,从项目相关的 Slack 频道或 Linear 页面调用的代理,很可能会预加载“projects”技能。

更间接的任务可能会在过程中逐渐暴露额外需求。如果用户要求代理为项目起草一份更新,我们可能最初只加载“projects”和“project updates”技能。随着工作推进,代理可能会意识到需要审查最近的问题和 PR,然后自行加载相应技能。

这样既能保持每个线程的上下文足够聚焦,又让整个 Linear Agent 支持广泛的功能;同时也让我们能逐渐扩展代理的能力,而不必用越来越长的提示和工具集拖累每一次交互。

在构建过程中,我们考虑过让代理直接访问 Linear SDK、编码环境、CLI 或 GraphQL API。暴露这些底层原语或许能带来更复杂的行为,但也会扩大出错的空间。

底层数据模型支持广泛的操作,而部分 UI 概念需要先经过解释才能映射到该模型。一个可能的失败模式是长尾请求:当代理找不到明显的完成路径时,可能会通过越来越投机性的操作来尝试完成任务。

我们权衡后决定用一定的广度换取可预测性。代理偶尔会拒绝无法安全完成的任务,但它犯错的余地小得多。

底层定制框架

在底层,Linear Agent 运行在我们从模型提供商 API 向上完全自有的定制技术栈上。它包括:

我们之所以选择自研技术栈,是因为现成的 Agent 框架(harness library)往往对执行流程有很强的预设:先把 prompt 和一组工具传进去,调用 run,然后等着 agent 依次调用工具、最终给出回复。这种模式应付简单交互没有问题,但我们希望对 Linear Agent 的执行有更细粒度的控制,以支持运行过程中需要的各种编排行为。

通过技能动态注入工具

当 agent 加载某个技能时,它需要在工具调用返回后立刻拿到该技能附带的工具,这样才能在回复用户之前继续调用这些新工具。我们还希望在实现时保留提供商的前缀缓存(prefix cache),否则缓存一旦失效,重新处理已有的上下文会明显推高成本。

有条件的工具调用审批

当某个操作有风险或难以撤销时,agent 应该在运行中途停下来,先征求用户确认再继续。大多数框架只支持粗粒度的审批规则,仅凭工具名称和参数判断是否需要确认;而我们希望审批逻辑能结合更多上下文。例如,agent 删除自己在本轮对话中创建的内容时无需询问,但删除一个已有的 issue 之前必须确认;同样,它可以在大多数评论线程中直接回复,但如果目标评论会同步到公开仓库,就应该停下来确认。

子代理的异步执行

当子代理执行时,父代理往往会连续空闲几十秒。对父代理来说,这看起来应该像一次同步工具调用;但在底层,父代理需要被挂起,这样在等待子代理期间就不会占用服务器资源。也就是说,我们得能够在一次工具调用进行到一半时挂起 agent 的回合,异步完成后续工作,等一切就绪后再恢复执行。

这些编排模式就算不用自研执行框架(harness)也能实现,但硬套现成方案会非常别扭,因为它们要兼容各种不同类型的 agent。我们按照 Linear Agent 自身的编排需求来设计执行框架,这样既能保持优雅高效,又能把心思放在打磨产品体验上。

自研执行框架的另一个好处是,可以快速响应规模化之后冒出来的各种长尾错误和边缘情况,尤其是使用模型提供商的一些新特性时——这类功能往往还没有被广泛采用,现有库也没来得及充分验证。

不断变化的目标

查看原文