news 2026/9/20 3:03:04

Composer依赖解析全指南:从报错排查到平台兼容与Lock文件实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Composer依赖解析全指南:从报错排查到平台兼容与Lock文件实践

如果你维护过任何一个用 PHP 写的 Web 项目,大概率对这段输出不陌生:在终端敲下composer update,光标停在Loading composer repositories with package information这一行久久不动,接着慢慢吐出Updating dependencies,然后屏幕底部冒出一句 “Your requirements could not be resolved to an installable set of packages.”,后面还挂着 Problem 1、Problem 2 一长串。第一次碰到时我整个人是懵的,以为项目代码写错了什么,后来才发现,这一整段输出其实每一步都有明确的含义,理解了它,你就读懂了 Composer 这套依赖管理工具的核心逻辑。

这篇文章就围绕这个场景展开,把 Composer 依赖解析流程、报错排查链路、平台兼容检查、镜像与缓存加速、composer.lock 的工程实践一次讲透。适合刚把 PHP 项目跑起来的入门者,也适合被线上composer install卡到怀疑人生的老手。

1. 那两行卡顿输出背后:Composer 依赖解析的完整流程

很多人把 Composer 当成"PHP 的 npm / pip",装上就能用,但一遇到输出卡住或报错就抓瞎,因为没有建立"它到底在做什么"的心智模型。我们先从最基础的执行流程说起。

1.1 从 composer.json 到 "Loading composer repositories":第一行输出做了什么

严格来说,Loading composer repositories with package information不是一行独立的代码输出,而是 Composer 的 Installer 在初始化阶段打印的阶段提示。翻译过来是"正在从所有配置的软件仓库加载包信息",这个动作发生在连接仓库、拉取元数据时。

Composer 的仓库(repositories)概念很容易和 Git 仓库混为一谈。这里的 repo 指的是"存放包信息的地方",它有两层来源:默认的 Packagist(repo.packagist.org)和你在 composer.json 里通过repositories字段额外声明的 VCS 仓库、路径仓库或 artifact 仓库。Composer 会把所有这些源合并成一份"可用的包列表",再去做版本求解。

这里有个关键历史差异。Composer 1 时代,Packagist 对外提供的是 provider-includes 元数据,每次运行都要把一大批 JSON 文件拉下来才能知道某个包有哪些版本,慢得离谱。Composer 2 改为 metadata-url 按需请求机制,只有当你真的需要某个包的信息时,它才去请求对应的元数据片段。这也是为什么同样一个项目,从 Composer 1 升到 2 之后,Loading composer repositories这行几乎一闪而过。

如果你在这行卡了很久,大概率是网络层面的问题:默认源在国外,或者公司内网访问外网受限。后面第四章我会详细说镜像配置。

1.2 "Updating dependencies" 是在解一道 SAT 题

Updating dependencies是 Composer 开始真正干活的标记。这个阶段,它会拿到第一步收集到的所有包的版本信息,再结合根项目 composer.json 的requirerequire-dev,交给内部的 Solver 去计算一套"最合适的版本组合"。

Composer 用的求解器本质上是 SAT(布尔可满足性)算法:每个包的不同版本相当于一个变量,包的依赖关系和版本约束相当于一组子句,求解器要找出一组赋值让所有子句都为真。听起来很深,实际行为你可以理解成拼图——A 说"我需要 >=1.0 的 B",B 说"我需要 ext-json 且不接受 C 2.0",C 说"我只能在 PHP 8 以上跑",求解器必须找到一块恰好同时满足这些条件的拼图。

这个阶段卡住,一般有两个原因。一是约束写得过宽,候选版本数量爆炸;二是某个包的元数据包含了大量 dev 分支版本,导致求解空间膨胀。多数情况下不是死循环,而是在进行大量尝试。你可以在命令里加--profile看耗时分布,也可以用-vvv看到详细的求解信息——虽然那个输出量对普通人来说基本是乱码,但至少能确认它没死。

1.3 从 "Resolving" 到 "Installing":你看到的每个阶段分别代表什么

正常执行composer update,你会连续看到几行阶段提示:

  • Loading composer repositories with package information:收集元数据。
  • Updating dependencies:运行求解器,确定要安装的确切版本。
  • Lock file operations/Installs: xxx packages:展示本次更新涉及的包数量。
  • Package operations下的Installing / Updating / Removing:实际下载并安装或删除包。
  • Generating autoload files:根据最终的包列表重新生成 PHP 自动加载文件。

