Function Calling流程
tool_calls结构,包含调用工具名、参数及唯一ID。 tool_calls需与tool_call_id对应,Harness解析执行后以tool消息回写结果。 get_weather工具参数为{"city":"上海"},模型响应包含tool_calls字段触发执行。Tools声明与实现
content字段,若含terminate: true则终止循环无需再次调用模型。Harness处理逻辑
executeTool函数接收调用ID和参数,校验失败则推送错误消息。Agent Loop循环机制
tool_calls则执行工具并更新上下文→重复循环直至无调用或满足终止条件。 shouldStopAfterTurn标志及用户消息注入时机(steering在工具执行后、follow-up在模型准备结束时)。终止条件
terminate: true或最终答案已结构化输出,则无需继续模型调用。 登录 后可发表评论
拉取内容: Harness 学习笔记分享(Part 1) | 粥里有勺糖
本文主要分享一下整个过程中的一些学习心得和个人的一些想法。
前言
“Agent” 从最开始文本对话式,到多模态(语音/图片/视频)智能体,再到现在的 Agent(能够辅助办公、开发、生成完整项目等),不同阶段 Agent 的定义也不一样。
最近的一阶段是 "Model + Harness = Agent"。
之前是高频使用各种智能体辅助写文档,改项目代码。
最近一段时间准备做一些带 Agent 能力的 AI 应用,所以先学习一下 Harness 相关的知识,知己知彼😋。
开局先来一张图
Harness 算是模型的运行时,各家 Agent 的差异也就在这部分,模型都是可以随时切换的。
所以没有 Harness 的「Agent」,大部分就是纯聊天。
先小小总结一下几个主要模块
tool_callsTools
Tools 是模型和外部沟通的桥:模型只负责发出结构化调用,真正去改文件、跑命令、调 API 的是具体 Tool 实现;调度和校验则由 Harness 来做。
先说 Function Calling
Function Calling(现在大部分时候也叫 Tool Calling)解决的问题是:
避免模型用自然语言描述去说明要调用的工具,而是让它输出可解析的调用结构。
没有这套约定时,模型可能写
我需要调用 get_weather(上海) 来获取天气信息
应用侧很难可靠地解析。有了 Function Calling:
tool_calls(调用谁、参数是什么、本次调用的 id)tool消息写回贴一张官方的图:function-calling#how-it-works
下面是我画的一版 Model / Harness / Tools 怎么流转起来的(对应上面 1→4 步):
调用示例
下面来个最简 demo(OpenAI Chat Completions,非流式),看一下调用结构:
ts
一次成功响应(需要调工具时)大致长这样:
ts
返回结果里,重点关注这几个:
message.tool_calls:决定是否需要执行工具tool_calls[].id:回写结果时需要一一对应上idtool_calls[].function.name/arguments:工具名和参数finish_reason:"tool_calls"表示「需要执行工具」;"stop"表示本轮结束解析执行示例
Harness 处理工具调用的核心逻辑是:校验 → 执行 → 回写 → 再请求。
ts
下面是第二次请求模型的 messages 示例,包含了工具调用结果:
json
小结
tool_calls参数tool_call_id回写 → 再请求Agent Loop
真正办事的 Agent,需要把上面的流程自动循环起来:组装上下文 → 请求模型 → 有调用就执行回写 → 再请求,直到给出最终答复,或被约束条件强制停掉。
当然我这里只是一个最简单的实现(用「有没有
tool_calls」来决定要不要自动推进),实际的循环推进条件可能还有其它的策略。精简的循环示意
ts
和纯对话的差别就在第 4 步:模型输出调用指令,Harness 调度 Tools,用执行结果继续推进。
其它条件
以 Pi: agent-loop 为例,默认仍是「本轮有没有工具调用」驱动自动续跑,同时还会看下面这些条件:
1)工具结果不一定需要回传
terminate: true字段来决定是否回传terminate: true,就不需要再请求模型ts
2)
shouldStopAfterTurn(回合结束后可选停)内置钩子:回合结束后由调用方决定是否继续;也可在这里选择性地压缩上下文。
3)steering / follow-up
这两个都是「往 Loop 里塞用户消息」,时机不同:
相关 loop 代码大概就 100 来行,比较容易理解。
小结
tool_calls则)执行工具 → 再请求进行下一轮运行时约束
Loop 能跑起来之后,接着就是:怎么让它停得住。
Prompt 只能做一些软约束:不可逆操作、调用死循环、反复执行等,需要在 Harness 代码里做硬约束。
常见约束
ts
校验
执行工具调用前加一层执行前校验:校验 schema,工具是否存在,参数是否合法等。
异常情况可以回写错误的 Tool 消息,让模型可以自主的重试,用户也能感知到。
小结
循环的终止与工具调用安全边界,靠 Harness 代码硬约束。
执行沙箱等下来仔细研究一下,后面再单开文章。
Context Engineering
上下文组装也是 Context Engineering 在 Harness 里的应用。
每次给模型推送的,不只是「用户刚输入的那一句」,而是 Harness 组装出来的完整上下文,常见包括:
role: "tool"消息示意代码
ts
小结
这块的细节实现,待我下去再多研究几个项目,单独出一期文章。
SKILL
可以看成是解决某类问题或完成某项任务的流程规范文档(SOP)。
SKILL 不是 Tool,可以看做是一大段提示词,实际执行仍是通过内部的指令描述调用 Tool 来完成任务。
SKILL 结构
常见就是一份 Markdown(可带 frontmatter),大致三块:
name、description(何时该用);可选权限(允许的工具) / 依赖说明references/、模板、脚本、workflow 等,这块一般是渐进式披露的md
怎么进 Context
学到的几种方式:
/skill:name)load_skill,SKILL 正文以 tool 结果回写三种方式的逐步流程(同一例子
weather-brief):小结
SKILL 可以由 Harness 发现并注入上下文,也可以给模型披露一些基本信息让模型自己决定加载哪个。
MCP
MCP(Model Context Protocol)是一套开放协议,用来标准化 AI 应用怎么连接外部系统(数据、工具等)。
本文先只看 Tools 部分:统一发现与调用。
收到
tool_calls后的执行路径:Harness → 选则 Client → Server → 返回的结果回写 Context。
listTools、callTool等常用方法传输:本地多用 stdio,远程多用 Streamable HTTP;数据传输协议使用 JSON-RPC。
MCP Server(Tools)
ts
接入到 Harness 中
下面提到的
registry可以看做是 Harness 里负责管理工具注册和发现的模块包含本地 Tools + MCP Tools 的管理
ts
CLient 连接是长生命周期的:
new Client→connect→listTools注册一次工具;callTool,复用之前的连接;close()。MCP 还包含 Resources 和 Prompt 能力,这块还要再下来研究一下与 Harness 的联动机制
最后
到这里 0-1 搞一个 MVP 版可跑的 Agent 应该是妥妥的了
下一篇(Part 2)大概会是以下的内容:
相关链接