摘要:agent 调工具,从头到尾没有一行 Java 代码在思考。本文把两个注解背后的调用链拆开:注册如何把方法变成模型面前的菜单、模型如何只点菜不做菜、框架如何反射派工回填,外加一台 MCP 服务器三行接入;最后用一条四个字的指令「帮我订舱」让模型自己证明------调不调,它说了算。
1、开场:注册了,就能自动调用吗
第 5 课结课当晚,我追着老师发了四句话,原样记在这儿:还是不是很懂整个代码调用链路;用到了 agentscope 哪几个类或者对象;是注册 tools 就能自动调用了吗;整个讲解的,是 agent 内部思考、推理、决定是否调用工具的思考过程吗。当时心里默认的答案是「注册了就会调」,实验收工,我把这个默认划掉了。
这四句话背后是三层误设,掰开就是三问:
- 是注册 tools 就能自动调用了吗?
- 决定是否调用工具的,是代码还是模型?
- 整条链路用到了哪几个类或者对象?
第二问的问法本身就带坑:它默认「有个东西在决定」,而我下意识以为那东西是代码------第四句就是这个坑的变体,把循环管道当成了思考主体。三问就是本篇的目录,第八节一条条还。
先把命题钉死:同一个 agent、同一份注册,那天我在面板上发了两条指令,模型那头接的是 DeepSeek。完整版:「帮我在 MSC AURORA 的 V-2609A 航次订 C-001 这票货」,三个回合走完,成交,单号 BK-20261008-1。极简版:「帮我订舱」,四个字,零工具调用,模型反手问我要船名------它只问了船名,没把其他信息一口气要齐,这个细节第六节有用。两条指令同一天先后发的,反差不是我剪出来的。

同一个 agent,差别只在指令。把面板日志和三处字节码调用点对着看完,结论可以先押在这儿:agent 调工具这件事,从头到尾没有一行 Java 代码在思考。模型只点菜,框架做菜,调不调它说了算。下面按这条链拆:你写的、注册、递菜单、炒菜,中间插一场对照实验,最后 MCP。
2、你写的部分,普通到失望
2.1 全部代码:就一个 POJO
官方文档对最轻路线的原话:
"The lightest-weight way: annotate plain methods with
@Tooland@ToolParam, then callToolkit#registerTool(Object)."
我照做了,结果普通到失望------失望在这儿是褒义词:你不需要学任何新东西,会写 Java 就会写工具。类在项目里真名是 WorkbenchTools,下面贴它的船期方法(照真类从简:真类里 name 是常量、方法体有演示数据,这里写字面量和省略号,其余原样;注解来自 io.agentscope.core.tool 包,MCP 那节还会用到 java.time.Duration):
java
/** 船货查询工具集:船期、舱单、货物详情,纯内存演示数据。 */
public class WorkbenchTools {
@Tool(name = "query_vessel_schedule", description = "按船名查询最新船期,返回航次列表(含 voyageId,查舱单时需要)")
public ToolResultBlock queryVesselSchedule(
@ToolParam(name = "vesselName", required = true,
description = "船名,如 MSC AURORA") String vesselName) {
// ...查你的数据...
return ToolResultBlock.text("MSC AURORA 最新船期:航次 V-2609A(voyageId=V-2609A),上海→鹿特丹......");
}
}
类里一共五个方法,同构,不逐个贴了------按工具名点一遍:query_vessel_schedule 就是上面这个;query_voyage_cargo 按航次查舱单,R1 那单双查询之一;query_cargo_detail 查货物详情;get_current_time 无参,探针一节要借它说话;create_booking 订舱,第五节的主角。五个方法五张卡,数量账 3.2 对。
同事凑过来看了一眼,问出来的三连我原样记着:「WorkbenchTools 就是一个普通的 class 吗?我自己定义的?里面的方法,注解 tool 就行了?」答案不变:是,普通的 class,你自己定义,方法挂注解就行。零继承、零接口、零 Spring 注解------给 agent 添加工具的全部 Java 工作量,就是一个 POJO。
依赖账也干净:除两个注解和返回类型 ToolResultBlock,方法签名里不出现 agentscope 的类型。挂的只是元数据,业务逻辑不依赖框架的运行时。换 Spring 的话说:没有 @Component、没有自动装配,注册是你亲手调的那一行。
2.2 契约两条与那个编译期坑
注解契约就两条,以 javap 口径说:@Tool 十个属性,@ToolParam 三个,日常真正打交道的是 name 和 description 两样。
- @Tool 的 name 可以不写,不写就用方法名驼峰原样------框架不做 snake_case 转换,3.2 节探针实证
- 我这五个全手写了 name,统一成 snake_case,图的是跟日志、跟事件流好对照。
description 是写给模型看的:同一个方法,javadoc 给同事看,description 给模型看,两份说明书一份都省不掉。模型对工具的全部了解就这几行字。这句话现在看着平淡,第六节它会变成全篇最值钱的一条实测。
@ToolParam 的坑在 name:没有默认值,漏写直接编译报错,「对于元素 'name', 批注 @ToolParam 缺少默认值」,工具活不到注册那一步。required 默认 true,想选填必须显式声明,create_booking 的 remark 就是这么写的。
返回值我实测过两条路:
- 返回 String(官方示例的写法,框架把字符串转给模型)
- 返回 ToolResultBlock(我自己项目的写法,方法签名里唯一的 agentscope 类型)
都能跑通,差别只在你有多想自己掌控回执的形状。其余 POJO 理论上给 @Tool 配 converter 就行------这条我没实测,不写死。
契约说完,动手就四步:
- 写个普通类,每个业务方法想清楚「给模型看的说明书怎么写」;
- 方法挂 @Tool、参数挂 @ToolParam,name 老老实实全写上;
toolkit.registerTool(new WorkbenchTools()),一行;- 起服务,面板发指令。
框架侧的接线只有第三步那一行,剩下几十行全是业务------你自己的查询逻辑和返回文案。验货也快:启动日志里躺着五条注册记录,每条后面缀着一句 "in group 'ungrouped'",这个引号里的词,下一节就要用。
3、注册:一次反射,两张卡片
3.1 registerTool 那一行干了什么
registerTool(new WorkbenchTools()) 一行,干的活比看上去多:反射扫描类里全部 @Tool 方法,每个方法生成两张卡。一张 JSON schema 给模型看,一个执行器给框架用(ReflectiveFunctionTool,包着你的方法做反射调用)。官方对 Toolkit 职责的原话:
"Toolkit --- the container that registers tools, MCP clients, and skills, exposes their JSON schemas to the model"
为什么是两张不是一张?一份方法定义要翻译成两种运行时形态:模型那头说协议里的 JSON,Java 这头要能真正 invoke。翻译的活,registerTool 在启动时一次干完。官方那句容器职责,说的就是这两件事:注册、暴露 schema。
这两张卡我按菜单本 和厨房本叫:菜单本(schema)决定模型看得见什么、能点什么;厨房本(执行器)决定真点了单,后厨调哪个方法。两张卡都在 Toolkit 手里。
菜单本这个比喻翻过车:上一课我给同事讲 toolkit,讲到第二遍,他的反馈三段「明白了;Tool 基本懂了;toolkit 还是没太懂」,然后直接要东西------「Toolkit 相关的核心代码,贴给我看看」。我把字节码调用点贴过去,这次他说全懂了。比喻讲到第二遍仍不懂,贴真实调用点一次通,资深 Java 读者的认知通道是代码,不是比喻。所以本篇给自己立个规矩:比喻只当路标,讲机理必贴真代码,从下一节开始贴。
3.2 schema 长什么样:四个跑出来的细节
菜单本上 create_booking 那页长这样,跑出来的实拍,不是手抄文档:
json
{
"type": "object",
"properties": {
"voyageId": {"type": "string", "description": "航次号,来自船期查询结果"},
"itemId": {"type": "string", "description": "货物条目 id,来自舱单查询结果"},
"remark": {"type": "string", "description": "可选备注"}
},
"required": ["voyageId", "itemId"]
}
先看 required 数组:voyageId、itemId 在,remark 不在。@ToolParam(required = false) 的字面翻译就是「进 properties,不进 required」------这一格先记下,第五节模型会反过来占它的便宜。再看 description------「来自船期查询结果」「来自舱单查询结果」,参数的来源就写在 schema 里,这句话第六节要用。模型的全部视野就是这张卡,它看不见你的 Java 代码。
光看自己的类不够,我另写了个探针类戳边界:演示类干演示的事,边界单独起探针,写单测带出来的习惯。业务五张卡加探针那张,六个 schema 全过一遍,四条发现全是跑出来的,没有一条来自文档:
- @Tool 不写 name,schema 里就是方法名驼峰原样------探针方法 probeNoName 注册出来就叫 probeNoName,没人替你转 snake_case;
- required=false 只进 properties、不进 required 数组,create_booking 的 remark 是活证;
- 无参工具的 parameters 永远是
{"type":"object","properties":{}},键在,只是空------按我的读法,schema 生成走统一模板,不按方法签名抄近路; - 注册日志 "in group 'ungrouped'" 是标签不是真组:getToolGroup("ungrouped") 返回 null,查无此组,别被日志骗去写处理逻辑。
数量也严丝合缝:WorkbenchTools 五个 @Tool 方法五张,加探针类那张,共六张------正好是 E1 实测的 6 个 schema。类→方法→卡片一一对应,没有隐藏加餐------以后看到 schema 计数变了,先数方法,再查组开关(第七节),顺序别反。顺带还一笔旧账:「5 个 schema 是什么?怎么来的 5 个?」当时被问住------那会儿探针还没写,账上只有业务五张。答案就是这行映射。

