一条 Prompt 在 Harness 中经过了谁?

今天要学习 DeepSeek Harness 如何把网页中的一条指令,变成模型请求、真实工具操作,再把执行过程送回界面。我们沿用"运行测试,失败就修复文件"的例子,按相同格式拆清每个对象的作用、输入、内部处理、输出和缺失后果。


小炎: 我先试着说一遍:Client 生成 Session 和 system prompt,发送给 Remote 服务器。Remote 按 sessionId 找到对应 Service,Controller 检查之后找到 Agent。Agent 循环请求模型、调用工具,同时保存 Session,最后把答案传回 Client。这个方向对吗?

大老师: 你抓住了"用户输入 → 执行 → 显示结果"的方向,但中间有四个分界需要修正。

Client 构建的是提交命令;普通网页提交并不在 Client 侧装配 system prompt。Remote 位于 Client,是远程方法的调用入口;真正接收网络请求的是 Host,也就是运行 Agent、管理会话和工具的执行端。Gateway 按接口入口找到 Service,业务层再按 sessionId 找到 Agent。Session 则贯穿整个执行过程,持续记录事实。

还有一个容易忽略的地方:提交接口先返回"已接纳",最终答案通过持续订阅返回。它们是两条返回路径。

我们先固定一个场景:网页已经打开会话 s_123,你发送:

text 复制代码
请运行测试,如果失败就修复 src/math.ts,然后告诉我结果。

为了让输入输出有具体形状,下面使用这些示例值:

text 复制代码
sessionId = s_123                 本次操作的会话
requestId = req_42                本次用户提交
rpcId = rpc_7                     本次网络调用
mode = queue                     排入后续 turn
cwd = /workspace/demo            工作目录
provider/model = deepseek/deepseek-chat
当前可见工具 = bash、read、edit

这些值与后面的测试输出用于教学,并非一次实际运行的录制。模型也不一定会采用我们列出的行动顺序。


小炎: 我需要先知道对象是什么。尤其是 Service,它为什么夹在 Gateway 和 Agent 之间?

大老师: Service 是注册到 Cordis 中、供其他组件调用的能力对象。Cordis 是 Harness 使用的组件装配框架;它让组件可以按名称取得能力、接收事件,并管理启动与释放。

比如 Session Controller 是处理会话相关外部命令的 Service;LLM Service 提供模型调用能力。Service 是一种角色,不是某条请求,也不是某个 Session。

再补齐两个相关名词。Cordis 的 Context 是组件取得服务、注册贡献和监听事件的运行入口。Harness 的 Scope 是能力可见范围,决定一个 Agent 能使用哪些工具、指令贡献以及相关监听。它们支持整条链,不能画成用户消息依次穿过的三个关卡。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
Cordis Context 连接组件与所需能力 服务注册、监听,以及调用者所在的运行环境 按当前上下文取得服务,管理贡献和监听的生命周期 调用者可以取得 llm、agents 等能力 需要由其他机制承担依赖连接与生命周期管理
Cordis Service 提供可复用的能力入口 所属 Context、名称、配置和方法参数 提供具体能力;不同 Service 的实现不同 例如 sessionController.prompt(request) 对应能力必须由调用者自行实现或通过其他入口提供
Harness Scope 限定 Agent 的能力视野 Agent 所在范围及其可见注册 决定装配时可见的指令、工具和贡献 本例可见 bash、read、edit 缺少范围机制时,难以让不同 Agent 使用不同能力组合

所以并不存在"根据 Session 找一个通用 Service,再让 Service 找 Agent"的统一流程。这里调用的具体 Service 是 Session Controller,它提供处理会话命令的方法。


小炎: 那能不能像网络协议一样分层?我不想把所有名字挤进一条箭头。

大老师: 可以按职责分层,但这是一张阅读地图,不是每层都会装配一次模型 prompt 的协议栈。