如果你只是在跑composer install,且 composer.lock 存在且有效,阶段会更简单:直接进入Installing dependencies from lock file,不再做"Updating dependencies"。这个行为的差异,是理解 Composer 安装锁定的基石,我会在第五章展开。

2. "Your requirements could not be resolved":依赖冲突的完整排查链路

这一行可以说是我见过最多的 Composer 报错。它后面的完整句是Your requirements could not be resolved to an installable set of packages.,翻译过来是"你的依赖要求无法被解析成一个可安装的包组合"。很多人一看到这个就急着去改 composer.json,或者把镜像切来切去,其实关键在于读懂它下面那一串 Problem。

2.1 先把一段典型报错逐行读明白

假设你执行composer update,得到类似这样的输出:

Loading composer repositories with package information Updating dependencies Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires phpunit/phpunit ^9.0 -> satisfiable by [phpunit/phpunit[9.0.0, ..., 9.6.10]]. - phpunit/phpunit 9.6.10 requires php >=7.3 -> your php version (7.2.34) does not satisfy that requirement.

第一行说的是根项目 composer.json 里要求 phpunit/phpunit ^9.0,而现有仓库里满足这个约束的版本有 9.0.0 到 9.6.10 这一堆。第二行说的是,其中一个版本 9.6.10 要求 PHP 版本 >=7.3,但当前环境是 PHP 7.2.34,不满足。于是求解器无论选哪个 9.x 版本,都无法消除这个冲突,只能报错。

注意,报错里的版本列表通常会省略中间版本号,写成[phpunit/phpunit[9.0.0, ..., 9.6.10]],这是 Composer 在压缩展示,不代表只有这两个版本可用。很多人看不懂这个省略号,以为包只有两个版本,这是一个很常见的误解。

2.2 排查链路的第一步:先别急着改 composer.json

遇到这个报错,我建议按下面的顺序快速过一遍,通常能解决八成问题:

  1. 确认 PHP 版本和已加载扩展。运行php -vphp -m,看看是否就是你预期的环境。如果是多版本 PHP 并存,确认终端里用的是哪一个。
  2. 运行composer diagnose。它会对 PHP 版本、JSON 扩展、网络连接、Git 配置等做一系列检查。如果提示某一行异常,先解决它再继续。
  3. 确认包名没有拼错。Packagist 对大小写敏感,比如phpunit/phpunit不能写成phpunit/PHPUnit。可以直接去 packagist.org 搜索确认。
  4. 确认版本约束本身写对了。^~*dev-xx的语义差异巨大,写错会导致约束无解。

这一步的核心是"先收集证据,再动手改"。我见过太多人一上来就删掉依赖版本号,结果装出来的包版本号和隔壁组完全不一致,最后线上炸了才追悔莫及。

2.3 用 composer why、prohibits 和 why-not 定位冲突源

如果根项目直接声明的依赖本身没有冲突,但报错里出现了第三方包互相要求的情况,就需要反向追踪了。Composer 2 提供了几个非常好用的定位命令。

  • composer why vendor/package:查一下已安装的包中,是谁依赖了 vendor/package。比如composer why guzzlehttp/guzzle,会列出所有依赖它的包及版本约束。
  • composer prohibits vendor/package 2.0:模拟"如果我要把 vendor/package 升级到 2.0,会是谁拦住我"。
  • composer why-not vendor/package 2.0:输出拦截方和具体版本约束,比prohibits的展示更清晰。

举个真实场景:你的项目需要foo/bar的 2.0 版本,但foo/bar 2.0要求baz/qux ^1.0,而另一个包foo/baz-wrapper要求baz/qux ^2.0。运行composer why-not baz/qux 1.0,Composer 会把这条链完整列出来,你立刻就能看到是谁在需求冲突的一端。

2.4 解决冲突的四种策略,以及各自的代价

