图解 DSH · 00 目录 术语表 下一章

先认识 Agent:一个会调用工具的循环

Agent 程序的核心是一个循环:问模型;模型要用工具,就执行,把结果接上再问。后面每一章,都是在这个循环身上做文章。

DSH 是什么?DSH(DeepSeek Harness)是 DeepSeek 开源的 Agent 程序,和 Claude Code、Codex 是同一类东西:你交代一件事,它让模型去想、让工具去做,直到办完。这本书把它拆开,看它是怎么搭起来的。

Agent 循环就是那个循环
负责来回传话
模型收一份请求,回一段话
自己不记事
工具模型可以请求调用的函数
比如 bash、读文件
系统提示词告诉模型它是谁、
该怎么干活的说明
这一章回答一个问题:一个 Agent 程序,到底在跑什么?答案就三句:
  1. 它在跑一个循环。每转一圈,把系统提示词、到目前为止的对话、工具清单打包,发给模型。
  2. 模型的回复里要用工具,就执行,把结果接到对话后面,再转一圈;不要工具了,循环结束。
  3. 模型自己不记事。每转一圈,前面的内容都要整份重发(对话太长时会先压缩,第 04 章讲)。

?先猜:模型怎么知道工具的结果?

例子:你说“数数这个目录里有几个 .py 文件”。模型看不到你的目录,于是回复:请用 bash 执行 ls *.py | wc -l。Agent 照办,拿到结果 7。

接下来再问模型时,模型是怎么知道结果是 7 的?

答案是 B。模型不记事,Agent 每次都把前面的内容整份重发,结果 7 接在最后。你在 ChatGPT 里看到它“记得”前面的话,靠的也是同样的办法。这件事由 Agent 循环来做。下面看它的代码。

点一个选项。

1Agent 循环的代码

回到开头的问题:Agent 程序到底在跑什么?第一步,先看它的核心:Agent 循环。

Agent 循环:你发来一句话之后

while (true) {
  const 回复 = await ctx.llm.stream({
    system: 系统提示词,
    messages: 会话里记下的所有消息,
    tools: 工具清单,
  })
  记进会话(回复)
  const 工具调用 = 回复里的 tool-call
  if (工具调用.length === 0) break   // 不要工具了,结束
  for (const 调用 of 工具调用) 记进会话(await 执行(调用))
}

这是简化过的 DSH 真实代码(中文是为了好读)。真实代码多了中途取消、出错重试这些分支,骨架就是这几行。四个角色都在里面:

两个词后面会一直用(DSH 官方用词):从你发来一句话,到循环 break、回复你,叫一个轮次;循环转一圈,也就是问一次模型、再执行它要的工具,叫一个步骤。一个轮次通常包含好几个步骤。

2拿刚才的例子跑一遍

第二步:看这个循环一圈一圈怎么转。重点看右边:第二次发给模型的请求,前面的内容全部重发了一遍。

现在:

谁在干活

Agent 循环拼请求、发给模型;执行工具、把结果接上
模型读完整份请求,决定下一步
bash 工具执行 shell 命令

最近一次发给模型的请求

    3在 DSH 里,这几样都是插件

    第三步:回头看循环代码里的 ctx.llm。连模型、管工具、拼系统提示词,都不是循环自己写的。

    在 DSH 里,这几样各是一个单独的部件,叫插件:

    循环只认 ctx 上的名字,不管背后是谁在干活。所以换个模型、加个工具,都不用动循环的代码。插件是怎么接上去的,下一章讲。

    ✓三道小题

    循环什么时候停?

    想好了,看答案
    模型的回复里没有工具调用的时候。(真实代码还有别的停法,比如被取消、被插件拦下,第 02 章会讲到。)

    模型一次要了两个工具。下一步发给模型的请求里有什么?

    想好了,看答案
    整份重发:系统提示词、你的话、模型上次的回复,再加上这两个工具的结果。

    数 .py 文件这件事,一共用了几个轮次、几个步骤?

    想好了,看答案
    1 个轮次,里面有 2 个步骤。第一步模型要了 bash;第二步拿到结果,模型给出答案,不再要工具。
    Agent 程序在跑一个循环:每一步把系统提示词、全部对话、工具清单发给模型;模型要工具就执行,结果接在后面再问;不要工具了,这一轮结束。模型不记事,所以每一步都整份重发。 在 DSH 里,连模型、管工具、拼系统提示词的,还有这个循环本身,都是插件。下一章:插件怎么接上去。

    想深入一点?

    这一章只讲了主线。想看 DSH 自己怎么描述这个循环: