Claude Code 源码分析:轮次上下文、事件注入与会话恢复

在设计一个支持多 Agent 协作的系统时,我们通常会面临几个核心的工程挑战,大家可以先思考一下自己会怎么解:

  1. 工具上下文超载:当接入了上百个 MCP 外部工具时,如果每轮都把全量 Schema 带给 LLM,会导致极高的 Token 成本和选择噪声,该如何动态管控?
  2. 长耗时子任务的阻塞与回调:当主 Agent 派发了一个需要运行十分钟的后台分析任务,主进程显然不能一直挂起死等。但如果放后台跑,随之而来的问题是:
    • 如果主 Agent 还在和用户对话(没有退出本轮循环),回调怎么优雅插入才不会打断当前推理?
    • 如果主 Agent 已经回复完毕并退出了本轮循环,任务完成时怎么重新拉起主 Agent?
    • 回复的消息要怎么注入,才能既被大模型注意到,又不会导致上下文历史混乱?
  3. 递归派生的失控:如果大模型产生幻觉,遇到报错就不停地派生新的子 Agent 去“外包”问题,如何防止系统资源被瞬间耗尽?
  4. 会话中断与恢复:用户随时可能关掉终端或者按 Ctrl+C 退出,进程死掉后,系统怎么保证重启时能一字不差地把之前的对话上下文(包括各种隐藏工具调用和系统事件)重新拼出来?

针对这些问题,Claude Code 给出的答案并不是“写更复杂的 Prompt 去警告模型”,而是从底层打造了一个事件驱动的异步运行时

它放弃了简单的 while(true) 循环,改为按 turn(轮次)动态组装上下文;它通过全局队列来接收异步长耗时任务的回调,并在安全的轮次边界把结果作为附件(Attachment)注入;最后,它将一切事件序列化到持久层的 transcript 里,使得会话可以在任何时候精准重建。

1. 动态工具发现(应对上下文超载)

如果每一轮都把所有工具 schema 都发给模型,不仅浪费 token,还会导致模型注意力稀释。Claude Code 采用的是**“先声明搜索入口,后按发现结果增量注入”**的策略。

算法与流程 (How)

  1. 首轮请求:只带上核心常驻工具(如 Bash、Read)和 ToolSearchTool,其余 MCP 工具(deferred tools)被隐藏。
  2. 模型搜索:模型如果发现缺少能力,调用 ToolSearchTool
  3. 返回引用:搜索结果不返回完整的工具 Schema,而是返回一个轻量级的 tool_reference 占位符。
  4. 历史扫描:在组装下一轮请求前,extractDiscoveredToolNames() 会遍历历史消息,读取 tool_result 里的 tool_reference,提取出“已经发现过的工具”,toolSearch.ts#L545-L592
    • 注:即使历史消息被压缩,系统也会从 compact_boundary 捞回这部分事实。
  5. 按需注入claude.ts 过滤工具列表,只有被扫描到的 deferred tools 才会被完整打包发给 API,claude.ts#L1154-L1267

原理与复杂度 (Why)

  • 总复杂度O(C + D)(C 为常驻核心工具数,D 为当前任务已发现的工具数)。扫描历史消息的动作纯本地执行,耗时仅数毫秒,成功将主导项(网络与推理成本)大幅削减。
  • 具象化类比:就像去图书馆,你手里只有一台“检索终端”(ToolSearchTool)。查到需要的书后,终端只吐出一张“借书小票”(tool_reference)。下次你去柜台发 API 请求时,系统看到小票,才会把厚厚的实体书(完整的 Tool Schema)搬给你。

2. 上下文与事件队列(应对并发与竞态)

Claude Code 发送给模型的请求,messages 不只是聊天记录,它还承载系统事件。子任务完成、工具结果返回、运行时插入的提醒,本质上都会进入同一条时间线。

为了更直观,可以先把一轮请求抽象成下面这个结构:

{
  "system": [
    "System Prompt",
    "System Context",
    "Memory Rules"
  ],
  "tools": [
    "tool definitions (full or deferred-filtered)"
  ],
  "messages": [
    { "role": "user", "content": "用户输入和环境上下文" },
    { "role": "assistant", "content": "模型回复或 tool_use" },
    { "role": "user", "content": "tool_result" },
    { "role": "user", "content": "<task-notification>...</task-notification>" }
  ]
}