层 解决的问题 主要对象
L1 用户提交 如何把人的意图变成客户端动作 UI、Client Session、Prompt command
L2 远程调用 如何把方法调用送到执行端 Remote、Descriptor、Connection、RPC envelope
L3 Host 接入 请求是否符合协议,应该交给哪个能力 Host Connection handler、Gateway、GatewayInvocation
L4 业务接纳 目标会话能否接收这条输入 Session Controller、命令处理器、Agent 解析器与 Registry
L5 Agent 调度 何时处理输入,何时继续模型---工具循环 Inbox、Agent、Turn、Step
L6 请求形成 本次模型究竟看到什么 PromptAssembly、各类 Projection、请求准备与构建
L7 外部执行 怎样调用模型,怎样执行工具 LLM Adapter、Model、工具执行管线
L8 返回显示 怎样把执行事实和状态呈现给人 follow、control、Client model、UI

另外,Session 是 L4--L8 都会使用的事实层。它既为下一次模型请求提供历史,也为界面提供可回放的记录。


小炎: 从点击发送开始,Client 到底构建了什么?sessionId 也是这时生成的吗?

大老师: 本例操作的是已有会话,所以复用 s_123。会话标识在创建会话时确定;当前创建接口允许调用方提供,也允许 Host 生成。每次发送消息并不会再创建一个 Session。

网页先组织一条 Prompt command。这里的 command 是"请把这份输入交给指定会话"的命令数据,不是发给模型的完整 prompt。

json 复制代码
{
  "sessionId": "s_123",
  "requestId": "req_42",
  "mode": "queue",
  "content": [
    {
      "type": "text",
      "text": "请运行测试,如果失败就修复 src/math.ts,然后告诉我结果。"
    }
  ]
}
对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
UI 接收人的操作 输入文本、当前会话、发送按钮点击 处理输入状态并发起 Client 调用 session.prompt(...) 人需要通过终端或程序等其他入口提交
Client Session 管理界面端的会话镜像与提交状态 s_123 和输入内容 登记待确认提交,调用 Remote,随后用 Host 更新维护镜像 本地 pending 状态与远程提交调用 UI 必须自行协调提交、确认和事件更新
Prompt command 明确提交的目标和语义 会话、提交标识、发送模式、文本 它是数据;由 Client 组织,由 Host 读取 上面的 JSON 数据 执行端无法明确这条输入属于哪个会话、按何种模式接收

Client Session 是界面镜像,Host 的 Core Session 是执行事实。 名字都有 Session,职责却不同;本地显示"已发送"也不等于 Host 已完成任务。


小炎: Remote 不是服务器,那为什么我能调用 remote.session.prompt()?

大老师: 因为 Remote 把远程接口包装成客户端可以调用的方法。RPC,也就是远程过程调用,解决的是"调用另一端的方法"这个问题。Remote 提供方法门面,Connection 提供传输。

Descriptor 是接口描述数据,说明方法对应哪个入口、参数怎样编码、结果怎样解码等。Endpoint 是接口入口名称;本例是 session/prompt。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
Client Remote 把客户端方法调用转换成远程调用 remote.session.prompt(command) 根据 Descriptor 准备参数和 endpoint 向 Connection 调用 session/prompt,参数放在 args 中 Client 需要手工维护远程接口约定
Remote Descriptor 描述两端共同遵守的接口契约 prompt 方法的声明 它是描述数据;调用器按它编码参数、定位方法、处理结果 endpoint、参数名及结果规则 手写参数与服务器方法更容易不一致
Connection / RPC caller 承担通信及响应配对 endpoint、参数和取消信号 生成 rpcId,发送请求,检查响应与 id;连接体系还管理持续流与连接状态 请求发往 Host,返回对应调用结果 同样的业务调用需要另一种传输实现
ClientRequest envelope 给网络请求一个可验证的外壳 rpc_7、session/prompt、参数 它是数据;传输层序列化,Host 解析 带 type、rpcId、method、payload 的 JSON 两端缺少统一的请求类型和配对规则

