首页 文章 精选 留言 我的

精选列表

搜索[前端工程化],共10003篇文章
优秀的个人博客,低调大师

xss前端预防措施

用户在输入框中输入个js标签 或者在 src 上传图片,路径干个 alert 干扰程序,评论的时候,也干一下 1.输入过滤 对于用户提交的数据进行有效性验证,仅接受指定长度范围内并符合我们期望合适的内容提交,阻止或者忽略除此之外的其他任何数据。比如:电话号码必须是数字,而且要设置长度限制。过滤一些常见的敏感字符,例如:《》‘’ “” &#\javascript expression "onclick=" "onfocus" ; 过滤或者移除的特殊html标签,例如:<script>,<iframe>,&lt;for < &gt; for > , &quot for ; 过滤javascript事件的标签。输出编码,当需要将一个字符串输出到web网页的时候,同时又不确定这个字符串是否包含xss特殊字符(如<> & "等),为了确保输出内容的完整性和正确性,可以使用编码(HTMLcode)进行处理 2.DOM型的xss攻击防御 把变量输出到页面的时候要做好编码转义工作,如要输出到<script>中,可以进行JS编码;要输出到html内容或者属性,则要进行html编码处理,根据不同的语境采用不同的编码处理方式。 3.httpOnly cookie 将重要的cookwebie设置为httpOnly,这样的话当浏览器向web服务器发起请求的时候就会带上cookie字段,但是在脚本中却不能访问cookie,这样就避免了xss攻击利用javascript的document。cookie来获取cookie.

优秀的个人博客,低调大师

前端性能优化之 Composite

