一阶逻辑在程序验证中的应用:从循环不变式到SMT求解
1. 为什么一阶逻辑是程序验证的分水岭写过几年代码、做过一些形式化验证的朋友多半有个体会命题逻辑那套东西用起来就像拿一把水果刀切菜简单场景够用稍微复杂一点就力不从心。命题逻辑只能表达“A 成立”“B 不成立”这种原子级别的真假判断它没法说“对于所有的 x如果 x 是自然数那么 x 加零等于 x”。而程序验证里最核心的那些断言——循环不变式、数组边界、递归函数的终止性——几乎全都需要“对所有”“存在”这样的量词来表达。这就是一阶逻辑登场的地方。一阶逻辑First-Order Logic简称 FOL在程序验证里的地位相当于微积分在物理学里的地位。它把命题逻辑的表达能力往上抬了一个维度引入了个体变量、函数符号、谓词符号和量词。有了这些你才能把“数组下标永远不越界”写成∀i. 0 ≤ i len → a[i] 合法把“链表没有环”写成更复杂的量化断言。没有一阶逻辑程序验证基本只能停留在玩具级别。这篇文章面向的是已经了解命题逻辑、写过一些带断言代码、想往形式化验证方向深入的开发者。我会从一阶逻辑的语法语义讲起重点放在它怎么和程序验证结合——包括霍尔逻辑里怎么用一阶逻辑表达前置条件、后置条件、循环不变式以及实际做验证时量词会带来哪些麻烦。文章里会有大量可以直接参考的公式写法和实操思路也会分享一些我在做验证时踩过的坑。如果你之前看一阶逻辑的教材觉得太数学、不知道怎么用到代码上这篇应该能帮你把两边接起来。2. 一阶逻辑的核心构件拆解2.1 从命题逻辑到一阶逻辑到底多了什么命题逻辑的世界里最小的单位是命题变元比如 P、Q、R它们非真即假没有内部结构。你说“P 蕴含 Q”逻辑系统只关心 P 和 Q 的真值组合不关心 P 里面到底说了什么。这在程序验证里很快就撞墙我想表达“变量 x 的值大于零”命题逻辑只能把它当成一个原子命题 P但 P 和“变量 y 的值大于零”之间有没有关系命题逻辑说不出来。一阶逻辑把原子命题拆开了。它引入了项term的概念变量、常量、函数应用都是项。比如x、0、f(x, y)、succ(n)都是项。然后引入谓词predicate谓词作用于项形成原子公式。比如x 0、parent(x, y)、sorted(a, lo, hi)。最后加上逻辑连接词与命题逻辑一样¬、∧、∨、→、↔和量词∀ 全称量词、∃ 存在量词。这个扩展带来的表达能力提升是质变。举个例子命题逻辑能表达“数组的第一个元素是零”但表达不了“数组所有元素都是零”。一阶逻辑一句∀i. 0 ≤ i n → a[i] 0就搞定了。程序验证里几乎所有的正确性断言都涉及“所有”或“存在”所以一阶逻辑是绕不过去的。2.2 语法项、公式与自由变量一阶逻辑的语法可以递归定义理解这个递归结构对后面写验证条件很有帮助。项的定义每个变量x, y, z, ...是项每个常量符号c, d, ...是项如果f是 n 元函数符号t1, ..., tn是项那么f(t1, ..., tn)也是项公式的定义如果P是 n 元谓词符号t1, ..., tn是项那么P(t1, ..., tn)是原子公式如果φ和ψ是公式那么¬φ、φ ∧ ψ、φ ∨ ψ、φ → ψ、φ ↔ ψ都是公式如果φ是公式x是变量那么∀x. φ和∃x. φ都是公式这里有个关键概念叫自由变量和约束变量。在∀x. P(x, y)里x被量词约束了y是自由的。自由变量在程序验证里通常对应程序的状态变量比如某个整型变量、某个数组而约束变量是逻辑内部的“临时变量”用来做量化。搞清楚这一点你才能理解为什么验证条件生成器要小心处理变量捕获问题。注意变量捕获是一阶逻辑里最容易出 bug 的地方。如果你把一个自由变量y代换到∀x. φ(x, y)里的y位置而代换项里恰好含有x就会发生捕获语义完全变了。程序验证工具在生成验证条件时通常会先把约束变量重命名成全新的名字来避免这个问题。2.3 语义结构、赋值与满足关系一阶逻辑的语义比命题逻辑复杂因为它需要一个结构structure来解释符号。一个结构M包含一个非空集合D叫论域domain对每个常量符号指定D中的一个元素对每个 n 元函数符号指定一个从D^n到D的函数对每个 n 元谓词符号指定一个D^n上的关系给定结构M和一个赋值σ把自由变量映射到D中的元素我们可以递归定义“公式φ在M和σ下为真”记作M, σ ⊨ φ。量词的语义是M, σ ⊨ ∀x. φ当且仅当对D中所有元素d都有M, σ[x ↦ d] ⊨ φM, σ ⊨ ∃x. φ当且仅当存在D中某个元素d使得M, σ[x ↦ d] ⊨ φ这套语义看起来抽象但对应到程序验证就很直观论域就是程序里所有可能的值整数、布尔、数组等结构就是这些值上的运算和关系加法、比较、数组读写赋值就是程序当前的状态。一个断言在某个状态下为真就是M, σ ⊨ φ。2.4 与程序验证的接口霍尔三元组里的逻辑霍尔逻辑的核心是三元组{P} C {Q}其中P是前置条件Q是后置条件C是程序代码。P和Q都是逻辑公式。在简单场景下命题逻辑够用但一旦程序里有数组、指针、递归就必须用一阶逻辑。举个最经典的例子计算数组元素之和的循环{true} i : 0; s : 0; while i n do s : s a[i]; i : i 1 end {s Σ_{j0}^{n-1} a[j]}后置条件里的Σ本质上是一个量化表达式用一阶逻辑可以写成递归定义的求和谓词或者用更底层的量词表达。循环不变式通常写成s Σ_{j0}^{i-1} a[j] ∧ 0 ≤ i ≤ n这里面既有量词求和又有算术约束。没有一阶逻辑这个不变式根本写不出来。3. 一阶逻辑在程序验证中的实操要点3.1 用一阶逻辑写循环不变式循环不变式是程序验证里最考验功力的部分而一阶逻辑是写不变式的唯一选择。我总结了一个实用的思路不变式 已处理部分的性质 未处理部分的约束 边界条件。以数组查找为例假设我们要验证一个线性查找函数找到目标值就返回下标找不到返回 -1。循环不变式可以这样写∀j. 0 ≤ j i → a[j] ≠ target这个公式的意思是所有已经检查过的位置下标小于 i都不等于目标值。再加上边界0 ≤ i ≤ n就构成了完整的不变式。退出循环时如果i n结合不变式可以推出“整个数组都不含目标值”从而证明返回 -1 是正确的。写这种不变式时有个常见错误量词的范围写错。比如写成∀j. 0 ≤ j n → a[j] ≠ target那就变成“整个数组都不含目标值”这显然不是循环中间状态该有的性质会导致验证失败。量词的范围必须精确对应程序已经处理的部分。实操心得写循环不变式时先把循环的“进度变量”比如 i找出来然后问自己“到目前为止我确定了什么”“还没确定什么”。前者用全称量词限定在已处理区间后者用存在量词或否定表达。这个套路能覆盖大部分数组和链表遍历场景。3.2 量词的实例化与验证条件生成程序验证工具比如基于霍尔逻辑的验证条件生成器在处理赋值语句时用的是最弱前置条件weakest precondition演算。对于赋值x : e后置条件Q的最弱前置条件是Q[x ↦ e]也就是把Q里所有自由出现的x替换成e。当Q里含有量词时替换就要小心了。比如Q ∀y. y x → P(y)赋值x : x 1之后最弱前置条件是∀y. y x 1 → P(y)。这个替换看起来简单但如果e里含有y比如x : y 1直接替换会得到∀y. y y 1 → P(y)这就错了因为y被捕获了。正确的做法是先把约束变量y重命名成新变量z得到∀z. z x → P(z)再替换x得到∀z. z y 1 → P(z)。这个细节在手工推导验证条件时特别容易忽略。我见过不少人在纸上推导时直接替换结果验证条件看起来“差不多对”但实际语义已经偏了。用工具的话工具会自动处理重命名但理解这个机制能帮你在验证失败时快速定位问题。3.3 处理存在量词斯科伦化与反例构造存在量词在验证里比全称量词更难处理。验证一个程序正确通常要证明“对所有可能的输入程序都满足后置条件”这天然是全称的。但前置条件里经常有存在量词比如“存在一个满足某性质的初始状态”。处理存在量词的一个标准技术是斯科伦化Skolemization把存在量词替换成新的函数符号斯科伦函数这些函数依赖于外层全称量词的变量。比如∀x. ∃y. P(x, y)斯科伦化后变成∀x. P(x, f(x))其中f是新的函数符号。这个变换保持可满足性但不保持等价性不过在验证场景下通常够用。反例构造时存在量词反而变成优势如果验证失败工具需要找到一个使公式为假的赋值这时候存在量词就变成“找一个见证”。很多验证工具会输出反例轨迹里面会具体给出某个存在量词的见证值这对调试非常有帮助。3.4 一阶逻辑的可判定性与工具选择一阶逻辑的一个根本问题是一般的一阶逻辑是不可判定的。也就是说不存在一个算法能在有限时间内判定任意一阶逻辑公式是否有效。这是丘奇-图灵定理的直接推论。但程序验证里用到的往往是一阶逻辑的可判定片段比如量词自由片段只含命题连接词和原子公式可以用 SAT 求解Presburger 算术只含整数加法和比较可判定数组理论加上数组读写操作在特定条件下可判定线性算术 未解释函数SMT 求解器的常见组合选择工具时关键是看你的验证条件落在哪个片段里。如果不变式里出现了非线性乘法比如x * y很多 SMT 求解器就搞不定了需要人工提供引理或者换用交互式定理证明器。我个人的经验是能用 SMT 自动搞定的尽量把公式限制在线性算术加数组理论这个片段实在不行再上交互式证明但那个学习曲线陡得多。逻辑片段可判定性典型工具适用场景命题逻辑可判定SAT 求解器布尔程序、配置验证量词自由一阶逻辑可判定SMT 求解器无循环的程序、断言检查Presburger 算术可判定SMT 求解器整数线性运算线性算术 数组可判定SMT 求解器数组遍历、内存安全含非线性算术不可判定交互式定理证明器数学算法、密码学一般一阶逻辑不可判定交互式定理证明器元理论、深层性质4. 完整实操从程序到验证条件的全流程4.1 案例程序与规格说明我们拿一个具体的例子走一遍完整流程。假设要验证一个函数它计算数组前 n 个元素的最大值max_array(a, n): m : a[0] i : 1 while i n do if a[i] m then m : a[i] end i : i 1 end return m前置条件n ≥ 1数组至少有一个元素。 后置条件∀j. 0 ≤ j n → m ≥ a[j]并且∃j. 0 ≤ j n ∧ m a[j]。前者说 m 不小于任何元素后者说 m 确实等于某个元素。两个合起来就是“m 是最大值”。4.2 循环不变式的推导与验证循环不变式需要捕捉循环中间状态的性质。对于这个程序不变式可以写成Inv: 1 ≤ i ≤ n ∧ (∀j. 0 ≤ j i → m ≥ a[j]) ∧ (∃j. 0 ≤ j i ∧ m a[j])这个不变式有三部分i 的范围、m 是前 i 个元素的上界、m 等于前 i 个元素中的某一个。初始化时i 1, m a[0]代入不变式1 ≤ 1 ≤ n成立∀j. 0 ≤ j 1 → m ≥ a[j]等价于m ≥ a[0]成立∃j. 0 ≤ j 1 ∧ m a[j]等价于m a[0]成立。循环体保持不变式假设进入循环体时不变式成立且i n。分两种情况如果a[i] m则m a[i]。新的全称部分对j i1要么j i由旧不变式m ≥ a[j]且m a[i] m ≥ a[j]所以m ≥ a[j]要么j im a[i]显然m ≥ a[i]。新的存在部分j i就是见证。如果a[i] ≤ m则m m。全称部分对j i由旧不变式保证对j i有m ≥ a[i]。存在部分沿用旧的见证。退出循环时i n不变式给出∀j. 0 ≤ j n → m ≥ a[j]和∃j. 0 ≤ j n ∧ m a[j]正好是后置条件。4.3 验证条件的生成与求解把上面的推理机械化就是验证条件生成。对每个赋值语句计算最弱前置条件对 if 分支分别计算两个分支的前置条件再取合取对循环用不变式作为循环头的前置条件然后生成两条验证条件一条是“不变式在进入循环时成立”另一条是“循环体保持不变式”。具体到这个例子生成的验证条件大致是VC1: n ≥ 1 → Inv[i ↦ 1, m ↦ a[0]] VC2: Inv ∧ i n ∧ a[i] m → Inv[i ↦ i1, m ↦ a[i]] VC3: Inv ∧ i n ∧ a[i] ≤ m → Inv[i ↦ i1, m ↦ m] VC4: Inv ∧ i ≥ n → ∀j. 0 ≤ j n → m ≥ a[j] VC5: Inv ∧ i ≥ n → ∃j. 0 ≤ j n ∧ m a[j]这些验证条件里含有量词和数组读写属于“线性算术 数组理论”片段现代 SMT 求解器比如 Z3、CVC5通常能自动判定。如果求解器返回 unsat验证条件取非后不可满足说明程序正确如果返回 sat会给出一个反例通常是不变式在某个中间状态不成立。注意实际工具生成的验证条件可能比上面复杂得多因为要处理数组的读写语义比如a[i]在赋值后是否改变。手工推导时可以用“数组是函数”的视角来简化把数组a看成一个从下标到值的函数a[i]就是函数应用赋值a[i] : v就是函数更新。这样处理起来更清晰。4.4 反例分析与调试技巧验证失败时工具给出的反例往往是一组变量赋值比如n 3, a [5, 2, 7], i 2, m 5。这时候要检查在这个状态下不变式的哪一部分不成立如果是全称部分不成立说明 m 没有覆盖到某个元素如果是存在部分不成立说明 m 不是数组里的元素。根据反例调整不变式再重新验证。我踩过的一个坑是不变式写得太弱导致验证条件虽然能通过但后置条件推不出来。比如只写∀j. 0 ≤ j i → m ≥ a[j]不写存在部分那么退出循环时只能证明 m 是上界不能证明 m 是数组里的元素后置条件的第二部分就推不出来。这时候工具可能会报“后置条件不成立”但反例看起来又“没问题”因为问题出在不变式不够强。解决办法是补上存在部分或者加强不变式。另一个坑是量词的范围和数组边界不一致。比如数组下标从 0 开始但不变式里写成1 ≤ j i漏掉了下标 0导致验证失败。这种错误很隐蔽因为公式看起来“差不多对”但语义上差了一个元素。建议写完后用几个具体的小例子手工代入检查一遍。5. 常见问题与排查速查5.1 量词相关的典型错误问题现象可能原因排查方法解决思路验证条件无法判定公式含非线性算术或复杂量词嵌套检查不变式里是否有乘法、除法引入引理或换用交互式证明反例中变量值看起来“不可能”量词范围写错或变量捕获手工代入小例子检查重命名约束变量修正范围后置条件推不出来不变式太弱检查不变式是否覆盖所有必要性质加强不变式补上缺失的合取项验证条件数量爆炸循环嵌套或分支过多统计生成的 VC 数量拆分函数减少循环嵌套求解器超时公式规模太大查看公式的项数和量词深度简化不变式或分块验证5.2 实操避坑清单量词范围要精确全称量词的范围必须严格对应程序已处理的部分多一个少一个都会导致验证失败。存在量词不能省很多人写不变式时只写上界性质忘了“m 是数组元素”这个存在性质导致后置条件推不出来。变量重命名要彻底手工推导时替换前先把所有约束变量重命名成新名字避免捕获。数组语义要统一把数组当函数处理读写都用函数应用和函数更新表达避免下标越界等边界问题。小例子先验证写完不变式后用 n1、n2 这样的小例子手工代入检查初始化和循环保持是否成立。工具输出要细读反例里的每个变量值都有意义不要只看“验证失败”就放弃反例往往能指出不变式的具体缺陷。5.3 工具选型建议如果你刚开始做程序验证建议从 SMT 求解器入手比如 Z3 或 CVC5它们对线性算术和数组理论的支持很好API 也友好。Python 的z3-solver包可以让你在几行代码里构造验证条件并求解。等遇到 SMT 搞不定的场景比如非线性算术、归纳证明再考虑交互式定理证明器比如 Coq 或 Isabelle但那个学习成本高得多适合有长期验证需求的团队。实操心得我一般会先用 SMT 求解器快速验证一版如果失败根据反例调整不变式如果求解器超时再考虑把问题拆小或者换工具。不要一上来就上重型工具那样容易在工具配置上耗掉大量时间反而忽略了不变式本身的设计。6. 从一阶逻辑到更远的地方一阶逻辑是程序验证的基石但它不是终点。实际项目里你会遇到一阶逻辑表达不了的性质比如“程序终止”这需要二阶逻辑或者不动点逻辑、“两个程序等价”需要更复杂的元理论。不过绝大多数工业级的验证任务——内存安全、数组边界、简单的功能正确性——都能在一阶逻辑的框架内解决。我个人的体会是把一阶逻辑用熟关键不在于背公式而在于建立“程序状态到逻辑公式”的映射直觉。看到一段代码能快速想到它的前置条件、后置条件、循环不变式分别该用什么量词结构来表达。这个直觉需要大量练习但一旦建立起来你会发现很多以前觉得“没法验证”的程序其实只是没找到合适的不变式。最后分享一个小技巧写不变式时先别管量词用自然语言把“循环到目前位置确定了什么”说清楚然后再逐句翻译成逻辑公式。自然语言里的“所有已经检查过的元素”“存在一个满足条件的下标”直接对应全称和存在量词。这个翻译过程能帮你避免很多逻辑上的疏漏。