定位到冲突源之后,有几个处理方向:

  1. 调整版本约束范围。把顶级依赖的约束放宽,比如从"foo/bar": "~1.2"改成"foo/bar": "^1.2 || ^2.0",给求解器更大的空间。前提是得确认第三方包确实有兼容这两个版本的版本存在。
  2. 升级或降级另一端的包。冲突往往不是发生在你要装的包身上,而是它和其他包互相制约。找出那棵依赖树里可以移动的"结点",用composer update foo/baz-wrapper --with-dependencies单独升级某个包。
  3. 利用 replace 和 provide。如果你维护 fork 包,或项目内部用私有包替换了某个公共包的子依赖,可以在 composer.json 里声明replace字段。这是比较高级的玩法,但也容易引发"我明明装了 A,代码里却猜不到 A 的版本"的问题,要有心理准备。
  4. 临时绕过。用--with-all-dependencies让 Composer 在更新时对其他包也放宽解析,或使用--ignore-platform-reqs跳过平台条件。这两种方式都能让安装继续,但都只是推迟问题,不是解决问题。尤其--ignore-platform-reqs,我建议只在 CI 构建和容器打包阶段使用,后面第三章单独讲。

3. Composer 的平台检查:当 "dependencies require" 撞上你的 PHP 环境

热词里有一句很典型:composer detected issues in your platform: your composer dependencies require...。这其实对应的是 Composer 2 在安装完成后执行的平台检查,或者依赖解析时的平台约束判断。它的本质是:Composer 不仅仅检查包之间的互相依赖,还要检查当前运行环境是否满足这些包对环境的要求。

3.1 它到底在检查什么

Composer 平台检查的对象有三类:

  • PHP 版本:比如php: >=8.1
  • PHP 扩展:形如ext-mbstringext-opensslext-pdo,只要包声明了这些依赖,就要求当前 PHP 进程加载了对应扩展。
  • 系统库:形如lib-curllib-iconv,通常通过 PHP 版本绑定和编译配置判断。

在解析依赖时,这些条件都会被当成普通的依赖约束参与求解。比如laravel/framework声明了"php": "^8.1""ext-mbstring": "*",那么安装它的前提就是你当前的 PHP 是 8.1 以上且开了 mbstring。

判断有没有加载某个扩展,最直接的方法是php -m,它会列出所有编译加载的模块。如果在终端里列出来的和你 Web 服务里跑的 PHP 不一致,那属于"用错了 PHP",不是 Composer 的问题。我遇到过一个同事排查了半天,最后发现他composer用的 PHP 是/usr/bin/php(7.4),而 nginx/php-fpm 用的是另一个版本的 8.1,服务器上有两套 PHP 并存,这就是平台检查意义上最常见的坑。

3.2 用 config.platform 声明目标运行环境

有一种情况很微妙:本地开发用 PHP 8.3,而生产服务器还是 PHP 7.4。如果你直接在本地跑composer install,解析出的依赖版本会以 8.3 为基准,可能选到一些要求 PHP 8.0+ 的包。等你把同样的 composer.lock 带到服务器上,结果就是装不上,或者装上了运行直接白屏。

Composer 提供了config.platform来解决这个"开发和线上版本不一致"的问题:

composer config platform.php 7.4.40

这行命令会在 composer.json 的config.platform下写入"php": "7.4.40"。之后 Composer 在解析依赖时,会"假装"当前平台是 PHP 7.4.40,从而选出一套在 7.4 上能跑的版本组合。

要注意,platform.php只影响依赖解析,不会改变你实际运行时的 PHP 进程版本。如果你的本机真的是 8.3,锁定了一个为 7.4 解析的依赖组合,其中某些包可能用了 PHP 8 的语法,运行时依然可能出问题。所以它解决的是"依赖选型"问题,不是"运行环境"问题。

另外,config.platform还可以指定扩展,比如composer config platform.ext-mbstring 1.0,模拟即使本机没装 mbstring 也能解析出需要它的包版本。但这里我的建议是:扩展层面尽量实际安装,不要只做模拟,不然后续跑业务代码还是会挂。

3.3 --ignore-platform-reqs 是张"遮羞布",用的时候要知道代价

--ignore-platform-reqs会忽略所有平台相关的依赖约束,包括 PHP 版本和扩展要求。在容器构建、CI 打包这类场景里,它经常被拿来"先装上再说"。