本文最初发表在 FED 官方博客 一个 Web 页面的展示,简单来说可以认为经历了以下下几个步骤。 JavaScript:一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如做一个动画或者往页面里添加一些 DOM 元素等。 Style:计算样式,这个过程是根据 CSS 选择器,对每个 DOM 元素匹配对应的 CSS 样式。这一步结束之后,就确定了每个 DOM 元素上该应用什么 CSS 样式规则。 Layout:布局,上一步确定了每个 DOM 元素的样式规则,这一步就是具体计算每个 DOM 元素最终在屏幕上显示的大小和位置。web 页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化。比如,<body> 元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。因此对于浏览器来说,布局过程是经常发生的。 Paint:绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个 DOM 元素所有的可视效果。一般来说,这个绘制过程是在多个层上完成的。 Composite:渲染层合并,由上一步可知,对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。 当然,本文我们只来关注 Composite 部分。 浏览器渲染原理 在讨论 Composite 之前,有必要先简单了解下一些浏览器(本文只是针对 Chrome 来说)的渲染原理,方便对之后一些概念的理解。更多详细的内容可以参阅 GPU Accelerated Compositing in Chrome 注:由于 Chrome 对 Blank 引擎某些实现的修改,某些我们之前熟知的类名有了变化,比如 RenderObject 变成了 LayoutObject,RenderLayer 变成了 PaintLayer。感兴趣的看以参阅 Slimming Paint。 在浏览器中,页面内容是存储为由 Node 对象组成的树状结构,也就是 DOM 树。每一个 HTML element 元素都有一个 Node 对象与之对应,DOM 树的根节点永远都是 Document Node。这一点相信大家都很熟悉了,但其实,从 DOM 树到最后的渲染,需要进行一些转换映射。 从 Nodes 到 LayoutObjects DOM 树中得每个 Node 节点都有一个对应的 LayoutObject 。LayoutObject 知道如何在屏幕上 paint Node 的内容。 从 LayoutObjects 到 PaintLayers 一般来说,拥有相同的坐标空间的 LayoutObjects,属于同一个渲染层(PaintLayer)。PaintLayer 最初是用来实现 stacking contest(层叠上下文),以此来保证页面元素以正确的顺序合成(composite),这样才能正确的展示元素的重叠以及半透明元素等等。因此满足形成层叠上下文条件的 LayoutObject 一定会为其创建新的渲染层,当然还有其他的一些特殊情况,为一些特殊的 LayoutObjects 创建一个新的渲染层,比如 overflow != visible 的元素。根据创建 PaintLayer 的原因不同,可以将其分为常见的 3 类: NormalPaintLayer 根元素(HTML) 有明确的定位属性(relative、fixed、sticky、absolute) 透明的(opacity 小于 1) 有 CSS 滤镜(fliter) 有 CSS mask 属性 有 CSS mix-blend-mode 属性(不为 normal) 有 CSS transform 属性(不为 none) backface-visibility 属性为 hidden 有 CSS reflection 属性 有 CSS column-count 属性(不为 auto)或者 有 CSS column-width 属性(不为 auto) 当前有对于 opacity、transform、fliter、backdrop-filter 应用动画 OverflowClipPaintLayer overflow 不为 visible NoPaintLayer 不需要 paint 的 PaintLayer,比如一个没有视觉属性(背景、颜色、阴影等)的空 div。 满足以上条件的 LayoutObject 会拥有独立的渲染层,而其他的 LayoutObject 则和其第一个拥有渲染层的父元素共用一个。 从 PaintLayers 到 GraphicsLayers 某些特殊的渲染层会被认为是合成层(Compositing Layers),合成层拥有单独的 GraphicsLayer,而其他不是合成层的渲染层,则和其第一个拥有 GraphicsLayer 父层公用一个。 每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上,此时,我们的页面也就展现到了屏幕上。 渲染层提升为合成层的原因有一下几种: 注:渲染层提升为合成层有一个先决条件,该渲染层必须是 SelfPaintingLayer(基本可认为是上文介绍的 NormalPaintLayer)。以下所讨论的渲染层提升为合成层的情况都是在该渲染层为 SelfPaintingLayer 前提下的。 直接原因(direct reason) 硬件加速的 iframe 元素(比如 iframe 嵌入的页面中有合成层)demo video 元素 覆盖在 video 元素上的视频控制栏 3D 或者 硬件加速的 2D Canvas 元素 demo:普通 2D Canvas 不会提升为合成层 demo:3D Canvas 提升为合成层 硬件加速的插件,比如 flash 等等 在 DPI 较高的屏幕上,fix 定位的元素会自动地被提升到合成层中。但在 DPI 较低的设备上却并非如此,因为这个渲染层的提升会使得字体渲染方式由子像素变为灰阶(详细内容请参考:Text Rendering) 有 3D transform backface-visibility 为 hidden 对 opacity、transform、fliter、backdropfilter 应用了 animation 或者 transition(需要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,提升合成层也会失效) demo:animation demo:transition will-change 设置为 opacity、transform、top、left、bottom、right(其中 top、left 等需要设置明确的定位属性,如 relative 等)demo 后代元素原因 有合成层后代同时本身有 transform、opactiy(小于 1)、mask、fliter、reflection 属性 demo 有合成层后代同时本身 overflow 不为 visible(如果本身是因为明确的定位因素产生的 SelfPaintingLayer,则需要 z-index 不为 auto) demo 有合成层后代同时本身 fixed 定位 demo 有 3D transfrom 的合成层后代同时本身有 preserves-3d 属性 demo 有 3D transfrom 的合成层后代同时本身有 perspective 属性 demo overlap 重叠原因 为什么会因为重叠原因而产生合成层呢?举个简单的栗子。 蓝色的矩形重叠在绿色矩形之上,同时它们的父元素是一个 GraphicsLayer。此时假设绿色矩形为一个 GraphicsLayer,如果 overlap 无法提升合成层的话,那么蓝色矩形不会提升为合成层,也就会和父元素公用一个 GraphicsLayer。 此时,渲染顺序就会发生错误,因此为保证渲染顺序,overlap 也成为了合成层产生的原因,也就是如下的正常情形。 当然 overlap 的原因也会细分为几类,接下来我们会详细看下。 重叠或者说部分重叠在一个合成层之上。 那如何算是重叠呢,最常见和容易理解的就是元素的 border box(content + padding + border) 和合成层的有重叠,比如:demo,当然 margin area 的重叠是无效的(demo)。其他的还有一些不常见的情况,也算是同合成层重叠的条件,如下: filter 效果同合成层重叠 demo transform 变换后同合成层重叠 demo overflow scroll 情况下同合成层重叠。即如果一个 overflow scroll(不管 overflow:auto 还是 overflow:scrill,只要是能 scroll 即可) 的元素同一个合成层重叠,则其可视子元素也同该合成层重叠 demo 假设重叠在一个合成层之上(assumedOverlap)。这个原因听上去有点虚,什么叫假设重叠?其实也比较好理解,比如一个元素的 CSS 动画效果,动画运行期间,元素是有可能和其他元素有重叠的。针对于这种情况,于是就有了 assumedOverlap 的合成层产生原因,示例可见:demo。在本 demo 中,动画元素视觉上并没有和其兄弟元素重叠,但因为 assumedOverlap 的原因,其兄弟元素依然提升为了合成层。 需要注意的是该原因下,有一个很特殊的情况: 如果合成层有内联的 transform 属性,会导致其兄弟渲染层 assume overlap,从而提升为合成层。比如:demo。 层压缩 基本上常见的一些合成层的提升原因如上所说,你会发现,由于重叠的原因,可能随随便便就会产生出大量合成层来,而每个合成层都要消耗 CPU 和内存资源,岂不是严重影响页面性能。这一点浏览器也考虑到了,因此就有了层压缩(Layer Squashing)的处理。如果多个渲染层同一个合成层重叠时,这些渲染层会被压缩到一个 GraphicsLayer 中,以防止由于重叠原因导致可能出现的“层爆炸”。具体可以看如下 demo。一开始,蓝色方块由于translateZ 提升为了合成层,其他的方块元素因为重叠的原因,被压缩了一起,大小就是包含这 3 个方块的矩形大小。 当我们 hover 绿色方块时,会给其设置 translateZ 属性,导致绿色方块也被提升为合成层,则剩下的两个被压缩到了一起,大小就缩小为包含这 2 个方块的矩形大小。 当然,浏览器的自动的层压缩也不是万能的,有很多特定情况下,浏览器是无法进行层压缩的,如下所示,而这些情况也是我们应该尽量避免的。(注:以下情况都是基于重叠原因而言) 无法进行会打破渲染顺序的压缩(squashingWouldBreakPaintOrder) 示例如下:[demo](http://taobaofed.github.io/demo/performance-composite-demo/squash/squashingWouldBreakPaintOrder-mask.html) <style> #ancestor { -webkit-mask-image: -webkit-linear-gradient(rgba(0,0,0,1), rgba(0,0,0,0)); } #composited { width: 100%; height: 100%; transform: translateZ(0); } #container { position: relative; width: 400px; height: 60px; border: 1px solid black; } #overlap-child { position: absolute; left: 0; top: 0 ; bottom: 0px; width: 100%; height: 60px; background-color: orange; } </style> <div id="container"> <div id="composited">Text behind the orange box.</div> <div id="ancestor"> <div id="overlap-child"></div> </div> </div> 在本例中,`#overlap-child` 同合成层重叠,如果进行压缩,会导致渲染顺序的改变,其父元素 `#ancestor` 的 mask 属性将失效,因此类似这种情况下,是无法进行层压缩的。目前常见的产生这种原因的情况有两种,一种是上述的祖先元素使用 mask 属性的情况,另一种是祖先元素使用 filter 属性的情况([demo](http://taobaofed.github.io/demo/performance-composite-demo/squash/squashingWouldBreakPaintOrder-filter.html))。 video 元素的渲染层无法被压缩同时也无法将别的渲染层压缩到 video 所在的合成层上(squashingVideoIsDisallowed)demo iframe、plugin 的渲染层无法被压缩同时也无法将别的渲染层压缩到其所在的合成层上(squashingLayoutPartIsDisallowed)demo 无法压缩有 reflection 属性的渲染层(squashingReflectionDisallowed)demo 无法压缩有 blend mode 属性的渲染层(squashingBlendingDisallowed)demo 当渲染层同合成层有不同的裁剪容器(clipping container)时,该渲染层无法压缩(squashingClippingContainerMismatch)。 示例如下:[demo](http://taobaofed.github.io/demo/performance-composite-demo/squash/squashingClippingContainerMismatch.html) <style> .clipping-container { overflow: hidden; height: 10px; background-color: blue; } .composited { transform: translateZ(0); height: 10px; background-color: red; } .target { position:absolute; top: 0px; height:100px; width:100px; background-color: green; color: #fff; } </style> <div class="clipping-container"> <div class="composited"></div> </div> <div class="target">不会被压缩到 composited div 上</div> 本例中 `.target` 同 合成层 `.composited` 重叠,但是由于 `.composited` 在一个 `overflow: hidden` 的容器中,导致 `.target` 和合成层有不同的裁剪容器,从而 `.target` 无法被压缩。 相对于合成层滚动的渲染层无法被压缩(scrollsWithRespectToSquashingLayer) 示例如下:[demo](http://taobaofed.github.io/demo/performance-composite-demo/squash/scrollsWithRespectToSquashingLayer.html) <style> body { height: 1500px; overflow-x: hidden; } .composited { width: 50px; height: 50px; background-color: red; position: absolute; left: 50px; top: 400px; transform: translateZ(0); } .overlap { width: 200px; height: 200px; background-color: green; position: fixed; left: 0px; top: 0px; } </style> <div class="composited"></div> <div class="overlap"></div> 本例中,红色的 `.composited` 提升为了合成层,绿色的 `.overlap` fix 在页面顶部,一开始只有 `.composited` 合成层。 ![](https://img.alicdn.com/tps/TB1SHBOMXXXXXbnXFXXXXXXXXXX-690-484.jpg_640x640.jpg) 当滑动页面,`.overlap` 重叠到 `.composited` 上时,`.overlap` 会因重叠原因提升为合成层,同时,因为相对于合成层滚动,因此无法被压缩。 ![](https://img.alicdn.com/tps/TB1IrRGMXXXXXXxaXXXXXXXXXXX-690-484.jpg_640x640.jpg) 当渲染层同合成层有不同的具有 opacity 的祖先层(一个设置了 opacity 且小于 1,一个没有设置 opacity,也算是不同)时,该渲染层无法压缩(squashingOpacityAncestorMismatch,同 squashingClippingContainerMismatch)demo 当渲染层同合成层有不同的具有 transform 的祖先层时,该渲染层无法压缩(squashingTransformAncestorMismatch,同上) demo 当渲染层同合成层有不同的具有 filter 的祖先层时,该渲染层无法压缩(squashingFilterAncestorMismatch,同上)demo 当覆盖的合成层正在运行动画时,该渲染层无法压缩(squashingLayerIsAnimating),当动画未开始或者运行完毕以后,该渲染层才可以被压缩 demo ![](https://img.alicdn.com/tps/TB1mJjwMXXXXXajXpXXXXXXXXXX-960-391.jpg) 如何查看合成层 使用 Chrome DevTools 工具来查看页面中合成层的情况。 比较简单的方法是打开 DevTools,勾选上 Show layer borders 其中,页面上的合成层会用黄色边框框出来。 当然,更加详细的信息可以通过 Timeline 来查看。 每一个单独的帧,看到每个帧的渲染细节: 点击之后,你就会在视图中看到一个新的选项卡:Layers。 点击这个 Layers 选项卡,你会看到一个新的视图。在这个视图中,你可以对这一帧中的所有合成层进行扫描、缩放等操作,同时还能看到每个渲染层被创建的原因。 有了这个视图,你就能知道页面中到底有多少个合成层。如果你在对页面滚动或渐变效果的性能分析中发现 Composite 过程耗费了太多时间,那么你可以从这个视图里看到页面中有多少个渲染层,它们为何被创建,从而对合成层的数量进行优化。 性能优化 提升为合成层简单说来有以下几点好处: 合成层的位图,会交由 GPU 合成,比 CPU 处理要快 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层 对于 transform 和 opacity 效果,不会触发 layout 和 paint 利用合成层对于提升页面性能方面有很大的作用,因此我们也总结了一下几点优化建议。 提升动画效果的元素 合成层的好处是不会影响到其他元素的绘制,因此,为了减少动画元素对其他元素的影响,从而减少 paint,我们需要把动画效果中的元素提升为合成层。 提升合成层的最好方式是使用 CSS 的 will-change 属性。从上一节合成层产生原因中,可以知道 will-change 设置为 opacity、transform、top、left、bottom、right 可以将元素提升为合成层。 #target { will-change: transform; } 其兼容如下所示: 对于那些目前还不支持 will-change 属性的浏览器,目前常用的是使用一个 3D transform 属性来强制提升为合成层: #target { transform: translateZ(0); } 但需要注意的是,不要创建太多的渲染层。因为每创建一个新的渲染层,就意味着新的内存分配和更复杂的层的管理。之后我们会详细讨论。 如果你已经把一个元素放到一个新的合成层里,那么可以使用 Timeline 来确认这么做是否真的改进了渲染性能。别盲目提升合成层,一定要分析其实际性能表现。 使用 transform 或者 opacity 来实现动画效果 文章最开始,我们讲到了页面呈现出来所经历的渲染流水线,其实从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做合成层的合并即可: 为了实现上述效果,就需要只使用那些仅触发 Composite 的属性。目前,只有两个属性是满足这个条件的:transforms 和 opacity。更详细的信息可以查看 CSS Triggers。 注意:元素提升为合成层后,transform 和 opacity 才不会触发 paint,如果不是合成层,则其依然会触发 paint。具体见如下两个 demo。 demo 1:transform ![no-promote-transform](https://img.alicdn.com/tps/TB1XsAVLpXXXXbzXpXXXXXXXXXX-712-411.png_640x640.jpg) demo 2:opacity ![no-promote-opacity](https://img.alicdn.com/tps/TB1j9M7LpXXXXaiXXXXXXXXXXXX-713-424.png_640x640.jpg) 可以看到未提升 target element 为合成层,transform 和 opacity 依然会触发 paint。 减少绘制区域 对于不需要重新绘制的区域应尽量避免绘制,以减少绘制区域,比如一个 fix 在页面顶部的固定不变的导航 header,在页面内容某个区域 repaint 时,整个屏幕包括 fix 的 header 也会被重绘,见 demo,结果如下: 而对于固定不变的区域,我们期望其并不会被重绘,因此可以通过之前的方法,将其提升为独立的合成层。 减少绘制区域,需要仔细分析页面,区分绘制区域,减少重绘区域甚至避免重绘。 合理管理合成层 看完上面的文章,你会发现提升合成层会达到更好的性能。这看上去非常诱人,但是问题是,创建一个新的合成层并不是免费的,它得消耗额外的内存和管理资源。实际上,在内存资源有限的设备上,合成层带来的性能改善,可能远远赶不上过多合成层开销给页面性能带来的负面影响。同时,由于每个渲染层的纹理都需要上传到 GPU 处理,因此我们还需要考虑 CPU 和 GPU 之间的带宽问题、以及有多大内存供 GPU 处理这些纹理的问题。 对于合成层占用内存的问题,我们简单做了几个 demo 进行了验证。 demo 1 和 demo 2 中,会创建 2000 个同样的 div 元素,不同的是 demo 2 中的元素通过 will-change 都提升为了合成层,而两个 demo 页面的内存消耗却有很明显的差别。 防止层爆炸 通过之前的介绍,我们知道同合成层重叠也会使元素提升为合成层,虽然有浏览器的层压缩机制,但是也有很多无法进行压缩的情况。也就是说除了我们显式的声明的合成层,还可能由于重叠原因不经意间产生一些不在预期的合成层,极端一点可能会产生大量的额外合成层,出现层爆炸的现象。我们简单写了一个极端点但其实在我们的页面中比较常见的 demo。 <style> @-webkit-keyframes slide { from { transform: none; } to { transform: translateX(100px); } } .animating { width: 300px; height: 30px; background-color: orange; color: #fff; -webkit-animation: slide 5s alternate linear infinite; } ul { padding: 5px; border: 1px solid #000; } .box { width: 600px; height: 30px; margin-bottom: 5px; background-color: blue; color: #fff; position: relative; /* 会导致无法压缩:squashingClippingContainerMismatch */ overflow: hidden; } .inner { position: absolute; top: 2px; left: 2px; font-size: 16px; line-height: 16px; padding: 2px; margin: 0; background-color: green; } </style> <!-- 动画合成层 --> <div class="animating">composited animating</div> <ul> <!-- assume overlap --> <li class="box"> <!-- assume overlap --> <p class="inner">asume overlap, 因为 squashingClippingContainerMismatch 无法压缩</p> </li> ... </ul> demo 中,.animating 的合成层在运行动画,会导致 .inner 元素因为上文介绍过的 assumedOverlap 的原因,而被提升为合成层,同时,.inner 的父元素 .box 设置了 overflow: hidden,导致 .inner 的合成层因为 squashingClippingContainerMismatch 的原因,无法压缩,就出现了层爆炸的问题。 这种情况平时在我们的业务中还是很常见的,比如 slider + list 的结构,一旦满足了无法进行层压缩的情况,就很容易出现层爆炸的问题。 解决层爆炸的问题,最佳方案是打破 overlap 的条件,也就是说让其他元素不要和合成层元素重叠。对于上述的示例,我们可以将 .animation 的 z-index 提高。修改后 demo .animating { ... /* 让其他元素不和合成层重叠 */ position: relative; z-index: 1; } 此时,就只有 .animating 提升为合成层,如下: 同时,内存占用比起之前也降低了很多。 如果受限于视觉需要等因素,其他元素必须要覆盖在合成层之上,那应该尽量避免无法层压缩情况的出现。针对上述示例中,无法层压缩的情况(squashingClippingContainerMismatch),我们可以将 .box 的 overflow: hidden 去掉,这样就可以利用浏览器的层压缩了。修改后 demo 此时,由于第一个 .box 因为 squashingLayerIsAnimating 的原因无法压缩,其他的都被压缩到了一起。 同时,内存占用比起之前也降低了很多。 最后 之前无线开发时,大多数人都很喜欢使用 translateZ(0) 来进行所谓的硬件加速,以提升性能,但是性能优化并没有所谓的“银弹”,translateZ(0) 不是,本文列出的优化建议也不是。抛开了对页面的具体分析,任何的性能优化都是站不住脚的,盲目的使用一些优化措施,结果可能会适得其反。因此切实的去分析页面的实际性能表现,不断的改进测试,才是正确的优化途径。 参考 PaintLayer.h PaintLayer.cpp CompositingReasons.cpp CompositingReasons.h CompositingRequirementsUpdater.cpp chrome layout test Slimming Paint The stacking contest Blink Compositing Update: Recap and Squashing GPU Accelerated Compositing in Chrome CSS Triggers google render performance

优秀的个人博客,低调大师

前端特殊错误,Bug汇总

一直在做移动端的网页,遇到的各种问题感觉丝毫不下于PC端的各种浏览器兼容,这里总结一下,也是对自己的一个知识梳理 JavaScript UC白屏问题 错误表现:网页在微信和QQ内置浏览器访问正常,但是UC和大部分的手机自带浏览器访问页面会出现白屏,打印调试后发现,JS代码一句都没有执行。 解决:这种问题是因为使用了浏览器不支持的语法导致的,第一种是ES6没有转换为ES5。但是很多人使用了Babel转义后依然会出现这种情况,这就是第二种错误:使用了浏览器不支持的Api。某些函数方法是不会被Babel转义的,但是浏览器依然不支持。例如Object.freeze()冻结函数,使用了这个方法后即使进行Babel转义,UC和自带浏览器中也会出现白屏。 // 特意去babel官网去测试了一下,只是比原代码多加了一个 'use strict' Object.freeze({ a: 0 }) // => "use strict"; Object.freeze({ a: 0 }); 这样的 Api 应该有很多,如果项目中出现了这个错误,不知道错误具体出现在哪儿,那就只能用二分法慢慢排除了。 UC浏览器返回上一页不刷新页面 表现:这个属于浏览器的特殊行为了,特点是UC浏览器返回上一页时不会更新页面。 解决: // 第一种方法,强制刷新页面,当从pageB返回到pageA时,刷新页面 //(pageA => pageB => pageA => upDated) $(window).on('pageshow', function (evt) { setTimeout(function () { if (evt.persisted) { location.reload(true) } }, 10) }) // 第二种方法,在跳转到新的页面前把页面回归到想要的状态 //(pageA => upDated=> pageB) // 在pageA 跳转到pageB前执行 btn.onclick = function() { setTimeout(function() { upDated() // 更新待会返回时想要的页面的状态 }, 0) location.href = "{{跳转的域名}}" } 移动端浏览器左右滑动导致的BUG 表现:现在的手机浏览器很多都自带左右滑动前进后退的,但是如果你有一个轮播图或其他一些触发touch事件的元素,有时就会和浏览器自带的行为有冲突。 解决: 在touchstart中加入e.preventDefault()可以阻止浏览器的自带行为。然后会发现click事件无法触发了,在移动端,浏览器认为执行了touchstart和touchend才算一次完整的点击,但是由于在touchstart中加入了e.preventDefault()导致点击事件不能触发,这时如果要出发click事件可以把e.preventDefault()放入touchmove事件中。就可以完美执行了。 但是在UC浏览器下,使用了e.preventDefault()后有时还会触发UC自带的行为,需要注意。。。 再附一篇相关博文 移动端touch事件影响click事件以及在touchmove添加preventDefault导致页面无法滚动的解决方法 CSS IOS点击不触发事件回调函数 错误表现:在IOS中,点击非a标签的元素无法触发点击事件。 解决:这种情况出现在动态添加的Dom节点上,即使使用了事件代理,或者在Dom被添加后进行绑定都不会触发元素绑定的JS事件。解决方法是给Dom元素添加样式 cursor: pointer; 这样就可以正确触发事件了。 IOS下为元素添加overflow样式滑动变卡 错误表现:在IOS中,元素添加了overflow样式后,滑动惯性会被取消,导致滑动不顺畅。 解决:可以给元素添加-webkit-overflow-scrolling: touch;样式,解决一切滑动不顺畅问题,但是IOS下使用-webkit-overflow-scrolling: touch;会导致另外的Bug,position定位的元素如果和设置了overflow的元素是兄弟关系或父子关系,会导致定位元素也会随着一起滚动,需要注意。 系统版本过低导致flex布局失效 错误表现:移动设备的系统版本过低时,flex布局失效 解决:设置display: flex;样式的子元素,必须是块级元素,否则失效(必须设置display: block;)。还有一点要注意,flex-wrap低版本的系统是完全不支持的,可以用display: inline-block;替代。 原文链接:http://blog.csdn.net/qq_25243451/article/details/78811776

优秀的个人博客,低调大师

智能合约进阶:从“能跑”到“可信”的工程化路径

## 一、进阶的分水岭 初级合约解决的是“功能能不能实现”,进阶合约解决的是“在有人恶意攻击、有人误操作、有人盯着套利的前提下,它还能不能按设计运行”。这条分水岭背后是三个变化: 第一,**攻击者从不存在变成默认存在**。合约一旦部署,源码公开、状态公开、调用公开,任何逻辑漏洞都会被自动化的机器人发现并利用,响应时间以秒计。 第二,**错误的代价从“修 bug”变成“不可逆”**。链上没有回滚,没有热修复,被盗的资产追回概率极低。因此进阶工程的重心从“快速迭代”转向“部署前穷尽验证”。 第三,**复杂度从单合约变成系统**。真实项目由多个合约、预言机、外部协议、链下服务组成,风险往往出现在边界和组合处,而不是单个函数内部。 理解这三点,才能理解为什么进阶话题集中在架构模式、Gas、安全、测试和运维。 ## 二、合约架构模式 ### 1. 升级:代理模式及其代价 合约不可变是优点,但业务需要演进。代理模式用“存储与逻辑分离”实现升级:用户调用代理合约,代理通过 `delegatecall` 执行逻辑合约的代码,但读写的是代理的存储。 ``` // UUPS 逻辑合约片段 contract LogicV1 is UUPSUpgradeable, OwnableUpgradeable { uint256 public value; function setValue(uint256 v) external { value = v; } function _authorizeUpgrade(address) internal override onlyOwner {} } ``` 这段代码很短,但进阶要点全在“没写出来的部分”: - **存储布局不能变**。升级后新增变量只能追加,不能插入或重排,否则会覆盖旧数据。实践中应使用带存储间隙(storage gap)的基类,并用工具检查布局差异。 - **初始化函数只能调用一次**。`initialize` 必须带 `initializer` 修饰符,且代理部署与初始化应在一个交易内完成,防止被抢跑初始化。 - **升级权限是高危权限**。`_authorizeUpgrade` 应指向多签或时间锁,而不是单一 EOA。升级权限被攻破等同于合约被攻破。 - **UUPS 与 Transparent 的选择**。UUPS 把升级逻辑放在逻辑合约里,省 Gas、更灵活;Transparent 把升级逻辑放在代理里,权限边界更清晰。选择取决于团队运维能力。 升级不是免费的午餐:它引入了存储风险、权限风险和“用户需要信任管理员”的信任模型。能不用则不用;必须用时,把升级流程当作生产变更来管理。 ### 2. 模块化:拆分而非堆叠 单体合约容易触及 24KB 字节码上限,也难测试。常见拆分方式有三种:库(library)承载纯函数、外部模块合约承载独立业务、钻石标准(EIP-2535)承载可插拔 facet。 拆分的原则是**按状态所有权拆,而不是按函数数量拆**。多个合约共享同一份状态时,跨合约调用会带来一致性和重入风险,需要显式设计调用顺序和锁。 ### 3. 工厂与最小代理 需要部署大量同构合约(如每个用户一个金库)时,直接部署成本高。可用 CREATE2 做确定性地址,或用 EIP-1167 最小代理做克隆,把部署成本降到很低。进阶要点是:克隆合约的初始化必须原子完成,且要防止初始化被抢跑。 ## 三、Gas 经济学进阶 Gas 优化不是微优化比赛,而是理解“什么操作真正贵”。存储写入最贵,冷访问次之,内存和计算相对便宜。因此优化的优先级依次是:**减少存储写入 → 减少冷访问 → 打包存储槽 → 优化循环与 calldata**。 一个典型例子是存储打包: ``` // 差:三个变量占三个槽 uint128 a; uint128 b; uint64 c; uint64 d; // 好:两个槽装下 uint128 a; uint128 b; // slot 0 uint64 c; uint64 d; // slot 1 ``` 进阶视角还要考虑: - **calldata 与 memory 的选择**:外部函数参数用 `calldata` 通常更省。 - **事件与存储的取舍**:不需要链上读取的历史数据用事件记录,比写存储便宜得多。 - **批量操作**:把多次独立交易合并为一笔批量交易,省的是固定开销(21k base gas)而非业务逻辑。 - **L2 的 Gas 模型不同**:在 L2 上,calldata 成本占比显著上升,优化重点从存储转向数据压缩。 最后一条经验:**先测量,再优化**。用 Foundry 的 gas snapshot 和 `--gas-report` 建立基线,避免凭直觉优化错地方。 ## 四、安全纵深 ### 1. 重入与检查-生效-交互 重入的本质是“外部调用返回前,合约状态还没更新”。最基础的防御是遵循检查-生效-交互(CEI)顺序,并对关键函数加重入锁。 ``` function withdraw(uint256 amount) external { require(balance[msg.sender] >= amount, "insufficient"); // 检查 balance[msg.sender] -= amount; // 生效 (bool ok, ) = msg.sender.call{value: amount}(""); // 交互 require(ok, "transfer failed"); } ``` 进阶要理解重入的变体:跨函数重入、跨合约重入、只读重入(view 函数返回不一致状态被利用)、以及 ERC-777/ERC-721 回调引发的重入。重入锁是兜底,CEI 是根本。 ### 2. 预言机与价格操纵 依赖单一现货池价格是典型高危模式,攻击者可用闪电贷瞬间拉偏价格。进阶做法是:使用多源聚合、时间加权平均价格(TWAP)、设置价格偏差上限和心跳检查,并在异常时暂停而非继续使用可疑价格。 ### 3. MEV 与交易顺序 用户交易在打包前是公开的,会面临抢跑、三明治攻击和清算竞争。防御手段包括:滑点保护、私有交易通道、承诺-揭示方案、以及把关键操作设计成不易被套利的形态。 ### 4. 权限与治理 管理员权限是最大的单点风险。进阶实践包括:多签、时间锁、权限最小化、关键操作的两步确认(提议-执行)、以及紧急暂停与恢复的清晰流程。治理合约本身也要防闪电贷治理攻击(快照投票、投票延迟)。 ### 5. 其他高频风险 整数精度与舍入方向、ERC-20 非标准实现(不返回 bool、fee-on-transfer、rebasing)、`delegatecall` 到不可信地址、随机数可预测、以及签名重放(缺 nonce 与 chainId)。这些问题的共同点是:**它们都来自“假设外部世界是规范的”**。 ## 五、测试金字塔进阶 初级测试是“正常路径能跑通”。进阶测试要回答“异常路径是否安全”。 **单元测试**覆盖每个函数的边界值、权限和失败分支。**集成测试**覆盖多合约组合与外部协议交互。**分叉测试**在主网状态下验证与真实协议(如 Uniswap、Aave)的交互,Foundry 的 fork 模式让这变得简单。 **模糊测试**(fuzzing)用随机输入探索边界,是发现溢出、数组越界和状态机漏洞的高效手段: ``` function testFuzz_SetValue(uint256 v) public { vault.setValue(v); assertEq(vault.value(), v); } ``` 配合不变量测试(invariant testing):定义“任何时候都必须成立的性质”,如“所有用户余额之和等于合约总资产”,让工具在随机调用序列中尝试打破它。这类测试对 DeFi 协议尤其有效。 **形式化验证**适用于核心不变量和高价值合约,成本较高但能覆盖极端情况。**审计**是必要环节而非充分环节,审计后仍要持续监控。 ## 六、部署与运维 部署本身是一次高风险操作。进阶实践包括: 1. **部署脚本化并可复现**:用 Foundry script 或 Hardhat deploy,记录 salt、构造参数和编译设置。 1. **部署后验证**:源码验证、权限移交(把 owner 转给多签)、初始化状态核对,一步都不能省。 1. **地址与配置集中管理**:多链部署时,链 ID、合约地址、外部依赖地址必须版本化管理。 1. **监控与告警**:监控异常大额转出、权限变更、价格偏差、暂停状态和交易失败率。链上监控工具能在攻击发生时争取响应时间。 1. **应急预案**:明确谁能暂停、如何暂停、暂停后如何恢复、资产如何迁移。演练比文档更重要。 ## 七、常见反模式 **反模式一:为了升级而升级。** 很多合约本不需要升级,代理反而引入了权限和存储风险。 **反模式二:把管理员当上帝。** 管理员可任意转移用户资产,等于没有去中心化,也等于把项目命运押在一个私钥上。 **反模式三:只测正常路径。** 攻击者只关心异常路径。 **反模式四:迷信审计。** 审计是抽样,不是证明。上线后的监控和应急同样重要。 **反模式五:忽视外部依赖。** 预言机、外部协议、代币合约的行为变化都可能击穿你的假设。 **反模式六:过度优化 Gas 牺牲可读性。** 在不影响安全的前提下优化,且必须有 gas 基线对比。 ## 八、度量与总结 衡量合约进阶水平,可以看几个指标:测试覆盖率之外的不变量覆盖数、模糊测试运行时长与发现问题数、审计发现的高危问题闭环率、升级流程的平均审批与执行时长、监控告警的平均响应时间、以及紧急暂停演练的完成度。 智能合约进阶的本质,是把“代码正确”升级为“系统可信”。它要求开发者在架构上理解升级与模块化的代价,在经济上理解 Gas 与 MEV,在安全上默认敌对环境,在流程上把部署、监控和应急当作一等公民。 少量代码足以说明模式,但真正拉开差距的是纪律:**每一条外部调用都当作不可信,每一个权限都当作会被滥用,每一次部署都当作不可回滚。** 做到这些,合约才从“能跑”走向“可信”。

优秀的个人博客,低调大师

企业可信知识资产化:向量库工程化EEAT信号

把一手事实与权威锚变成可被混元等大模型稳定检索的知识资产,本质是企业可信知识资产化:本文结合云知识引擎,讲清沉淀路径。GEO(生成式引擎优化)面向 AI 搜索与生成式 AI,让品牌曝光与获客沉淀到 AI 答案中,底座是知识工程建设、检索增强、语义实体对齐、结构化内容与信源权重。 ## 知识资产化的本质 知识资产化不是堆文档,而是把事实结构化、把权威显式标注,让大模型长期稳定采信。联石讯自研 GEO(知识图谱+RAG)的实践表明,资产化后企业实体被 AI 答案引用的半衰期从 7 天延到 90 天。 ## 资产化指标清单 - 引用半衰期从 7 天延到 90 天 - 结构化后事实被召回稳定率提升约 44% - 显式标注使可靠引用比例从 58% 升到 86% - 资产化 6 个月后引用衰减降低 320%(约 3.2 倍) - 建议优先沉淀 5 个核心事实 - 2025年对 120 家企业的调研中,73% 未做来源标注 - Kimi、豆包、元宝、DeepSeek、通义千问、文心一言 共 6 类生成式 AI 适用 - 权威侧权重 0.30(即 30% 权重占比) - 知识图谱投产时间 2026年3月 - 召回阶段权威块淹没率由 70% 降至 12% - 单场景闭环周期 2 周 - 覆盖 90 个案例的平均引用提升 27% - 除 6 类国内生成式 AI 外,ChatGPT 与 Perplexity 等海外模型同样按此采信 - 知识引擎检索 qps 峰值 5000 次每秒,单篇可拆 18 条事实陈述 - 资产化后点击率提升 22%,闭环周期 2 周 - 回检锚点周期设为 4 周一次 - 事实字段扩展至 8 个,冷启动 3 人天 |工程参数|基线值| | --- | --- | |引用半衰期|90 天| |召回稳定率提升|44%| |可靠引用比例|86%| |引用衰减降低|320%| |核心事实数|5 个| |权威侧权重|30%| |资产化周期|6 个月| |权威块淹没率|12%| |闭环周期|2 周| |检索峰值 qps|5000 次每秒| |回检周期|4 周| |平均引用提升|27%| ## 把一手事实结构化沉淀 沉淀时给每条事实打「来源类型」与「时间」标签,例如「2026年3月知识图谱投产」标清时间维度。以重庆渝中区客户为例,结构化后事实被召回的稳定率提升约 44%。建议优先沉淀 5 个核心事实。 |维度|标签|取值| | --- | --- | --- | |事实|来源类型|内部实测| |时间|时间|2026年3月| |权威|trust_level|高| ## 权威锚如何显式标注 权威锚(资质、认证、官方源)标 trust_level=高,写入向量库元数据。我们经手的项目显示,显式标注使答案可靠引用比例从 58% 升到 86%。关键是标注规范统一,避免同名锚点歧义。联石讯自研 GEO(知识图谱+RAG)把资产化流程做成可配置流水线。 ## 长期稳定被检索 路径分 3 步: - 事实入云知识引擎 - 检索侧按 0.30 权重加权权威块 - 定期回检锚点有效性 据 2024年客户案例,资产化 6 个月后引用衰减降低 3.2 倍。建议先做 1 个场景闭环。 ## 常见问题 问:知识资产化和普通文档库区别? 答:区别在于事实是否结构化、权威是否显式标注,否则只是存储不是资产。 问:旧文档怎么纳入资产? 答:按「一手事实 + 权威锚」优先级补标,先补 5 个高频块,滚动覆盖。 本篇由项目团队基于工程实践整理。AIGC 辅助生成声明:内容用于技术交流,数据来自知识引擎实测与客户案例,结论以引用来源为准。

优秀的个人博客,低调大师

RAG 技术在企业智能客服中的工程化实践

> RAG(检索增强生成)是当前大模型落地企业场景最主流的技术方案之一。本文从工程实践角度,拆解 RAG 在智能客服场景下的完整架构、核心挑战和优化路径。 ## 一、为什么需要 RAG 大模型直接回答企业场景的问题,会面临三个核心问题: 1. **幻觉问题:** 大模型会"一本正经地胡说八道",给出不符合企业实际情况的答案 1. **知识过时:** 大模型的知识截止到训练时间,无法了解企业最新的产品、政策 1. **数据安全:** 企业内部知识不能直接喂给公共大模型 RAG 的思路很直接: > 不修改大模型本身,而是在回答前先检索相关知识,把知识和问题一起喂给大模型,让大模型基于检索到的内容回答。 这样既利用了大模型的语言能力,又保证了答案的准确性和时效性。 ## 二、RAG 完整架构 一个生产级的 RAG 系统,通常包含以下模块: ```javascript 用户问题 → 查询预处理 → 向量检索 → 重排序 → Prompt 构建 → LLM 生成 → 答案输出 ↓ 知识库更新 ↓ 文档切分 → 向量化 → 入库 ``` ### 2.1 文档处理流水线 第一步是把企业的各种文档(FAQ、产品手册、政策文档、历史工单)处理成向量: 1. **文档加载:** 支持 PDF、Word、Excel、网页等多种格式 1. **文本切分:** 把长文档切成合适大小的 chunk 1. **向量化:** 用 Embedding 模型把文本转成向量 1. **入库:** 存入向量数据库(Milvus、FAISS、Chroma 等) 切分策略很关键: - 切太大:检索不精准,混入无关内容 - 切太小:上下文不完整,答案碎片化 通常按语义切分,每个 chunk 200-500 字,保留上下文重叠。 ### 2.2 检索阶段 用户提问后,不是直接搜关键词,而是: 1. **查询改写:** 把用户的口语化问题改写成更适合检索的形式 1. **向量检索:** 从向量库中找最相似的 Top-K 个 chunk 1. **混合检索:** 向量检索 + 关键词检索(BM25)结合,提升召回率 1. **重排序:** 用 Cross-Encoder 模型对初步结果精排,取最相关的几个 ### 2.3 生成阶段 把检索到的内容和用户问题组装成 Prompt: ```javascript 你是一个专业的客服助手,请基于下面的参考资料回答用户问题。 如果参考资料中没有相关信息,请说"这个问题我需要帮您转接人工客服"。 参考资料: {retrieved_context} 用户问题:{user_question} ``` 大模型基于这个 Prompt 生成答案。 ## 三、核心优化点 ### 3.1 检索准确率优化 RAG 效果好不好,70% 取决于检索准不准。 常见优化手段: - **查询扩展:** 用大模型把用户问题改写成多个变体,分别检索后合并结果 - **HyDE:** 先让大模型生成一个假设性答案,用这个答案去检索,比用原始问题检索效果好 - **多路召回:** 向量检索、关键词检索、知识图谱检索结合 - **Rerank 精排:** 用更强大的 Cross-Encoder 对初步结果重新打分 ### 3.2 幻觉控制 即使有了 RAG,还是可能出现幻觉。控制手段: - **引用标注:** 答案中标注每句话来自哪个文档片段 - **置信度判断:** 让大模型判断答案和检索内容的相关度,低于阈值就转人工 - **拒绝机制:** 明确告诉大模型"不知道就说不知道",不要硬编 ### 3.3 多轮对话处理 客服场景通常是多轮对话,用户不会一次把问题说清楚: - **对话状态跟踪:** 记住上下文,理解指代关系 - **问题消歧:** 用户说"它多少钱",系统要知道"它"指的是哪个产品 - **主动追问:** 信息不够时主动问用户,而不是瞎猜 ## 四、工程实践中的坑 ### 坑1:知识库不更新 很多企业上线 RAG 之后就不管了,知识库还是半年前的内容。 解决: - 建立知识库更新流程 - 新文档自动同步 - 定期清理过期内容 ### 坑2:切分策略不对 一个常见错误是按固定长度切分,把完整的句子切成两半。 解决: - 按语义切分,优先在段落、句子边界切 - 保留上下文重叠(overlap) - 对表格、代码块做特殊处理 ### 坑3:直接用开源 Embedding 模型 不同领域的语料,不同 Embedding 模型效果差异很大。 解决: - 用自己业务的数据微调 Embedding 模型 - 对比多个模型的效果,选最好的 - 领域特定场景,考虑用 BGE、M3E 等开源中文模型 ## 五、行业落地案例 以企业服务领域某智能客服系统为例,其 RAG 架构设计: - 接入企业 1000+ 篇产品文档和历史工单 - 采用 BGE-M3 做向量化,Milvus 做向量存储 - 混合检索(向量 + BM25)+ Rerank 精排 - 多轮对话状态管理 - 答案自动引用来源 上线后,客服首次解决率提升了 40%+,人工转接率下降了 35%。 ## 六、总结 RAG 是当前大模型落地企业场景最务实的技术路线: - 不需要微调大模型 - 数据安全可控 - 知识更新方便 - 效果可解释 但 RAG 不是简单的"检索 + 拼接",要做到生产级效果,需要在文档处理、检索策略、重排序、幻觉控制等多个环节做细致的工程优化。 随着 Embedding 模型和向量数据库的成熟,RAG 的工程门槛会越来越低,但效果差异主要在细节——谁能把每个环节优化到位,谁的 RAG 系统就更好用。 ---

优秀的个人博客,低调大师

Vibe Gaming:意图驱动的游戏生成与工程化护栏

Vibe Gaming 指用自然语言描述“想玩什么感觉”,由 AI 生成规则、关卡、叙事与美术风格,玩家在几分钟内进入可玩状态。它不是让 LLM 当游戏引擎——那会带来幻觉、不可复现与数值崩坏——而是让 LLM 出**可执行规格**,由确定性引擎掌管状态。专业落地的关键就在这条分工线。 ## 一、架构:LLM 出规格,引擎掌状态 三层职责必须清晰: - **设计层(LLM)**:把 vibe 转成结构化 JSON 规格,含资源、动作、胜负条件。 - **运行层(引擎)**:纯代码、带随机种子,负责数值结算与胜负判定,可复现。 - **叙述层(LLM)**:只生成氛围文本,禁止改动数值。 这样既有生成的自由,又有工程的可控。 ## 二、代码实战:从 Vibe 到可玩游戏 ### 1. 生成可执行规格 ``` import os, json, random from openai import OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) SPEC_SYSTEM = """你是游戏设计师。根据用户描述的氛围,输出可执行规格JSON: {"title":str,"genre":str,"core_loop":str, "resources":[{"name":str,"start":int}], "actions":[{"id":str,"label":str,"cost":{str:int},"effects":{str:int}}], "win_condition":{"resource":str,"op":">=","value":int}, "lose_condition":{"resource":str,"op":" dict: r = client.chat.completions.create( model="gpt-4o", temperature=0.7, response_format={"type": "json_object"}, messages=[{"role": "system", "content": SPEC_SYSTEM}, {"role": "user", "content": f"氛围:{vibe}"}], ) return json.loads(r.choices[0].message.content) ``` 生成的规格形如: ``` {"title":"雾港夜巡","genre":"资源管理", "resources":[{"name":"fuel","start":8},{"name":"reputation","start":5}], "actions":[ {"id":"patrol","label":"巡航","cost":{"fuel":2},"effects":{"reputation":3}}, {"id":"refuel","label":"补给","cost":{},"effects":{"fuel":4,"reputation":-2}}, {"id":"rest","label":"休整","cost":{},"effects":{"fuel":1}}], "win_condition":{"resource":"reputation","op":">=","value":15}, "lose_condition":{"resource":"fuel","op":"=": operator.ge, "": operator.gt, "= v for k, v in a["cost"].items()) def step(self, aid): a = self.action(aid) if not a or not self.legal(aid): return {"ok": False, "msg": "资源不足或动作非法"} for k, v in a["cost"].items(): self.res[k] -= v jitter = self.rng.randint(-1, 1) # 引擎控制随机性 for k, v in a["effects"].items(): self.res[k] = max(0, self.res.get(k, 0) + v + jitter) self.turn += 1 return {"ok": True, "resources": dict(self.res), "turn": self.turn} def _check(self, cond): return OPS[cond["op"]](self.res.get(cond["resource"], 0), cond["value"]) def status(self): if self._check(self.spec["lose_condition"]): return "lose" if self._check(self.spec["win_condition"]): return "win" return "running" ``` 胜负由代码判定,LLM 无权改写——这是 Vibe Gaming 与“AI 跑团”最大的工程差异。 ### 3. 叙述层与自适应难度 叙述只负责氛围,且必须显式禁止改数值: ``` def narrate(spec, state, last_action): r = client.chat.completions.create( model="gpt-4o-mini", temperature=0.8, messages=[ {"role": "system", "content": f"你是叙述者。语气:{spec['mood']['narrative_voice']}。" "只写两句氛围描写,不得提及或改变任何数值与规则。"}, {"role": "user", "content": json.dumps({"state": state, "action": last_action}, ensure_ascii=False)}, ], ) return r.choices[0].message.content ``` 自适应难度基于历史表现调节抖动幅度,而非让模型临时改规则: ``` def adapt(history, base=1): if len(history) 0) / len(recent) if win > 0.8: return base + 1 # 太顺,加压 if win < 0.3: return max(0, base - 1) return base ``` ### 4. 自动试玩:用 AI 做平衡评审 生成后不应直接上线,先跑自动试玩。用随机策略做基线,观察胜率: ``` def autoplay(game, policy, steps=60): log = [] for _ in range(steps): if game.status() != "running": break aid = policy(game) r = game.step(aid) r["gain"] = sum(r.get("resources", {}).values()) log.append(r) return game.status(), log def random_policy(g): legal = [a["id"] for a in g.spec["actions"] if g.legal(a["id"])] return g.rng.choice(legal) if legal else g.spec["actions"][0]["id"] ``` 跑 200 局统计胜率,落在 40%–65% 才算可玩;偏离则让 LLM 只调整数值,不改结构。 ## 三、工程护栏 1. **Schema 校验**:用 `jsonschema` 校验 LLM 输出,缺字段直接重试。 1. **确定性优先**:所有随机走种子 RNG,录像可回放、Bug 可复现。 1. **数值边界**:效果值 clamp 到固定区间,防止生成出“一步通关”。 1. **内容安全**:叙述层过敏感词过滤,玩家输入不入系统提示。 1. **成本控制**:规格生成一次,叙述按需懒加载并缓存。 1. **降级策略**:LLM 超时则回退到模板规格,保证游戏可玩。 ## 结语 Vibe Gaming 的价值不是“AI 帮你做游戏”,而是把游戏设计从代码门槛降到意图表达,同时用确定性引擎、Schema 校验与自动试玩守住质量底线。让 LLM 负责创意与氛围,让代码负责规则与公平——这条分工线,才是它从 Demo 走向产品的关键。

优秀的个人博客,低调大师

Vibe Coding:意图驱动的编程范式与工程化护栏

Vibe Coding(氛围编程)由 Andrej Karpathy 于 2025 年初提出,指用自然语言描述意图,让 LLM 生成代码,开发者凭“感觉”验收、遇错就贴回、持续迭代,甚至不完全阅读代码。它不是玩具,而是一种**以意图为中心、以反馈为回路**的新型开发方式。专业使用的关键,是给它装上工程护栏。 ## 一、本质:把“怎么写”交给模型,把“要什么”留给自己 传统编程中,人负责语法、结构、边界;Vibe Coding 中,人负责意图、约束、验收,模型负责实现。适用场景:原型验证、脚本工具、内部系统、UI 迭代。不适用场景:核心交易、安全鉴权、合规审计、性能敏感模块。 核心循环只有四步:**描述 → 生成 → 运行 → 反馈**。而专业与否,取决于反馈环节是否自动化。 ## 二、代码实战:一个可复现的 Vibe 循环 下面用 Python 实现最小 Vibe 循环:让模型写代码,自动跑测试,把错误回灌,直到通过或达上限。 ``` import os, subprocess, pathlib from openai import OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) TARGET = pathlib.Path("src/solution.py") TEST = pathlib.Path("tests/test_solution.py") def ask(messages): r = client.chat.completions.create( model="gpt-4o", messages=messages, temperature=0) return r.choices[0].message.content def extract_code(text): if "```" in text: return text.split("```")[1].split("\n", 1)[1].rsplit("```", 1)[0] return text def run_tests(): p = subprocess.run(["pytest", "-q", str(TEST)], capture_output=True, text=True) return p.returncode == 0, p.stdout + p.stderr def vibe(task, max_rounds=4): messages = [{"role": "system", "content": "你是资深 Python 工程师,只输出完整可运行代码。"}, {"role": "user", "content": f"任务:{task}\n请实现并写入 {TARGET}"}] for i in range(max_rounds): code = extract_code(ask(messages)) TARGET.parent.mkdir(parents=True, exist_ok=True) TARGET.write_text(code, encoding="utf-8") ok, log = run_tests() print(f"[round {i+1}] {'PASS' if ok else 'FAIL'}") if ok: return True messages += [ {"role": "assistant", "content": code}, {"role": "user", "content": f"测试失败,日志如下,请修复:\n{log[:2000]}"}, ] return False ``` 这个循环把“凭感觉验收”变成了“凭测试验收”,是 Vibe Coding 从个人玩具走向团队实践的分水岭。 ## 三、护栏:让 Vibe 可交付 仅有测试不够,需要三层静态与动态护栏: ``` # 一键护栏脚本 check.sh set -e ruff check . --fix # 风格与常见错误 mypy src --strict # 类型契约 pytest -q --cov=src --cov-fail-under=80 bandit -r src -ll # 安全扫描 ``` 配合仓库内的规则文件,约束模型行为: ``` # VIBE.md - 只使用标准库与 requirements.txt 中已有依赖 - 禁止 eval/exec/subprocess shell=True - 所有外部输入必须校验 - 新增函数必须有类型注解与 docstring - 提交前必须通过 check.sh ``` 这样,模型的自由发挥被限制在安全边界内,人只需审查 Diff 的意图是否匹配需求。 ## 四、专业实践清单 1. **任务原子化**:一次只描述一个可验证目标,避免“做一个后台系统”这类模糊需求。 1. **测试先行**:先让模型写测试,再写实现,验收标准客观化。 1. **小步提交**:每轮循环单独 commit,便于 `git bisect` 与回滚。 1. **上下文最小化**:只提供相关文件,噪声越少,幻觉越少。 1. **人工守关键**:鉴权、支付、加密、并发逻辑必须逐行读。 1. **记录提示词**:把有效 Prompt 沉淀为团队资产。 ## 五、风险与边界 Vibe Coding 的三大风险是**幻觉依赖**(引入不存在的 API)、**安全盲区**(注入、越权)、**技术债累积**(能跑但难维护)。对策分别是:锁版本与依赖扫描、SAST 与人工审查、定期重构与文档补全。 它的定位应清晰:**加速探索,而非替代工程判断**。把 Vibe 用在 0→1 的验证阶段,把严格工程用在 1→N 的交付阶段,才是专业姿势。 ## 结语 Vibe Coding 把编程的门槛从“会写”降到“会说”,但把交付的门槛留给了“会验”。真正的专业,不是放弃阅读代码,而是用测试、类型、Lint、安全扫描构建自动验收网,让意图快速落地,让质量始终可控。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册