当前位置: 首页 > article >正文

* Spring AI 的Tool Calling 工具调用

Function Calling让大模型拥有“动手能力”https://blog.csdn.net/weixin_55772633/article/details/160636233?spm1011.2415.3001.5331官网地址https://docs.spring.io/spring-ai/reference/api/tools.html一、什么是 Tool CallingTool Calling工具调用早期称为 Function Calling是 AI 应用中的一种常见模式它允许大语言模型与一组外部 API 或工具进行交互从而突破模型自身的知识局限。工具主要用在两种场景中信息检索型模型无法实时获取当前日期、天气等动态信息通过工具可以从外部数据源获取实时数据弥补模型知识的不足。例如查询给定地点的当前天气。执行动作型在软件系统中执行实际操作如发送邮件、在数据库中创建新记录、提交表单、触发工作流等。⚠️重要安全认知工具调用的实际执行逻辑由客户端应用程序提供模型本身只能请求调用工具并提供输入参数永远无法真正访问工具的底层 API。这一点对于保障系统的安全性至关重要。二、工作原理四阶段流程Spring AI 的工具调用遵循以下标准流程将工具的定义名称、描述、输入参数的 JSON Schema加入聊天请求AI 模型分析用户输入自主判断是否需要调用某个工具如果模型决定调用工具它会返回一个包含工具名和输入参数的响应应用收到请求后在本地执行对应的 Java 方法将方法执行结果返回给模型模型基于工具返回的结果生成最终的自然语言回复三、快速入门动手写一个工具下面我们从一个简单的例子开始在 Spring AI 中实现并调用一个工具。3.1定义Tool返回结果类为了让大模型理解返回的字段也方便前端处理我们定义一个CourseInfo类并添加JsonPropertyDescription注解。package com.edu.tools; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; Data Builder NoArgsConstructor AllArgsConstructor public class CourseInfo { JsonPropertyDescription(课程id) private Long id; JsonPropertyDescription(课程名称) private String name; JsonPropertyDescription(课程价格单位为元货币为人民币) private double price; JsonPropertyDescription(课程学习有效期单位月) private Integer validDuration; JsonPropertyDescription(适用人群例如初学者) private String usePeople; JsonPropertyDescription(课程详细介绍) private String detail; }3.1.1 JsonPropertyDescription在 Spring AI 内部的实际效果当你在Tool方法返回一个包含JsonPropertyDescription注解的类时Spring AI 在构建工具调用请求时会将该类的 JSON Schema 传递给 AI 模型。模型在解析工具返回值时这些描述信息可以帮助它理解每个字段的业务含义决定是否需要在最终回复中重新组织或转述这些字段3.1.2其他常用注解增强字段描述除了JsonPropertyDescription你还可以组合使用注解作用示例JsonProperty(access Access.READ_ONLY)标记字段只用于返回不接受模型传入自动生成的courseIdJsonFormat(pattern yyyy-MM-dd)指定日期字段的格式JsonFormat(pattern yyyy-MM-dd) private Date createDate;JsonProperty(required true)标记字段在返回时必定存在关键业务字段3.2用Tool注解定义工具package com.edu.tools; import cn.hutool.core.lang.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; import java.util.Random; Component Slf4j RequiredArgsConstructor public class CourseTools { Tool(description 根据课程名称查询课程详细信息) public CourseInfo queryCourseByName(ToolParam(description 课程名称) String courseName) { log.info(调用根据课程名称查询课程详细信息工具课程名称{}, courseName); //todo 实际从数据库查询这里直接模拟一个数据 CourseInfo courseInfo CourseInfo.builder() .id(1L) .name(courseName AI大模型应用开发 UUID.randomUUID()) .price(new Random(1000).nextDouble()) .validDuration(new Random(10).nextInt()) .usePeople(开发者) .detail(学好AI大模型应用开发月入百万) .build(); log.info(返回课程详细信息{}, courseInfo); return courseInfo; } }Tool注解提供了几个常用属性属性说明name工具名称不指定时默认为方法名。同一类中不能有同名工具description工具描述强烈建议提供详细描述这是模型理解工具用途的关键returnDirect是否直接将工具结果返回给客户端而不再经过模型处理resultConverter自定义工具调用结果的转换器ToolParam支持以下属性description参数的描述帮助模型理解参数的含义和格式要求required参数是否必需默认为true。如果参数被标注为Nullable默认会被视为可选除非明确指定requiredtrue3.3注册Tool到ChatClientAutowired private CourseTools courseTools; Override public FluxChatEventVO streamChatJson(String question, String sessionId) { // 大模型输出内容的缓存器用于在输出中断后的数据存储 var outputBuilder new StringBuilder(); return chatClient .prompt() .advisors(MessageChatMemoryAdvisor.builder(redisChatMemory) .conversationId(sessionId) .build()) .tools(courseTools) // 添加工具 .system(x-x.text(aiPromptResources.getSystemChatMessage()) .param(now, LocalDateTime.now() .format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)))) .user(question) .stream() .chatResponse() .doFirst(() - GENERATE_STATUS.put(sessionId, true)) // 开始生成时置为 true .doOnError(throwable - GENERATE_STATUS.remove(sessionId)) .doOnComplete(() - GENERATE_STATUS.remove(sessionId)) .doOnCancel(() - { // 当输出被取消时保存输出的内容到历史记录中 redisChatMemory.add(sessionId, new AssistantMessage(outputBuilder.toString())); }) .takeWhile(response - GENERATE_STATUS.getOrDefault(sessionId, false)) // 关键控制流是否继续 .map(chatResponse - { var text chatResponse.getResult().getOutput().getText(); outputBuilder.append(text); // 累积文本 return ChatEventVO.builder() .eventType(ChatEventTypeEnum.DATA.getValue()) .eventData(text) .build(); }) .concatWith(Flux.just(ChatEventVO.builder() .eventType(ChatEventTypeEnum.STOP.getValue()) //结束标识 .build())); }3.4测试基础查询四、实现课程卡片展示4.1问题分析前端需要接收结构化的JSON数据才能渲染卡片。而大模型返回的只是自然语言文本。我们需要在流式输出的最后额外追加一段约定好的JSON数据格式如下{ eventType: 1003, eventData: { courseInfo_1589905661084430337: { id: 1589905661084430337, name: 可能是史上最全的微服务技术栈课程, price: 199.0, validDuration: 9999, usePeople: 有一定的Java开发基础..., detail: 可能是史上最全的微服务技术栈课程... } } }4.2解决方案ToolResultHolder requestId生成请求唯一标识每次对话请求生成一个requestId通过ToolContext传递给Tool。Tool执行后存储结果Tool执行完毕后将课程数据以requestId为key存放到全局容器ToolResultHolder中。流输出末尾读取并追加在Flux输出流的最后根据requestId从容器中取出数据如果存在则构造卡片事件追加到流中。4.3代码实现4.3.1. 定义ToolResultHolderpackage com.edu.holder; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ToolResultHolder { private static final MapString, MapString, Object HANDLER_MAP new ConcurrentHashMap(); public static void put(String key, String field, Object result) { HANDLER_MAP.computeIfAbsent(key, k - new HashMap()).put(field, result); } public static MapString, Object get(String key) { return key null ? null : HANDLER_MAP.get(key); } public static void remove(String key) { HANDLER_MAP.remove(key); } }4.3.2在chatClient中传递requestId4.3.3 修改CourseTools接收ToolContext并存储结果4.3.4末尾追加卡片数据public FluxChatEventVO streamChatJson(String question, String sessionId) { // 大模型输出内容的缓存器用于在输出中断后的数据存储 var outputBuilder new StringBuilder(); // 生成唯一请求id var requestId IdUtil.fastSimpleUUID(); return chatClient .prompt() .advisors(MessageChatMemoryAdvisor.builder(redisChatMemory) .conversationId(sessionId) .build()) .tools(courseTools) // 添加工具 .toolContext(Map.of(requestId,requestId)) .system(x-x.text(aiPromptResources.getSystemChatMessage()) .param(now, LocalDateTime.now() .format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)))) .user(question) .stream() .chatResponse() .doFirst(() - GENERATE_STATUS.put(sessionId, true)) // 开始生成时置为 true .doOnError(throwable - GENERATE_STATUS.remove(sessionId)) .doOnComplete(() - GENERATE_STATUS.remove(sessionId)) .doOnCancel(() - { // 当输出被取消时保存输出的内容到历史记录中 redisChatMemory.add(sessionId, new AssistantMessage(outputBuilder.toString())); }) .takeWhile(response - GENERATE_STATUS.getOrDefault(sessionId, false)) // 关键控制流是否继续 .map(chatResponse - { var text chatResponse.getResult().getOutput().getText(); outputBuilder.append(text); // 累积文本 return ChatEventVO.builder() .eventType(ChatEventTypeEnum.DATA.getValue()) .eventData(text) .build(); }) .concatWith(Flux.defer(() - { // 流结束后检查是否有工具结果 var map ToolResultHolder.get(requestId); if (CollUtil.isNotEmpty(map)) { ToolResultHolder.remove(requestId); // 清理 var cardEvent ChatEventVO.builder() .eventData(map) // 此处map结构为 { courseInfo_xxx: CourseInfo对象 } .eventType(ChatEventTypeEnum.PARAM.getValue()) // 约定事件类型1003 .build(); return Flux.just(cardEvent, ChatEventVO.builder() .eventType(ChatEventTypeEnum.STOP.getValue()) //结束标识 .build()); } return Flux.just(ChatEventVO.builder() .eventType(ChatEventTypeEnum.STOP.getValue()) //结束标识 .build()); })); }4.3.5测试五、Tool额外数据丢失问题修复实录课程查询和预下单功能给前端返回的数据中包含了eventType为1003的数据这个叫作额外数据给前端提供前端是不会显示到页面的正常对话是没问题的但是数据存储到Redis是没有保存进去的如下5.1问题分析数据流向回顾一次完整调用中数据流如下请求层生成requestId通过ToolContext传递给ToolTool层查询课程数据将结果存入ToolResultHolderkey为requestId输出层流结束时从ToolResultHolder.get(requestId)取出卡片数据构造eventType1003事件发送给前端存储层RedisChatMemoryRepository负责将对话消息保存到Redis问题出在第4步保存消息时只保存了UserMessage和AssistantMessage的文本内容、metadata、toolCalls等标准字段完全没有涉及ToolResultHolder中的卡片数据。5.2根本原因RedisChatMemoryRepository在序列化消息时并不知道ToolResultHolder中存了什么。它只能拿到Spring AI框架提供的Message对象而AssistantMessage中没有params字段来存放额外的结构化数据。我们需要做两件事在保存助手消息时将对应的卡片数据params也持久化到Redis在读取历史消息时能恢复这些params并返回给前端关键在于如何将ToolResultHolder中临时存储的数据绑定到具体的某条助手消息上5.3 Bug解决这个bug的解决思路就是在RedisChatMemoryRepository中保存数据时获取到ToolResultHolder中的数据将数据保存到params中即可。但是ToolResultHolder中的数据是与requestId关联的requestId是我们自己生成的在RedisChatMemory中是没有的所以这个问题的关键就是如何获取到requestId了只要有了requestId就可以获取到数据进行保存了。如何传递requestId其实同样也是可以借助于ToolResultHolder来完成我们可以把ToolResultHolder看作是一个通用的容器可以放Tool的结果也可以放其他的内容只要及时的删除即可。有了这个思路问题就好解决了解决代码如下5.3.1 映射消息id和请求idpublic FluxChatEventVO streamChatJson(String question, String sessionId) { // 大模型输出内容的缓存器用于在输出中断后的数据存储 var outputBuilder new StringBuilder(); // 生成唯一请求id var requestId IdUtil.fastSimpleUUID(); return chatClient .prompt() .advisors(MessageChatMemoryAdvisor.builder(redisChatMemory) .conversationId(sessionId) .build()) .tools(courseTools) // 添加工具 .toolContext(Map.of(requestId,requestId)) .system(x-x.text(aiPromptResources.getSystemChatMessage()) .param(now, LocalDateTime.now() .format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)))) .user(question) .stream() .chatResponse() .doFirst(() - GENERATE_STATUS.put(sessionId, true)) // 开始生成时置为 true .doOnError(throwable - GENERATE_STATUS.remove(sessionId)) .doOnComplete(() - GENERATE_STATUS.remove(sessionId)) .doOnCancel(() - { // 当输出被取消时保存输出的内容到历史记录中 redisChatMemory.add(sessionId, new AssistantMessage(outputBuilder.toString())); }) .takeWhile(response - GENERATE_STATUS.getOrDefault(sessionId, false)) // 关键控制流是否继续 .map(chatResponse - { // 对于响应结果进行处理如果是最后一条数据就把此次消息id放到内存中 // 主要用于存储消息数据到 redis中可以根据消息di获取的请求id再通过请求id就可以获取到参数列表了 // 从而解决在历史聊天记录中没有外参数的问题 var finishReason chatResponse.getResult().getMetadata().getFinishReason(); if (StrUtil.equals(STOP, finishReason)) { var messageId chatResponse.getMetadata().getId(); ToolResultHolder.put(messageId, requestId, requestId); } var text chatResponse.getResult().getOutput().getText(); outputBuilder.append(text); // 累积文本 return ChatEventVO.builder() .eventType(ChatEventTypeEnum.DATA.getValue()) .eventData(text) .build(); }) .concatWith(Flux.defer(() - { // 流结束后检查是否有工具结果 var map ToolResultHolder.get(requestId); if (CollUtil.isNotEmpty(map)) { ToolResultHolder.remove(requestId); // 清理 var cardEvent ChatEventVO.builder() .eventData(map) // 此处map结构为 { courseInfo_xxx: CourseInfo对象 } .eventType(ChatEventTypeEnum.PARAM.getValue()) // 约定事件类型1003 .build(); return Flux.just(cardEvent, ChatEventVO.builder() .eventType(ChatEventTypeEnum.STOP.getValue()) //结束标识 .build()); } return Flux.just(ChatEventVO.builder() .eventType(ChatEventTypeEnum.STOP.getValue()) //结束标识 .build()); })); }5.3.2在MessageUtil中增加params参数的处理public static String toJson(Message message) { var myMessage BeanUtil.toBean(message, MyMessage.class); // 设置消息内容 myMessage.setTextContent(message.getText()); if (message instanceof AssistantMessage assistantMessage) { myMessage.setToolCalls(assistantMessage.getToolCalls()); // 通过 messageId 获取 requestId再通过 requestId 获取参数列表如果有就存储起来 // 最后删除 messageId 对应的数据 var messageId Convert.toStr(assistantMessage.getMetadata().get(id)); var requestId Convert.toStr(ToolResultHolder.get(messageId).get(requestId)); var params ToolResultHolder.get(requestId); if (ObjectUtil.isNotEmpty(params)) { myMessage.setParams(params); } ToolResultHolder.remove(messageId); } if (message instanceof ToolResponseMessage toolResponseMessage) { myMessage.setToolResponses(toolResponseMessage.getResponses()); } return JSONUtil.toJsonStr(myMessage); }5.3.3在反序列化Message对象时无法给AssistantMessage设置params属性所以需要继承AssistantMessage来扩展params属性package com.tianji.aigc.memory; import lombok.Getter; import lombok.Setter; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.content.Media; import java.util.List; import java.util.Map; Setter Getter public class MyAssistantMessage extends AssistantMessage { private MapString, Object params; public MyAssistantMessage(String content, MapString, Object properties, ListToolCall toolCalls, ListMedia media, MapString, Object params) { super(content, properties, toolCalls, media); this.params params; } }MessageUtil:package com.edu.memory; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; import com.edu.holder.ToolResultHolder; import org.springframework.ai.chat.messages.*; /** * 消息转换工具类提供消息对象与JSON字符串之间的转换功能主要用于Redis存储格式转换 */ public class MessageUtil { /** * 将Message对象转换为Redis存储格式的JSON字符串 * * param message 需要转换的原始消息对象 * return 符合Redis存储规范的JSON字符串 */ public static String toJson(Message message) { var myMessage BeanUtil.toBean(message, MyMessage.class); // 设置消息内容 myMessage.setTextContent(message.getText()); if (message instanceof AssistantMessage assistantMessage) { myMessage.setToolCalls(assistantMessage.getToolCalls()); // 通过 messageId 获取 requestId再通过 requestId 获取参数列表如果有就存储起来 // 最后删除 messageId 对应的数据 var messageId Convert.toStr(assistantMessage.getMetadata().get(id)); var requestId Convert.toStr(ToolResultHolder.get(messageId).get(requestId)); var params ToolResultHolder.get(requestId); if (ObjectUtil.isNotEmpty(params)) { myMessage.setParams(params); } ToolResultHolder.remove(messageId); } if (message instanceof ToolResponseMessage toolResponseMessage) { myMessage.setToolResponses(toolResponseMessage.getResponses()); } return JSONUtil.toJsonStr(myMessage); } /** * 将Redis存储的JSON字符串反序列化为对应的Message对象 * * param json Redis存储的JSON格式消息数据 * return 对应类型的Message对象 * throws RuntimeException 当无法识别的消息类型时抛出异常 */ public static Message toMessage(String json) { var myMessage JSONUtil.toBean(json, MyMessage.class); var messageType MessageType.valueOf(myMessage.getMessageType()); switch (messageType) { case SYSTEM - { //return new SystemMessage(myMessage.getTextContent()); return SystemMessage.builder() .text(myMessage.getTextContent()) .build(); } case USER - { return UserMessage.builder() .text(myMessage.getTextContent()) .metadata(myMessage.getMetadata()) .media(myMessage.getMedia()) .build(); } case ASSISTANT - { /*return AssistantMessage.builder() .content(myMessage.getTextContent()) .media(myMessage.getMedia()) .toolCalls(myMessage.getToolCalls()) .build();*/ return new MyAssistantMessage(myMessage.getTextContent(), myMessage.getMetadata(), myMessage.getToolCalls(), myMessage.getMedia(), myMessage.getParams()); } case TOOL - { return ToolResponseMessage.builder() .responses(myMessage.getToolResponses()) .metadata(myMessage.getMetadata()) .build(); } } throw new RuntimeException(Message data conversion failed.); } }5.3.4测试结果