本例的网络请求可表示为:

text 复制代码
POST api/session/prompt
Content-Type: application/json
json 复制代码
{
  "type": "client-request",
  "rpcId": "rpc_7",
  "method": "session/prompt",
  "payload": {
    "args": {
      "request": {
        "sessionId": "s_123",
        "requestId": "req_42",
        "mode": "queue",
        "content": [
          {
            "type": "text",
            "text": "请运行测试,如果失败就修复 src/math.ts,然后告诉我结果。"
          }
        ]
      }
    }
  }
}

网页传输器使用相对路由;这里展示的是本例的 endpoint 与参数结构。requestId 标识用户提交,rpcId 标识一次传输调用,它们不能混用。


小炎: 那类似 FastAPI 的服务器部分是谁?为什么检查完网络请求,还要再检查一次?

大老师: 最接近服务器路由职责的是 Host 的接入处理器、Gateway 和 Controller 这一组,但它们分别解决不同问题。

接入层处理认证与网络格式;Gateway 按接口契约把请求转成方法调用;Controller 判断业务上能否接收。JSON 合法不意味着会话存在,方法名正确不意味着内容非空。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
Host Connection handler 接收并检查网络请求 HTTP 请求、JSON 外壳、调用方信息 按接入配置检查认证、路由、内容类型、外壳和 method 一致性 可交给 Gateway 的调用数据 协议错误和业务逻辑会混在一起
Typert Gateway 按接口契约派发调用 endpoint=session/prompt 与命名参数 查 Descriptor,准备参数,解析对应 Service 与方法 调用 Session Controller 的 prompt(request, signal) 接入层必须自行实现契约检查和派发
GatewayInvocation 携带一次调用的上下文 调用方、endpoint、取消信号等 保存并提供本次调用的身份与取消信息 被调用方法所需的调用上下文 方法难以关联调用来源及本次调用的取消状态
Session Controller 提供会话 API 入口 解码后的 command 与 signal 委派给负责命令、历史或状态的内部处理器 本例得到命令处理器的接纳结果 外部调用需要其他会话业务入口
Session Command Controller 实施提交的业务准入 s_123、req_42、content、mode 检查内容,解析 Agent,处理重复提交和附件准入;构造用户消息并调用 followup 或 steer 输入进入 Agent;返回 { accepted: true } 合法网络请求仍无法按会话业务规则执行

这里要记住两种查找:

text 复制代码
endpoint = session/prompt → 调哪个 Service 方法?
sessionId = s_123         → 这个方法操作哪个 Session / Agent?

Gateway 并不是拿 s_123 去决定调用 Session Controller 还是 LLM Service。接口入口已经说明了本次要调用什么能力。


小炎: "找到 Agent"又涉及 Registry、SessionStore 和持久化,它们有什么区别?

大老师: 它们分别管理执行者、已打开的会话和磁盘上的记录。首先定义 Agent :它是协调输入、模型调用与工具执行的运行对象。Core Session 则记录这个运行过程中的事实,二者不能互相代替。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
API Agent 解析器 把会话 id 解析成可操作的 Agent s_123 查询 live Agent;必要时协调从保存的会话恢复,并协调重复恢复请求 当前可操作的 agent(s_123),或明确失败 Controller 需要自己处理已有 Agent 与恢复路径
Agent Registry / agents 管理进程中的 Agent Agent id、创建或恢复参数 保存 live Agent,提供查找、创建、恢复等能力 对应 Agent 实例 业务层无法统一定位与管理执行者
SessionStore / sessions 管理已打开的 Core Session 会话 id 与会话创建、打开请求 管理内存中的 Session 生命周期,并与持久化能力协作 session(s_123) 运行中的会话需要其他管理机制
Session persistence 保存和读取会话记录 Session 日志、元数据、会话标识 将记录保存到持久介质,支持后续读取与恢复 重启后仍可读取的记录 进程结束后不能仅靠内存找回会话
Core Session 保留执行事实并提供不同视图 用户消息、模型消息、工具结果、运行边界等 追加事件,维护当前有效消息视图与请求相关记录 历史事件、模型消息投影、界面历史来源 下一步模型与界面缺少共同的事实依据