相关新闻

基于Django和LDA的旅游推荐系统设计与优化

基于Django和LDA的旅游推荐系统设计与优化

1. 项目背景与核心价值 旅游行业正面临信息过载的挑战。根据行业调研数据,一个热门旅游城市在旺季时,各大平台每天新增的用户评论可达数万条。这些非结构化的文本数据中蕴含着游客的真实体验和偏好,但传统的关键词匹配推荐系统很难挖掘出深层…

2026/10/15 11:56:49 阅读更多 →
具身智能系统进化:从单点能力到自主闭环的工程实践

具身智能系统进化:从单点能力到自主闭环的工程实践

1. 从“单点智能”到“系统进化”:具身智能的竞争逻辑变了过去两年,具身智能的讨论大多集中在单一能力上:某个机械臂抓取成功率又提高了几个百分点,某个四足机器人跑得更稳了,某个仿真环境里训练出的策略终于能迁移到真…

2026/10/15 11:55:49 阅读更多 →
机器人数据泛化瓶颈与RoboAug增强策略:3.5万次真机实测解析

机器人数据泛化瓶颈与RoboAug增强策略:3.5万次真机实测解析

1. 从3.5万次真机实测说起:机器人数据泛化到底卡在哪机器人圈子里有句老话:算法不够,数据来凑。但真正在一线做过具身智能项目的人都知道,这句话只对了一半——数据多了确实能涨点,但涨到某个瓶颈之后,你再…