但它真的只是"遮羞布"。装上一个需要ext-redis的包,而镜像里没有这个扩展,最终运行时Call to undefined function Redis::connect()这种错一定会找上门。我见过最离谱的一次,是别人在 Dockerfile 里对composer install加了--ignore-platform-reqs,结果容器跑起来之后 php-fpm 加载PhpRedis相关的业务代码直接 fatal error,整个服务挂了半个多小时。

如果你必须在无扩展环境下完成依赖安装,我建议至少做三步:

  1. composer install时用--no-dev,避免 dev 依赖带来额外平台负担。
  2. 安装完成后,在真正的运行环境里执行composer platform-check,让 Composer 自动扫描已安装包的环境要求并进行核对。
  3. 在 CI 流水线里增加一个步骤检查composer platform-check是否通过,通不过就让流水线失败。

platform-check这个命令相当于是对"遮羞布"的兜底补偿,让一次性错误变成自动化检查的一部分,这也是我能接受的唯一一种--ignore-platform-reqs用法。

4. 下载依赖慢与失败:镜像仓库、缓存与 prefer-dist 的调优

“卡在 Loading composer repositories 半天”“Downloading 总是失败”“明明指定了版本却装了个旧包”这类问题,基本都集中在网络和缓存两个层面。这一节我把我实际调优过的方案全部列出来。

4.1 慢的根源在哪

Composer 的安装过程有两处网络请求:

  • 请求仓库元数据(对应Loading composer repositories with package information)。
  • 下载包本体(对应Package operations阶段的Downloading)。

默认 Packagist 和 GitHub 的访问速度在国内很不可控,尤其是小水管服务器上,拉一个大点的包集可能要几分钟。除了网络,还有一个隐蔽因素:Composer 1 时代没有并发下载,需要靠hirak/prestissimo插件才能并行拉包;Composer 2 原生支持并发下载,大幅改善了下载阶段,但元数据请求如果指向海外,依然可能很慢。

4.2 换镜像,本质是换仓库源

最好的解决办法是配置国内可用的 Composer 镜像仓库,把默认的 Packagist 指向一个离你更近的服务:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

配置完可以用composer config -gl查看全局配置确认是否生效。这里的-g是写入全局,去掉则写入当前项目。团队项目里我建议写进项目级配置,并把改好的 composer.json 提交到版本库,保证所有人一致。

换镜像之后,Loading composer repositories的耗时通常会从几十秒降到一两秒。注意:这个操作改的是"获取包信息和包文件"的来源,不会改变包的版本解析逻辑。部分私有包如果只存在于自己的 Git 仓库,你还需要把repositories里配置的 VCS repo 一并写好,它们会与镜像源并存。

4.3 缓存机制:拿到旧包的第一嫌疑对象

Composer 在本机有缓存目录,按平台不同,通常在:

  • Linux/macOS:~/.cache/composer/
  • Windows:%LOCALAPPDATA%\Composer

缓存里存的是下载好的包压缩包(files 目录)和元数据中间结果。正常情况下这是好事,第二次安装能省流量。但如果你改了版本约束,或者你用的仓库元数据已经被更新,Composer 的缓存可能让你"以为装的是新版本,其实还在用旧元数据"。

这时候优先执行:

composer update --no-install

它会让 Composer 重新拉取一次元数据并更新 lock 文件内容(不实际安装),通常能刷新缓存判断。如果还不行,再考虑composer clear-cache。但要注意,clear-cache是核弹,清完所有项目都要重新下载,建议只在确定元数据异常时使用。

另外,shell 环境变量COMPOSER_MEMORY_LIMIT也比较常被忽略。依赖很多的项目在解析阶段可能内存吃紧,出现Composer ran out of memory,可以设成COMPOSER_MEMORY_LIMIT=-1让它不限制,或设置一个较大的值,比如512M

4.4 prefer-dist 与 prefer-source:一个是下载,一个是克隆

Composer 获取包有两种方式:dist 和 source。

  • dist:从仓库下载压缩包,快、体积小,是默认推荐方式,对应输出里的Downloading
  • source:直接克隆 Git 仓库,对应输出里的Cloning,适合需要修改包源码调试的场景。

默认配置是prefer-dist,这没问题。但有的包在 dist 下载失败时,Composer 会自动回退到 source 方式,导致安装过程突然开始git clone,非常慢。如果你在 CI 环境遇到这种问题,可以用--prefer-dist强制只用压缩包,减少意外。