全貌五步:①本节讲完,②③下一节,④⑤炒菜那节。
4、菜单每轮重递,模型只点菜
4.1 菜单在哪拼:每轮现递
先交代循环骨架。ReActAgent 的循环分两个相位:Reasoning(推理拍)和 Acting(执行拍),交替往复------推理一轮,该执行就执行,带着结果再推理。菜单在 Reasoning 这一侧递。
拼菜单的位置是从字节码里抠出来的:Reasoning 方法里有个 lambda,名字就叫 lambdareasoning16(javac 按出现顺序编号),干的事是把 Toolkit.getToolSchemas() 的返回值拼起来,作为第二参数传给 Model.stream:
java
// 调用点一:菜单每轮现拼(方法名取自 ReActAgent 字节码,原样;实调是带过滤参的重载,示意从简)
model.stream(messages, toolkit.getToolSchemas(), ...)
也就是说,注册发生在启动时,递菜单发生在每一轮,每轮推理重新拿一遍。菜单的内容就是 3.2 那几张 schema 卡,模型看到的跟你贴的标本一字不差,没有多一分少一分。
「每轮现拼」有两个直接后果,后面都会兑现:组开关拉了推了,下一轮菜单立刻变(第七节);菜单每轮一字不差,前缀缓存吃得住(第五节账单)。两个后果,一头连着抽屉,一头连着钱。
4.2 最容易想错的一步:点菜的不是做菜的
收到菜单后,模型的输出只有两种:给用户的回答,或者 ToolUseBlock------一段结构化意图,里面就是工具名加一张参数表。它可以一次点不止一道,R1 那种同轮双查询就是一条消息塞了两个,第五节细看。把 ToolUseBlock 打开,意思就一个(形状示意,非日志原文):「我想调 create_booking,参数如下」。
请注意:到这里,工具仍然没有被调用。ToolUseBlock 只是意图,模型没有任何渠道执行你的 Java 代码,它连你的类路径在哪儿都不知道。
这一步最容易被面板骗过去:界面上看见工具被调用了,就以为模型「会执行代码」。你在界面上看见的「工具被调用」,全名其实叫「框架替模型执行了它点的菜」。中间隔着框架,意图落地是下一节的事,框架替它跑腿。意图和执行分开,是这条链上最重要的分工,后面的 MCP、组开关、权限闸门,全挂在这条分工上。
5、框架炒菜:三轮轨迹
5.1 派工那一行
派工行先贴。同一份字节码里,Reasoning 之后是 Acting,就一行:
java
// 调用点二:意图落地(ReActAgent → Toolkit,方法名原样)
dispatchToolCalls(...) // 里面转手调 toolkit.callTools(...)
callTools 拿着 ToolUseBlock,按名字去厨房本翻执行器,反射调用你的方法;返回值包成 ToolResultBlock,跟原意图按 id 配对,塞回对话进下一轮。下一轮模型看到的对话里,就多了这条带结果的记录------R2 反哺 remark,靠的就是它。4.2 说的「框架替它跑腿」,跑的就是这几步------一去一回,严丝合缝。
5.2 876 行里的裁决帧
完整指令那单的全程,SSE 落了 876 行。原始流不贴,只抽裁决帧------三轮,各有各的事。时序先给一张图,逐段再拆。