这里最重要的一点是:messages 不只是聊天记录,它还承载系统事件。Claude Code 自定义了几种特殊的 XML 结构,并把它们伪装成 user 角色发给大模型。

举几个真实的注入例子:

1. Task Notification(后台子任务完成通知)
这里有一个很核心的问题:大模型怎么知道这个通知对应的是它之前发出的哪个任务?
答案是:通过双向 ID 绑定。
当大模型最初调用 AgentTool 时(比如带有一个 tool_use_id="tool_123"),AgentTool 会立即返回一个包含了新生成 agentId(比如 a3f2c1...)的执行结果,告诉大模型“任务已后台启动,请记住这个 ID”。
当任务真正跑完时,系统构造的通知里,不仅会带上 task-id,还会带上原始的 tool_use_idLocalAgentTask.tsx#L248-L253。这样大模型一看到这个通知,就能瞬间回忆起它的前因后果:

{
  "role": "user",
  "content": "<task-notification>\n<task-id>a3f2c1b4d5e6</task-id>\n<tool_use_id>tool_123</tool_use_id>\n<output-file>/path/to/output.jsonl</output-file>\n<status>completed</status>\n<summary>Agent \"Analyze logs\" completed</summary>\n<result>Found 3 errors.</result>\n</task-notification>"
}

防压缩保护与抗遗忘设计(The “Grep vs Attention” Problem):
这里有一个更深层的问题:Attention 机制在长上下文中寻找这种无意义的随机 ID(如 toolu_01A...),其准确度是远不如传统程序的 grep 的。因为无意义字符会被 Tokenizer 撕裂,且中间的历史记录极易发生 “Lost in the Middle” 遗忘现象。
如果子任务跑了很久,期间主对话不断进行,触发了自动压缩(Compact),这个 ID 甚至会被彻底丢弃。
为了把“软性”的 Attention 提升到 grep 级别的可靠性,Claude Code 在工程上做了两层极强的防御:

  1. 物理距离拉近(对抗 Lost in the Middle)
    既然模型记不住中间的 Token,Claude Code 的 compact.ts 就强行把所有 pendingrunning 状态的任务 ID 提取出来,打包成 Attachment,永远塞在离当前生成位置最近的地方(新一轮输入的末尾)compact.ts#L541-L544
    这相当于系统用确定性的 grep(代码里的 filtermap)帮模型把“针”从大海里捞了出来,直接摆在模型的眼皮底下。同时在摘要指令里强制要求保留正在运行的任务线索,prompt.ts#L157

  2. 强语义锚点(对抗 Token 撕裂)
    系统不只是把孤零零的 ID 扔过去,而是用 XML 标签 <task-notification><tool_use_id> 将其紧紧包裹,并附带了 <summary>(任务的自然语言描述,如“分析日志”)。
    这样,Attention 机制在匹配时,不仅匹配那个被撕裂的随机 ID 序列,还能匹配到具有极强语义的 XML 标签和任务描述。这极大地放大了这个区块在向量空间里的特征,让 Attention 更容易“抓”住它。

2. File Attachment(文件附件)
比如用户用 /add 命令,或者拖拽文件进来,或者系统主动附带的上下文文件。

{
  "role": "user",
  "content": "File: src/utils/logger.ts\n```typescript\nexport function log(msg) { console.log(msg); }\n```"
}

3. Memory Attachment(记忆规则附件)
系统会把 project_memory.md 里的内容作为隐藏上下文附加进去。

{
  "role": "user",
  "content": "<memory-attachment>\n<file-path>/Users/xxx/.trae-cn/memory/projects/.../project_memory.md</file-path>\n<content>\n- 始终使用 TypeScript 严格模式\n- 优先使用 React Hooks\n</content>\n</memory-attachment>"
}

这意味着:子任务完成、记忆加载、文件挂载,本质上都会变成这种“伪造的 user 消息”进入同一条时间线。

2.1 上下文如何组装

上下文组装分为两层。

2.1.1 进入循环前的静态准备

进入 query 循环前,会先准备变化较少的内容:

  1. System Prompt:角色、安全边界、基础行为约束。
  2. User Context:工作目录、环境状态、会话附加信息。

这一层决定“你是谁、当前在哪个环境里工作”。

2.1.2 循环内的动态注入