本例首先尝试找到已经运行的 Agent,不能把"根据 sessionId 找到 Agent"理解成"寻找之前某个 HTTP 请求并继续执行"。HTTP 调用会结束,会话可以继续存在。

SessionStore 也不能简单定义为磁盘数据库;管理已打开对象与保存对象是两个职责。


小炎: Agent 已经找到了,为什么接口只返回 accepted,不直接返回修复结果?

大老师: 因为接纳输入和完成任务之间,可能隔着排队、多次模型调用、多次工具执行、等待用户或失败。

Inbox 是 Agent 的待处理输入队列。queue 通过 followup() 把消息安排给后续 Turn ;Turn 是一轮任务执行的边界。一轮里可以包含多个 Step;Step 是循环中一次模型调用及其工具处理的执行边界。一次 Step 还可能因为重试包含多个模型请求尝试。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
ReactLoopInbox 安排输入在哪个边界生效 带 req_42 来源的用户消息,目标 next-turn 记录排队输入;到相应边界由 Agent 领取 本轮领取的用户消息 运行中纠偏与后续任务容易混在一起
ReactLoopAgent 协调模型---工具循环 Inbox、Session、可用能力和运行配置 调度 Turn/Step,形成请求,处理模型流与工具,决定继续或结束 Session 事实与执行状态变化 请求与工具之间缺少统一调度者
Turn 表示一轮执行的开始和结束 本轮输入与运行状态 Agent 记录 turn 边界并循环 Step 一轮执行的完成、阻塞或中断等结果 难以区分"这轮结束"与"某次模型调用结束"
Step 表示一次循环推进 已领取输入、当前历史和能力 Agent 形成请求并处理模型/工具结果;必要时重试 助手消息、工具结果、Step 结束状态 无法清楚说明何时重新装配、何时只是重试

Turn 和 Step 是执行边界,不能因为它们在对象表中出现,就假定各自都是一个长期 Service 实例。

业务接纳成功后,RPC 的成功值是:

json 复制代码
{ "accepted": true }

它表示该提交已进入目标 Agent 的输入路径;重复的已接纳提交也可能得到同样确认。它不承诺"测试通过"或"修复成功"。Agent 可以在确认响应返回途中已经开始执行,因此也不能保证所有执行事件都在网页收到 accepted 之后才到达。


小炎: 我想先把这段画对。accepted 应该接在 Agent 前面吗?

大老师: 应该画成返回 Client 的分支。Agent 处理的是用户消息,不是拿着 accepted 继续调用模型。

sequenceDiagram participant UI as UI / Client Session participant R as Client Remote participant C as Client Connection participant H as Host 接入 participant G as Gateway participant S as Session Controller / 命令处理器 participant A as Agent 解析器 / Registry participant L as 目标 Agent participant I as Inbox UI->>R: command:s_123、req_42、queue、content R->>C: session/prompt + 命名参数 C->>H: HTTP + rpc_7 + 请求外壳 H->>G: 已通过接入检查的调用 G->>S: prompt(request, signal) S->>A: 按 sessionId 解析 Agent A-->>S: agent(s_123) S->>L: followup(UserMessage) L->>I: 排入 next-turn 并安排唤醒 S-->>G: accepted=true G-->>H: 调用成功值 H-->>C: rpc_7 对应的响应 C-->>R: RPC 结果 R-->>UI: 接纳确认 Note over L,I: 执行由 Agent 调度,可与确认响应传输并行 Note over UI,L: 最终消息与状态通过另行订阅返回

