构建优秀的垂直领域 Agent:以 Shortcut 为例 ✍ Peter Wang🕐 2026-06-12📦 21.4 KB 🟢 已读 𝕏 文章列表 本文探讨了如何构建一个在实际应用中表现卓越的垂直领域 Agent。作者结合开发 Shortcut 电子表格 Agent 的经验,提出核心原则是“对任务分布的忠实压缩”。文章详细介绍了“分层缓存”架构,将上下文分为 L1(常驻核心)、L2(按需规格)和 L3(兜底参考),以平衡效率与准确性。同时强调了“单工具优于多工具”的策略,主张通过代码执行来统一能力,降低模型决策复杂度,从而在长尾任务中实现高准确率。 AgentLLM架构设计上下文管理垂直领域性能优化Tool Use代码执行缓存策略 # Building a Good Vertical Agent **作者**: Peter Wang **日期**: 2026-06-11T21:51:48.000Z **来源**: [https://x.com/BrainsAndTennis/status/2065190286519906657](https://x.com/BrainsAndTennis/status/2065190286519906657) ---  How do you build an agent that actually performs in a domain — one customers pick because it's better? The basics have been standardized over the past year: an agent is a while-loop around a model that calls tools until the task is done. Give it a filesystem, give it a shell, and let it do most things through that. You can write it in an afternoon, and most people have. Everyone can build an agent — it really isn't that hard, and, as I'll spell out, it isn't that deep either. What separates a good one from a toy isn't cleverness; it's a real understanding of your domain and the patience to do some tedious, careful work in the few places that matter. I've spent almost a year now building the Shortcut agent, which is widely considered the most accurate spreadsheet agent around — it's deployed inside three of the largest four multistrategy hedge funds, where being wrong is expensive and nobody grades on a curve. We don't have Microsoft's or Anthropic's distribution. What we have is that the agent is right more often, and in this domain that has been the single most compelling reason customers pick us. So agent performance is the question I think about all day. And here's the gap I keep running into: plenty is written about building agents, but few about building good ones. Look at how much the field varies on something as basic as tool count — Codex and Claude Code ship ~30 tools each; Pi ships 7. When popular agents disagree 4x on the most basic design question, it's a tell: there's no agreed-on principle. So I'm sharing mine, from a year of building one, to demystify the process for anyone writing their own. Here it is: a good agent is a faithful compression of its task distribution. The rest of this is just what that means, and what it forces you to build. ## Context as a layered cache Assume you don't own the environment and you didn't train the model. Then three things are yours to design — the system prompt, the tools, and the artifacts (skills, curated docs, references) — and they're all the same thing: the agent's context. So the game is simple to state. With the model fixed, accuracy is a function of context quality: bloated context buries the signal, missing context forces guessing, and both cost you accuracy. And accuracy is what you're selling — the relationship isn't linear, a task that scores 99% is worth 10x more than one that scores 95%. But your users don't bring you a uniform distribution of problems to solve. They bring you a long tail: ``` how often | | ████ | ████ | ████ | ████ | ████ | ████ | ████ | ████ | ████ | ████ ▓▓▓▓ | ████ ▓▓▓▓ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ +----------------------------------------------------> task variety ████ bread-and-butter the bulk of every session ▓▓▓▓ crucial-but-occasional a handful of times a session ░░░░ the long tail each one rare — but there are many, and each still has to work ``` The agent has to handle all of it. But it cannot hold the union of everything in context at once — that's the bloated-prompt failure mode. So the real objective is sharper than "have everything available": minimize the context spent per task, averaged over the task distribution. This is exactly the problem a CPU faces. A program might touch gigabytes of data, but the storage right next to the processor is tiny — so computers stack memory in tiers: a small, instant cache (L1), bigger-and-slower ones below it (L2, L3), then main memory and disk. It works because access is long-tailed too: keep the hot set in the fast tier, reach down to the slow tiers only for the rare stuff. A "cache miss" is when what you need isn't in the fast tier and you pay to fetch it from a slower one — exactly the cost you're avoiding on the common path. Agents should have the same structure. Build your context as L1 / L2 / L3. ``` +---------------------------------------------+ L1 | ALWAYS RESIDENT - tiny, instant. | | The 80%. Lives in the system prompt. | +---------------------------------------------+ | miss -> one cheap call v +---------------------------------------------+ L2 | ON DEMAND - curated English specs. | | The next ~15%. One discovery step to load. | +---------------------------------------------+ | miss -> read the skill, then search v +---------------------------------------------+ L3 | ESCAPE HATCH - the raw API tome. | | The long tail. 3-6 grep calls to mine. | +---------------------------------------------+ ``` Almost every optimization trades compression of information against speed of discovery. Put something in L1 and it's instant, but it costs prompt tokens on every single task whether it's used or not. Push it to L3 and it costs nothing until needed — but then it costs several tool calls to find. Your job is to place each capability at the tier that minimizes total cost across the distribution. That's the whole craft. Let me make it concrete with the domain I know best. ## Aside: one tool, not thirty Before the hierarchy, the substrate. Every spreadsheet capability I'm about to describe — every read, every write, every curated lookup — is code executed under a single tool. ``` async function execute() { const data = await sheet.getCellRange("Sheet1!A1:D200"); // ...read, compute, write... } ``` The agent writes code; the code calls our functions; the functions touch the sheet. There is no read_range tool, no write_range tool, no make_chart tool. There is one tool, and the API lives inside the code. Why? Because model accuracy degrades as you add tools. That's been consistent in our own experiments. Every tool you add is more schema in the prompt, more surface to confuse, more ways to pick the wrong one, especially if the tools occupy overlapping responsibilities. A single execute_code tool collapses all of that into one decision — write code — and lets the model compose capabilities with the full expressive power of a programming language or DSL instead of stitching together rigid tool calls (more on this in a future post). This matters for the hierarchy because it means all three cache tiers are reachable from the same place: the model is always writing code, and L1/L2/L3 are just which functions it knows it can call, and how much work it had to do to find them. ## L1 — the bread and butter: reading and writing cells This is the 80%. If reading and writing cell ranges isn't excellent, nothing else matters. So this is where we've spent absurd, disproportionate effort. Look at what a single getCellRange actually does. Reading a range is an act of compression Reading a 200-row revenue table: ``` Common formulas are abbreviated like F1, F2, etc. A2:North | B2:1200 | C2:9.99 | D2:11988(F1) A3:South | B3:840 | C3:9.99 | D3:8391.6(F1) A4:West | B4:1500 | C4:9.99 | D4:14985(F1) ... (196 more rows, each one line) ... =F1 -> =RC[-2]*RC[-1] --- Style patterns --- D2:D201: 200 cells (numbers) → numberFormat:#,##0.00, font.color:#1A7F37 A2:A201: 200 cells (text) → font.bold:true --- Context from cells above --- A1:Region | B1:Units | C1:Price | D1:Revenue ``` Three things are happening. First, formula aliasing. A 500-row column of =A2*B2, =A3*B3, … is 500 near-identical formulas. We normalize each formula to R1C1 form — so =A2*B2 and =A3*B3 both become =RC[-2]*RC[-1] — count the patterns, and any pattern that appears more than ten times collapses to a short alias like F1. The model sees F1 repeated plus one legend line, instead of 500 formulas. Big token savings, zero information loss. Second, free row and column context. When you read C5:E20, what do those bare numbers mean? We scan leftward for the row labels and upward for the header row (picking the header by voting on which nearby row has the most text cells) and attach them, so the model gets Region | Q1 | Q2 and North America | … for free and never has to guess what a grid of numbers represents. Third, style compression. Formatting is information too — a bold red cell with a 0.00% number format is telling you something — but listing the full style of every cell would swamp the values. So we group cells by identical style, collapse each group to its connected range, and print one line per group: the range, the cell count, and a compact description. Six hundred formulas became one legend line. Four hundred styled cells became two lines. And the header row the model never explicitly asked for is right there at the bottom. That's the whole table, losslessly, in a fraction of the tokens a raw dump would cost. Every one of these is the compression-vs-discovery tradeoff, won decisively for the common case. Writing cells: tell the model what it actually changed, and what looks wrong Writing is harder than it looks, because a single execute_code call can change hundreds of cells, and the agent needs to know what happened without re-reading the whole sheet. So after the code runs, we hand back a structured diff of every cell that changed — and, just as importantly, we compress and triage it. The code: ``` async function execute() { const rows = await sheet.getCellRange("Sheet1!A2:C201"); for (let i = 0; i < rows.length; i++) { const r = i + 2; await sheet.setCell(`D${r}`, `=B${r}*C${r}`); } } ``` The diff that comes back: ``` --- CELL DIFF SUMMARY --- (Formatted display values shown. ∅ = undefined/empty.) Changed without issues: 199 total cells Sheet1!Row 2 (D): 1 cells → D2: ∅ -> 11,988 [=B2*C2] Sheet1!Row 3 (D): 1 cells → D3: ∅ -> 8,391.6 [=B3*C3] ... (sampled rows) ... Sheet1!Row 201 (D): 1 cells → D201: ∅ -> 4,995 [=B201*C201] ... and 189 more rows Cells that need review: MUST FIX: INVALID_FORMULA: 1 total cells Sheet1!Row 57 (D): 1 cells → D57: ∅ -> #REF! [=B57*C57] ``` Two kinds of compression are doing the work here. First, the diff is grouped and sampled, not dumped. Changed cells are grouped by sheet and row, each row shown as a column range with a count (Row 2 (D): 1 cells), and only a deterministic sample of cells per row and rows per section is printed, with "… and N more" tallies for the rest. Two hundred writes become a handful, and the agent still knows the totals. Second, the diff is categorized. Clean writes land under "Changed without issues." Anything that looks suspicious — an invalid formula like #REF!, an untagged hardcoded number, a hardcoded number buried inside a formula, an implausibly large percentage — gets pulled into a "Cells that need review" section, and the worst offenders are flagged MUST FIX. That #REF! in row 57 would be trivial to miss in a wall of two hundred green diffs; here it's surfaced at the top with a label. The feedback loop isn't "here's what changed," it's "here's what changed, and here's the part you probably got wrong" — a built-in linter on the agent's own edits. L1 in one line: the operations on the steep part of the curve get feature-engineered, token-compressed, consequence-reporting wrappers that live in the prompt forever. They're expensive to build, and you build them anyway, because the agent pays the cost on every task. ## L2 — curated English, on demand You cannot put everything in L1. Conditional formatting, pivot tables, charts, data validation, copy/move semantics — each is important, each shows up a few times a session, and each has enough surface that documenting it in the system prompt would bloat every task that doesn't use it. Classic L2. So we wrote curated capability specs in English, fetched on demand, exactly like skill mds. The model calls, from inside its code: ``` console.log(general.getConditionalFormattingInfo()); console.log(general.getPivotTableInfo()); console.log(general.getChartInfo()); console.log(general.getDataValidationInfo()); console.log(general.getAPIInfo("addSpanAt")); // any single function, by name ``` These aren't dumps of type signatures. They're hand-written prose — a few hundred lines each — that describe the canonical way to accomplish the task, including the knowledge the raw API will never give you. Take the pivot-table spec. It doesn't just list methods; it teaches the whole recipe, in the right order: ``` const pt = sheet.originalSheet.pivotTables.add("SalesPivot", "SalesData", 0, 0, ...); pt.suspendLayout(); pt.add("Region", "Region", rowField); pt.add("Quarter", "Quarter", columnField); pt.add("Amount", "Sum of Amount", valueField, 8); // 8 = sum pt.resumeLayout(); ``` and it bakes in the things you would otherwise learn only by failing repeatedly: that you must suspendLayout()/resumeLayout() around a batch of changes or the table rebuilds on every call; that a value field's aggregation has to be passed as a raw integer (8 for sum) because the friendly enum doesn't exist at runtime. None of that is a quirky footgun — it's the actual shape of doing pivots correctly, written down once by someone who already paid for it. The key property: this costs zero tokens until the task needs it. A task that never touches pivots never pays for the pivot docs. One console.log is the entire discovery cost — a single cache miss, served fast. The same idea, for executable tools L2 isn't only for docs. We apply the identical pattern to deferred tools — web_search, web_crawl, create_website, etc. Their schemas don't sit in the prompt. Instead there's a meta-tool wall: ``` get_tool_info("web_search") → returns the schema, marks it "fetched" execute_tool("web_search", …) → REFUSES unless you fetched it first ``` The set of fetched tools is, literally, a session-scoped cache. The model loads a schema once, and from then on it's resident. Same compression-vs-discovery tradeoff, same resolution: keep the prompt small, pay a one-step miss when you actually need the capability. This is the same idea as deferred tools on Claude but we're not locked to one vendor's tool-loading feature to get the behavior. ## L3 — the raw tome, and the skill that maps it Then there's the long tail: the one obscure thing we never wrapped and never wrote a spec for. You can't anticipate it — by definition. But the agent still has to be able to get there, or it hits a wall and fails the task. Concretely, this is where requests like these end up: - "Add a sparkline to each row summarizing its trend" — sparklines are a real but rarely-touched API surface. - "Set the chart's secondary axis to log scale and recolor just the third series" — a chart property three levels deep that no curated spec bothered to cover. - "Insert a hyperlink from this cell to that named range, and group these shapes" — drawing/shape/hyperlink corners nobody asked about until now. So L3 is the complete raw API — the entire Office.js surface (Excel plugin) or the entire SpreadJS surface (Shortcut web), dumped to disk. It's a machine-generated reference that is 70k lines long. It contains everything. It's also completely unusable as prompt context — you'd never paste it in. The trick: you give it a skill — a short map that teaches it how to mine the tome with bash: ``` # from the advanced-api SKILL.md — the recommended workflow grep -n '"charts.add"' api-reference.json -A 5 # find a method grep -n '"pivots\.' api-reference.json | head # list a namespace grep -n '"ChartConfig"' api-reference.json -A 10 # resolve a type grep -n '"isEnum": true' api-reference.json -B2 -A10 # enumerate enums ``` The skill is ~100 lines. It says: here's the structure, here's how each method and type entry is shaped, here's the grep recipe for each kind of question. With it, the agent goes from "tens of thousands of lines I can't read" to "the 3-6 greps that surface exactly the signature I need." That's the L3 access cost — real, but bounded, and only paid by the rare task that reaches this deep. And the system prompt makes the escape hatch explicit, so the model knows the path exists and when to take it: > API HIERARCHY — There are 2 levels of API capability. Wrapped API: convenience functions; some listed directly, others via getAPIInfo(...). NEVER guess — read the docs in FULL. Raw API: use when the wrapped API doesn't cover your need… If the wrapped API can't do it, use the raw API — don't compromise. That last clause is the whole point of L3. The agent should never be stuck. It can miss in L1, drop to L2, and if even the curated spec is silent, descend into the raw tome and still come out with the answer in a sane number of calls. ## How the prompt budget actually splits It's worth looking at where the tokens go, because the hierarchy shows up directly in the system prompt's shape. The bulk of the prompt is L1 — on the order of a few hundred lines. Core read/write operations, the execute_code contract, the key types and the handful of methods the agent uses on essentially every task, plus the execution and safety guidelines. This is the part that's resident on every single call, so it's also the part we fight hardest to keep tight. L2 is a thin slice on top — roughly 50 lines. It isn't the specs themselves; it's a curated allowlist of the "blessed" methods and the pointers that tell the agent the getXInfo(...) specs exist and when to reach for them. The specs' actual content stays out of the prompt until a console.log pulls it in. L3 is essentially 5 lines, the name and description of the skill.md, and other references scattered elsewhere. The raw reference — 70k lines — lives entirely on disk and never touches the prompt. All that's resident is the short skill file and the one line in the API-hierarchy section pointing at it. So the budget mirrors the frequency curve: most of the prompt is spent on the 80% case, a little on signposting the 15%, and almost nothing on the long tail — which is exactly the allocation the cache-hierarchy framing predicts. ## The recipe, ported to your domain Spreadsheets are just my example. The structure transfers to any domain. The compression in those system prompts and curated specs is really an encoding of the distribution of your users and the tasks they do — and you, in your domain, understand that distribution better than anyone. So your job is three questions: 1. What do you wrap into L1? The bread-and-butter operations on the steep part of the frequency curve. Make them brutally token-efficient and fast, and make them report consequences. Spend disproportionate effort here — the agent pays this cost on every task. 2. What do you defer to L2? The important-but-occasional capabilities. Write them as curated, English, gotcha-aware specs reachable in one discovery step. Encode the canonical recipe and the constraints, not just the signatures. 3. What is your escape hatch (L3)? The raw, complete substrate — plus a skill that teaches the agent to mine it. It doesn't have to be ergonomic. It has to be reachable, complete, and findable in a bounded number of steps. The agent must be able to — and will — eventually find the right information. Get those three placements right and you've built an agent that is fast on the common case, capable on the occasional one, and never truly stuck on the rare one — all while keeping context small enough that the model stays sharp. ## The hierarchy doesn't disappear — it moves One closing observation. What counts as L1 is not fixed; it drifts with model strength. Early, weak models needed tiny, single-purpose tools and everything spelled out. Today's models can absorb a larger L2 spec in one shot and reason over more raw L3 detail without choking. So as models improve, yesterday's L3 becomes tomorrow's L2, and yesterday's L2 collapses into L1. The agent's responsibility expands outward; the tiers slide down a level. But the hierarchy itself never goes away — because context will always be scarce relative to everything you could put in it, and noise will always cost you accuracy. There is no model so large that "put the right thing in front of it at the right time" stops mattering. Bigger context windows tempt people to paste in more. The better instinct is the one CPUs settled on decades ago: summaries in cache, details on demand, the raw substrate as the last resort. Build your agent's context like a memory hierarchy, and accuracy follows. ## 相关链接 - [Peter Wang](https://x.com/BrainsAndTennis) - [@BrainsAndTennis](https://x.com/BrainsAndTennis) - [255K](https://x.com/BrainsAndTennis/status/2065190286519906657/analytics) - [#REF](https://x.com/search?q=%23REF&src=hashtag_click) - [#REF](https://x.com/search?q=%23REF&src=hashtag_click) - [deferred tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference) - [Upgrade to Premium](https://x.com/i/premium_sign_up) - [5:51 AM · Jun 12, 2026](https://x.com/BrainsAndTennis/status/2065190286519906657) - [255.6K Views](https://x.com/BrainsAndTennis/status/2065190286519906657/analytics) - [View quotes](https://x.com/BrainsAndTennis/status/2065190286519906657/quotes) --- *导出时间: 2026/6/12 19:40:17* --- ## 中文翻译 # 构建优秀的垂直领域智能体 **作者**: Peter Wang **日期**: 2026-06-11T21:51:48.000Z **来源**: [https://x.com/BrainsAndTennis/status/2065190286519906657](https://x.com/BrainsAndTennis/status/2065190286519906657) ---  如何构建一个在特定领域真正表现出色的智能体——一个因为做得更好而被客户选择的智能体? 过去一年里,基础架构已经标准化:智能体就是一个围绕模型的 while 循环,它调用工具直到任务完成。给它一个文件系统,给它一个 shell,让它通过这些完成大部分事情。你一个下午就能写出一个,大多数人也确实这么做过。每个人都能构建智能体——这真的没那么难,而且正如我将详细说明的那样,它也没那么深奥。区分一个优秀智能体和玩具的关键不在于聪明程度;而在于对领域的深刻理解,以及在少数关键环节进行枯燥、细致工作的耐心。 我花了将近一年时间构建 Shortcut 智能体,它被广泛认为是目前最准确的电子表格智能体——它部署在全球最大的四家多策略对冲基金中的三家,在这些地方,犯错代价高昂,没人会按曲线打分。我们没有微软或 Anthropic 那样的分发渠道。我们拥有的是智能体更准确,在这个领域,这是客户选择我们的唯一最有说服力的理由。因此,智能体性能是我整天思考的问题。 这是我不断遇到的差距:关于构建智能体的文章很多,但关于构建优秀智能体的文章却很少。看看这个领域在像工具数量这样基本的问题上有多大差异——Codex 和 Claude Code 各自发布了约 30 个工具,而 Pi 发布了 7 个。当热门智能体在最基本的设计问题上存在 4 倍的分歧时,这就说明了一个问题:没有公认的原则。因此,基于一年的构建经验,我将分享我的原则,为任何正在编写自己智能体的人揭开这一过程的神秘面纱。 这就是我的原则:优秀的智能体是其任务分布的忠实压缩。接下来的内容仅意味着什么,以及它迫使你构建什么。 ## 上下文作为分层缓存 假设你不拥有环境,也没有训练模型。那么只有三件事由你设计——系统提示词、工具和工件(技能、精选文档、参考资料)——它们本质上是一回事:智能体的上下文。 所以这个游戏说起来很简单。在模型固定的情况下,准确性是上下文质量的函数:臃肿的上下文会掩埋信号,缺失的上下文会导致猜测,两者都会损害准确性。而准确性正是你出售的东西——这种关系不是线性的,一个得分 99% 的任务比得分 95% 的任务价值高出 10 倍。 但是,你的用户不会带来统一分布的问题让你解决。他们带来的是一条长尾: ``` 频率 | | ████ | ████ | ████ | ████ | ████ | ████ | ████ | ████ | ████ | ████ ▓▓▓▓ | ████ ▓▓▓▓ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ ░░░░ +----------------------------------------------------> 任务多样性 ████ 日常核心业务(bread-and-butter) 每个会话的主体 ▓▓▓▓ 关键但偶尔出现 每个会话几次 ░░░░ 长尾 每一个都罕见——但数量众多, 且每一个都必须能工作 ``` 智能体必须处理所有这些任务。但它不能一次性在上下文中容纳所有内容的并集——这就是臃肿提示词的失败模式。因此,真正的目标比“让所有内容都可用”更尖锐:在任务分布上,最小化每个任务花费的上下文。 这正是 CPU 面临的问题。一个程序可能会接触数 GB 的数据,但处理器旁边的存储却很小——因此计算机将内存分层堆叠:一个小的、即时的缓存(L1),下面是更大但更慢的(L2、L3),然后是主内存和磁盘。它之所以有效,是因为访问也是长尾的:将热数据集保留在快速层级中,只在处理罕见数据时才向下访问慢速层级。“缓存未命中”就是你需要的东西不在快速层级中,你必须付费从较慢的层级获取它——这正是你在常用路径上试图避免的成本。 智能体应该具有相同的结构。将你的上下文构建为 L1 / L2 / L3。 ``` +---------------------------------------------+ L1 | 常驻内存 - 极小,即时。 | | 那 80%。常驻于系统提示词中。 | +---------------------------------------------+ | 未命中 -> 一次廉价的调用 v +---------------------------------------------+ L2 | 按需加载 - 精选的英文规范。 | | 接下来的 ~15%。一次发现步骤即可加载。 | +---------------------------------------------+ | 未命中 -> 读取技能,然后搜索 v +---------------------------------------------+ L3 | 逃生舱 - 原始 API 巨著。 | | 长尾。需要 3-6 次 grep 调用来挖掘。 | +---------------------------------------------+ ``` 几乎每一次优化都是在信息压缩和发现速度之间进行权衡。将某样东西放入 L1,它是即时的,但无论是否使用,它都会在每项任务上消耗提示词 token。将其推入 L3,它在需要之前不消耗任何东西——但之后需要花费几次工具调用来找到它。你的工作是将每种能力放置在能最小化分布总成本的层级上。这就是这门手艺的全部。让我用我最熟悉的领域来具体说明。 ## 顺便说一句:一个工具,而不是三十个 在分层之前,先谈谈底层。我即将描述的每一个电子表格功能——每一次读取、每一次写入、每一次精心设计的查找——都是在单个工具下执行的代码。 ``` async function execute() { const data = await sheet.getCellRange("Sheet1!A1:D200"); // ...读取、计算、写入... } ``` 智能体编写代码;代码调用我们的函数;函数操作表格。没有 read_range 工具,没有 write_range 工具,没有 make_chart 工具。只有一个工具,API 位于代码内部。 为什么?因为随着你增加工具,模型的准确性会下降。这在我们要自己的实验中是一致的。你添加的每一个工具都是提示词中更多的模式,更多令人困惑的表面,更多选错工具的方式,特别是当工具承担重叠的职责时。单个 execute_code 工具将所有这些折叠为一个决定——编写代码——并允许模型使用编程语言或 DSL 的全部表达能力来组合功能,而不是拼接僵硬的工具调用(更多内容将在未来的文章中介绍)。 这对分层很重要,因为这意味着所有三个缓存层都可以从同一个地方访问:模型总是在编写代码,L1/L2/L3 只是它知道可以调用的函数,以及为找到它们做了多少工作。 ## L1 —— 日常核心业务:读写单元格 这就是那 80%。如果读取和写入单元格范围不能做到出色,其他一切都无关紧要。因此,我们在这一块投入了不成比例的、荒谬的精力。看看单个 getCellRange 实际上做了什么。 读取范围是一种压缩行为 读取一个 200 行的收入表: ``` 常用公式缩写为 F1、F2 等。 A2:North | B2:1200 | C2:9.99 | D2:11988(F1) A3:South | B3:840 | C3:9.99 | D3:8391.6(F1) A4:West | B4:1500 | C4:9.99 | D4:14985(F1) ... (另外 196 行,每行一行) ... =F1 -> =RC[-2]*RC[-1] --- 样式模式 --- D2:D201: 200 个单元格 (数字) → numberFormat:#,##0.00, font.color:#1A7F37 A2:A201: 200 个单元格 (文本) → font.bold:true --- 来自上方单元格的上下文 --- A1:Region | B1:Units | C1:Price | D1:Revenue ``` 这里发生了三件事。 首先,公式别名。一列 500 行的 =A2*B2, =A3*B3, … 就是 500 个几乎相同的公式。我们将每个公式规范化为 R1C1 形式——这样 =A2*B2 和 =A3*B3 都变成 =RC[-2]*RC[-1]——计算模式,任何出现超过十次的模式都会折叠成一个简短的别名,如 F1。模型看到的是重复的 F1 加上一行图例,而不是 500 个公式。节省了大量 token,零信息丢失。 其次,免费的行和列上下文。当你读取 C5:E20 时,那些光秃秃的数字意味着什么?我们向左扫描行标签,向上扫描标题行(通过投票选择附近哪一行文本单元格最多来作为标题),并将它们附加,这样模型就免费获得了 Region | Q1 | Q2 和 North America | …,永远不必猜测数字网格代表什么。 第三,样式压缩。格式也是信息——一个粗体红色的单元格,带有 0.00% 的数字格式,正在告诉你某些东西——但是列出每个单元格的完整样式会淹没数值。所以我们按相同的样式对单元格进行分组,将每个组折叠为其连接的范围,并每组打印一行:范围、单元格计数和紧凑的描述。 六百个公式变成了一行图例。四百个带样式的单元格变成了两行。而模型从未明确要求的标题行就在底部。这就是整个表格,无损耗,仅占原始 dump 所需 token 的一小部分。每一个都是压缩与发现的权衡,在常见情况下取得了决定性胜利。 写入单元格:告诉模型它实际改变了什么,以及什么看起来是错的 写入比看起来更难,因为一次 execute_code 调用可能会改变数百个单元格,而智能体需要知道发生了什么,而无需重新读取整个表格。因此,在代码运行后,我们交回每个已更改单元格的结构化差异——同样重要的是,我们对它进行压缩和分类。 代码: ``` async function execute() { const rows = await sheet.getCellRange("Sheet1!A2:C201"); for (let i = 0; i < rows.length; i++) { const r = i + 2; await sheet.setCell(`D${r}`, `=B${r}*C${r}`); } } ``` 返回的差异: ``` --- 单元格差异摘要 --- (显示格式化的显示值。∅ = 未定义/空。) 无问题更改:共 199 个单元格 Sheet1!Row 2 (D): 1 个单元格 → D2: ∅ -> 11,988 [=B2*C2] Sheet1!Row 3 (D): 1 个单元格 → D3: ∅ -> 8,391.6 [=B3*C3] ... (抽样行) ... Sheet1!Row 201 (D): 1 个单元格 → D201: ∅ -> 4,995 [=B201*C201] ... 以及另外 189 行 需要审查的单元格: 必须修复: INVALID_FORMULA: 共 1 个单元格 Sheet1!Row 57 (D): 1 个单元格 → D57: ∅ -> #REF! [=B57*C57] ``` 两种压缩在这里起作用。 首先,差异是分组和抽样的,而不是倾倒。已更改的单元格按工作表和行分组,每行显示为带有计数的列范围(Row 2 (D): 1 cells),并且每行和每节只打印确定性的单元格样本,其余的用“… 和 N 个更多”进行计数。两百次写入变成了几次,智能体仍然知道总数。 其次,差异是分类的。干净的写入列在“无问题更改”下。任何看起来可疑的东西——像 #REF! 这样的无效公式、未标记的硬编码数字、埋在公式中的硬编码数字、大得离谱的百分比——都会被拉入“需要审查的单元格”部分,最严重的违规者会被标记为 MUST FIX。在第 57 行的那个 #REF! 在两百个绿色差异的墙中很容易被忽略;在这里,它被标记并显示在顶部。反馈循环不是“这是改变了什么”,而是“这是改变了什么,以及这是你可能搞错的部分”——一个内置在智能体自己编辑上的 linter。 用一句话总结 L1:曲线陡峭部分的操作获得了经过特性工程、token 压缩、后果报告的封装器,它们永远存在于提示词中。它们构建成本很高,但你无论如何都要构建它们,因为智能体在每项任务上都要付出成本。 ## L2 —— 精选的英文,按需加载 你不能把所有东西都放在 L1。条件格式、数据透视表、图表、数据验证、复制/移动语义——每一个都很重要,每个会话出现几次,每一个都有足够的表面,在系统提示词中记录它会膨胀每一个不使用它的任务。典型的 L2。 所以我们用英文编写了精心策划的能力规范,按需获取,就像技能 md 一样。模型从其代码内部调用: ``` console.log(general.getConditionalFormattingInfo()); console.log(general.getPivotTableInfo()); console.log(general.getChartInfo()); console.log(general.getDataValidationInfo()); console.log(general.getAPIInfo("addSpanAt")); // 任何单个函数,按名称 ``` 这些不是类型签名的倾倒。它们是手写的散文——每个几百行——描述了完成任务的标准方法,包括原始 API 永远不会给你的知识。以数据透视表规范为例。它不仅列出方法;它教授整个配方,按正确的顺序: ``` const pt = sheet.originalSheet.pivotTables.add("SalesPivot", "SalesData", 0, 0, ...); pt.suspendLayout(); pt.add("Region", "Region", rowField); pt.add("Quarter", "Quarter", columnField); pt.add("Amount", "Sum of Amount", valueField, 8); // 8 = sum pt.resumeLayout(); ``` 它融入了你只能通过反复失败才能学到的东西:你必须在一批更改周围调用 suspendLayout()/resumeLayout(),否则表会在每次调用时重建;值字段的聚合必须作为原始整数传递(8 代表求和),因为友好的枚举在运行时不存在。这些都不是古怪的陷阱——这是正确执行数据透视表的实际形状,由已经付出代价的人写下一次。 关键属性:这在任务需要之前不消耗任何 token。一个从未接触过数据透视表的任务永远不会为数据透视表文档付费。一次 console.log 就是整个发现成本——单次缓存未命中,快速服务。 同样的想法,适用于可执行工具 L2 不仅用于文档。我们将相同的模式应用于延迟工具——web_search、web_crawl、create_website 等。它们的模式不放在提示词中。相反,有一个元工具墙: ``` get_tool_info("web_search") → 返回模式,标记为“已获取” execute_tool("web_search", …) → 除非你先获取它,否则拒绝 ``` 获取的工具集实际上是一个会话范围的缓存。模型加载一次模式,从那时起它就驻留了。同样的压缩与发现的权衡,同样的解决方案:保持提示词小,当你实际需要该能力时,支付一步未命中的代价。这与 Claude 上的延迟工具的想法相同,但我们并不局限于一家供应商的工具加载功能来获得这种行为。 ## L3 —— 原始巨著,以及映射它的技能 然后是长尾:那一个我们从未封装、从未编写规范的晦涩东西。你无法预料它——从定义上讲。但智能体仍然必须能够到达那里,否则它会碰壁并使任务失败。具体来说,是这样的请求最终到达的地方: - “为每一行添加一个迷你图以总结其趋势”——迷你图是一个真实但很少接触的 API 表面。 - “将图表的辅助轴设置为对数刻度,并仅重新着色第三个系列”——一个图表属性,三层深,没有精选规范费心覆盖它。 - “插入一个从该单元格到该命名范围的超链接,并对这些形状进行分组”——绘图/形状/超链接角落,直到现在才有人问起。 所以 L3 是完整的原始 API——整个 Office.js 表面或整个 SpreadJS 表面,转储到磁盘。它是一个机器生成的参考,长达 7 万行。它包含了一切。作为提示词上下文它也完全不可用——你永远不会把它粘贴进去。 诀窍:你给它一个技能——一个简短的地图,教它如何用 bash 挖掘巨著: ``` # 来自 advanced-api SKILL.md —— 推荐的工作流程 grep -n '"charts.add"' api-reference.json -A 5 # 查找方法 grep -n '"pivots\.' api-reference.json | head # 列出命名空间 grep -n '"ChartConfig"' api-reference.json -A 10 # 解析类型 grep -n '"isEnum": true' api-reference.json -B2 -A10 # 枚举枚举 ``` 该技能约 100 行。它 s
A Agent Harness 拆解:AI Agent 的工程化基础设施 本文深入探讨了 Agent Harness 的概念,即包裹在 LLM 外部、将无状态模型转化为可用智能体的完整软件基础设施。文章引用了 Anthropic、OpenAI 和 LangChain 的实践,详细拆解了生产级 Harness 的 12 个核心组件(如编排循环、记忆系统、上下文管理、验证循环等),并阐述了如何通过优化这层“操作系统”来解决遗忘、工具调用失败和上下文腐烂等工程难题。 技术 › Harness Engineering ✍ 土豆本豆🕐 2026-05-21 AgentHarnessLLM架构设计LangChainClaudeOpenAI上下文管理工程化Agent拆解
代 代理工具的解剖结构 文章深入剖析了将无状态 LLM 转变为功能强大的智能体所需的“代理框架”基础设施。内容涵盖编排循环、工具工程、内存、上下文管理、安全护栏及子代理编排等 12 个核心组件。文章对比了 Anthropic、OpenAI 和 LangChain 的实现策略,指出生产级应用的关键在于模型周围的工程架构,而非模型本身。 技术 › Agent ✍ Akshay🕐 2026-04-07 AgentLLM架构设计OpenAILangChainClaude工程化上下文管理工具调用多智能体
技 技能链:将智能体技能重新定义为情境行动 本文探讨了智能体系统中技能实现的局限性,指出技能不应是静态提示,而应是动态的情境行为。作者介绍了在 Slate 系统中通过引入“线程”和“分叉”机制,实现了上下文隔离的自动化技能调用,并提出了“编排技能”的概念,即通过组合其他技能来完成复杂任务链。 技术 › Skill ✍ akira🕐 2026-04-02 AgentLLM智能体SkillSlate技能链架构设计上下文管理自动化多智能体
你 你不知道的 Claude Code:架构、治理与工程实践 本文基于作者深度使用 Claude Code 的实战经验,深入剖析了 Claude Code 的六层架构模型。文章详细阐述了上下文治理的成本构成与最佳实践,解释了 Skills、Hooks、Tools 和 Subagents 的区别与设计原则,并分享了如何利用 Prompt Caching 和 Plan Mode 优化系统性能与稳定性。 技术 › Claude ✍ Tw93🕐 2026-03-21 Claude CodeLLMAgent架构设计工程实践上下文管理Prompt Engineering
C Claude Code 工程化最佳实践与架构指南 本文系统性地介绍了 Claude Code 的工程化实战知识库,涵盖了从基础配置到高级架构设计的方方面面。内容解读了创建者的定制技巧,深入解析了 Command、Agent、Skills 的核心架构与 Memory 作用域,并针对 Monorepo 场景提供了上下文管理策略。文章还总结了上下文管理、Agent 设计、调试方法及权限安全的实践经验,并分享了如何利用高级 API 特性(如程序化工具调用、动态过滤)来显著降低 Token 消耗并提升准确率。 技术 › Claude Code ✍ shanraisshan🕐 2026-02-23 AgentMCPMemory最佳实践上下文管理架构设计DevOpsLLM开源
R Run Your Harness Outside of the Sandbox (Why and How) 本文探讨了2026年以来关于Agent运行位置的争论,指出行业趋势是将Agent运行在沙箱之外。作者详细解释了沙箱内运行的三个主要问题:爆炸半径、信任边界和沙箱的间歇性运行,并提出了将沙箱作为工具暴露的正确架构,最后提供了基于Vercel AI SDK的实现示例和生产环境中的挑战。 技术 › Agent ✍ Nathan Flurry🕐 2026-07-28 Agent沙箱架构设计DevOps后端LLM安全性生产环境Vercel AI SDK状态管理
构 构建智能代理的三层架构:Loop、Graph与Harness 本文提出解决Agent重复读取数据和Token浪费的三层架构方案:Loop负责单元工作的收集-行动-验证闭环;Graph通过分发和并行管理复杂任务;Harness作为运行时环境提供工具和上下文隔离。文中提供了具体的代码实现思路和实战演示。 技术 › Agent ✍ Archive🕐 2026-07-25 AgentLoopGraphHarness架构设计上下文管理Token优化验证机制代码实现
T The context gold rush: Why everyone is building the same thing 文章分析了当前 AI 领域的“淘金热”——上下文管理。作者指出,从 Jevons 悖论到数据主权,多因素驱动了初创公司和大企业竞相构建公司大脑或 LLM 知识库。尽管切入角度各异(如个人知识库、代理内存、可观测性工具等),但核心目标一致:为未来的智能体劳动力提供数据上下文层。文章认为该领域潜力巨大,但也面临产品同质化。 技术 › Agent ✍ Sam Z Liu🕐 2026-07-24 上下文管理Agent公司大脑LLM数据主权竞争格局
H How to master graph engineering 本课程教授如何构建 AI 智能体图,涵盖图的基本概念、关键模式(如菱形模式)、停止规则及人工审批环节。包含三个实战案例:深度研究台、SEO 内容生成器和市场推广套件,旨在提升业务效率并控制成本。 技术 › Agent ✍ Machina🕐 2026-07-23 AgentGraphLLMClaudeWorkflow工程化自动化架构设计效率实战
3 3 Years of Graph Engineering with LangGraph 文章回顾了 LangGraph 三年来的发展,探讨了将智能体系统建模为图(Graph)的实践与价值。作者分析了何时使用图结构以平衡确定性与自主性,并指出生产级智能体通常需要循环和动态转换。最后,文章强调图工程并非全新概念,但随着节点的进化,现在的图更多是在编排智能体而非单一的 LLM 调用。 技术 › Agent ✍ Sydney Runkle🕐 2026-07-22 LangGraphGraph EngineeringAgentLLM架构设计循环确定性工作流
G Graph Engineering 101: When a Loop Isn’t Enough 文章探讨了AI Agent从简单的ReAct循环向图工程架构的演进。循环模式在处理复杂、多步骤及需人工介入的任务时存在状态持久化、错误处理和分支逻辑的局限性。图工程通过显式的节点、边和状态管理,解决了并发、暂停恢复及复杂流程控制问题,为构建更健壮的Agent系统提供了架构基础。 技术 › Agent ✍ Alex Prompter🕐 2026-07-22 AgentGraph EngineeringReActLangGraph架构设计状态管理LLM
彻 彻底告别Loop Engineering:一文读懂 Graph Engineering 本文介绍了AI Agent工程从Prompt到Loop再到Graph的演进。Graph Engineering通过图结构重新规划任务关系,实现并行处理、明确依赖、隔离失败,从而解决线性流程在复杂任务中效率低、易失控的问题。 技术 › Agent ✍ AI超元域🕐 2026-07-21 Graph EngineeringAgentLLM架构设计Claude Code工作流并行处理Dynamic Workflows