2026/10/15 11:55:49 阅读更多 →

最新新闻

CRMEB多商户系统移动端容器组件开发实践

CRMEB多商户系统移动端容器组件开发实践

1. 项目背景与核心价值CRMEB多商户系统作为一款基于PHP开发的电商解决方案,其移动端二次开发能力直接决定了系统的灵活性和定制化程度。在实际项目中,我们经常遇到这样的需求:客户需要在不改动核心代码的前提下,快速实现个性化的移…

2026/10/15 14:52:32 阅读更多 →
CRMEB多商户系统移动端容器组件开发指南

CRMEB多商户系统移动端容器组件开发指南

1. 项目背景与核心价值CRMEB多商户系统作为一款基于PHP开发的电商解决方案,其移动端二次开发能力直接决定了系统的扩展性和定制化程度。在实际项目中,我们经常遇到这样的需求场景:某连锁品牌需要为旗下不同区域的加盟商提供差异化的移动端界面…

2026/10/15 14:52:32 阅读更多 →
一元线性回归的统计本质与业务归因实战

一元线性回归的统计本质与业务归因实战

1. 项目概述:为什么一元线性回归不是“入门玩具”,而是数据决策的底层标尺你打开Excel,选中两列数据,右键加趋势线,勾上“显示公式”——那个y ax b弹出来的一刻,很多人以为自己已经掌握了回归。但真正用…

