Files
deepseek-harness/packages/core/agent-loop/README.zh.md
T

12 KiB
Raw Blame History

description, kind
description kind
面向用户与维护者的默认 agent 驱动器说明,用于选择、配置或调试 agent 的创建方式以及轮次与步骤的运行方式。 package-reference

@deepseek-ai/dsh-agent-loop

English | 中文

概述

dsh-agent-loop 创建 agent——全新创建或从持久化历史恢复——并运行轮次与步骤生命周期:领取提示词、组装请求、流式接收模型响应、分发工具调用,并把每个结果追加回会话日志。作为默认驱动器,它实现 dsh-agentAgent 接口并在此注册工厂,因此插件通过 ctx.agents 创建与驱动 agent,而不必依赖本包。声明式配置项会在启动时自动启动 agent,maxParallelToolCalls 限制同时运行的并行安全工具调用数量。它是 harness 唯一的具象循环——超出「调用模型、运行工具、重复」的所有内容都属于监听事件分类体系的插件。标准组合请选择它作为驱动器;如需替换,请实现 Agent 并通过 ctx.agents 注册。

目录


使用本包

在任何应运行 agent 的组合中挂载 dsh-agent-loop。它提供 ctx.agents 背后的驱动器,并启动你在配置中声明的 agent;标准演示组合是 examples/agent-spine-demo

配置声明式 agent

配置中声明的 agent 会在插件加载时自动启动。每个条目需要一个 id 标签;模型调用还同时需要 providermodelagent/request 可以在分发前补齐缺失的这一对值)。

- name: '@deepseek-ai/dsh-agent-loop'
  config:
    maxParallelToolCalls: 10
    agents:
      - id: 'main'
        provider: deepseek
        model: deepseek-chat
        reasoningEffort: high
        cwd: /workspace
字段 默认值 含义
maxParallelToolCalls 10 每个步骤同时在途的并行安全工具调用数;1 为串行
agents[].id 必填 稳定标签;未设置 sessionId 时,全新会话会生成 ${id}-session-<uuid>
agents[].provider / agents[].model 模型路由;分发前两者都必须存在
agents[].reasoningEffort 非空的初始推理等级;agent/request 可以覆盖它
agents[].maxTokens 正数的逐请求输出 token 上限
agents[].cwd 全新会话的工作目录
agents[].sessionId 确切身份:首次使用创建,重新挂载时恢复已实体化的历史
agents[].resumeSessionId 加载这个持久化会话而不是创建新会话;与 sessionId 互斥

生成的配置目录是每个受支持字段的穷尽式真源。适配器会校验有效推理等级,循环则把它记录在请求头中。maxParallelToolCalls 也是整个 agent-loop 设置分节,因此叠加在该条目之上的用户层无需重启即可限制下一组工具调用。

以编程方式创建或恢复 agent

插件与宿主通过 ctx.agents.create() 创建 agent,通过 ctx.agents.resume() 恢复持久化会话;两者都返回 AgentHandle,其 dispose() 拥有确切的 teardown 能力。循环会把每个创建的 agent 运行到完成——只有调用方需要自行拆除 agent 时才需要句柄。

const handle = await ctx.agents.create({
  sessionId,
  agentOptions: { provider: 'deepseek', model: 'deepseek-chat' },
  setup: (agentCtx) => { /* scoped tools, prompt sections, listeners */ },
})

一个步骤做什么

每个步骤都会发送该 agent 渲染后的系统提示词、其可见工具 schema 与会话的派生历史;模型的工具调用经过受守卫的工具流水线,每个被接纳的事实都会在下一步据此派生之前追加到会话日志。并行安全调用最多可重叠 maxParallelToolCalls 个;独占调用单独运行并构成排序屏障。取消是协作式的:agent.cancel() 中止当前活动,并在未设置 keepInbox 时清除待处理工作;被取消的流会终结已送达用户的文本。


理解实现

实现细节——点击展开

本节解释该包如何实现上述行为;可观察约定已在使用本包中完整说明。

设计理念