这个图只画提交路径。把订阅和模型循环也全部塞进来,会再次让人误以为它们都在等待这一次 HTTP 返回。


小炎: 接下来以 prompt 装配为主线。每一层是不是都会装配 prompt,只是装配内容不同?

大老师: 并不是。Client 组织命令,Remote 组织远程参数,Connection 组织网络外壳。它们转换的是提交的表示形式。真正面向模型的装配在 Agent 的 Step 中发生。

PromptAssembly 是一次装配产生的材料对象。Plugin 是装入运行环境、注册能力或贡献的组件;Agent 向当前 Scope 中可见的贡献收集材料:

text 复制代码
sections:行为指令段落 → system prompt
contexts:当前运行状态 → 运行上下文消息
tools:工具名称、说明和参数结构 → 模型可见工具定义
variables:段落中需要代入的值 → 渲染时使用

比如本例可以收集到工作目录 /workspace/demo、编码指令,以及 bash、read、edit 的工具定义。收集结果并不是已经发给供应商的 HTTP 请求。

这里的 Projection,投影,指把一组数据转换成某种用途的表示。例如把装配材料变成需要记录的上下文消息,或把 Session 的当前有效历史变成模型消息。

对象或方法 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
systemPrompt.assemble() / PromptAssembly 汇集本 Step 可见的材料 Agent Scope、已注册贡献、当前环境 收集并排序指令与上下文,收集工具和变量 四类材料的装配结果 动态能力难以反映到当前模型视野
RuntimeContextProjection 将运行状态变化转成消息 工作目录等当前快照与旧快照 比较内容;相同去重,变化或清空时产生相应表示 必要的 runtime-context 用户消息 环境变化可能丢失,或每步重复堆入相同内容
agent/pre-step 扩展点 在进入 Step 前调整或拒绝 已领取用户消息、候选上下文消息、运行位置 可由注册组件改写消息或拒绝本次进入 允许进入时的消息与装配材料 统一的 Step 准入与改写需要放到其他位置
renderPrompt() 把指令材料变成文字 已排序 sections 与 variables 插值、过滤空内容、连接段落 本次 system 文本 结构化段落尚不能作为模型指令使用
prepareRequest() / PreparedLlmCall 确定模型与适配器能力 路由配置、turn/step、取消信号 经过请求扩展点,准备 provider/model 与适配器 已解析配置、可调用的适配器及能力信息 后续无法可靠决定请求该怎样交给哪个模型
SystemPromptProjection 决定 system 如何进入事实与请求视图 system 文本、旧记录、适配器能力 决定追加、替换、清除或不变 必要的 system/message 提交 system 的变化难以按当前模型协议记录和投影
buildRequest() / Harness Request 形成内部模型请求 模型配置、工具、Session 有效历史 记录必要的请求配置变化,取得消息和工具定义历史,构造请求 messages、tools、toolHistory、配置与 signal 装配材料还没有变成实际可调用的请求

本例对应的关键顺序是:

text 复制代码
领取 Inbox 输入
→ 收集 PromptAssembly
→ 计算运行上下文变化
→ agent/pre-step 决策
→ 开始 Step,渲染 system 文本
→ prepareRequest,确定路由与适配器
→ 按适配器能力记录 system,首次尝试追加本 Step 新消息
→ buildRequest
→ 调用模型

表中的 prepareRequest()、buildRequest() 是 Agent 的方法;PromptAssembly、PreparedLlmCall、Request 是数据或调用准备结果。它们不是一批独立服务器。


小炎: Session 在这里提供什么?是不是把完整日志直接塞进 prompt?

大老师: Session 提供当前有效的模型消息视图,不把所有运行事件原样发送给模型。

Surface 是 Session 当前有效消息视图的结构。deriveMessages() 从它导出模型消息;压缩等机制可以改变有效视图,同时保留原始日志。模型需要用户、助手和工具反馈,不需要把每个内部调度事件当成聊天文本。

