4 · 你自己做系统时怎么选

这一章把前面所有内容转成可操作的判断。不是「学哪个」,而是「在你的约束下,哪个设计是对的」。

4.1 第一个问题:用户在不在场

这是分水岭。先回答它,后面一半的决策会自动确定。

你的智能体运行时,有人在看着屏幕吗? ┌─ 是(交互式)─────────────────────────────────┐ │ · 危险操作 → 弹确认框 │ │ · 权限系统的终点可以是"问人" │ │ · 上下文可以提示用户手动处理 │ │ · 投入应该向【交互体验】倾斜 │ │ · 参考 Claude Code │ └────────────────────────────────────────────────┘ ┌─ 否(自主运行)───────────────────────────────┐ │ · 危险操作 → 必须有自动决策规则 │ │ · 必须有告警 + 事件系统 │ │ · 必须有注入检测 │ │ · 必须有执行隔离 │ │ · 投入应该向【可靠性和安全】倾斜 │ │ · 参考 Hermes │ └────────────────────────────────────────────────┘ ┌─ 都有 ────────────────────────────────────────┐ │ ★ 最常见的真实情况 │ │ → 【按触发源分层】:同一个循环,不同的权限 │ │ (这正是 Hermes 第 12 章那张图的结论) │ └────────────────────────────────────────────────┘

4.2 第二个问题:工作负载是单一的还是多样的

单一工作负载多样工作负载
例子:只做代码助手 / 只做客服 / 只做数据分析 例子:一个平台,上面跑各种各样的智能体
该做:把上下文策略、工具集、提示词全部针对这一种负载做死。深度优化 该做:抽象基类 + 可插拔。允许每种负载配自己的策略
不该做:过早抽象。你会为了一个永远不会有第二种实现的接口,付出永久的复杂度 不该做:把某一种负载的假设写进核心。它会在第二种负载出现时炸掉

最常见的错误是「预防性抽象」:还没有第二种实现,就先定义一个抽象基类。

代价:你的接口是凭想象设计的,而不是从两个真实实现里提炼的。等真的出现第二种实现时,你会发现接口不合适,然后要么改接口(破坏第一个实现),要么让第二个实现别扭地适配。

更好的做法:先写死。当出现第二个真实需求时,再从两个具体实现里提炼接口。那时你提炼出来的接口才是对的。

4.3 第三个问题:谁来扩展

扩展者该提供的机制不该提供的
只有你自己 直接改代码。也许加几个配置项 插件系统。你在给自己制造麻烦
你的团队 技能(Markdown) + 配置。零代码,人人可写 复杂的插件 API
其他工程团队 工具注册 + 钩子 + MCP 让他们能替换核心策略
不可信的第三方 MCP(跨进程隔离) 同进程插件。一个崩溃会拖垮你

记住 Hermes 第 9 章那个权限梯度:扩展的门槛应该和它能造成的破坏成正比。

技能(纯文本、人人可写、最多让智能体走错路)
→ MCP(跨进程、崩溃隔离、只能提供工具)
→ 插件(同进程、能挂钩子、但钩子只能追加不能替换)
→ 核心策略(能替换整个上下文,但单选,必须用户显式配置)

不要提供一个万能的插件接口让所有人都能做所有事。

4.4 无论选哪条路,都必须做的八件事

这是第 2 章八条共识的行动版本:

#要做的事具体动作
1给上下文记账 算清楚:系统提示词多少 token、工具定义多少、每轮增长多少。任何常驻内容都要能说出它为什么值这个价
2渐进式披露 工具、技能、文档、记忆 —— 全部改成「目录常驻 + 内容按需」。并且在描述质量上投入时间
3至少两层安全 「能力收窄」(不给危险工具)+ 「执行隔离」(容器/沙箱)。规则匹配不算一层可靠的防御
4默认失败即关闭 检查每一处 try/except:出异常时是放行还是拒绝?把所有「出错就跳过」改成「出错就拒绝」
5加硬限制 委派深度、并发数、单次输出长度、遍历跳数、缓存条目数。每一个「理论上不会太大」的量都要有上限
6能力查询而非身份判断 搜索代码里所有的 if xxx == "某个具体名字",改成 if xxx.supports_yyy()
7错误分类 至少分出三类:可重试的临时故障(短退避)/ 配置或凭据问题(长退避或告警)/ 逻辑错误(不重试,直接报告)
8两段式操作带租约 任何「先声明后完成」的操作(工具调用、任务认领、锁),都要处理「声明了但没完成」:补偿或超时

4.5 面试场景:怎么把这些讲出来

如果被问「你怎么设计一个 Agent 系统」

不要从「有一个循环,模型调工具,工具返回结果」开始讲。这是所有人都会说的,说明不了什么。

从约束开始讲:

「首先要确定两件事:用户在不在场,以及工作负载是单一还是多样

