返回知识库
0

第四章:核心与 Agent 循环

本章目标:帮助你理解 Agent、AgentLoop、FactoryOwnership、Turn/Step 生命周期、Cancel 机制和 Scope 模型。阅读本章后,你应该能回答"一次 Agent 交互在代码层面经历了哪些步骤"以及"如何扩展或拦截 Agent 的行为"。


4.1 核心包概览

dsh 的核心包组(packages/core/)是产品的 API 脊柱:

预览
源码

core/ 核心包

会话事件日志

Prompt 段落组装

工具注册表

Agent 接口

作用域原语

驱动

执行

调用

执行

core/session

core/agent-loop

core/system-prompt

core/tools

core/agent

core/scope

ReactLoopAgent

Turn/Step 循环

LLM 服务

工具管道

graph TB
    subgraph "core/ 核心包"
        A[core/session] -->|"会话事件日志"| B[core/agent-loop]
        C[core/system-prompt] -->|"Prompt 段落组装"| B
        D[core/tools] -->|"工具注册表"| B
        E[core/agent] -->|"Agent 接口"| B
        F[core/scope] -->|"作用域原语"| B
    end

    B -->|"驱动"| G[ReactLoopAgent]
    G -->|"执行"| H[Turn/Step 循环]
    H -->|"调用"| I[LLM 服务]
    H -->|"执行"| J[工具管道]

各包职责

拥有的内容ctx 键
core/session仅追加的 SessionEvent 日志和内存存储ctx.sessions
core/system-promptPrompt 段落和工具 schema 组装ctx.systemPrompt
core/tools带范围的工具注册表和受保护执行管道ctx.tools
core/agentAgent 接口、活注册表和 agent/* 事件ctx.agents
core/agent-loop默认驱动实现(ReactLoopAgentctx.agentLoop
core/scope每个 Agent 的带范围注册原语库,无键

4.2 Agent 接口

4.2.1 Agent 是什么

Agent 是一个可取消的工作单元,拥有:

  • Inbox:接收用户消息和注入上下文

  • Session:关联的会话日志

  • Scope:带范围的注册边界

  • Options:配置选项

// 来自 packages/core/agent/src/types.ts(概念性)
interface Agent {
  /** 唯一标识 */
  readonly id: SessionId
  /** 消息收件箱 */
  readonly inbox: Inbox
  /** 关联的会话 */
  readonly session: Session
  /** Agent 作用域 */
  readonly ctx: Context
  /** 选项 */
  readonly options: AgentOptions
}

4.2.2 AgentFactory(Agent 工厂)

AgentFactory 负责创建 Agent 实例。通过 ctx.agents 访问。

// 来自 packages/core/agent/src/index.ts
interface AgentFactory {
  createAgent(options: CreateAgentOptions): AgentHandle
}

interface AgentHandle {
  // 句柄:取消、状态查询等
  // 可以通过 abort controller 取消
}

4.2.3 CreateAgentOptions

interface CreateAgentOptions {
  /** 活的 agent/session 身份 */
  readonly sessionId: SessionId
  /** 会话创建元数据 */
  readonly meta?: {
    readonly cwd?: string
    readonly parentSession?: SessionId
    readonly isSeeded?: boolean
    readonly origin?: 'subagent'
    readonly delegationDepth?: number
    readonly agentPreset?: string
  }
  /** fork 继承的前缀长度 */
  readonly inheritedEventCount?: SessionLogOffset
  /** 初始 replay/fork 历史 */
  readonly seed?: readonly SessionEvent[]
}

4.3 AgentLoop:驱动循环

4.3.1 ReactLoopAgent

ReactLoopAgent 是默认的 Agent 驱动实现。它在 packages/core/agent-loop/src/agent.ts 中定义。

// 来自 packages/core/agent-loop/src/agent.ts
class ReactLoopAgent implements Agent {
  readonly inbox: Inbox
  private phase: Phase
  readonly scope: Scope
  readonly ctx: Context
  private readonly dispatch: AgentEventDispatch

  constructor(
    private loopCtx: Context,
    public readonly id: SessionId,
    public readonly options: AgentOptions,
    public readonly session: Session,
  ) {
    this.dispatch = agentEvents(loopCtx, this)
    this.inbox = new Inbox(session, {
      inserted: (message) => { this.dispatch.emit('agent/inbox/inserted', { message }) },
      discarded: (message) => { this.dispatch.emit('agent/inbox/discarded', { message }) },
      claimed: (message, turn) => { this.dispatch.emit('agent/inbox/claimed', { message, turn }) },
    })
  }
}

4.3.2 Phase 状态机

ReactLoopAgent 通过 Phase 状态机管理其生命周期:

预览
源码

wake 请求

turn 开始

维护完成

turn 开始

turn 结束

turn 结束 + wake 请求

idle

maintenance

running

stateDiagram
    [*] --> idle
    idle --> maintenance: wake 请求
    idle --> running: turn 开始
    maintenance --> idle: 维护完成
    maintenance --> running: turn 开始
    running --> idle: turn 结束
    running --> maintenance: turn 结束 + wake 请求
type Phase =
  | { kind: 'idle'; lastTurn: number }
  | { kind: 'maintenance'; abort: AbortController; lastTurn: number; wakeRequested: boolean }
  | { kind: 'running'; abort: AbortController; turn: number; step: number; wakeRequested: boolean }

4.3.3 Turn 与 Step 的关系

预览
源码

Turn(回合)

Step 2(可选)

下一个 claim → 更多 step

Step 1

step/start

claim input

assemble prompt

agent/pre-step

agent/request → llm/stream

assistant/message

tool/call* → tools/execute

step/end

turn/start

agent/turn-stopping

turn/end

graph TB
    subgraph "Turn(回合)"
        T1["turn/start"]
        subgraph "Step 1"
            S1["step/start"]
            S2["claim input"]
            S3["assemble prompt"]
            S4["agent/pre-step"]
            S5["agent/request → llm/stream"]
            S6["assistant/message"]
            S7["tool/call* → tools/execute"]
            S8["step/end"]
        end
        subgraph "Step 2(可选)"
            S9["下一个 claim → 更多 step"]
        end
        T2["agent/turn-stopping"]
        T3["turn/end"]
    end

关键定义

  • Step:一次模型请求 + 它调用的工具

  • Turn:零或多个 Step:它在第一个 input 被 claim 前打开,在没有任何欠债时关闭


4.4 Turn 完整流程

4.4.1 标准 Turn 流

turn/start
  claim next-step input plus one queued message
  assemble prompt sections + tool schemas
  -> agent/pre-step                   reject | enter(messages, startsRequestSeries?)
     reject, or a first enter rewritten empty -> close the turn with no step
     step/start
     append entered messages as user/message
     derive model history from the log
     agent/request -> llm/stream -> assistant/chunk* -> assistant/message
     tool/call* -> tools/pre-execute -> tools/execute -> tools/post-execute -> tool/result*
     step/end
     tools owe another request, or next-step input arrived -> claim -> next step
  -> agent/turn-stopping
turn/end

4.4.2 事件分类

预览
源码

Waterfall 事件

agent/pre-step
必须调用 next()

agent/request
必须调用 next()

llm/stream
必须调用 next()