另外,toolHistory() 这个名字很容易误导:它记录的是历史工具定义及增删变化,供适配器投影能力使用。执行过哪些工具、返回了什么,则通过相应消息和结果进入 deriveMessages() 的历史。

本例第一次请求的模型视野,可以简化成:

text 复制代码
system:Agent 行为指令与当前可见的工具使用指导
user:请运行测试,如果失败就修复 src/math.ts,然后告诉我结果。
user:当前运行上下文,例如 cwd=/workspace/demo
tools:bash、read、edit 的定义
config:provider/model 等模型配置

这是帮助理解的内容示意,不是某个供应商的逐字段 HTTP 格式。实际指令文本与消息投影方式取决于当前贡献和适配器。

没有 Session 提供共同事实层,模型下一步看到的历史与网页展示的历史就需要分别维护,更容易出现"界面显示过,模型却不知道"或"模型用过,恢复后却找不到依据"的问题。


小炎: 请求发出之后,模型和工具各自做什么?模型返回 bash 就等于命令已经执行了吗?

大老师: 模型返回工具调用,表示提出行动。真正运行命令的是 Harness 的工具执行能力。

LLM Adapter 是模型协议适配器,把 Harness 的统一请求转换成供应商协议,并将返回的数据归一化。Tool call 是模型给出的工具名称与参数。Sandbox 是操作系统层面的执行限制;审批和 guard 则是执行前的策略检查。这些角色都不能与模型的判断混为一谈。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
LLM Adapter 隔离模型供应商协议差异 Harness Request,选择 DeepSeek 转换消息与工具,发送请求,将返回归一化 异步 StreamChunk 流 Agent 必须直接理解每家供应商协议
Model 在当前视野内决定下一步 指令、历史、工具定义 生成文本或结构化行动请求 可能给出 bash 调用,命令为 pnpm test 需要其他机制选择行动并生成回答
Tool guard / Approval / Sandbox 判断并限制可执行行动 tool call、参数、策略与执行环境 检查准入;策略需要时请求审批;按环境限制执行 允许、拒绝或等待,以及受限执行条件 行动缺少相应策略检查或系统约束
Tool Executor 执行已准入的工具 bash 调用、取消信号 调用真实工具实现,收集结果 例如退出码 1 和测试失败输出 模型只提出动作,文件与进程不会因此改变

假设工具实际返回:

text 复制代码
command: pnpm test
exitCode: 1
output: src/math.ts:8 expected 3 received 4

Agent 把助手的工具调用与工具结果记录到 Session。注意,工具成功运行、测试通过、用户目标完成,是三个不同判断:命令可以成功启动,却报告测试失败。


小炎: 那 Step 2 会收到什么?是不是把工具结果放进 Inbox,再拼一次完整 prompt?

大老师: 工具结果进入 Session 后,下一次请求就能通过模型消息投影看到它。继续循环并不要求 Inbox 一定出现新消息。

Inbox 管理新输入何时生效;Session 保留已经发生的对话与执行事实。某些工具还会另外提供补充上下文,运行中的用户也可能发来 steer,这些才可能成为下一 Step 要领取的输入。

比较项 Step 1 Step 2
新领取的输入 本例的用户指令 可以为空;也可能有纠偏或补充上下文
历史事实 原有会话历史及本次已接纳的模型输入 增加 Step 1 的助手工具调用和测试结果
装配材料 收集当前指令、环境、工具、变量 再次收集,以反映可能变化的能力和环境
模型配置 准备本次 provider/model 与能力 重新准备;可受请求路由影响
本次可能输出 请求执行 pnpm test 结合失败信息,可能先读取 src/math.ts 再判断修改

这里的"重新装配"不意味着把相同的 system 和环境消息无限追加。未变化的内容会通过相应记录与投影机制复用或去重;新请求仍会形成完整的当前视野。对本例而言,最关键的新内容是工具反馈。