每一轮推理都会重新整理上下文,同时推进本轮 tool_use 的执行、异步 task 的创建,以及已完成 task 通知的回流。仅用“调用 LLM -> 执行工具 -> 排干队列”概括这一过程,会遗漏 task 生命周期中的三个关键环节:创建点、并行关系和回流顺序。

更接近真实实现的主流程可分成三段:

  1. 先执行本轮 assistant 产出的 tool calls
  2. 如果其中有 AgentTool 的异步分支,就在这一轮里创建 task 并启动后台 agent
  3. 等这一轮工具阶段结束后,再统一 drain 队列,把已完成 task 的通知作为 attachment 注入

主流程可概括为:

func QueryLoop(state State) {
    for {
        systemPrompt := appendSystemContext(state.SystemPrompt)
        compacted := compactHistory(state.Messages)
        input := prependUserContext(compacted, state.UserContext)

        response := callLLM(systemPrompt, input, state.Tools)

        if response.HasToolCalls {
            for _, call := range response.ToolCalls {
                if call.IsAgentTool && call.ShouldRunAsync {
                    task := createAgentTask(call)
                    startTask(task)
                    state.Messages = append(state.Messages, createToolAck(task))
                } else {
                    result := executeTool(call)
                    state.Messages = append(state.Messages, result)
                }
            }
        }

        queued := getQueuedEventsForCurrentAgent(state.AgentID)

        for _, event := range queued {
            attachment := createAttachmentMessage(event)
            state.Messages = append(state.Messages, attachment)
        }

        if shouldStop(response) {
            break
        }
    }
}

这里要注意三件事。

第一件事:task 不是循环自己“长出来”的,而是在工具执行阶段创建的

一轮 assistant 响应里,只要出现 AgentTooltool_use,真正的 task 创建就发生在工具执行阶段,而不是发生在后面的 getQueuedEvents()。异步分支下,AgentTool 会先生成 agentId,再调用 registerAsyncAgent(...) 把后台 task 注册进状态,并启动异步生命周期,AgentTool.tsx#L686-L764

所以从时序上说:

  1. assistant 先产出 tool_use
  2. 主循环进入工具编排阶段。
  3. AgentTool 在这里把异步子 Agent 包装成 task。
  4. task 自己在后台继续跑。

后面的队列阶段处理的不是“创建 task”,而是“消费已经完成的 task 通知”。

第二件事:工具编排是简单的贪心分批,而非复杂的 DAG

要理解工具是怎么调用的,首先要明白大模型(LLM)输出的格式:大模型本身并没有显式的“并发”或“串行”标记,它只是按推理顺序,在一轮响应里一口气输出一个包含多个 tool_use 对象的数组(这就是 Parallel Tool Use)。

但这些工具究竟该同时跑还是排队跑?这完全由 Claude Code 本地的 runTools() 函数通过“贪心分组”来决定:

  • 什么是并发安全的 Tool? 源码中,凡是实现里写死 isConcurrencySafe() { return true; } 的工具(如 FileReadToolAgentTool)都是安全的。凡是没有写或者返回 false 的工具(如 FileEditTool 这种可能写坏文件的,或 BashTool 这种可能污染环境的)就是不安全的。
  • 贪心切片规则:系统按顺序遍历 LLM 输出的工具数组:
    1. 如果当前工具是安全的,且上一个也是安全的,就把它们塞进同一个并发 Batch
    2. 只要遇到不安全的工具,就立刻打断当前分组,把它单独包成一个串行 Batch
    3. 这意味着,串行 Batch 里永远只有一个工具。如果大模型连续输出了三个不安全的工具,系统会把它们切成三个独立的串行 Batch,toolOrchestration.ts#L86-L116

举个具象化的例子:如果大模型依次输出了 [读文件A, 读文件B, 改文件C, 跑Bash D],系统会切成三个 Batch:

  • Batch 1(并发)[读文件A, 读文件B] -> 使用 Promise.all 机制同时触发,不需要等前一个返回。
  • Batch 2(串行)[改文件C] -> 必须等 Batch 1 彻底跑完,再单独跑 C。
  • Batch 3(串行)[跑Bash D] -> 必须等 Batch 2 彻底跑完,再单独跑 D。

这并不是一个复杂的有向无环图(DAG)调度,而是一个非常轻量的线性扫描。

这里最容易产生误解的问题是:Tool 和 Task 是一个概念吗?长耗时任务怎么做到不阻塞主循环的?

