首屏导读 · 本教程配套付费专栏 大模型工程师修炼手记19.9 元AI 编程 / Agent 实战 本文同主题系统课程· AI时代程序员的自我提升49.9 元AI 时代成长方法论。单篇不过瘾订阅解锁全量源码、实战与答疑文末附资料包领取方式 ↓引言AI真的会“用工具”吗当你在聊天框里输入“北京今天天气怎么样”时你是否好奇过AI是怎么知道实时天气的它自己上网查的吗其实AI本身并不具备实时获取信息的能力——它只是通过训练学到了“如何调用工具”这一技能。所谓“工具调用”就是让大模型在需要时输出一个结构化的指令比如“查询天气城市北京”然后由外部程序执行这个指令再把结果返回给模型最终生成自然语言回答。整个过程就像你指挥一个助手去办事你动嘴助手动手。目前实现工具调用的主流方式有三种提示工程模拟通过精心设计的prompt让模型输出固定格式的JSON外部解析执行。原生Tool Calling大模型原生支持的标准接口输出结构化的tool_calls请求。MCP协议新兴的开放协议将工具封装为独立服务实现跨语言、动态扩展。本篇我们将从最基础的提示工程模拟入手手把手带你实现一个完整的工具调用流程。这种方式不依赖任何高级功能适用于任何大模型也是理解工具调用原理的最佳起点。一、核心思想让模型当“编剧”程序当“演员”在没有原生函数调用支持的情况下我们需要通过提示词“教会”模型如何表达调用工具的意图。核心思路如下告诉模型有哪些工具可用名称、功能、参数。要求模型在需要调用工具时输出一个特定格式的JSON包含工具名和参数。外部程序解析这个JSON执行对应的工具函数。可选将工具执行结果再次交给模型生成自然语言回答。这个过程的巧妙之处在于模型并不真正执行任何代码它只是“纸上谈兵”——输出调用指令。真正的“动手”由我们的Java程序完成。这样既利用了模型的智能又保证了安全性和可控性。二、实战准备Spring Boot Spring AI 环境搭建我们将在Spring Boot项目中集成Spring AI以OpenAI为例其他模型类似。首先创建Spring Boot项目3.2添加依赖dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-openai/artifactIdversion1.0.0.M6/version/dependency/dependencies在application.yml中配置OpenAI API密钥spring:ai:openai:api-key:${OPENAI_API_KEY}chat:options:model:gpt-3.5-turbotemperature:0.0# 降低随机性保证输出稳定三、定义可执行的工具函数我们实现两个简单工具天气查询和计算器。注意这些是真实的Java方法之后将由我们的程序调用。ComponentpublicclassWeatherToolExecutor{// 模拟天气查询实际可调用真实APIpublicStringgetWeather(Stringcity){MapString,StringweatherMapMap.of(北京,晴10-22℃,上海,多云15-25℃,广州,雨20-28℃);StringweatherweatherMap.get(city);returnweather!null?weather:未找到city的天气信息;}// 模拟计算器使用JavaScript引擎生产环境建议使用安全计算库publicStringcalculate(Stringexpression){try{ScriptEngineenginenewScriptEngineManager().getEngineByName(JavaScript);Objectresultengine.eval(expression);returnString.valueOf(result);}catch(Exceptione){return表达式错误e.getMessage();}}}这里我们使用Component将工具类注册为Spring Bean方便后续注入。四、设计提示词让模型学会输出JSON提示词是整个流程的关键。我们需要清晰描述每个工具的功能和参数。规定输出格式必须是一个JSON对象。提供几个示例few-shot帮助模型理解。下面是我们设计的系统提示词你是一个智能助手可以根据用户问题调用以下工具 1. get_weather获取城市天气 参数{city: 城市名称} 2. calculate计算数学表达式 参数{expression: 数学表达式如 22} 如果用户问题需要调用工具请只输出一个JSON对象格式为 { tool: 工具名称, args: {参数名: 参数值} } 不要输出其他任何内容。 如果不需要调用工具直接回答用户问题。 示例 用户北京今天热吗 输出{tool: get_weather, args: {city: 北京}} 用户计算 3.5 * 2 输出{tool: calculate, args: {expression: 3.5 * 2}} 用户你好 输出你好有什么可以帮你的吗注意我们将这个提示作为system消息发送这样模型会在每次对话中遵循这些规则。五、编写服务类解析JSON并执行工具接下来我们创建一个服务类整合以上逻辑ServicepublicclassPromptToolService{privatefinalChatClientchatClient;privatefinalWeatherToolExecutortoolExecutor;privatefinalObjectMapperobjectMappernewObjectMapper();publicPromptToolService(ChatClient.Builderbuilder,WeatherToolExecutortoolExecutor){this.chatClientbuilder.build();this.toolExecutortoolExecutor;}publicStringprocess(StringuserInput){// 1. 调用模型获取响应StringresponsechatClient.prompt().system(SYSTEM_PROMPT)// 上一步定义的提示词.user(userInput).call().content();// 2. 尝试解析为JSONtry{// 清理可能的多余字符如Markdown代码块Stringcleanedresponse.replaceAll(json|,).trim();if(cleaned.startsWith({)cleaned.endsWith(})){JsonNodejsonobjectMapper.readTree(cleaned);Stringtooljson.get(tool).asText();JsonNodeargsjson.get(args);// 3. 根据工具名执行对应方法Stringresultswitch(tool){caseget_weather-toolExecutor.getWeather(args.get(city).asText());casecalculate-toolExecutor.calculate(args.get(expression).asText());default-未知工具tool;};// 4. 返回执行结果也可以再次调用模型生成自然语言回答return工具返回result;}}catch(Exceptione){// 解析失败记录日志System.err.println(JSON解析失败: e.getMessage());}// 不是JSON直接返回模型原始回答returnresponse;}// 将SYSTEM_PROMPT定义为常量privatestaticfinalStringSYSTEM_PROMPT 你是一个智能助手可以根据用户问题调用以下工具 1. get_weather获取城市天气 参数{city: 城市名称} 2. calculate计算数学表达式 参数{expression: 数学表达式如 22} 如果用户问题需要调用工具请只输出一个JSON对象格式为 { tool: 工具名称, args: {参数名: 参数值} } 不要输出其他任何内容。 如果不需要调用工具直接回答用户问题。 示例 用户北京今天热吗 输出{tool: get_weather, args: {city: 北京}} 用户计算 3.5 * 2 输出{tool: calculate, args: {expression: 3.5 * 2}} 用户你好 输出你好有什么可以帮你的吗 ;}六、测试一下我们写一个简单的Controller来测试RestControllerpublicclassChatController{AutowiredprivatePromptToolServicepromptToolService;GetMapping(/chat)publicStringchat(RequestParamStringmessage){returnpromptToolService.process(message);}}启动应用用浏览器或curl测试GET /chat?message北京今天天气怎么样 响应工具返回晴10-22℃GET /chat?message计算 25*410 响应工具返回110GET /chat?message你好 响应你好有什么可以帮你的吗完美模型成功输出了JSON程序正确解析并执行了工具。七、进阶将工具结果再次交给模型生成自然语言目前我们直接返回了工具的执行结果虽然清晰但不够自然。比如用户问“北京今天热吗”工具返回“晴10-22℃”如果直接回复这句显得生硬。更好的做法是把工具结果作为上下文再次调用模型让它生成一个完整的回答。修改process方法在执行工具后将结果组装成新的用户消息再次调用模型publicStringprocess(StringuserInput){// 第一步获取模型响应可能包含JSONStringfirstResponsechatClient.prompt().system(SYSTEM_PROMPT).user(userInput).call().content();// 尝试解析JSONtry{StringcleanedfirstResponse.replaceAll(json|,).trim();if(cleaned.startsWith({)cleaned.endsWith(})){JsonNodejsonobjectMapper.readTree(cleaned);Stringtooljson.get(tool).asText();JsonNodeargsjson.get(args);// 执行工具StringtoolResultswitch(tool){caseget_weather-toolExecutor.getWeather(args.get(city).asText());casecalculate-toolExecutor.calculate(args.get(expression).asText());default-未知工具;};// 将工具结果作为上下文再次调用模型生成自然语言回答StringfinalAnswerchatClient.prompt().system(你是一个智能助手请根据工具返回的结果用自然的语言回答用户。).user(用户问题userInput\n工具返回toolResult).call().content();returnfinalAnswer;}}catch(Exceptione){// 解析失败忽略}returnfirstResponse;}测试GET /chat?message北京今天天气怎么样 响应北京今天天气晴朗气温在10到22摄氏度之间适合外出活动。这样回答更加自然。八、处理多轮对话简单的记忆机制在上面的实现中每次对话都是独立的模型不记得之前说过什么。如果用户接着问“那上海呢”模型无法关联上下文。我们可以通过维护一个会话记忆来改善。Spring AI提供了ChatMemory接口和MessageChatMemoryAdvisor可以方便地添加记忆功能。但为了保持本篇的独立性我们先用一个简单的手动实现ServicepublicclassPromptToolWithMemoryService{privatefinalChatClientchatClient;privatefinalWeatherToolExecutortoolExecutor;privatefinalObjectMapperobjectMappernewObjectMapper();// 简单内存存储key为sessionIdvalue为历史消息列表privatefinalMapString,ListMessagesessionMemorynewConcurrentHashMap();// ... 构造函数等publicStringprocess(StringuserInput,StringsessionId){// 获取或创建历史记录ListMessagehistorysessionMemory.computeIfAbsent(sessionId,k-newArrayList());// 将用户消息加入历史history.add(newUserMessage(userInput));// 构建包含历史的promptPromptpromptnewPrompt(history,ChatOptions.builder().model(gpt-3.5-turbo).temperature(0.0).build());// 调用模型StringresponsechatClient.prompt(prompt).system(SYSTEM_PROMPT).call().content();// 处理工具调用省略与之前类似// ...// 将助手回答加入历史history.add(newAssistantMessage(finalAnswer));returnfinalAnswer;}}这样同一sessionId的对话会累积记忆模型就能理解上下文了。九、优缺点与适用场景优点通用性强任何大模型都可以使用不依赖厂商特定功能。完全可控输出格式、工具列表、执行逻辑都由自己定义。易于理解适合教学和快速原型帮助理解工具调用的本质。缺点格式不稳定模型可能输出格式错误的JSON或编造不存在的工具。解析繁琐需要编写额外的解析和异常处理代码。无法并行调用一次只能请求一个工具除非设计数组格式。多轮记忆需要手动维护。适用场景使用不支持原生Tool Calling的模型如某些开源模型。需要对工具调用进行高度定制如特殊的安全校验。教学演示帮助新人理解原理。十、小结本篇我们通过提示工程的方式成功模拟了函数调用实现了天气查询和计算器功能。虽然这种方法有一些局限性但它让我们深入理解了工具调用的核心思想模型负责“想”程序负责“做”。下一篇文章我们将介绍Spring AI原生的Tool Calling功能看看它是如何优雅地解决上述问题的让代码简洁10倍hrefhttps://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-e1cc28b339.css relstylesheet hrefhttps://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-d1e89cace4.css relstylesheet内容来源csdn.net作者昵称Tom·Ge原文链接https://blog.csdn.net/gedonshen/article/details/158463020作者主页https://blog.csdn.net/gedonshen标签#人工智能 #AI编程确定要放弃本次机会福利倒计时::立减 ¥普通VIP年卡可用立即使用延伸阅读 · 我的付费专栏觉得这篇文章对你有帮助我把同类主题的系统化内容沉淀成了付费专栏欢迎订阅支持持续输出专栏定价内容大模型工程师修炼手记19.9 元AI 编程 / Agent 深度实战AI时代程序员的自我提升49.9 元AI 时代成长方法论本文配套代码 / 资料包欢迎在评论区留言「求代码」我会私信发送完整资源
企业数字化 ERP 产品动态
相关推荐
洛雪音乐桌面版:一次搜索聚合多源歌曲的免费开源音乐播放器 洛雪音乐桌面版:一次搜索聚合多源歌曲的免费开源音乐播放器 【免费下载链接】lx-music-desktop 一个基于 Electron 的音乐软件 项目地址: https://gitcode.com/GitHub_Trending/lx/lx-music-desktop
把歌名敲进顶部搜索框回车,酷我、酷狗、网易云… · 2026/9/26 2:43:45
中文用户全线中招,Claude网页限速,只因回复里有汉字 万万没想到,Claude竟然被自己的「AI味儿」给坑惨了!
万万没想到,Claude竟然被自己的「AI味儿」给坑惨了!
今天凌晨,Anthropic放出一篇硬核的工程复盘。
他们把一个内部研究版Claude扔进Slack频道,「连轴… · 2026/9/26 4:12:07
Meta不再谈元宇宙,关键词改为“个人超级智能” 2026年的Meta Connect大会,元宇宙“隐身”了,个人超级智能成为绝对主角。
2026年的Meta Connect大会,元宇宙“隐身”了,个人超级智能成为绝对主角。
美国当地时间9月23日,Meta在加州门洛帕克举行年度Connect开发者大… · 2026/9/26 4:12:07
WorkBuddy数据与隐私设置全指南:工作区授权、缓存迁移与记忆管理 1. 为什么数据与隐私设置值得单独拎出来讲很多人上手 WorkBuddy 的时候,注意力全在"怎么让它帮我干活"上——写代码、抓数据、生成网站、跑自动化工作流,恨不得第一天就把所有 Skill 都装一遍。结果用了两三周,突然发现工作台里堆了… · 2026/9/26 4:12:07
AI大模型编程实战:用TaoToken统一Key接入Cline与CC Switch的配置指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 4:12:07
软考架构设计师论文 —— 论软件系统测试(3) 接前一篇文章:软考架构设计师论文 —— 论软件系统测试(2) 本文内容参考:
软考架构设计师论文 —— 论系统性能测试技术及其应用(1)_软件架构师论文 软件测试-CSDN博客
软考架构设计师论文 —— 论单元测试方法及其应用(3)_软考架构设计师论文 如何回应问题2-CSDN博客… · 2026/9/26 4:12:01
DeepSeek LeetCode 153. 寻找旋 Java实现 LeetCode 153 题「寻找旋转排序数组中的最小值」要求以 O(log n) 的时间复杂度找出旋转后的升序数组中的最小元素。由于数组无重复元素,可以使用二分查找。
Java 实现
class Solution {public int findMin(int[] nums) {int left 0;int right nums.length - 1;whi… · 2026/9/26 4:12:01
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46