2026/10/15 14:52:32 阅读更多 →
Django开发历史科普网站的全栈实践

Django开发历史科普网站的全栈实践

1. 项目背景与核心价值作为一个基于Django框架开发的香港历史科普网站,这个毕业设计项目涵盖了从后端开发到前端展示的全流程实现。不同于简单的静态网站,该项目需要处理历史事件的时间线数据、多媒体资源管理以及用户交互功能,对Python全栈开…

2026/10/15 14:52:32 阅读更多 →
技术人学PMP:从项目管理引论到实战避坑指南

技术人学PMP:从项目管理引论到实战避坑指南

1. 为什么我决定啃下项目管理这块硬骨头干了七八年技术,代码写得不算差,项目也跟过不少,但心里一直有个疙瘩:每次项目延期、资源打架、需求反复横跳的时候,我总觉得自己像个救火队员,哪儿冒烟往哪儿冲&…

2026/10/15 14:52:32 阅读更多 →
深入理解Rust泛型与Trait:从重复代码到优雅抽象的实战指南

深入理解Rust泛型与Trait:从重复代码到优雅抽象的实战指南

1. 为什么你写的 Rust 代码又臭又长我见过不少从其他语言转过来的开发者,写了一个月的 Rust 后,最大的感受不是“安全”,而是“累”。同样的逻辑,用 TypeScript 可能五六十行搞定,到了 Rust 里要写一百多行&#xff0c…

2026/10/15 14:51:31 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →