尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C#巧用OpenXmlSDK:向PPTX插入幻灯片并规避常见坑
简介面向需要在未安装Office环境下操作PowerPoint文件的.NET开发者该zip压缩包使用OpenXmlSDK 2.0完整演示了向PowerPoint 2010插入新幻灯片的实现思路与核心代码。整个工程共11个文件其中6个C#源码文件承担主要逻辑2个resx资源文件用于界面显示settings文件保存配置csproj和sln则定义项目结构可直接编译运行压缩包整体仅13KB结构紧凑适合逐行研读。代码覆盖了打开PPTX文件、读取幻灯片集合、新建SlidePart、为幻灯片分配唯一ID、更新presentation.xml中的引用以及保存更改等关键步骤并附带一个空白PPTX演示文件用于测试验证。通过该项目可直观理解PPTX作为ZIP压缩包的内部XML组织方式掌握在不依赖Office环境下创建和修改演示文稿的编程方法已有479人学习下载。对于自动化报告生成、批量文档处理等实际需求这是一套清晰、可复用的OpenXmlSDK 2.0操作范本。1. 向 PowerPoint 中插入新幻灯片为什么我要绕开 Interop 选 OpenXmlSDK做文档自动化的人迟早会遇到这个需求给一份现成的 PPTX 批量插入几个新幻灯片页次固定、内容从数据库或 Excel 里读一套流程跑完生成几百份汇报材料。我最早用的方案是 PowerPoint Interop调用 COM 接口操作 Application 对象单机跑没问题一放到服务器上就翻车——office 权限、并发冲突、进程残留每次部署都像是在拆定时炸弹。后来换成 OpenXmlSDK 2.0 直接改 PPTX 的 XML彻底绕开了这些麻烦。这个方案的核心思路是PPTX 本质是一个 ZIP 包里面全是 XML 文件PowerPoint 只是把 XML 渲染成页面而 OpenXmlSDK 提供了强类型的 API 来读写这些 XML不需要本机安装 Office也不需要启动任何 Office 进程。这篇文章我会把从打开 PPTX 包、读取演示文稿主部件、复制占位模板到定位 slideId 并写入新幻灯片的完整流程拆给你看所有代码基于 C#功能定位明确给已有 PPTX目标格式 PowerPoint 2010 及更高版本插入新幻灯片。适合谁来读一种是做文档自动化、报表生成、投标文件批处理的开发者另一种是手里已经有模板 PPTX、想用脚本批量套版生成 PPT 的运维或实施人员。不需要你精通 XML但最好对 C# 和 Visual Studio 不陌生。下面从 PPTX 的内部结构说起这是所有后续操作的基础。2. 拆开 PPTX先理解文件格式再动手写代码2.1 PPTX 的“三件套”slide 部件、slideLayout 部件、演示文稿主部件PPTX 文件不是一个单文件而是一个 ZIP 压缩包里面包含了幻灯片、母版、版式、主题、图片、字体等一系列 XML 部件。你用 WinRAR 或 7-Zip 手动解压一个 PPTX就能看到ppt/目录下躺着这些核心文件路径作用ppt/presentation.xml主部件定义幻灯片集合、幻灯片尺寸、母版引用、版式引用等ppt/slides/slideN.xml每一页幻灯片的内容页面上的文字、图形、表格都在这里ppt/slideLayouts/slideLayoutN.xml版式文件定义幻灯片继承的占位符位置、背景样式ppt/slideMasters/slideMasterN.xml母版文件定义整套幻灯片的主题风格、字体、颜色方案[Content_Types].xml内容类型声明告诉 OpenXmlSDK 每个部件是什么类型_rels/.rels及各部件目录下的.rels文件部件之间的相互关系定义了“谁引用谁”OpenXmlSDK 2.0 对应的命名空间是DocumentFormat.OpenXml.PowerPoint其中三个最核心的类就是Presentation、Slide、SlideLayout。这三个类对应上面的三个关键 XML 文件操作时先拿到主部件再通过关系引用找到版式和目标幻灯片。2.2 为什么必须用 SDK 而不是直接改 XML直接解压 XML 然后手工替换听起来也是一个可行方案我早期也这么干过。但有个非常现实的问题如果你直接往presentation.xml里追加一条p:sldId看起来逻辑没问题实际上你根本不知道新幻灯片应该放在哪个位置因为这个 XML 的p:sldIdLst顺序决定了幻灯片的播放顺序。往里加一条还容易但如果你需要“把新幻灯片插到第 5 页和第 6 页之间”你要先找到目标位置的 sldId 节点在它后面插入新节点——这个逻辑用字符串替换来写丑且容易出错。更麻烦的是新Slide部件必须同时注册到[Content_Types].xml和presentation.xml.rels里少了任何一步PowerPoint 打开都会报“文件已损坏无法打开”。而 OpenXmlSDK 的SlidePart创建和PresentationPart关系注册是内部接好的我只要调用CreateSlidePart()与AddSlideToPresentation()的配套操作SDK 会自己处理这三个文件的状态同步。这也是我推荐用 SDK 而不是自己撸 XML 的根本原因。2.3 OpenXmlSDK 2.0 的对象模型速览代码里你始终会打交道的几个对象PresentationDocument入口代表整个 PPTX 文件.Open(path, true)以可写方式打开。PresentationPart通过PresentationDocument.PresentationPart拿到对应presentation.xml。Presentation强类型的 XML 根节点对象.SlideIdList属性对应p:sldIdLst里面每一个SlideId对应一页。SlidePart.Slide保存页面内容.SlideLayoutPart引用该页使用的版式。SlideLayoutPart.SlideLayout保存版式内容。一个最容易踩的误区拿到PresentationPart之后用PresentationPart.SlideParts和PresentationPart.SlideIds能分别拿到“所有幻灯片部件”和“所有 sldId 列表”这两个东西看起来差不多但绝不是一回事。SlideParts是文档里所有实际的 slide 部件而SlideIds是页面排序列表插入顺序只改SlideIds的顺序不需要动任何页面内容。后面执行插入操作时会分别用到这两者。3. C# 操作复制模板页、改内容、插入任意位置3.1 最稳妥的方案先复制现有幻灯片再改内容新手最容易踩的坑是“从零创建幻灯片”。从零建Slide对象不是不行但你要自己画占位符、自己指定版式继承关系代码量成倍增加而且出来的页面样式十有八九和模板对不上。我一般会这样做先找一张目标模板页通常是有完整占位符结构的页面比如“标题和内容”这种版式把它复制一份改掉里面的文本然后作为新页插到指定位置。这样做的优势有三个第一继承关系完整版式、母版、主题全部自动带过去不用手动维护第二页面内容和模板完全对齐不会出现字体、颜色、大小不一致第三代码量小不容易出错。下面是我封装好的方法。/// summary /// 在指定的 PowerPoint 演示文稿中将选中幻灯片复制后插入到目标位置 /// /summary /// param namepresentationPathPPTX 文件完整路径/param /// param namesourceSlideIndex源幻灯片索引从0开始/param /// param nameinsertPosition目标插入位置从0开始比如0表示插到第一页前面等于总数表示插到末尾/param /// returns新插入的 SlidePart 对象/returns public static SlidePart DuplicateSlide(string presentationPath, int sourceSlideIndex, int insertPosition) { using (PresentationDocument presentationDocument PresentationDocument.Open(presentationPath, true)) { PresentationPart presentationPart presentationDocument.PresentationPart; if (presentationPart null) throw new ArgumentException(文件不是一个有效的 PPTX 演示文稿。); // 获取当前所有幻灯片部件 ListSlidePart slideParts presentationPart.SlideParts.ToList(); if (sourceSlideIndex 0 || sourceSlideIndex slideParts.Count) throw new ArgumentOutOfRangeException(sourceSlideIndex, 源幻灯片索引超出范围。); // 源幻灯片部件 SlidePart sourceSlidePart slideParts[sourceSlideIndex]; // 1. 复制源幻灯片产生一个新的 SlidePart SlidePart targetSlidePart presentationPart.AddNewPartSlidePart(); using (Stream sourceStream sourceSlidePart.GetStream()) using (Stream targetStream targetSlidePart.GetStream()) { sourceStream.CopyTo(targetStream); } // 2. 复制关系源幻灯片引用了哪个版式新幻灯片也引用同一个版式 string sourceLayoutId sourceSlidePart.GetIdOfPart(sourceSlidePart.SlideLayoutPart); string targetLayoutId targetSlidePart.AddPart(sourceSlidePart.SlideLayoutPart, sourceLayoutId); // 说明如果模板页有依赖图片或图表还需要遍历复制所有外部引用这里不展开 // 3. 找到当前 SlideIdList按位置插入新的 SlideId SlideIdList slideIdList presentationPart.Presentation.SlideIdList; if (slideIdList null) throw new InvalidOperationException(演示文稿中不存在 slideIdList无法执行插入操作。); // 生成一个新的 SlideIdID 规则最大现有ID 1保证唯一 uint maxSlideId slideIdList.Select(slideId slideId.Id.Value).DefaultIfEmpty(0).Max(); uint newSlideId maxSlideId 1; // 计算新的关系 ID uint maxSlidePartId presentationPart.Presentation.SlideIdList .Select(slideId slideId.RelationshipId.Value) .Select(relId (uint)int.Parse(relId.Substring(relId.LastIndexOf(d) 1))) .DefaultIfEmpty(0) .Max(); string newRelationshipId rId (maxSlidePartId 1).ToString(); SlideId newSlideIdElement new SlideId(); newSlideIdElement.Id newSlideId; newSlideIdElement.RelationshipId newRelationshipId; newSlideIdElement.SetAttribute(new OpenXmlAttribute(, r:id, http://schemas.openxmlformats.org/officeDocument/2006/relationships, newRelationshipId)); // 4. 插入到指定位置 if (insertPosition 0) { slideIdList.PrependChild(newSlideIdElement); } else if (insertPosition slideIdList.Count()) { slideIdList.AppendChild(newSlideIdElement); } else { SlideId targetSlideId slideIdList.ElementAt(insertPosition - 1); targetSlideId.InsertAfterSelf(newSlideIdElement); } presentationPart.Presentation.Save(); return targetSlidePart; } }3.2 代码逻辑梳理四步走缺一不可上面这段代码核心逻辑按顺序分为四步第一步是复制源幻灯片。这里用presentationPart.AddNewPartSlidePart()创建一个新的空部件然后把源SlidePart的GetStream()流直接CopyTo过去。底层是 XML 流复制相当于复制了整个slideN.xml文件。这保证了新页面在内容层面和母页完全一致包括所有占位符位置、文本、布局。第二步是关系复制。GetIdOfPart()取到源幻灯片和它所属SlideLayoutPart的关系 ID然后新部件AddPart()到同一个版式部件。这样才能保证新幻灯片继承一样的布局。注意这里有个隐藏坑如果模板部分引用了图片、图表、SmartArt 等外部资源只有版式关系还不够这些图片资源也需要一并复制关系否则新页面会变成“缺图”状态。第三步是生成新的SlideId。SlideId的Id是一个uint类型的唯一标识RelationshipId是rIdN形式的字符串指向新SlidePart和主部件之间的关系。这里我从现有SlideId和RelationshipId里分别取最大值再加一避免冲突。使用SetAttribute是给SlideId补上r:id属性——SDK 2.0 里这个属性有时不会被自动初始化但 PowerPoint 2010 打开时会校验它少了就报错。第四步是插入到指定位置。PrependChild插到最前面AppendChild插到最后面InsertAfterSelf插到指定SlideId后面。这里对位置的处理是整个操作最灵巧的地方我先拿了目标位置前一个SlideId然后InsertAfterSelf这样新幻灯片就恰好落在目标索引处。用这个办法用户不需要理解 XML 的 insert 语义直接给“想插在第几页前面”的索引即可。3.3 关于保存操作的注意每次操作完务必调用presentationPart.Presentation.Save()。这一步把改动写回presentation.xml而前面AddNewPartSlidePart()和AddPart()已经把新部件的内容和关系写进了压缩包。不调Save()PowerPoint 打开文件看不到任何变化但文件没有坏——因为 XML 没改只是新部件多出来了这个属于“垃圾数据”。但如果你按上面代码保存了却不复制关系那就会出大问题下一章细说。4. 处理 SlideLayout 与 Master 关系插入新幻灯片最常见的报错源头4.1 为什么新幻灯片有时候打不开从[Content_Types].xml说起OpenXmlSDK 有个特性我称之为“黑匣子写入”它会在内存里更新部件树但是否真的把新部件写进 ZIP 包取决于你是否显式调用对应Part的Save()方法或根节点的Save()。很多初学的人Presentation.Save()调了但新SlidePart没保存打开 PPTX 一看发现ppt/slides/下确实多了一个 XML 文件[Content_Types].xml里也有它的 Override 声明但 PowerPoint 就是打不开。原因在于新 slide 部件对应的.rels文件没有生成[Content_Types].xml虽然写了但没有关系文件告诉 PowerPoint 这个 slide 是谁引用的主部件无法加载它。这种情况我亲眼见过不止一次。解决办法是在AddNewPartSlidePart()之后马上调一次targetSlidePart.Slide.Save()或者在复制内容流之后单独保存新部件。下面的代码是修复姿势// 修复在复制流后强制保存新 SlidePart slidePartNew.Slide.Save();4.2 版式索引如何保证新页面的版式存在且被正确引用SlideLayoutPart的作用相当于页面的“皮肤”如果新幻灯片引用的版式在presentation.xml.rels中没有被声明PowerPoint 会尝试用默认版式替代结果就是页面整体偏移或者干脆报“版式不可用”。这个坑出现在版本不统一的场景里你打开了一个 PPTX它的源幻灯片引用的是slideLayout3.xml但演示文稿的slideMaster1.xml里并没有列出这个布局。这时候AddPart(sourceSlidePart.SlideLayoutPart)实际上是在做一个无序引用PowerPoint 2010 不买账。这种事情我建议你规避不要尝试修复非法的 PPTX。先自己走一遍校验把目标文件用 PowerPoint 打开一次完全正常再运行脚本。如果确实无法预检代码里加一个引用存在性检查// 检查源幻灯片引用的版式是否在母版中存在 SlideMasterPart masterPart presentationPart.PresentationPart.SlideMasterParts.First(); bool layoutExists masterPart.SlideLayoutParts.Any(layout layout sourceSlidePart.SlideLayoutPart); if (!layoutExists) throw new InvalidOperationException(源幻灯片引用的版式不在当前母版中复制可能失败。);这层检查看起来多此一举实际在模板库比较庞大的企业里非常管用。你永远不知道 HR 部门递过来的那个“母版模板”里藏了多少历史残留。4.3 多母版场景复制后页面样式不对的根因PPTX 支持多母版每个母版可以挂多个版式。如果你的演示文稿有多个母版源幻灯片用 A 母版下的一个版式新幻灯片复制后也继承了 A 母版的版式这本身没毛病。问题出在你手动改版式或者改背景时很多人发现新页面的背景和模板不一致就直觉认为是“复制出了问题”。但实际原因是他改的是slideMaster1.xml而新页面引用的其实是slideMaster2.xml下面的版式。这种问题排查起来极其费时因为从 OpenXmlSDK 的 API 层面往下看SlidePart能直接访问到SlideLayoutPart但无法直接访问SlideMasterPart唯一的路径是通过SlideLayoutPart.SlideMasterPart。我的建议是复制完成后立即打印新页面的版式和母版 ID确认两个关键信息// 记录新页面的版式和母版关系 SlideLayoutPart layoutPart slidePartNew.SlideLayoutPart; SlideMasterPart masterPart layoutPart.SlideMasterPart; Console.WriteLine($新幻灯片引用的版式 ID: {layoutPart.GetIdOfPart(layoutPart)}); Console.WriteLine($所属母版 ID: {masterPart.GetIdOfPart(masterPart)});这个输出对于后续排查“样式不对、字体不对、背景不对”极其有价值。5. 常见问题排查这五个坑我都踩过5.1 PowerPoint 提示“文件已损坏”无法打开现象代码执行完毕生成的新 PPTX 用 WPS 能打开但 PowerPoint 2010 直接弹窗提示文件损坏问是否要修复。原因大概率是SlideId元素缺少r:id属性或者关系 ID 和实际PresentationPart内部关系不匹配。我用 3.1 节代码第一次跑完就遇到这个问题后来发现newSlideIdElement.SetAttribute补属性那行不写全PowerPoint 就会翻脸。解决确保新SlideId同时具备三件套——Id数字、RelationshipIdrIdN 形式的字符串、r:id命名空间属性。其中第三个尤其容易被忽略因为你在 Visual Studio 的调试时 Visual Tree 里看起来一切正常但序列化成 XML 出来就少了属性。5.2 插入位置不对新页跑到了最后一页现象明明传了insertPosition2表示要做第 3 页生成的文件里它却出现在最后一页。原因我犯过两次这种错误——都是忘了SlideIdList里的顺序不等于SlideParts集合的顺序。SlideParts是IEnumerableSlidePart你按它的索引找源幻灯片没问题但slideIdList.ElementAt(insertPosition - 1)是在SlideIdList上取元素两者顺序在某些文件里确实不一致PowerPoint 允许不按顺序排列引用。解决插入位置永远在SlideIdList上计算不要用SlideParts索引混着用。另外插入前先打印当前顺序foreach (var slideId in presentationPart.Presentation.SlideIdList) { SlidePart part (SlidePart)presentationPart.GetPartById(slideId.RelationshipId); Console.WriteLine($当前顺序: {slideId.Id.Value} - {part.Uri}); }5.3 复制出的新页面文字内容不变现象源幻灯片改了文字但新复制出来的页面始终显示的是旧文本怎么改占位符都没反应。原因复制SlidePart的时候源SlidePart的底层GetStream()返回的是流副本你改的“新页面”是修改前的旧 XML。本质是复制发生在Save()之前源文本还没写进去。解决先确认你要复制的“源”是一个已经保存状态完整、内容确定被持久化的文件。如果你在同一个流程里先动态创建并修改了源幻灯片再复制它记得先sourceSlidePart.Slide.Save()。5.4 新页面上的图片消失了只剩一张空白占位符现象模板页里有图片复制出来的新页面上图片没了只留一个空框。原因SlidePart的复制只复制了slideN.xml本身图片是外部部件它作为ImagePart被源幻灯片以关系形式引用不会自动跟过来。解决逐个遍历源幻灯片的所有关系找出非版式、非母版、非主题的外部资源部件复制到新SlidePart并重建关系。代码如下foreach (var relation in sourceSlidePart.Parts) { if (relation.OpenXmlPart is ImagePart || relation.OpenXmlPart is ChartPart || relation.OpenXmlPart is DiagramDataPart) { var newPart targetSlidePart.AddPart(relation.OpenXmlPart); // 注意AddPart 会自动绑一个随机关系 ID应该用源关系 ID targetSlidePart.AddPart(relation.OpenXmlPart, relation.RelationshipId); } }这个逻辑本质上就是把sourceSlidePart.Parts里的外部资源全部注册到新SlidePart。5.5 插入后旧页面的超链接全部失效现象插入一张新幻灯片后原来所有页面的超链接、页内跳转全部失效点起来没反应。原因我在批量插入 20 页的案例里遇到过一次。问题出在复制的SlidePart与源幻灯片共享了同一个SlideLayoutPart而某些超链接是基于RelationshipId定位的新页面添加的RelationshipId和现有的发生了冲突导致关系错乱。解决生成新SlideId的RelationshipId时不要只用PresentationPart内部关系编号 1而是先检查presentationPart.Parts里是否已有相同 IDuint nextId 1; while (presentationPart.Parts.Any(p p.RelationshipId rId nextId)) nextId;这样生成的关系 ID 一定全局唯一不会冲掉旧页面的跳转链接。6. 进阶技巧从“能跑”到“难出问题”把插入逻辑做成可复用工具前面几章解决的问题本质上可以汇总成一个“从模板页克隆并插入任意位置”的工具函数。但工具化之后你还会遇到批量插入、跨文档复制、性能优化这三种进阶场景。最后一个技巧我用一个场景来讲假设你要给一份 40 页的汇报模板批量插入 5 张同结构的新页面每张页面的数据从 Excel 里读一次性生成 100 份不同的 PPTX。这种场景如果每页都重新打开源文件、复制、插入性能会很难看。我会这样做先把源SlidePart的 XML 一次性读进内存批量插入时只做字符串级的占位符替换然后对所有新SlidePart执行同一个AddNewPartSlidePart()和关系复制流程最后一次性Presentation.Save()。这比每页都打开、保存、关闭快一个数量级。注意替换占位符时别用String.Replace因为SlideId生成需要关系唯一性我会在整个插入完成后再统一保存// 批量插入预先存好模板 SlidePart 的 XML 内容 string templateXml; using (var stream sourceSlidePart.GetStream()) using (var reader new StreamReader(stream)) templateXml reader.ReadToEnd(); // 对每一个新页面基于模板 XML 替换内容后创建新 SlidePart foreach (var dataRow in dataRows) { string newXml templateXml.Replace({{TITLE}}, dataRow[标题].ToString()) .Replace({{CONTENT}}, dataRow[内容].ToString()); SlidePart newSlidePart presentationPart.AddNewPartSlidePart(); using (StreamWriter writer new StreamWriter(newSlidePart.GetStream())) writer.Write(newXml); // 这里还要复制版式关系等不再重复 } presentationPart.Presentation.Save();这种一次性批量构造的方式比反复打开关闭文件稳定得多也规避了大部分“文件占用”的问题。我在实际生产环境跑过最高单次生成 300 份 PPTX没出过岔子。另一个很实用的细节是操作前的备份。我无论跑什么批处理执行前永远先复制一份原文件带.bak后缀生成结果后对比确认无误再删备份。这个习惯救过我很多次——有一次目标文件在共享盘上被别人打开着PowerPoint 2010 锁文件锁得很死我的代码没用FileShare.ReadWrite打开直接抛“文件被占用”但祸不单行的是我的程序已经先File.Copy覆盖了一次原文件好好的原始模板就被我的脚本覆盖没了。从那以后我每次操作都强制先File.Copy(originalPath, backupPath)用备份文件做后续流程确认产出正常后才把结果复制回去。希望帮到你。另外验证这个插入后的文件是否能被 PowerPoint 2010 正常打开最省事的办法不是人工打开而是把文件路径传给一个简单的 OpenXmlSDK 打开命令成功打开就说明结构完整using (PresentationDocument doc PresentationDocument.Open(outputPath, false)) { int slideCount doc.PresentationPart.SlideParts.Count(); Console.WriteLine($验证通过共 {slideCount} 页); }走到这一步你的插入逻辑已经不只是“能跑”而是真正可以交出去给业务部门批量使用了。本文还有配套的精品资源点击获取
RELATED