还有一个小技巧:在电脑本地调试第三方包时,可以临时用--prefer-source,这样能拿到完整的 Git 历史,方便git bisect定位包里的问题。调试完再改回默认。

5. composer.lock 的工程价值:一次线上回滚事故的复盘

如果说前三章解决的是"装不上"的问题,这一章解决的是"装出了灾难"的问题。composer.lock的重要性,我只讲一个真实案例。

之前有个项目,同事在服务器上跑部署脚本,里面写的是composer update --no-dev。结果某天一个第三方包发布了新版本,和线上 PHP 7.4 环境不兼容,部署后整个线上服务挂了。回滚时发现没有备份 lock 文件,也没有备份 vendor 目录,只能对着 git 历史慢慢猜之前用的版本,折腾了快两个小时。从那以后,我在所有团队的部署规范里都强制执行一条:部署环境永远只准跑 composer install,不准跑 composer update

5.1 lock 文件是怎么工作的

composer.lock记录了当前项目在特定时刻经过求解后的完整依赖版本清单,包括每个包的确切版本、来源、哈希和依赖关系信息。它和 composer.json 的关系是:

  • composer.json 声明"我愿意接受哪些范围的版本"。
  • composer.lock 锁定"这次实际安装了哪些具体版本"。

只要 lock 文件存在且与 composer.json 的 content-hash 匹配,composer install就会严格按 lock 里的版本安装,不会重新求解。这保证了开发、测试、生产三套环境的依赖完全一致。composer update则重新读取 composer.json 进行求解,并生成新的 lock 文件。

lock 文件里有一个字段叫content-hash,它根据 composer.json 的 require 等配置生成。每次改动 composer.json,这个哈希就会变化。如果你改了 composer.json 却忘了执行composer update,再跑composer install时 Composer 2 会警告 lock 文件与 composer.json 不同步,并提示运行composer update --lock来更新哈希。

5.2 线上部署的标准动作

我目前在 CI/CD 里使用的 Composer 命令基本固定:

composer install --no-dev --prefer-dist --no-interaction --no-progress --optimize-autoloader

参数意义:

  • --no-dev:跳过 require-dev 里的包,生产环境不装调试工具。
  • --prefer-dist:优先压缩包下载,避免 git clone。
  • --no-interaction:避免任何交互式提问导致部署脚本挂起。
  • --no-progress:减少日志输出,Log 采集更干净。
  • --optimize-autoloader:把 PSR-4 和 PSR-0 映射转成 classmap,提升生产环境自动加载速度。

如果你需要对上一版本进行快速回滚,最稳妥的是带上 lock 文件一起回滚,然后重新执行composer install。vendor 目录不需要提交到版本库,但 lock 文件一定要提交。

5.3 什么时候才应该执行 composer update

很多团队把composer update当成普通升级动作,每次开发前都敲一遍,这是最破坏可复现性的习惯。我更推荐的流程是:

  1. composer outdated查看有哪些包可以更新。
  2. 明确本次要更新哪些包,优先使用composer update vendor/package vendor/package2 --with-dependencies,只更新选定目标及其依赖,避免一次把所有包全部升级。
  3. 更新前先确认 lock 文件有提交,或备份一份。
  4. 更新后跑完测试用例再合并代码。

如果确实需要全量更新,也建议在独立分支上进行,生成的新 lock 文件经过测试再合入主干。全量composer update在版本约束很宽松的项目里,往往会带来意料之外的"惊喜"。

5.4 lock 文件过期不是小事

前面提到,composer install遇到 lock 过期,Composer 2 会给警告:“Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run 'composer update' to update the lock file.”

很多人忽略这个警告直接继续,结果就是:lock 里的版本和 composer.json 的声明脱节。比如你把phpunit/phpunit^9.0改成了^10.0,但 lock 还是旧的 9.x 版本,install 依然会装 9.x,而你还以为自己在用 10.x。

正确做法是:只要改动了 composer.json 中的 require 或 require-dev,就尽快执行composer update --lockcomposer update <对应包名>,确保 lock 与 composer.json 同步。composer update --lock这个命令很实用,它只更新 lock 文件的 content-hash,不改变已锁定的版本,适合"我只调整了配置但不打算此时升级包"的场景。