还要区分 新 Step 和 同一 Step 的重试。当前实现对一个已进入的 Step 使用同一份装配材料;每次模型请求尝试会重新准备和构建请求,本 Step 新领取的消息只在首次尝试追加,避免因为重试复制用户输入。

flowchart TB I[Inbox:在执行边界领取输入,可为空] --> P[本 Step:收集与渲染材料] P --> B[准备模型配置,记录必要变化,构建请求] S[(Core Session:执行事实与有效消息视图)] -->|deriveMessages 提供历史| B B --> M[LLM Adapter 与 Model] M --> D{输出是否包含工具调用?} D -->|有| T[准入检查与工具执行] T -->|工具结果| S M -->|助手消息| S P -->|必要的新指令与上下文| S B -->|必要的请求配置记录| S T --> N{是否需要继续?} N -->|继续,允许没有新输入| I N -->|结束| E[收束本轮] D -->|没有工具调用,普通完成| E E -->|运行边界| S

这是普通执行主线的简图,省略了失败、阻塞、取消与重试分支。Session 在旁边持续参与,工具结果也没有被画成必经 Inbox 的输入。


小炎: 最后模型回答出来,为什么还需要 follow、control 和 Client model?

大老师: 因为界面需要持续观察执行过程,而一次提交 RPC 已经结束。还需要同时处理历史、正在生成的输出,以及会话状态。

Stream,流 ,是持续送来多条更新的返回方式。Frame,帧 ,是一条流中的更新单位。Cursor,游标,说明历史读取到哪里,帮助断线后补齐。Client model 再把这些更新折叠成 UI 可以使用的状态。

对象 作用 输入(本例) 内部处理 输出(本例) 没有它会怎样
Session 事件与观察机制 将执行事实提供给观察者 用户、助手、工具及运行边界的变化 Session 记录事实,相关观察器提供历史与变化;持久化能力另行负责保存 可回放的记录与更新通知 界面和恢复逻辑缺少统一变化来源
session.follow 跟随一个会话的历史与实时助手输出 会话 id、历史位置、订阅取消信号 提供会话记录,并承载助手实时流帧 测试调用与结果记录,以及正在生成的回答片段 需要另一种会话跟随与历史补齐机制
session.control 跟随 Host 范围的会话状态投影 控制订阅与当前会话投影 提供状态基线和后续投影变化 当前哪些会话存在、是否运行等状态 界面需要其他机制维护 Host 范围的会话状态
Client model / Projection 把更新变成界面状态 历史事件、实时帧、状态变化 合并更新,维护历史位置与本地视图 可渲染的消息、工具卡片与运行状态 UI 必须逐个处理原始协议更新
Conversation / UI 展示执行过程和结果 Client 的会话视图 渲染文本、工具过程和状态 人看到测试失败、检查文件、后续回答 执行仍可发生,但人没有这一展示入口

所以不能把所有实时助手输出都划给 control。当前 follow 也承载助手的实时流帧;control 关注 Host 范围的会话状态投影。取消 Agent 是另一个命令,不是"关闭流就取消任务"。

flowchart LR C[Client 提交] -->|prompt RPC| H[Host 业务接纳] H -->|接纳确认 accepted| C H --> A[Agent 调度与执行] A --> S[(Session 执行事实)] S --> F[follow:历史记录] A -->|助手实时帧| F A -->|执行状态影响会话投影| P[Host 会话状态投影] P --> K[control:状态基线与更新] F --> V[Client model / UI] K --> V

这里画的是数据来源和返回职责,不代表每次提交都要新开两个流。界面可以已有订阅,且响应与流更新的网络到达顺序不能当成业务顺序。


小炎: 那我把原来的 11 步改成下面这一版,应该怎样说才完整?