tools/*
必须调用 next()

活的扩展点

agent/pre-step

agent/request

llm/stream

tools/pre-execute

tools/execute

tools/post-execute

持久化会话事件

turn/*

step/*

user/message

assistant/*

tool/*

graph LR
    subgraph "持久化会话事件"
        A["turn/*"]
        B["step/*"]
        C["user/message"]
        D["assistant/*"]
        E["tool/*"]
    end

    subgraph "活的扩展点"
        F["agent/pre-step"]
        G["agent/request"]
        H["llm/stream"]
        I["tools/pre-execute"]
        J["tools/execute"]
        K["tools/post-execute"]
    end

    subgraph "Waterfall 事件"
        L["agent/pre-step<br/>必须调用 next()"]
        M["agent/request<br/>必须调用 next()"]
        N["llm/stream<br/>必须调用 next()"]
        O["tools/*<br/>必须调用 next()"]
    end

4.4.3 agent/pre-step 的作用

agent/pre-step 是一个 waterfall 事件,它决定模型看到什么:

// 概念性例子
ctx.on('agent/pre-step', (decision, next) => {
  // 1. 可以重写消息
  decision.messages = decision.messages.filter(m => !m.suppressed)
  
  // 2. 可以添加上下文
  decision.messages.push({
    role: 'system',
    content: getCurrentContext()
  })
  
  // 3. 可以设置 startsRequestSeries
  if (isNewSeries) {
    decision.startsRequestSeries = true
  }
  
  // 4. 必须调用 next()
  return next(decision)
})

可以做什么

  • 重写 claimed messages

  • 拒绝 messages(reject)

  • 添加/删除上下文

  • 设置 startsRequestSeries

4.4.4 tool 执行管道

预览
源码
sequenceDiagram
    participant Loop as Agent Loop
    participant Pre as tools/pre-execute
    participant Exec as tools/execute
    participant Post as tools/post-execute
    participant Tool as 实际工具

    Loop->>Pre: waterfall('tools/pre-execute', input)
    Note over Pre: 预处理、验证、拦截
    Pre-->>Loop: modified input
    Loop->>Exec: waterfall('tools/execute', input)
    Exec->>Tool: 实际执行
    Tool-->>Exec: result
    Exec-->>Loop: result
    Loop->>Post: waterfall('tools/post-execute', result)
    Note over Post: 后处理、审计
    Post-->>Loop: final result
sequenceDiagram
    participant Loop as Agent Loop
    participant Pre as tools/pre-execute
    participant Exec as tools/execute
    participant Post as tools/post-execute
    participant Tool as 实际工具

    Loop->>Pre: waterfall('tools/pre-execute', input)
    Note over Pre: 预处理、验证、拦截
    Pre-->>Loop: modified input
    Loop->>Exec: waterfall('tools/execute', input)
    Exec->>Tool: 实际执行
    Tool-->>Exec: result
    Exec-->>Loop: result
    Loop->>Post: waterfall('tools/post-execute', result)
    Note over Post: 后处理、审计
    Post-->>Loop: final result

4.5 Cancel 机制

4.5.1 取消的原因

type AgentCancelCause =
  | { kind: 'user' }          // 用户取消
  | { kind: 'turn-end' }      // Turn 正常结束
  | { kind: 'error' }         // 错误导致取消
  | { kind: 'dispose' }       // Fiber 卸载

4.5.2 取消的传播

预览
源码

idle

running

Cancel 请求

当前阶段

忽略

abort controller

LLM 流取消

工具执行中断

Phase → idle

graph TB
    A[Cancel 请求] --> B{当前阶段}
    B -->|"idle"| C[忽略]
    B -->|"running"| D[abort controller]
    D --> E[LLM 流取消]
    D --> F[工具执行中断]
    D --> G[Phase → idle]

4.5.3 错误恢复

当 LLM 请求失败时:

// 概念性描述
type RequestErrorAction =
  | { kind: 'retry' }     // 重试请求
  | { kind: 'abort' }     // 中止 turn
  | { kind: 'continue' }  // 跳过,继续下一个 step

错误链(errorChain)管理重试策略和错误传播。


4.6 Scope 模型

4.6.1 什么是 Scope

Scope 是每个 Agent 的带范围注册原语。它允许在特定 Agent 的上下文中注册服务,而不影响其他 Agent。

// 来自 packages/core/scope
interface Scope {
  // 带范围的注册
}

4.6.2 Scope 的使用

// 创建带范围的上下文
const agentCtx = ctx.extend({ agent: myAgent })

// 在 Agent 作用域内注册
agentCtx.on('agent/step', (step) => {
  // 这个监听器只在这个 Agent 的上下文中生效
})

4.6.3 Agent.ctx

每个 Agent 有一个关联的 Context:

interface Agent {
  /** Agent 作用域的上下文 */
  readonly ctx: Context
}