并不是。 Tool 和 Task 是完全不同的层级。
普通的 Tool(如 FileReadToolFileEditTool)就是一个普通的函数调用,不管它是并发发出去的还是串行发出去的,主循环都会严格 await 它的真实结果。

但 Task 是一个有完整生命周期、有独立输出文件、能通过队列发送通知的后台运算实体Task.ts#L6-L14
在 Claude Code 里,只有 AgentTool(以及特定模式下的 BashTool)才会孵化出 Task。

秘密就在这里:AgentTool 源码里写死了 isConcurrencySafe=true。如果模型一轮里输出了三个 AgentTool

  1. 它们会被主循环放进并发 batch,用 Promise.all 同时调度
  2. 调度后,AgentTool 并没有在当前函数里死等子 Agent 跑完,而是用 void runAsyncAgentLifecycle(...) 把真实的子 Agent 循环抛到了后台,并向主循环瞬间秒回 { status: 'async_launched' }AgentTool.tsx#L686-L764
  3. 主循环拿到这三个秒回的 async_launched,认为“工具执行完毕”,于是开开心心地进入了下一轮(响应用户)。
  4. 而此时,后台其实已经长出了三个并行的 Task,它们跑完后会通过队列把通知回调给主线程。

所以,并发的根源不是主循环有魔法,而是 LLM 决定了一次要调用多个工具 + AgentTool 把死循环推到了后台

第三件事:task 通知的回流顺序由队列决定,不由创建顺序直接决定

task 跑完后,不会直接修改主线程 messages,而是构造 <task-notification> 消息并调用 enqueuePendingNotification(...) 入统一队列,LocalAgentTask.tsx#L194-L262

这个队列本身有明确的顺序规则:

所以一个 task 从创建到被主线程看到,至少经历三层顺序:

  1. tool_use 顺序:assistant 在本轮里按什么顺序发出调用。
  2. 完成顺序:task 实际何时跑完。
  3. 消费顺序:主线程何时按优先级和作用域把通知取出来。

这套顺序保证了多 Agent 协作中的上下文一致性,也把事件注入时机限制在可控的轮次边界上。

3. 主子 Agent 的异步互动与防递归机制

主 Agent 派出一个子任务后,双方不是共享一份可随时改写的 messages,而是通过事件队列衔接。

3.1 异步交互的三个场景

这就回答了开头提出的“长耗时任务”问题:

  • 场景 A(主 Agent 还在和用户对话):子任务完成后,通知先进入队列,不会打断当前主 Agent 的推理或工具调用。等主 Agent 这一轮正常结束,运行到“排干队列”阶段时,才会取出通知,并包装成一条 user 角色的 attachment 元消息追加到末尾。这种“轮次边界注入”保证了当前推理的原子性。
  • 场景 B(主 Agent 已经空闲/退出):主 Agent 已结束上一轮推理,退出了循环。此时后台的 queueProcessor 发现队列里有属于主 Agent 的完成通知,它会把这条通知当作一条新的用户输入,在当前会话的上下文中重新启动下一轮 query。这并非强行原地唤醒旧执行栈,而是基于历史上下文的正常续写。
  • 场景 C(用户插话冲突):如果用户刚发了一条新消息,同时后台子任务也刚好完成。这两条信息都会作为 attachment 加入新一轮的输入尾部。系统通过把通知降级为 later 优先级,并在提示词中显式约束,确保模型不会因为只盯着子任务结果而漏掉用户的插话。

3.2 Agent ID 与防止无限递归 (How & Why)

系统没有把“禁止无限递归”只交给 Prompt,而是做了严格的运行时硬拦截。

  1. 稳定身份agentId 并非顺序分配,而是每次 spawn 时通过 crypto.randomBytes(8) 早期生成的随机短 ID,uuid.ts#L19-L27。它不仅标识子任务,还被用作隔离资源(如 worktree)和队列通知的作用域标记。
  2. 状态染色与硬拒绝:当主 Agent 派生(Fork)子 Agent 时,系统会在初始上下文中注入一段强约束文本(告知其是 Fork 角色),forkSubagent.ts#L171-L198。更关键的是,在执行 AgentTool 前,系统会检查当前环境是否已有 Fork 标记。如果有,直接抛出 Error: Fork is not available inside a forked worker,从入口处掐断递归,AgentTool.tsx#L318-L356
  3. 拓扑限制:同进程的 Teammate 不能派生后台代理,以维持团队扁平结构并确保生命周期安全,AgentTool.tsx#L266-L280