大老师: 可以这样向别人讲:

  1. 网页把用户文本整理成指定会话的提交命令,并显示待确认状态。
  2. Client Remote 根据接口描述,把方法调用转换成 endpoint 与参数。
  3. Connection 添加网络调用标识和请求外壳,发往 Host。
  4. Host 接入层检查认证和协议格式,Gateway 按 endpoint 找到 Service 方法。
  5. Session Controller 的命令处理逻辑检查内容与业务条件,并按 sessionId 解析目标 Agent。
  6. 用户消息按 queue 或 steer 语义进入 Agent 的输入路径;接纳结果返回 Client。
  7. Agent 在相应执行边界领取新输入,开启或继续 Turn/Step。
  8. 每个新 Step 收集当前能力与上下文,结合 Session 有效历史形成模型请求。
  9. LLM Adapter 调用模型;模型提出工具调用时,Harness 检查并执行。
  10. 用户输入、模型输出、工具反馈和运行事实持续进入 Session,Agent 据此决定继续或结束。
  11. 已建立的订阅把历史、实时输出和状态送到 Client,Client 更新界面。

其中第 6 步的"已接纳"和第 11 步的"最终答案"是不同返回。对于终端、SDK 或其他程序入口,前面的 Client/Remote/Gateway 路径可能不同;这里讲的是普通网页调用,不能要求所有入口都照搬这一组对象。


小炎: 所以 Harness 不只是"把 prompt 发给模型",还要把哪些责任分开?

大老师: 至少要分开提交表示、传输契约、业务接纳、执行调度、事实记录和观察显示。每一层的输出只完成自己的职责:网络成功不等于业务接纳,业务接纳不等于模型完成,模型完成也不等于测试通过。

总结:从调用顺序看责任边界

一条用户指令先成为 Client 命令,再成为远程方法参数和网络请求;Host 将其派发给业务 Service,业务层把输入交给目标 Agent。Agent 按 Turn/Step 调度,结合当前能力与 Session 历史构造模型请求。模型的行动请求由工具执行能力落实,反馈记录到 Session,并影响后续 Step。界面通过持续订阅观察事实与状态。

整条链中,endpoint 选择能力入口,sessionId 选择执行会话,requestId 标识用户提交,rpcId 配对网络调用。Session 是共同事实层,Client 是它的界面镜像;accepted 是接纳确认,最终消息另行返回。

可以迁移到其他系统的一条设计认识是:接收任务、执行任务、记录事实和展示进度需要清楚的责任边界。判断一次调用"成功"之前,先说明它确认了哪一层的成功。


源码复查入口(当前工作区版本):

学习进度:本文整理和校正 N03 的调用边界,N03 仍为"讲解中";不新增 C14"模型请求装配"的学习进度。后续候选仍为 N04"执行世界的能力图"。

相关推荐
方方洛1 小时前
ai-agent教程-06-主流框架
人工智能·llm·agent
诺伦1 小时前
从一键分发到 AI 自动运营:内容矩阵工具三代技术架构拆解 | RiseClaw玄策
python·agent·架构设计·增长运营·ai自动运营·内容营销自动化
sg_knight2 小时前
Claude Desktop 的 Chat 模式实测:桌面端比网页版多出来的能力
llm·agent·ai编程·claude·anthropic·claude-code
墨心@3 小时前
AskUserQuestion 工具
agent·coding agent·harness·zcode
SharpCJ4 小时前
Koog(4)—— Agent Skills 和 Tools
llm·agent
七夜zippoe4 小时前
Agent 事件驱动架构:从轮询到事件触发的架构升级
ai·架构·agent·轮询·事件驱动·事件触发
yuzhiboyouye5 小时前
longchain就像已做好的项目和大模型的连接器一样
agent
yuzhiboyouye5 小时前
langchain和spring-boot功能上有什么区别
agent
笑小枫5 小时前
AgentScope 学习系列 - 04:Tool 工具调用实战
agent·智能体开发·agentscope·java开发智能体