用户在场,安全的终点可以是问人;不在场,就必须有自动决策规则、告警系统和执行隔离,因为出了问题没人会发现。

工作负载单一,就把策略写死做深度优化;多样,就需要抽象基类 —— 但要区分哪些是可叠加的能力,哪些是必须单选的策略。

然后是三个跑不掉的约束:上下文是最稀缺的资源,所以一切设计围绕渐进式披露;安全必须分层,因为每一层都不可靠;默认值必须是 fail-closed。」

如果被追问细节,这些是有分量的具体例子
被问到可以举的例子
上下文管理五级阶梯(每级的信息损失递增,从最轻的开始);select_contextcompress 的正交(读 vs 写,那个误用故事)
提示词缓存前缀匹配,一个字节不同就全部失效;分叉子智能体为了共享缓存做到字节级一致
安全_CMDPOS 命令位置锚定和那次 gh pr create --title 误拦事故;绕过免疫检查放在模式判断之前
多智能体MAX_DEPTH = 1(用硬限制消灭指数爆炸);set_spawn_paused(区分「停止接受新工作」和「终止现有工作」)
可靠性孤儿 tool_use 必须在每条中止路径合成结果;try_register_running_job 防定时任务堆叠
重试凭据池按 HTTP 状态码决定冷却时长(429 短、401 长、5xx 极短)

4.6 最后:一个可以带走的判断框架

面对任何一个智能体系统的设计决策,依次问: ① 这个决定会往常驻上下文里加东西吗? → 加多少 token?每轮都要付吗?值吗? → 能不能改成"目录 + 按需加载"? ② 这个决定涉及安全吗? → 它是哪一层?还有别的层吗? → 如果这一层失效,会怎样? ③ 这里有没有一个"理论上不会太大"的量? → 加个上限。现在就加。 ④ 这里在判断"你是谁"吗? → 改成判断"你能做什么"。 ⑤ 出错时会发生什么? → 是放行还是拒绝?(应该是拒绝) → 是哪一类错误?(决定重试策略) ⑥ 这是一个两段式操作吗? → "声明了但没完成"怎么办? ⑦ 我现在要加的抽象,有第二个真实实现吗? → 没有的话,先写死。

这七个问题不需要你记住任何一个具体实现。

它们是从 Claude Code 的 176,391 字分析和 Hermes 的 141,079 字分析里提炼出来的、真正可迁移的部分。

具体的实现会过时 —— 模型会变、API 会变、框架会变。但这些约束来自「智能体」这个形态本身,它们不会变。

4 · How to Choose When You Build Your Own

This chapter turns everything so far into decisions you can act on. Not “which one to learn from,” but “under your constraints, which design is right.”

4.1 The First Question: Is the User Present?

This is the dividing line. Answer it first and half the remaining decisions settle themselves.

While your agent runs, is someone watching the screen? ┌─ Yes (interactive) ──────────────────────────────────────────────┐ │ · Dangerous actions → pop a confirmation prompt │ │ · The permission system can end in "ask a human" │ │ · Long context can prompt the user to handle it manually │ │ · Investment should tilt toward [interaction experience] │ │ · Reference: Claude Code │ └──────────────────────────────────────────────────────────────────┘ ┌─ No (autonomous) ────────────────────────────────────────────────┐ │ · Dangerous actions → there must be automatic decision rules │ │ · There must be alerting + an event system │ │ · There must be injection detection │ │ · There must be execution isolation │ │ · Investment should tilt toward [reliability and safety] │ │ · Reference: Hermes │ └──────────────────────────────────────────────────────────────────┘ ┌─ Both ───────────────────────────────────────────────────────────┐ │ ★ The most common real-world situation │ │ → [Layer by trigger source]: one loop, different permissions │ │ (exactly the conclusion of the diagram in Hermes Chapter 12) │ └──────────────────────────────────────────────────────────────────┘

4.2 The Second Question: Is the Workload Uniform or Varied?

A single workloadVaried workloads
Examples: only a coding assistant / only customer support / only data analysis Example: one platform running all kinds of agents
Do: take the context strategy, toolset, and prompts and hard-wire all of them for that one workload. Optimize deeply Do: abstract base classes + pluggability. Let each workload bring its own strategy
Don't: abstract prematurely. You'll pay permanent complexity for an interface that will never have a second implementation Don't: bake one workload's assumptions into the core. It blows up the moment a second workload shows up

The most common mistake is “preventive abstraction”: defining an abstract base class before a second implementation exists.

The cost: your interface is designed from imagination, not distilled from two real implementations. When the second one actually arrives, you'll find the interface doesn't fit, and then you either change the interface (breaking the first implementation) or contort the second one to fit.

The better approach: hard-wire it first. When a second real need appears, distill the interface from the two concrete implementations. The interface you extract then is the one that's actually right.

4.3 The Third Question: Who Will Extend It?