相关文章:

* Spring AI 的Tool Calling 工具调用

Function Calling:让大模型拥有“动手能力”: https://blog.csdn.net/weixin_55772633/article/details/160636233?spm1011.2415.3001.5331 官网地址:https://docs.spring.io/spring-ai/reference/api/tools.html 一、什么是 Tool Calling&…...

哔哩下载姬Downkyi实战指南:B站视频高效下载与内容管理解决方案

哔哩下载姬Downkyi实战指南:B站视频高效下载与内容管理解决方案 【免费下载链接】downkyi 哔哩下载姬downkyi,哔哩哔哩网站视频下载工具,支持批量下载,支持8K、HDR、杜比视界,提供工具箱(音视频提取、去水印…...

TensorFlow自动微分终极指南:从基础概念到实战应用的完整教程

TensorFlow自动微分终极指南:从基础概念到实战应用的完整教程 【免费下载链接】TensorFlow-Course :satellite: Simple and ready-to-use tutorials for TensorFlow 项目地址: https://gitcode.com/gh_mirrors/te/TensorFlow-Course TensorFlow自动微分技术…...

定时执行:按时间自动触发AI任务

定时执行:按时间自动触发AI任务📝 本章学习目标:本章介绍流程编排,让AI Agent执行更加规范可控。通过本章学习,你将全面掌握"定时执行:按时间自动触发AI任务"这一核心主题。一、引言:…...

本地大模型赋能命令行:tlm工具实现离线AI助手与Shell工作流融合

1. 项目概述:当命令行遇上本地大模型 如果你和我一样,每天有超过一半的时间是在终端里度过的,那你肯定也经历过这样的时刻:面对一个复杂的文件处理需求,脑子里知道要做什么,但就是敲不出那条完美的命令组合…...

为Godot引擎深度集成Lua:模块编译、特性解析与开发实践

1. 项目概述:为Godot引擎注入Lua灵魂如果你和我一样,既是Godot引擎的忠实拥趸,又对Lua脚本语言那简洁、高效和易于嵌入的特性情有独钟,那么你肯定也想过一个问题:为什么Godot不能原生支持Lua呢?GDScript固然…...

如何构建高效TensorFlow数据流水线:数据集生成器完整指南

如何构建高效TensorFlow数据流水线:数据集生成器完整指南 【免费下载链接】TensorFlow-Course :satellite: Simple and ready-to-use tutorials for TensorFlow 项目地址: https://gitcode.com/gh_mirrors/te/TensorFlow-Course TensorFlow-Course数据集生成…...

AI生成Emoji全攻略:从Stable Diffusion微调到SVG表情包实战

1. 项目概述:当AI开始“画”表情包最近在GitHub上闲逛,发现了一个让我眼前一亮的项目:segersniels/genmoji。简单来说,这是一个用AI生成自定义表情符号(Emoji)的工具。你可能觉得,表情包嘛&…...

SD-WebUI-Inpaint-Anything 插件:解锁自定义修复模型的完整指南

SD-WebUI-Inpaint-Anything 插件:解锁自定义修复模型的完整指南 【免费下载链接】sd-webui-inpaint-anything Inpaint Anything extension performs stable diffusion inpainting on a browser UI using masks from Segment Anything. 项目地址: https://gitcode.…...

Godot 4与Blender无缝资产导入:Importality插件原理与实战

1. 项目概述:当Godot 4遇上Blender,一场资产导入的革命如果你是一名独立游戏开发者,或者是一个小型游戏工作室的成员,那么你大概率对这两个名字不陌生:Godot和Blender。前者是一个功能强大、开源免费的游戏引擎&#x…...

【备考高项】模拟预测题(一)综合知识及答案详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】 【第2题】 【第3题】 【第4题】 【第5题】 【第6题】 【第7题】 【第8题】 【第9题】 【第10题】 【第11题】 【第12题】 【第13题】 【第14题】 【第15题】 【第16题】 【第17题】 【第18题】 【第1…...

终极安全指南:HackerNews React GraphQL项目的认证与数据保护实践

终极安全指南:HackerNews React GraphQL项目的认证与数据保护实践 【免费下载链接】hackernews-react-graphql Hacker News clone rewritten with universal JavaScript, using React and GraphQL. 项目地址: https://gitcode.com/gh_mirrors/ha/hackernews-react…...

AI赋能产品情绪价值:中国制造跳出同质化内卷的关键路径

前言:AI深度创作的15000字文章这篇文章是作者开始接触AI后近两年的思考与心得,借助AI工具深度创作而成,总字数15000字。这是一篇严肃、有深度、有价值的文章,预计阅读需要20分钟,希望能对读者工作提供启发和参考&#…...

终极指南:解密snarkOS BFT共识机制如何实现高性能ZK应用的关键突破

终极指南:解密snarkOS BFT共识机制如何实现高性能ZK应用的关键突破 【免费下载链接】snarkOS A Decentralized Operating System for ZK Applications 项目地址: https://gitcode.com/gh_mirrors/sn/snarkOS snarkOS BFT共识机制是Aleo网络中的核心创新&…...

全球AI大洗牌:xAI解散、Anthropic崛起,国产大模型加速奔跑谁能笑到最后?

【全球AI大洗牌拉开帷幕】一场关乎未来十年科技话语权的全球AI大洗牌,已经正式拉开帷幕。就在外界以为全球大模型竞赛已到阶段性终点时,剧情有了新走向。5月8日,凤凰网科技从知情人士处了解到,阶跃星辰将完成近25亿美元融资&#…...

4I-SIM超分辨显微技术:原理、实现与生物应用

1. 4I-SIM技术原理深度解析 超分辨显微技术领域近年来最引人注目的突破之一,就是结构光照明显微镜(SIM)的迭代升级。作为一名长期从事生物医学成像的研究者,我亲眼见证了传统宽场显微镜如何被SIM技术颠覆,而4I-SIM又将…...

Switch Transformers:稀疏激活MoE模型原理与工程实践指南

1. 项目概述:Switch Transformers 的革新与挑战最近在开源社区里,Switch Transformers 这个项目又火了一把。如果你关注过大规模语言模型,特别是像 GPT-3、T5 这类巨无霸,那你肯定知道它们的训练成本高得吓人,动辄需要…...

OpenSpeedy完全指南:免费开源游戏加速工具让游戏体验飞跃

OpenSpeedy完全指南:免费开源游戏加速工具让游戏体验飞跃 【免费下载链接】OpenSpeedy 🎮 An open-source game speed modifier. 项目地址: https://gitcode.com/gh_mirrors/op/OpenSpeedy OpenSpeedy是一款完全免费且开源的游戏加速工具&#xf…...

08:字符三角形

/*** 【题目名称】字符三角形<p>* 【题目来源】http://noi.openjudge.cn/ch0101/08/** author 潘磊&#xff0c;just_panleijust.edu.cn* version 1.0*/import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input new Scanne…...

CodeGreen:跨平台软件能耗精准测量工具解析

1. CodeGreen&#xff1a;跨平台软件能耗精准测量工具解析在当今计算环境中&#xff0c;软件能耗已成为影响运营成本和环境可持续性的关键因素。随着AI工作负载的爆炸式增长&#xff0c;传统性能优化已无法满足绿色计算的需求。CodeGreen应运而生&#xff0c;这是一款面向开发者…...

如何用AKShare快速搞定金融数据获取?终极实战指南

如何用AKShare快速搞定金融数据获取&#xff1f;终极实战指南 【免费下载链接】akshare AKShare is an elegant and simple financial data interface library for Python, built for human beings! 开源财经数据接口库 项目地址: https://gitcode.com/gh_mirrors/aks/akshar…...

Vale编译器构建系统详解:跨平台编译与依赖管理终极指南

Vale编译器构建系统详解&#xff1a;跨平台编译与依赖管理终极指南 【免费下载链接】Vale Compiler for the Vale programming language - http://vale.dev/ 项目地址: https://gitcode.com/gh_mirrors/val/Vale Vale编译器是一款高性能、内存安全的编程语言编译器&…...

Claude Stacks:AI开发环境一键打包与共享的CLI工具实战

1. 项目概述&#xff1a;Claude Stacks&#xff0c;你的AI开发环境“打包神器”如果你和我一样&#xff0c;深度使用Claude Code作为日常开发的主力AI助手&#xff0c;那你一定遇到过这个痛点&#xff1a;好不容易在本地项目里配置好了一整套顺手的MCP服务器、自定义命令和智能…...

Universal Pokemon Randomizer完全手册:3步打造你的专属宝可梦世界

Universal Pokemon Randomizer完全手册&#xff1a;3步打造你的专属宝可梦世界 【免费下载链接】universal-pokemon-randomizer Public repository of source code for the Universal Pokemon Randomizer 项目地址: https://gitcode.com/gh_mirrors/un/universal-pokemon-ran…...

Zed编辑器Cursor深色主题移植:设计解析与深度定制指南

1. 项目概述&#xff1a;从 Cursor 到 Zed 的深色主题移植如果你和我一样&#xff0c;是那种对编辑器外观有“强迫症”的开发者&#xff0c;那么找到一个既护眼又符合审美的主题&#xff0c;绝对是提升编码幸福感的第一步。最近&#xff0c;我在 Zed 编辑器上发现了一个让我眼前…...

10个必学的sd-webui-oldsix-prompt使用技巧:从新手到高手的进阶之路

10个必学的sd-webui-oldsix-prompt使用技巧&#xff1a;从新手到高手的进阶之路 【免费下载链接】sd-webui-oldsix-prompt sd-webui中文提示词插件、老手新手炼丹必备 项目地址: https://gitcode.com/gh_mirrors/sd/sd-webui-oldsix-prompt 想要在Stable Diffusion WebU…...

GitSavvy快捷键配置终极指南:提升Git操作效率的10个技巧

GitSavvy快捷键配置终极指南&#xff1a;提升Git操作效率的10个技巧 【免费下载链接】GitSavvy Full git and GitHub integration with Sublime Text 项目地址: https://gitcode.com/gh_mirrors/gi/GitSavvy GitSavvy是Sublime Text编辑器中最强大的Git集成插件之一&…...

tickets抢票软件错误排查手册:常见问题及解决方案大全

tickets抢票软件错误排查手册&#xff1a;常见问题及解决方案大全 【免费下载链接】tickets 大麦、大麦网 演唱会抢票软件&#xff0c;一个基于 tauri rust vue 调用接口的抢票软件。 项目地址: https://gitcode.com/gh_mirrors/ti/tickets tickets抢票软件是一款基于…...

终极CubeFS社区贡献指南:从新手到核心贡献者的完整路径

终极CubeFS社区贡献指南&#xff1a;从新手到核心贡献者的完整路径 【免费下载链接】cubefs cloud-native distributed storage 项目地址: https://gitcode.com/gh_mirrors/cu/cubefs CubeFS 作为一款云原生分布式存储系统&#xff0c;凭借其高可用、弹性扩展和多场景适…...

3分钟掌握百度网盘提取码智能获取工具:告别繁琐搜索的终极方案

3分钟掌握百度网盘提取码智能获取工具&#xff1a;告别繁琐搜索的终极方案 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘分享链接的提取码而反复切换浏览器标签、在各种论坛中盲目搜索吗&#xff1f;baidupan…...