为什么这样做:大模型在处理报错时极易产生幻觉,试图不断转包任务。这种物理拦截将原本可能呈 O(B^N) 指数级爆炸的派生链强制锁定为 O(1),确保了算力和进程资源的绝对安全。

3.3 主 Agent 会退出吗

会,这里的“退出”要分清两个层次:

  1. 退出本轮 query 循环:这是常态,不是异常。
  2. 退出整个会话进程 / 丢失内存态:这是另一个问题,要靠 transcript 恢复。

先看第一层。Claude Code 的主循环不是一个永不返回的常驻 worker,而是“一次 turn 的执行器”。在 query.ts 里,如果这一轮模型流式输出结束后没有新的 tool_useneedsFollowUp 为假,就会走完成分支并返回,query.ts#L1062-L1182。另外,aborted_streamingprompt_too_longimage_errormax_turnsaborted_tools 这些路径也都会让本轮提前结束,query.ts#L1051-L1051query.ts#L1711-L1711

所以主 Agent 并不是“一直在后台跑一个 while 不停监听”,而是:

  1. 用户提交一条输入。
  2. 启动一轮 query。
  3. 该轮把该处理的工具调用、附件注入、后续追问处理完。
  4. 没有 follow-up 时,本轮返回,主线程回到 idle。
什么场景下会看起来像“没退出”

有两类常见情况:

  1. 本轮里还在持续 follow-up:比如模型还在调用工具,或者工具结果回来后又触发下一轮推理,这时候 query 还没结束。
  2. 长耗时任务被后台化:例如主会话被用户 background 到 LocalMainSessionTask,原来的前台 UI 会退出当前交互面,但真正的任务在后台 task 里继续跑,完成后再发通知,LocalMainSessionTask.ts#L1-L10LocalMainSessionTask.ts#L164-L218
退出本轮后,消息怎么处理

如果主线程已经 idle,新消息不会去“唤醒旧的 JS 调用栈”,而是走队列和下一次执行:

  • 正在执行时,新的 prompt / task notification 会先入队,handlePromptSubmit.ts#L313-L350
  • 主线程空闲后,queueProcessor 会只取属于 main thread 的命令,按 mode 分批交给新的 executeInput(),也就是新开一轮处理,而不是恢复旧栈帧,queueProcessor.ts#L52-L87
  • 如果是子 Agent 通知,则在下一轮 query 的 attachment 阶段被 drain 并注入,query.ts#L1547-L1643

所以更准确地说,Claude Code 的主 Agent 是“会话级常驻,轮次级退出”。

4. 为什么要把事件做成 Attachment

Attachment 不是普通聊天内容,而是“让模型可见、让界面可隐藏”的系统载体。

它有三个作用:

  1. 结构化承载事件:任务通知、错误信息、远端资源状态都能统一表示。
  2. 隔离 UI 和模型:模型能读到,终端界面不一定直接展示。
  3. 支持多轮演进:系统可以在后续轮次继续追加、折叠或移除这类元信息。

如果没有这层抽象,系统事件就只能硬塞进普通对话文本里,后续既难管理,也容易污染用户视图。

5. 会话如何持久化

5.1 不是退出时一次性保存

Claude Code 不是等会话结束后再把整段 messages 全量写盘,而是持续追加到 JSONL 文件。

  • 每产生一轮有效消息,就进入待刷盘队列。
  • 后台以高频追加写的方式,把新消息写到会话文件末尾。

这样做有两个直接收益:

  1. 崩溃损失小,因为大部分历史已经落盘。
  2. 长会话性能更稳定,因为不需要每次重写整个文件。

5.2 恢复会话时怎么做

恢复时不需要构造一套额外的快照协议,直接读取 JSONL 并回放即可:

  1. 读出历史消息和历史附件。
  2. 重新解析成 messages
  3. 再次送回 QueryEngine

因为会话里记录的不只是对话,还有系统事件,所以恢复出来的是一条完整的运行时间线。

这里还要区分两种“续上之前的对话”:

场景 A:同一个进程里的下一轮对话

在 SDK / 会话引擎层,QueryEngine 明确是 “one QueryEngine per conversation”,每次 submitMessage() 只是同一会话里的新一轮 turn,messages、文件缓存、usage 等状态会直接沿用,QueryEngine.ts#L176-L183QueryEngine.ts#L209-L242