通过 ctx.agent 可以获取当前上下文关联的 Agent:

declare module '@deepseek-ai/cordis' {
  interface Context {
    agents: AgentRegistry
    agent?: Agent  // 当前上下文关联的 Agent
  }
}

4.7 FactoryOwnership

4.7.1 概念

FactoryOwnershipAgentLoop 中的内部类,管理:

  • 活跃 Agent 的 teardown

  • 配置启动工作

// 来自 packages/core/agent-loop/src/index.ts
class FactoryOwnership {
  private accepting = true
  private readonly teardown = new AbortController()
  private readonly inactive = Promise.withResolvers<void>()
  
  constructor(private readonly loop: AgentLoop) {}
  
  signal(): void {
    // 通知所有 Agent 关闭
  }
}

4.7.2 teardown 顺序

当 AgentLoop 卸载时:

  1. 发出 teardown 信号

  2. 等待所有活跃 Agent 完成当前 step

  3. 按反向注册顺序清理所有 disposers

  4. 清理 Scope


4.8 Prompt Assembly(Prompt 组装)

4.8.1 组装流程

预览
源码

session 日志

deriveMessages

模型历史

system-prompt

prompt sections

tools

tool schemas

renderPrompt

完整 Prompt

graph TB
    A[session 日志] --> B[deriveMessages]
    B --> C[模型历史]
    D[system-prompt] --> E[prompt sections]
    F[tools] --> G[tool schemas]
    E --> H[renderPrompt]
    G --> H
    C --> H
    H --> I[完整 Prompt]

4.8.2 PromptAssembly

interface PromptAssembly {
  // 系统提示段落
  sections: PromptSection[]
  // 工具 schema
  toolSchemas: ToolSchema[]
  // 完整的渲染结果
  // renderPrompt(assembly) → 完整消息列表
}

4.8.3 Model History 派生

模型历史从会话日志派生:

// 概念性描述
function deriveMessages(log: SessionEventLog): Message[] {
  // 从日志中提取:
  // 1. user/message 事件 → UserMessage
  // 2. assistant/message 事件 → AssistantMessage
  // 3. tool/result 事件 → ToolResultMessage
  // 按 seq 排序,保持时序
}

关键原则Model-visible ⟺ logged——任何到达模型请求的内容都必须可从日志重建。


4.9 Injection 机制

4.9.1 agent.inject()

通过 agent.inject() 可以向下一个模型请求注入上下文:

// 概念性描述
agent.inject({
  type: 'context',
  content: '用户刚才提到了关于部署的问题'
})

这会:

  1. 将内容添加到下一个 admitted request

  2. 不产生持久化的会话事件

  3. 在 prompt 中作为临时上下文出现


4.10 小结

概念一句话解释
Agent可取消的工作单元,拥有 Inbox 和 Session
AgentFactory创建 Agent 的工厂
ReactLoopAgent默认的 Agent 驱动实现
Turn零或多个 Step 的交互单元
Step一次模型请求 + 工具调用
PhaseAgent 状态机(idle/maintenance/running)
Scope每个 Agent 的带范围注册原语
FactoryOwnershipAgentLoop 的 teardown 管理

下一步第五章:LLM 能力——深入理解 LLM 服务定义、DeepSeek Provider、Adapter、Router 和重试策略。

DeepSeek-Harness / 04-核心与 Agent 循环 0 0 iliuqi
2026-09-04T07:48:53.152474326Z 2026-09-04T07:56:56.450941600Z