相关推荐

CMake+CUDA工程化配置指南:从构建原理到常见坑位排查

CMake+CUDA工程化配置指南:从构建原理到常见坑位排查

我第一次在一台没有图形界面的机器上折腾 CUDA 时,最大的痛点其实不在 kernel 本身,而在于“工程到底怎么被干净地编译出来”。当时随手在 Makefile 里写 nvcc 规则,换一台机器、换一个环境就要重新折腾一遍。后来把整个项目切换到 CMakeCUDA…

📅 2026/10/17 7:39:19
Agent工程师崛起:从写代码到定规则,大模型应用落地的关键角色

Agent工程师崛起:从写代码到定规则,大模型应用落地的关键角色

最近小半年,我身边技术圈的聊天内容明显跑偏了。以前大家凑在一起,聊的是某套系统又扛住了多少QPS、某条SQL怎么优化、某个服务的容错怎么做;现在这些话题的出场率直线下降,取而代之的是这些问题:你那个智能体任务一多…

📅 2026/10/17 7:39:19
Dify可视化工作流:三步编排LLM与外部工具实战指南

Dify可视化工作流:三步编排LLM与外部工具实战指南

1. 为什么用可视化工作流来编排LLM和工具最近不少做AI应用的朋友都在聊同一个问题:模型本身越来越强,但真正落地到业务里,最难的反而不是提示词怎么写,而是怎么把LLM、外部工具、业务逻辑串成一条能稳定跑的流水线。用代码调API当…