R1,同轮并行双查询。指令发出去,第一条 assistant 消息里塞了两个 tool_calls:查船期、查舱单一起发,AG-UI 事件流上两股 TOOL_CALL_* 交织推进,光参数增量 ARGS 就有 70 条 delta。两个查询互不依赖,谁也不吃谁的返回,这才并行得起来------「订舱」被拆成两个只读查询,拆解是模型自己做的。更有意思的是 V-2609A 和 C-001 指令里都给了,它还是先查:确认航次真在船期表、货物真在舱单,才肯往下走。指令给了还查是求证,不是多余------它不肯拿没证实的前提去写数据;这一轮也是我全程最意外的一幕,本以为一句指令一个动作。
R2,create_booking,remark 是它自己补的。voyageId、itemId 之外,模型把 R1 舱单结果里的货描抄进了 remark:「电子产品,集装箱 TEMU1234」。货描和集装箱号就躺在 R1 那条返回里,它拿上一轮的工具结果反哺了这一轮的参数------required=false 不等于摆设:schema 允许不填,模型判断这里值得填。3.2 记下的那格,在这儿兑现。
R3,汇报。正文给出 BK-20261008-1,末尾主动加一句:该结果仅为内存演示数据,未实际落库------这句话系统提示里没有,工具返回值里有,它搬得很自然。跟服务端日志对账,「create_booking 模拟登记:bookingId=BK-20261008-1」,逐字对上。876 行里裁决帧就这几处,其余是同一件事的逐条流水,抓帧读,别被行数吓住。
5.3 token 账单与下一个伏笔
账单逐个对,每个数字回答一个问题:
- CUSTOM=3,回答「这单跑了几轮」------点菜两轮(R1 双查询、R2 订舱)加汇报一轮,第 2 课的点菜轮数加一公式第三次全中;
- cached 256→896→1152,回答「菜单每轮重递亏不亏」------同 run 逐轮爬升,菜单每轮一字不差,对上第 4 课的前缀缓存账;
- input 首轮 739,回答「挂上工具菜单会不会膨胀」------不带工具菜单的旧实验输入在 748 上下,同量级,没膨胀。
三个数字三个问题,账平了,这单才算真跑完。
还有一个座位空着。派工前有一道 evaluateOne,每个调用先换回一个 PermissionVerdict------权限裁决。它卡在链路的哪个精确位置,我从字节码里只证到了「派工前」这一层,再细没验,不写死。这是本课新考古的第三处调用点;要算全史是四处,第 2 课那处 summary 空工具表(List.of())是旧账。本课没人配过权限,它一路抬杆,闸门已经在链路上,不是将来才加的。但记住这个对象:下一篇,它是主角。
6、让模型自己证明
6.1 一场对赌
同一天,同一个 agent,我把指令砍到四个字:「帮我订舱」。发之前我赌它不敢调,两手都备好了:它要是调了,我看它编的参数错在哪;不调,我看它怎么问。
实测命中第二种:0 个 TOOL_CALL,纯反问。reasoning 流里它的自述(英文,大意):Need more info... So just ask for vessel name。回复原文:「订舱需要几个信息,麻烦您提供:1. 船名(例如 MSC AURORA)------我先查船期拿航次号 2. 货物条目------从该航次的舱单里选定具体货物(itemId)。您把船名发我,我这就查最新船期。」它把整条路线和盘托出,却只开口要了起点。注意这个不对称:路线已经在它脑子里了,缺的只是船名。
6.2 description 工程学
先替你把问题问出来:R1 里模型凭什么知道先查船期、再从舱单挑 itemId?当时的我答不上来。回去把系统提示词翻了一遍:它只给了一句泛指令------「货代查询请使用对应查询工具,需要多步时按依赖顺序逐步调用」------一个字没提订舱,也没说哪步查什么、参数从哪拿。这条「先查后订」的具体路线,系统提示给不了,只可能来自工具自己的 description:
- query_vessel_schedule:「按船名查询最新船期,返回航次列表(含 voyageId,查舱单时需要)」------下一步去哪,写在本职说明书里;
- create_booking:参数说明写明 voyageId 来自船期查询、itemId 来自舱单查询(3.2 标本原文)------参数从哪拿,也写在说明书里。
注意这个写法:依赖方向不是我在系统提示里编排的,是每段 description 只说「我是什么、我吃什么、我的输出喂给谁」,模型把几段话连起来,自己读出了路线。这是从实测反推的机理,反推有实锤:系统提示里没写这条路线,链路却发生了。
第二问再补一刀:模型手里有全套 schema,仍选择反问而不是编参数------宁反问,不编造。理由不难猜:create_booking 要 voyageId 和 itemId,四个字里一个都没有,编一个出来就是把货订到不存在的航次上。反问的代价是多一轮对话,编造的代价是一张废单,它选了便宜的。
所以那句判断值得单独一行:能不能调 ,schema 说了算;敢不敢调,description 说了算。description 别当注释写------javadoc 它一个字不看,description 是它真正的说明书,也是这条链路上你还能说了算的那段。回头看 create_booking 那两条参数说明------itemId 来自舱单、voyageId 来自船期------这不算文笔,算把参数来源写进了说明书。模型要自己找参数,你就得告诉它去哪找。
7、MCP 与工具抽屉
7.1 接入一桌现成菜,就几行
手写的菜上完了,上外来菜。接现成的 server-everything,就几行:
java
McpClientWrapper mcp = McpClientBuilder.create("everything")
.stdioTransport("cmd", "/c", "npx", "-y", "@modelcontextprotocol/server-everything")
.initializationTimeout(Duration.ofMinutes(2))
.buildSync();
toolkit.registerMcpClient(mcp).block();
官方原话:
"Use
McpClientBuilderto build anMcpClientWrapper, then register it on theToolkit"
(这句原则没错,但文档示例里的链式写法在 2.0.3 对不上号,能跑的是我贴的这个签名------漂移账本节末尾一起算。)这几行里信息量最大的是 stdioTransport:把一个 npm 包当子进程拉起来,前提是本机有 Node/npx。wrapper 是一台服务器的连接,不是一个工具------注册完一口气进来 13 个:注册前 toolkit 侧 getToolNames() 是空的,注册后 echo、get-sum、get-env、get-tiny-image......13 个裸名。
更要紧的是这 13 道外来菜跟手写菜的关系。同一个池子。手写工具反射出来的执行器,跟 McpTool 共用 ToolBase 基类,事件流零区别------getToolSchemas 一并暴露,callTools 一并派工,组开关一并管束。注册动作是两种------registerTool、registerMcpClient,进了这道门之后,所有机制一视同仁。模型分不出哪道菜是自己家炒的;对你的排障也是好消息,出了问题只查一张菜单、一套派工日志,不用分两条路。
7.2 四个坑
前三个坑都在 Windows 上:
- npx 在 Windows 是 .cmd,直接塞给 stdioTransport 起不来,前面加 "cmd", "/c"------代码里那两个参数就是它;
- registerMcpClient 返回 Mono<Void>,不 .block() 就是异步放飞:服务起来时工具还没进门,面板一片空白,症状像「没注册上」,其实注册还在半路;
- 首跑 npx 现场下载包,默认超时等不起,initializationTimeout 给到 2 分钟。
坑二值得多说一句:坑不在技术,在症状离原因太远------面板一空白,我先怀疑的是注册 API 本身。block() 是 Reactor 的常识,坑在场景。第四坑最阴,命名。官方文档说 MCP 工具挂在命名空间下:
"MCP tools are exposed in the toolkit under the namespace
mcp__{server_name}__{tool_name}to avoid collisions"
我按这个前缀写过滤器,第一跑 AssertionError:2.0.3 里没有这个前缀,13 个全是裸名。对账办法很土:wrapper.listTools() 列一遍,toolkit 侧 getToolNames() 列一遍,两边交叉,裸名即真名------官方前缀在这个版本查无实据。这是我在这个框架上记下的第 5 处文档漂移(前四处散在前几课的速查表:McpClientBuilder 文档示例签名、ToolGroupScope 多写了一个 SESSION、「basic 组」说法、tools.json 白名单),规矩不变:文档当线索,实测为准。
7.3 工具抽屉:五个词三十秒重学
组的词汇表一共五个词:组、组内、激活、开关、成员关系。前一天我刚把组开关折腾三回、记下四个数,第二天口测还是拼不拢------现眼实录在第八节,这儿先把抽屉重学一遍,三个动作:
java
toolkit.createToolGroup("mcp", "外部 MCP 工具", false); // 造抽屉:第三参 false = 出厂推上
toolkit.addToolToGroup("mcp", "get-sum"); // 登记:持久,跟开关无关
toolkit.updateToolGroups(List.of("mcp"), true); // 拉开/推上:整组布尔
这组数我是在一只空 Toolkit 上单独验的------应用里那只还带着五个业务工具,起点不是 13。把 get-sum 塞进推上的抽屉,schema 数 13→12;拉开,回 13;再推上,又 12------连起来 13→12→13→12,四个数全是数 getToolSchemas() 数出来的。
生效点规则就藏在这串数字里:未激活组的工具从菜单上消失------根本没这道菜,不是置灰。置灰是界面语义,看得见点不动;消失是菜单语义,模型压根不知道它存在。4.1 的伏笔在这儿收口:不是框架实时改了模型那边的什么,是每轮现拼的菜单,把抽屉状态原样带给了模型。
这个区别决定排障方向:模型「不用」某个工具时,先分清它是没进菜单(注册、组开关),还是进了菜单它不点(schema、description)。登记与开关是两本账,拆开记:登记本上 get-sum 那个名字自始至终没掉,拉开就回来------两本账拆开,13→12→13→12 才不会读成「工具丢了」。
8、收口:思考发生在模型侧
开场三问,一条条还。
-
是注册 tools 就能自动调用了吗?没有「自动」这回事:注册只解决「能不能调」------菜单上有没有这道菜、参数怎么必填;「敢不敢调」是 description 的事;「调不调」永远是模型的判断。「帮我订舱」就是活证:全套 schema 摆在面前,它一个没点。
-
决定是否调用工具的,是代码还是模型?模型。思考发生在模型侧,agent 只是壳:Reasoning、Acting 的循环是机械的,ToolUseBlock 是意图不是执行,智能只在最后一环。你写的每一行 Java 都是确定性的,不确定的只有模型那一下。第四句也顺手拆了------「agent 内部的思考过程」是误设,管道只递菜、回菜,不思考。
-
整条链路用到了哪几个类或者对象?当年我问这个,想要一张能对着看的图;现在主链清单就是下面几行,每个名字前文都给过调用点或实拍:
- 你写的普通类,@Tool/@ToolParam 挂注解;
- Toolkit,手里两张卡------schema 卡 getToolSchemas 给模型,执行器卡 ReflectiveFunctionTool 给框架;
- 循环引擎 ReActAgent:Reasoning 每轮拼菜单(lambdareasoning16),Acting 里 dispatchToolCalls→callTools 派工;
- ToolUseBlock 与 ToolResultBlock,一去一回按 id 配对;
- 接了 MCP 再添 McpClientWrapper 和 McpTool,与手写工具同池。
初稿写完的第二天,我给自己来了个口测,当场现眼。第一题「agent 怎么调工具」,我写下「经过了 LLM 掉用 tool」------半句,还把「调用」打成了「掉用」,意图和执行分开那层干脆没提。
第二题「registerTool 干了什么」,憋出六个字:「无法识别注册」。反射、两张卡,一样没沾。第三题组、激活、组内、开关、成员关系,原话是「这个不清楚,再讲一下」------前一天亲手折腾三回、记下四个数,第二天五个词拼不拢。这跟前几课的褪色教训一个剧本:跑通时的懂,褪得比想象快,这篇文章就是给褪色上的保险。
带走三句
- 没有魔法:链路上每一步都是确定性的代码,三处调用点都指认过了,不确定的只在模型那一次出手;
- 两个说了算:能不能调 schema 说了算,敢不敢调 description 说了算------说明书含糊,它宁可反问;
- 一套抽象通吃两个世界:ToolBase 同基类,getToolSchemas 一视同仁,你手写的和 npx 拉来的坐同一张菜单。
临走三问
- 注册日志里有某个工具,模型却从来没调过它,你第一件事去查什么?
- 不走 agent,你在代码里直接拿到执行器去调 create_booking------会发生什么,跟模型点菜那一路差在哪一环?
- 把 query_voyage_cargo 挪进一个未激活的组,下一轮模型的菜单上还有这道菜吗?把 addToolToGroup 的登记也删掉呢?
三道都答对了,这一课才算真的带走。
五课下来,从把 agent 跑起来,到对着字节码指认每一环,这条链算是走通了。下一篇(0006)换主角:权限与人在回路(HITL)------本课只露了一面的 PermissionVerdict 在那里当班:写操作先拦下,等审批。
共约 6077 字 · 阅读约 15 分钟
如果这篇文章对你有帮助,欢迎转发给需要的同事和朋友。