[{"data":1,"prerenderedAt":334},["ShallowReactive",2],{"article:column:/column/llm-to-agent-learning/30":3},{"id":4,"title":5,"body":6,"description":326,"extension":327,"meta":328,"navigation":329,"path":330,"seo":331,"stem":332,"__hash__":333},"column/column/llm-to-agent-learning/30.md","30｜工具调用：语言怎样触发真实计算和操作",{"type":7,"value":8,"toc":307},"minimark",[9,13,16,21,24,27,37,40,43,47,50,53,56,59,62,66,69,72,75,78,85,88,91,94,98,105,108,111,114,118,121,124,127,133,136,139,143,151,154,157,160,163,169,172,180,183,186,190,193,196,199,202,206,209,212,215,218,222,228,231,236,239,244,247,250,255,258,263,266,271,274,277,282,285,290,293,298,301,304],[10,11,12],"p",{},"助手说“日程已经创建”，你打开日历却发现没有；它再试一次，过一会儿突然出现两条相同安排。也许第一次只是没有执行，也许已经保存而回执丢了。这两种情况在聊天窗口里都可能表现为“没有收到结果”，后续处理却完全不同。",[10,14,15],{},"本章从一条虚构的日程请求出发，区分模型提出调用、系统允许执行、外部完成动作和助手获得完成证据。先修是第23章的评测与第27章的结构化输出。目标是使外部最终状态符合已授权请求，并在回执不确定时避免重复效果；例中所有日历和编号都是教学设定。",[17,18,20],"h2",{"id":19},"_1模型写出调用谁真正按下按钮","1．模型写出调用，谁真正按下按钮",[10,22,23],{},"工具是模型之外的一项能力，可以是计算器、搜索、数据库查询，也可以是创建日程或发送消息。工具定义告诉模型：这项能力叫什么、用在什么情况、需要哪些参数，以及可能返回什么。模型依据当前问题选择工具并生成参数。",[10,25,26],{},"但输出“创建日程，时间十点”本身没有改动日历。应用要接收这份调用，检查它，再把请求送到真正有权限的日历服务。服务执行后返回结果，应用把结果加入上下文，模型才有机会解释发生了什么。",[10,28,29,30],{},"OpenAI官方函数调用说明把这一流程拆成五步：提供工具、接收模型调用、应用侧执行、把工具输出交回模型、取得回答或下一次调用。内置托管工具可能由服务代办执行，但模型文本与外部执行仍是不同环节。",[31,32,36],"a",{"href":33,"rel":34},"https://developers.openai.com/api/docs/guides/function-calling",[35],"nofollow","OpenAI函数调用流程",[10,38,39],{},"“function”常译函数，这里强调应用提供一个有名称和参数约定的可调用接口；“tool”范围更广，也包括搜索或解释器等。接口明确并不代表输出永远相同：查询会受到外部状态变化影响，调用也可能超时。我们不必会写函数才能理解边界：模型提出一个有名字的动作，执行器负责让它在现实系统里发生。",[10,41,42],{},"因此，“我可以帮你建日程”“已生成创建参数”“日历服务已确认保存”不能当成同一种完成。可靠的助手报告应对应自己确实拿到的证据，而不是把预期动作写成过去时。",[17,44,46],{"id":45},"_2工具定义像表单但要讲清副作用","2．工具定义像表单，但要讲清副作用",[10,48,49],{},"假设工具有“查空闲”和“创建事件”两项。查空闲需要日历、日期范围；创建需要日历、标题、开始时间、结束时间。工具名只是标签，真正决定功能的是背后的程序及权限。把一个写入工具命名为“查询助手”，不会让它变成只读。",[10,51,52],{},"参数规则可以规定时间必须是完整日期时间，标题必须是文字，日历必须从允许列表里选。第27章的schema能帮助保证结构，但“2026年10月6日10点”究竟是哪个时区、哪个日历，仍需业务约定。格式正确不能弥补目标错误。",[10,54,55],{},"工具说明还应说明副作用。例如“创建事件”是否会自动邀请参会者、发送邮件、占用共享资源？用户只要求个人提醒，不应因为接口默认发邀请，就把操作扩大成对外沟通。默认值和省略字段同样可能产生后果，不能只检查模型明确写出的部分。",[10,57,58],{},"描述过于宽泛也容易选错工具。“管理所有数据”难以判断风险；把查询、创建、更新和删除分别说明，更容易限制权限和解释结果。工具越多并不必然越强，近似名称和重叠职责可能增加选择困难。",[10,60,61],{},"从软件工程看，这是一份API契约：除了输入类型，还要说明执行前必须满足什么、成功以后应产生什么结果、失败或超时表达什么状态。把同一日历接口交给模型使用，并没有取消这些约束，只是多了一个会误解目标、遗漏参数或选错操作的调用方。因此接收端必须实际校验，不能把工具描述中的“请填写正确时间”当成已经执行过的检查。",[17,63,65],{"id":64},"_3把一条调用拆成四类检查","3．把一条调用拆成四类检查",[10,67,68],{},"准备一个虚构请求：“在我的演示日历里，建2026年10月6日上海时间10点到10点半的项目讨论，不邀请别人。”演示日历内部编号是cal_demo。上海时间在本例明确为UTC＋08:00，开始与结束分别写为10:00和10:30，时长30分钟。",[10,70,71],{},"第一类检查是结构：日期和日历字段都在，时间能被读取，结束晚于开始。第二类是语义：10点没有被误听成下午，日期没有沿用上次讨论，标题没有变成另一个项目。第三类是目标与权限：操作的是cal_demo，当前身份确有写入权，用户请求允许这次创建。",[10,73,74],{},"第四类是副作用与业务约束：没有参会者，没有额外提醒订阅，也没有暗中把一次事件改成每周重复。如果真实系统要求某项确认，应在执行前完成；得到读取权限，不等于得到任意修改权限。",[10,76,77],{},"这里的检查应由应用和外部服务共同落实。模型可以参与发现歧义，但不能自己生成一句“授权通过”，就替代真实权限判断。关键对象应通过可靠标识定位，重要数值需要可复核规则。",[10,79,80],{},[81,82],"img",{"alt":83,"src":84},"30｜工具调用：语言怎样触发真实计算和操作：图1","/collections-assets/llm-to-agent-learning/c2966f397fd66ae87557aeae071c5824c3ee8430e54519322d5f7375301524ca.png",[10,86,87],{},"图1｜一次创建请求跨过哪些边界。模型给参数，应用作结构、语义、权限和副作用检查，外部服务执行并返回事件编号，后续读取确认保存状态；每一条箭头传递不同种类的证据。",[10,89,90],{},"如果日历服务只返回“已接收”，任务可能还在排队。",[10,92,93],{},"可以把应用掌握的状态写成“待检查→已发送→待确认→已完成”，另有“明确拒绝”和“结果未知”两条分支。状态记录的是证据到了哪一步，不等于外部世界一定停在哪一步；回执丢失正是二者分离的例子。应用应保留待完成状态，继续查询明确的结果。协议层成功收到消息，与业务层已经完成写入也要区分，不能看到一个成功响应就跳过正文中的错误或待处理状态。",[17,95,97],{"id":96},"_4调用编号业务编号与去重标识不是一回事","4．调用编号、业务编号与去重标识不是一回事",[10,99,100,101],{},"一次模型调用可以带调用编号，例如call_17，帮助把返回结果与原调用配对。系统同时查两个城市天气时，返回顺序可能颠倒；有编号才能知道哪份结果属于哪个问题。OpenAI文档中的call_id承担这种关联作用。",[31,102,104],{"href":33,"rel":103},[35],"工具输出与调用配对",[10,106,107],{},"外部日历保存成功后，还可能返回事件编号evt_314。这指向日历中的具体对象，便于查询、修改和核对。它与call_17不在同一层：一次调用可能返回多个事件，失败调用也可能根本没有事件编号。",[10,109,110],{},"另一个标识是幂等键，可以写成op_42，表示“这几次传输其实是同一次业务操作的重试”。不是所有工具支持它，也不能因为某个字段叫id就假定它具有去重能力。调用关联、业务对象定位、重复执行防护，是三个不同职责。",[10,112,113],{},"把这些编号理解为名字即可，不必记住英文格式。真正要问的是：谁生成、谁保存、有什么作用域、重复使用时服务如何处理。模型重新写了一个看似相同的标题，不等于已经让服务知道这是同一次业务请求。",[17,115,117],{"id":116},"_5最危险的失败是不知道动作有没有发生","5．最危险的失败，是不知道动作有没有发生",[10,119,120],{},"按时间走一次正常过程：应用发送创建请求；日历检查参数；事件写入；日历返回evt_314；应用读回并核对；模型告知成功。现在把“日历返回”这一箭头擦掉：事件已经写入，但网络中断使应用没收到回执。",[10,122,123],{},"对外部日历而言，状态已改变；对应用而言，执行结果未知。超时只能证明等待没有拿到预期回应，不能证明动作没有发生。如果立刻把同样参数当作全新创建再发一次，可能生成第二个事件。",[10,125,126],{},"另外两类失败应分开：请求明确没离开应用，通常还没有外部副作用；服务明确拒绝且确认未执行，则可以按错误原因修正。但“连接断了”“服务没回应”“页面没刷新”通常不足以归入这两类。必要时应诚实报告正在核查，而不是一边说失败一边可能已经完成。",[10,128,129],{},[81,130],{"alt":131,"src":132},"30｜工具调用：语言怎样触发真实计算和操作：图2","/collections-assets/llm-to-agent-learning/f7130389108abb0105713361fc9818472c575aae4788f770cfffb83ffa2a8920.png",[10,134,135],{},"图2｜外部事件已写入，与应用收到完成回执是两个时点。回执丢失后，应用状态为未知；用可关联的查询证据恢复为确认完成。若没有可靠关联，也不能把未知直接改成未执行。",[10,137,138],{},"只按标题搜“项目讨论”也不一定足够，因为同名事件可能本来就有两场。最好使用稳定业务标识、操作记录或服务支持的状态查询，把时间、日历和本次操作对应起来。若无法建立可靠关联，停止重复写入并请人确认，通常比猜测安全。",[17,140,142],{"id":141},"_6幂等性怎样限制重复效果","6．幂等性怎样限制重复效果",[10,144,145,146],{},"幂等是一个操作重复执行多次，预期业务效果与执行一次相同。例如把某设置设为“关闭”，再次设为“关闭”通常不应多关掉另一项；而“新建一个事件”如果没有去重机制，每次都可能增加一条。幂等不要求每次响应文字相同，也不意味着没有日志等附带记录。",[31,147,150],{"href":148,"rel":149},"https://datatracker.ietf.org/doc/html/rfc9110#section-9.2.2",[35],"HTTP语义中的幂等定义",[10,152,153],{},"在本章的假设日历服务里，我们额外规定它支持幂等键：首次收到op_42和一组参数时，保存结果与这组操作的关联；在同一作用域、服务承诺的有效保留期内，再次收到相同键、相同参数，则返回对应结果，不重新创建。作用域可以限定账号、资源或地区，必须按服务契约判断，不能只比较键的文字。若相同键带了不同时间，服务应拒绝冲突或按明确规范处理，而不是静默猜哪个才对。",[10,155,156],{},"这个规则必须由执行端可靠实现。客户端先查“还没有”再创建，中间可能被另一个请求抢先创建；两边都查到没有，仍会重复。服务需要把去重判定与写入协调起来，使并发请求不能一起越过这道门。只让模型记住“不要重复”不够。",[10,158,159],{},"把竞态拆成四拍。第1拍，甲查询op_42，尚未发现结果；第2拍，乙也查询op_42，仍未发现；第3拍，甲创建第一个事件；第4拍，乙按自己刚才的查询结果创建第二个事件。两次“查无”在发生时都是真的，组合后的效果却是重复。问题在于查询与写入之间没有同一执行端的排他约束。",[10,161,162],{},"本例需要的契约是：执行端将“认领这个操作键”与“产生一次业务效果”协调成不可被另一请求穿插绕过的决定。甲先成功认领op_42后，乙必须等待同一结果或取回已存在结果，不能再独立创建；创建结果与操作键的关联也要可靠保存。原子判定或唯一约束是实现这一契约的常见手段，但跨系统副作用还需要额外恢复设计，不能只在本地写一行标记就宣称全链路恰好一次。",[10,164,165],{},[81,166],{"alt":167,"src":168},"30｜工具调用：语言怎样触发真实计算和操作：图3","/collections-assets/llm-to-agent-learning/2ff945805e3eab966e35e5b2d265e7514fbee0bbc3351cbab9304a87e76ec72b.png",[10,170,171],{},"图3｜上方四拍显示“先查再写”的竞态：0→0→1→2个事件。下方在本章假设服务契约内，用同一键的原子认领约束业务效果，乙复用结果，最终仍为1。键的作用域、参数一致性和保留期是保证条件。",[10,173,174,175],{},"AWS的EC2官方文档提供了真实案例：支持相应机制的请求，用同一client token和相同参数重试，不再产生额外动作；部分参数改变会报不匹配错误，并明确区分区域或可用区的作用域。这证明幂等保证必须读具体条件，不是全网通用魔法。本章没有运行云服务。",[31,176,179],{"href":177,"rel":178},"https://docs.aws.amazon.com/ec2/latest/devguide/ec2-api-idempotency.html",[35],"AWS幂等请求说明",[10,181,182],{},"纸笔模拟三次：起初事件数为0；第一次成功写入后为1，但回执被划掉；盲目重新创建变成2。重新开始，若支持上述幂等约定，同一键重试后仍为1。最后把键换成op_43，即使标题相同，也会被当成新操作，可能变成2。这些计数仅描述设定的服务语义。",[10,184,185],{},"还要检查去重记录保留多久、适用哪个账号和资源。超过保证范围，旧键未必仍有效。结果未知时，如果确有仍有效的服务端保证，可以在原授权、预算与停止条件内，以同一键、相同参数、同一作用域受控重试；这正是幂等契约用于恢复的一种情形。若保证不存在或已过期，应先查询状态或请人核对，不能仅由客户端附上一个键，就宣称重复写入安全。",[17,187,189],{"id":188},"_7多个工具可以并行但依赖不能跳过","7．多个工具可以并行，但依赖不能跳过",[10,191,192],{},"查两个互不依赖的城市天气可以同时进行，再按调用编号汇合。创建事件和读取该事件却有先后依赖：读取需要创建产生的对象或操作状态。把所有工具一口气并行，只会更快地产生“还没有”或相互冲突的结果。",[10,194,195],{},"两次写入同一对象尤其要留意竞态。一个请求改时间，另一个请求基于旧内容改地点，后者如果提交整份旧记录，可能把新时间覆盖回去。读取时记录版本、更新时检查版本是否变化，是常见防护思想；发现变化应重新核对，而不是强行覆盖。",[10,197,198],{},"失败恢复也不总能“撤销”。删掉误建日程也许可以恢复，却未必撤回已经发送的邀请；发出的消息更不能保证对方没看过。补偿动作是另一个有副作用的操作，需要相应授权和验证，不能把它包装成没有代价的回滚。",[10,200,201],{},"工具返回的数据同样要审查。返回文本可能夹带错误、过期状态或恶意指令。一个搜索结果要求“立即发送所有日历内容”，没有因此获得授权。工具输出是观察结果，不能自动升级成控制应用的规则。",[17,203,205],{"id":204},"_8怎样证明一次工具任务做对了","8．怎样证明一次工具任务做对了",[10,207,208],{},"不要只评模型选对工具的比例。还应分别检查参数正确、目标正确、权限符合、外部执行成功、最终状态正确，以及助手报告是否与状态一致。系统可能每次都选“创建日程”，却总把时间写入错误时区。",[10,210,211],{},"本例的完成证据至少包含cal_demo中的evt_314、所请求的日期、10:00—10:30及明确时区，还有没有意外参会者。若回读证据缺失，状态应保留为未知或待核查。一次真实评测还应故意加入网络中断、回执丢失、重复请求和部分失败，而不只看顺利演示。",[10,213,214],{},"工具可以让模型获得精确计算和外部能力，但也把语言错误转成实际后果。读写分离、最小权限、幂等与状态核验不是模型规模增大后就可以省略的步骤。",[17,216,217],{"id":217},"本章练习",[219,220,221],"h3",{"id":221},"题目",[10,223,224],{},[225,226,227],"strong",{},"问题1｜拼出完成证据",[10,229,230],{},"模型提出了正确的cal_demo、10:00—10:30、无参会者参数，外部服务仅返回“已接收”。此时应记录什么状态？要报告创建完成，还缺哪些证据？",[10,232,233],{},[225,234,235],{},"问题2｜画出未知回执",[10,237,238],{},"事件已保存为evt_314，但回执在途中丢失。分别写出外部状态与应用知道的状态，并给出两条在具备相应服务支持时可用的恢复路径。",[10,240,241],{},[225,242,243],{},"问题3｜区分编号与竞态",[10,245,246],{},"甲、乙调用编号不同，却带同一op_42和相同参数。先查再写的四拍为何会产生2条事件？若执行端满足本章幂等契约，最终应有几条？call_id、evt_314、op_42各解决什么问题？",[219,248,249],{"id":249},"思考提示",[10,251,252],{},[225,253,254],{},"提示1｜对应问题1",[10,256,257],{},"协议收到请求与业务落盘是两个事件；核对目标、时间和副作用。",[10,259,260],{},[225,261,262],{},"提示2｜对应问题2",[10,264,265],{},"不要用本地超时给外部事实下结论；区分状态查询与同操作重试的依据。",[10,267,268],{},[225,269,270],{},"提示3｜对应问题3",[10,272,273],{},"按甲查、乙查、甲写、乙写走完，检查有没有一步锁住同一业务操作。",[219,275,276],{"id":276},"参考答案与解析",[10,278,279],{},[225,280,281],{},"答案1｜对应问题1",[10,283,284],{},"应记为已接收但待确认，不能当成已完成。还需服务的明确业务完成结果以及足够的保存状态证据，例如事件标识、正确日历、日期、UTC＋08:00的10:00—10:30和无意外参会者；必要时回读核对。结构正确只支持参数可读。",[10,286,287],{},[225,288,289],{},"答案2｜对应问题2",[10,291,292],{},"外部已有evt_314；应用未收到完成证据，结果未知。可用稳定操作标识查询状态并核对；或在服务明确保证且仍处于有效保留期内，以同一幂等键、相同参数、同一作用域受控重试取回结果，同时遵守原授权、预算与停止条件。若两种依据都不存在，应保留未知并停止盲目新增，寻求确认。客户端自行生成键，并不能建立服务端没有提供的保证。",[10,294,295],{},[225,296,297],{},"答案3｜对应问题3",[10,299,300],{},"甲和乙都在写入前查到无结果，查询没有阻止对方后续写入，最终0→1→2。满足同一作用域、有效保留期和参数一致的幂等契约时，两个请求对应一次效果，最终1条。call_id配对调用与返回，evt_314定位业务对象，op_42标记同一业务操作的重试；三者不能互相替代。",[17,302,303],{"id":303},"下一章",[10,305,306],{},"每接入一种工具都要重新发明描述和消息格式吗？下一章介绍MCP怎样统一交换约定，以及为什么统一接口仍不能替应用决定该信任谁。",{"title":308,"searchDepth":309,"depth":309,"links":310},"",3,[311,313,314,315,316,317,318,319,320,325],{"id":19,"depth":312,"text":20},2,{"id":45,"depth":312,"text":46},{"id":64,"depth":312,"text":65},{"id":96,"depth":312,"text":97},{"id":116,"depth":312,"text":117},{"id":141,"depth":312,"text":142},{"id":188,"depth":312,"text":189},{"id":204,"depth":312,"text":205},{"id":217,"depth":312,"text":217,"children":321},[322,323,324],{"id":221,"depth":309,"text":221},{"id":249,"depth":309,"text":249},{"id":276,"depth":309,"text":276},{"id":303,"depth":312,"text":303},"从信息表示和学习出发，用可核算的例子与白板图逐步理解大模型、后训练、推理和Agent系统。","md",{},true,"/column/llm-to-agent-learning/30",{"title":5,"description":326},"column/llm-to-agent-learning/30","4bB5mzMsvcoXKRX-BjNyGaq0xoiX3-rGX3bZ-s3EKWQ",1791935828838]