ExtenderMechanism to provideDon't provide
Only you Edit the code directly. Maybe add a few config options A plugin system. You're making trouble for yourself
Your team Skills (Markdown) + config. Zero code; anyone can write one A complex plugin API
Other engineering teams Tool registration + hooks + MCP The ability to replace core strategies
Untrusted third parties MCP (cross-process isolation) In-process plugins. One crash takes you down with it

Remember the permission gradient from Hermes Chapter 9: the barrier to extending should scale with the damage the extension can do.

Skills (plain text, anyone can write one, worst case the agent takes a wrong turn)
→ MCP (cross-process, crash-isolated, can only provide tools)
→ Plugins (in-process, can attach hooks, but hooks can only append, never replace)
→ Core strategies (can replace the entire context, but single-select and must be explicitly configured by the user)

Don't offer one universal plugin interface that lets everyone do everything.

4.4 Eight Things You Must Do Whichever Road You Take

This is the action version of the eight consensus points from Chapter 2:

#What to doConcrete action
1Account for context Work out the numbers: how many tokens in the system prompt, how many in tool definitions, how much growth per turn. Anything resident must be able to justify its price
2Progressive disclosure Tools, skills, docs, memory — convert all of them to “catalog resident + contents on demand.” And put time into description quality
3At least two layers of safety “Capability narrowing” (don't hand out dangerous tools) + “execution isolation” (container/sandbox). Rule matching doesn't count as a reliable layer
4Fail closed by default Inspect every try/except: on exception, does it allow or refuse? Change every “on error, skip” to “on error, refuse”
5Add hard limits Delegation depth, concurrency, single-output length, traversal hops, cache entries. Every quantity that “shouldn't get too big in theory” needs a cap
6Capability queries, not identity checks Search the code for every if xxx == "some specific name" and change it to if xxx.supports_yyy()
7Classify errors At least three classes: retryable transient failures (short backoff) / config or credential problems (long backoff or alert) / logic errors (no retry; report immediately)
8Two-phase operations carry leases Any “declare first, complete later” operation (tool calls, task claims, locks) must handle “declared but never completed”: compensate or time out

4.5 The Interview: How to Talk About All This

If you're asked “how would you design an agent system”

Don't open with “there's a loop, the model calls tools, the tools return results.” Everyone says that; it proves nothing.

Open with the constraints:

“First, two things need settling: whether the user is present, and whether the workload is uniform or varied.

If the user is present, safety can end in asking a human; if not, there have to be automatic decision rules, an alerting system, and execution isolation, because nobody will notice when something goes wrong.

If the workload is uniform, hard-wire the strategy and optimize deeply; if it's varied, you need abstract base classes — but distinguish the capabilities that can stack from the strategies that must be single-select.

Then come three constraints you can't escape: context is the scarcest resource, so everything is designed around progressive disclosure; safety must be layered, because no layer is reliable; and the default must be fail-closed.”

If they press for details, these are concrete examples that carry weight
If asked aboutExamples you can give
Context managementThe five-rung ladder (each rung loses more information; start with the gentlest); the orthogonality of select_context and compress (read vs. write, and the misuse story)
Prompt cachingPrefix matching — one differing byte invalidates all of it; forked subagents go byte-identical to share the cache
Safety_CMDPOS command-position anchoring and the gh pr create --title false-block incident; bypass-immunity checks placed before the mode check
Multi-agentMAX_DEPTH = 1 (a hard limit that kills exponential blowup); set_spawn_paused (separating “stop accepting new work” from “terminate existing work”)
ReliabilityOrphaned tool_use must get a synthesized result on every abort path; try_register_running_job keeps scheduled tasks from piling up
RetriesThe credential pool sets cooldown length by HTTP status code (short on 429, long on 401, very short on 5xx)

4.6 Finally: A Decision Framework You Can Take With You

For any design decision in an agent system, ask in order: ① Does this decision add anything to the resident context? → How many tokens? Paid every turn? Worth it? → Can it become "catalog + load on demand" instead? ② Does this decision touch safety? → Which layer is it? Are there other layers? → If this layer fails, what happens? ③ Is there a quantity here that "shouldn't get too big in theory"? → Add a cap. Add it now. ④ Is this checking "who are you"? → Change it to checking "what can you do." ⑤ What happens on error? → Allow or refuse? (It should be refuse) → Which class of error? (This decides the retry strategy) ⑥ Is this a two-phase operation? → What about "declared but never completed"? ⑦ The abstraction I'm about to add — does it have a second real implementation? → If not, hard-wire it for now.

These seven questions don't require you to remember any specific implementation.

They are the genuinely transferable part, distilled from 176,391 characters of analysis of Claude Code and 141,079 characters of analysis of Hermes.

Specific implementations will go out of date — models change, APIs change, frameworks change. But these constraints come from the “agent” form itself, and they won't.