📅 2026/10/17 7:34:19
MORE NEWS

更多资讯

📰

改进型永磁同步电机DTC仿真模型:滞环与SVPWM转矩脉动抑制对比

简介:一套面向电机控制研究人员与电气工程专业学生的永磁同步电机(PMSM)直接转矩控制(DTC)改进版Simulink仿真模型,涵盖从电机本体、转矩磁链估算到滞环控制器与逆变器开关逻辑的完整闭环结构。压缩包共包含…

📰

AI生成式美术与手工版画融合:ArtCraft文创落地全流程解析

做文创这行,最尴尬的不是没想法,而是想法到了落地环节全变味。我去年接手一个节气主题的明信片设计,AI生成的概念草图一张比一张惊艳,结果打印机一跑,边缘假滑得像个塑料玩具,纸张纹理全部丢失,…

📰

artcraft手作材料包全流程指南:从设计稿到量产售卖

最近常有人问我,artcraft到底是什么。说实话,它不是一个能一句话讲清楚的软件,也不是某个平台的固定栏目。artcraft是我给自己一堆手作实验起的项目代号,ar t 和 craft 拼在一起,听起来像“艺术手工”,但更…

📰

大模型微调过拟合怎么办?三招实战反过拟合方案

直接用一句话说完:大模型过拟合不是它笨,是你没给它“开卷考之外的新题”。这是我在多次微调实验里最强烈的感受——训练集准确率一路冲到99.8%,验证集却卡在71%,一翻训练日志,loss曲线优雅得近乎完美,可一…

📰

《artcraft》:以艺术与工艺为核心的建造游戏设计与技术解析

不需要过分解读这个标题,它的字面意思就是“艺术工艺”。我基于这个理解,把它当作一款独立游戏项目的名称来处理,围绕其核心玩法、技术实现和创作理念展开。以下为博文正文。1. 项目缘起:为什么想做一个“艺术工坊”式的建造游戏说…

📰

AI预审如何降低招投标投诉驳回率:规则引擎与大模型融合实践

1. 项目背景与核心需求拆解先交代一下背景。我上一阶段参与的某公司电子招投标平台改造项目,客户提出的核心诉求非常直接:把投标投诉材料被驳回的比例降下来。他们在业务复盘时发现,近一个季度提交的投诉件里有接近三分之一被监管端退回&…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