6. 最后再分享几个我踩过的 Composer 坑

前面每一章都带了排错思路,最后我再补充几个实操中容易被忽略的细节,算是我这几年和 Composer 打交道积累下来的碎碎念。

第一个是磁盘空间composer install下载的 dist 包会先解压到临时目录,再移动到 vendor。如果服务器/tmp所在分区空间不足,会出现“Not enough memory”或者解压失败,但报错信息不一定很明显。检查时记得用df -h看看 /tmp 和项目所在分区,别光盯着内存。

第二个是多目录项目里的多份 lock。有的项目拆成多个子目录,每个子目录各有一套 composer.json,却没有统一脚本管理。这种情况下,漏更新某一个子目录的 lock,部署时就会装出不一致的依赖。我建议在 CI 里对所有子项目的 lock 都做完整性校验,至少让composer install --dry-run跑一遍。

第三个是包的版本号带不带 v 前缀。有些标签在 GitHub 上叫v1.0.0,但 Packagist 展示时通常不带 v,写约束的时候用1.0.0而不是v1.0.0。如果你写"v1.0.0"作为精确版本,有时会解析到一个奇怪的 dev 分支,导致行为不符合预期。

第四个是改了代码不生效,先怀疑 opcache,再怀疑 autoload。很多人部署完新代码发现还是旧逻辑,第一反应是缓存问题。但如果你在composer installcomposer dump-autoload时用了--classmap-authoritative,又刚好改了某个类的位置,旧的 classmap 可能被 CDN 或 opcache 缓存住。这种情况建议重新执行composer dump-autoload -o,并确认 PHP-FPM 的 opcache.validate_timestamps 配置合理。

这些坑单看都不大,串在一起却能消耗一个下午。Composer 的最大价值不是帮你“装上包”,而是让你能精准控制“装哪些版本的包”。理解它每一步在做什么,遇到问题才能不慌。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 3:03:01

高效利用GitHub热榜:项目筛选、拆解与落地经验

每天早上到工位&#xff0c;我先花十几分钟把 GitHub 热榜项目的日榜过一遍。2026-09-10 这期榜单&#xff0c;说实话信息量不小&#xff0c;AI 类项目开始往落地走&#xff0c;开发工具类也进入“卷细节”的阶段。这篇文章我会按自己的筛选习惯&#xff0c;把当天上榜的几个方…

作者头像 李华
网站建设 2026/9/20 3:01:16

Codex科研工作流实战:从选题到模拟审稿的保姆级教程

说实话&#xff0c;我在把 Codex 真正塞进自己的科研流程之前&#xff0c;一直觉得它就是个写代码的辅助工具&#xff0c;无非是自动补全、生成几个脚本。直到我完整跑了一遍“研究问题 → 文献综述 → 实验分析 → 论文写作 → 模拟审稿”这条链路&#xff0c;才发现 Codex 最…

作者头像 李华
网站建设 2026/9/20 3:00:54

基于Hadoop+Spark+Kafka+Hive的民宿推荐系统设计与实现

1. 毕业设计选题背后的技术选型逻辑——为什么是这套大数据组合拳每年做计算机毕业设计的学生&#xff0c;十个里面有八个会在选题阶段纠结一件事&#xff1a;既要保证工作量、让评委觉得有技术含量&#xff0c;又怕自己撑不起一个复杂度太高的系统。民宿推荐系统这个题目恰好卡…

作者头像 李华
网站建设 2026/9/20 2:59:27

Win11系统服务优化指南:禁用10个服务释放内存提升性能

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

作者头像 李华
网站建设 2026/9/20 2:59:02

Omost 图像生成工作流完整拆解:LLM 如何用 Canvas 代码构图

Omost 图像生成工作流完整拆解&#xff1a;LLM 如何用 Canvas 代码构图 【免费下载链接】Omost Your image is almost there! 项目地址: https://gitcode.com/GitHub_Trending/om/Omost Omost 把 LLM 的"写代码"能力换成了可渲染的图像合成产物&#xff1a;模…

作者头像 李华
网站建设 2026/9/20 2:58:52

Hermes-Agent与Ollama离线对接:大模型完全本地化运行指南

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

作者头像 李华