该包是公开 Agent 约定的唯一具象实现。它在 ctx.agents 上把自身注册为 AgentFactory,因此消费方从不导入本包;每个创建 agent 的所有权归属于调用方 fiber 与循环提供方,并汇合到同一个记忆化的完全停稳边界。每个可观察效果都通过会话事件与 agent/* 分类体系发生——包内部从不属于公开表面。

请求 header 与适配器默认值

agent/request 返回后,ctx.llm.prepareCall() 会在活跃轮次信号下校验适配器持有的字段,并解析推理强度和输出 token 默认值。循环会在解析、request/header 记录与分派期间保留同一个适配器。循环会为首次请求、变化的 envelope、显式消息序列起点、表层替换后的请求及恢复写入完整 header;同一序列内内容未变的步骤、重试与普通后续轮次继承最新 header。下一次 waterfall 前,循环移除适配器默认字段,使当前路由重新解析它们;显式设置则保留。未处理的路由仍以 NO_ADAPTER 失败。

源码地图

文件 职责
src/index.ts 插件入口:AgentLoop 服务、配置 schema、声明式 agent 启动、工厂注册
src/agent.ts 具体 ReactLoopAgent 驱动器:收件箱、轮次/步骤状态机、取消
src/tool-calls.ts 工具调度:独占屏障与有界并行池
src/runtime-context.ts 每步骤 runtime-context 快照处理
src/constants.ts DEFAULT_MAX_PARALLEL_TOOL_CALLS
src/invariant.ts 不变式配套:从会话日志重建请求

创建与拆除

创建是同一个受回滚保护的事务:构造私有会话、具象 agent 与带作用域上下文;等待可选 setup;进入两个注册表;依次宣告 session/createdagent/created;发出 agent/session-start;此后才启动驱动器。Setup 抛出、commit 失败或所有者 dispose 都会回滚事务而不发布任一 id。Teardown 顺序是停止并排空、撤销作用域、detach agent、再 detach 会话,且每次 detach 都绑定到确切进入的对象,因此陈旧 disposer 无法移除之后出现的同 id 替代项。

轮次与步骤流程

驱动器在其整个生命周期内拥有一个 agent,并在 ctx.agents.withInitiator(agent, ...) 内运行。在轮次边界,它先打开持久轮次,再原子领取待处理的 next-step 输入与一条排队提示词;在步骤之间则只领取 next-step 输入。agent/pre-step 决定什么进入该步骤;每次成功的模型调用都恰好追加一个引用其分片 seq 的 assistant/message 锚点,被取消的流则追加带 interrupted: true 的锚点并携带已交付前缀,使下一次请求包含用户看到的内容。在步骤内,独占调用形成屏障,并行安全调用使用有界滚动池;策略、持久结果与结果上下文保持模型顺序。

失败与取消

最终适配器选择、分发与迭代失败以终止结束的形式到达并进入 agent/request-error;拥有恢复权的监听器返回 { kind: 'retry' } 且不调用 next(),未被处理的失败则是终态。Middleware、结果处理、工具及其他扩展失败仍会抛出并直接关闭轮次——插件失败结束的是轮次,不是循环。取消后未分发的模型工具调用会收到合成的 tool/callABORTED_BEFORE_DISPATCH 结果对。显式取消决策 拥有信号生命周期。


进一步探索

包级约定对大多数消费方已经足够;需要周边领域与设计原理时再阅读以下页面。


模型体验

完整对话请求

模型看到什么

每个步骤中,循环会发送针对该 agent 渲染的系统提示词、可见工具 schema 与会话的派生消息。它提供 providermodelcwd 变量值,但不添加固定文案。

Token 影响

系统文本与 schema 在每个步骤都会再次计入。逐 agent 作用域决定贡献,而权威组装 waterfall 可以改变最终请求,并使其监听器负责保持协议连贯。

KV Cache 影响

只有在同一提供方与模型路由下,且系统文本、schema 与此前历史都保持逐字节一致时,请求才保持仅追加。携带 token 的组装改写或组合变更可能从第一个改变的请求 token 起使复用失效。

保留的消息历史

模型看到什么

已接纳的 user 消息、assistant 消息、工具调用与结果、注入上下文与 steering 都会记录,并在后续步骤中发送。原始流分片、生命周期边界与其他仅写入日志的事件会被排除。

Token 影响

输入会随每条表层消息增长,直到压缩(compaction)替换遮蔽较旧节点;包含多个步骤的工具轮次会在每个步骤重新发送累积的历史。

KV Cache 影响

普通历史增长仅追加,并保留可复用条目。表层替换或压缩会从第一个被遮蔽的历史 token 起使复用失效。

取消后未分发的调用

模型看到什么

如果后续请求回放一个中止的步骤,取消所阻止分发的每个工具调用都有错误码 ABORTED_BEFORE_DISPATCH,结果文本为 Error: tool call aborted before dispatch

Token 影响

每个跳过的调用都会在历史中保留一个固定错误结果,直到压缩将其遮蔽。

KV Cache 影响

仅追加;每个合成结果都位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。

已知限制与延期工作

这些限制说明循环何时需要特别留意。它们是当前包约束,不是任务积压。

  • 分类是一元的:安全性取决于比较同级调用或资源的调用必须保持独占(原理)。
  • 配置标签默认对应新会话:省略 sessionId 时,每次启动都会创建新的 ${id}-session-<uuid>;如需确切的恢复或创建行为,必须显式提供稳定的 sessionId,而 resumeSessionId 要求已有持久化历史。
  • 配置 agent 没有逐 agent persona 字段或 setup 钩子:它们使用部署 persona;只有编程式 ctx.agents.create() / resume() 工厂选项支持带作用域的 persona 与工具组合。
  • 没有内置轮次预算:工具调用或 steering 会让当前轮次继续;限制失控轮次的策略必须从既有生命周期扩展点(如 agent/turn-stopping)执行取消。

开发备注

维护者的工作上下文——点击展开

无。