这时候并不存在“重新拼历史”的成本,因为历史本来就在内存里的 mutableMessages 上继续追加,QueryEngine.ts#L430-L456

场景 B:进程结束后,再次恢复会话

如果整个进程结束,内存里的 agent 实例当然不会永久常驻。Claude Code 依赖 transcript 把会话重新拼回来:

  1. 每次新用户消息先落盘,再进入 query,这样即使 API 还没返回也可以 resume,QueryEngine.ts#L436-L463
  2. recordTranscript() 会把新增消息追加到 JSONL,并用 parentUuid 串起链路,sessionStorage.ts#L1408-L1449
  3. 恢复时先加载 transcript 文件,找到最新 leaf,再沿着 parentUuid 逆向回溯,重建整条 conversation chain,sessionStorage.ts#L2316-L2355sessionStorage.ts#L2069-L2206
  4. 对于并行 tool use 产生的分叉,恢复逻辑还会做一次补救,把同组 assistant 和 orphaned tool_result 再拼回去,避免 resume 后历史缺块,sessionStorage.ts#L2096-L2206

所以“每个用户再次发起对话,agent 是否一直保持”这个问题的答案是:

  • 同一会话、同一进程内:会话对象和消息状态通常还在,直接开新 turn。
  • 进程已结束或显式 resume:不是把旧 agent 原地复活,而是从 transcript 重建出等价的会话状态,再继续后面的 turn。

5.3 长上下文如何控制

为了避免历史无限增长,运行时通常还会配合三层控制:

  1. 滑动窗口或摘要压缩,保留最近关键轮次。
  2. 追加元数据,标记哪些历史已经被折叠或摘要化。
  3. 把长期稳定规则放进系统级上下文,避免被历史裁剪掉。

这三层分工很清楚:

  • 对话历史可以压缩。
  • 事件日志可以回放。
  • 长期规则必须稳定保留。

4. 批判性总结:LLM 时代的系统架构折衷

纵观 Claude Code 的这套运行时设计,它触及了当前 AI Agent 架构的最底层矛盾:大模型的强项在于“模糊的语义理解与生成”,而传统软件工程的基石是“确定性的符号匹配与状态流转”。

当前的妥协(Claude Code 的做法)

Claude Code 选择的路线是:用极度厚重的传统工程代码给大模型做“外骨骼装甲”

  • 用确定性的代码管理状态:TypeScript 写的各种队列、异步 Task、事件注入、摘要保留、文件读写,把所有容易产生幻觉的“脏活累活”全部接管。
  • 把状态“喂”到模型嘴边:通过 Attachmenttask-notification,系统像保姆一样把大模型需要的状态精准地摆在它眼皮底下,然后只让模型用 Attention 去做它最擅长的“语义缝合”。
  • 避免竞态与作用域隔离:子任务完成不直接改写 messages,而是先入队、后排干,保证注入时机可控。每个 Agent 只消费属于自己的事件,结果不会互相串线。

这是当前受限于 LLM 能力瓶颈下,最务实、最可靠的解法

代价与局限性

这种设计的代价也是极其高昂的:系统的“胶水代码”极其厚重。

我们需要写大量的逻辑去“猜测”模型的注意力会在哪里衰减(比如对抗 “Lost in the Middle”),然后手动去干预上下文的组装;我们要写大量的正则和权限校验去防范模型的幻觉调用;我们要维护复杂的 JSONL 追加流来保证模型的状态不会因为崩溃而丢失。

整个框架有超过一半的代码,是在为大模型的“不可靠”打补丁。

未来的更优解探讨

如果从 Unix 哲学的“做一件事并做好”来看,目前的 LLM 架构是臃肿的。

未来的模型也许会内化这些能力,比如内置一个特殊的 [SEARCH] token:当模型在生成过程中遇到一个陌生的 ID(如 toolu_01A...)时,它不是去算庞大的、不精确的 Attention 矩阵,而是直接在底层触发硬编码的搜索算法(类似真正的 grep 或数据库索引),瞬间拿到匹配结果再继续生成。

只有当模型能原生融合**“冯·诺依曼架构的确定性计算”与“神经网络的泛化能力”**,我们才能摆脱今天这种厚重的胶水代码,迎来终极的 Agent 架构形态。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