【AIAgent项目-AI生成PPT-09】灵活排版

别再让 AI 瞎编坐标:用布局树让 PPT 自动排得稳

一句话先说结论:AI 负责决定"哪些内容放一起、横着排还是竖着排、比例大概是多少";程序负责把这些意图稳定地换算成坐标。这样既保留 AI 的灵活性,又不把像素级几何计算交给概率模型。

文章目录

一、痛点场景:AI 生成的 PPT,为什么总有几页"看起来不对劲"?

想象一下,你在做一份季度经营复盘。输入"收入增长、区域对比、渠道贡献"后,AI 很快生成了十几页 PPT。标题、图表、结论都有了,但有几页图片和文字互相压住;有的左右边距不一致;还有一页只写了三条要点,却留下大块空白。你让模型"把左边图片缩小一点",它改完又把标题挤到了画布外。

这不是模型不会写内容,而是让它直接承担了不适合它的工作:精确计算每个元素的 x / y / width / height。大语言模型擅长从语义上组织内容,却不适合作为几何求解器。它一次给出的坐标看似合理,换一组文字、字体或图片后就可能失效。

另一个常见方案是给模型一批固定模板。它确实稳定,但模板一多就难维护;模板一少,页面就容易长得一样。三条要点和八条要点共用同一块区域,也会分别出现留白过大和内容溢出的情况。

本文结合上传教程中的 AI PPT 项目,拆解一套更稳的做法:使用 layout_tree 描述排版意图,再用 Python 的 flex_solve 确定性计算坐标。原教程页面可从这里查看:《9. 灵活排版》原始页面。1

二、先说明白:这里的"灵活排版"是什么?

这里讲的是 AI PPT 项目中的一套自定义布局树与坐标求解机制 ,不是把 CSS 的 display: flex 原封不动搬到 PowerPoint。它借鉴了"按行或按列排列、按权重分配空间"的思路,但布局树、权重规则、归一化、PPTX 导出和质量护栏都由项目自己定义。

可以把它理解成一张"排版施工图"。树节点只表达结构:容器是 row(横向)还是 column(纵向),孩子之间按什么比例分空间,间隙有多大;叶子节点对应真实内容块。求解器递归遍历这棵树,最后才输出每个块的矩形坐标。

下面是一个"左侧大图、右侧标题和三条要点"的结构。模型只需要给出这种结构关系,不需要直接猜坐标。

字段 大白话含义 常见用途
row / column 孩子横排还是竖排 先分左右,再把右边内容上下排列
ratios 横向各列的宽度权重 [38, 62] 表示左侧约 38%、右侧约 62%
grow 纵向孩子分配剩余高度的权重 标题 0.4、正文 1.0,正文获得更多高度
gap_pt 子块之间的间距 统一控制卡片或内容块留白
bleed 允许叶子越过安全边距延伸到画布边缘 全幅背景图、通栏大图

专业一点说,树是布局的中间表示(Intermediate Representation,IR)。模型生成的是语义结构,求解器把结构编译成几何数据。这个分层的关键价值,是让模型输出的不确定性止步于"结构提案",而不直接污染渲染坐标。

三、它是怎么演进过来的?从"坐标猜测"到"意图编译"

在上传教程描述的项目设计里,方案大致经历了三步。第一步是让模型直接输出坐标;很快遇到重叠、边距不齐和内容变化后失效的问题。第二步改用固定槽位模板:layout_id 选择一套预定义矩形,再把内容填进去;它稳定,但难覆盖所有内容组合,重复感也明显。第三步采用嵌套布局树:保留固定布局用于特殊页面,同时新增 layout_mode = "flex",由 layout_tree + flex_solve 处理可变布局。1

这不是说固定模板过时了。企业系统里常见的好设计是混合模式 :品牌封面、法律声明页、强约束的财务页继续用固定模板;内容组合多、需要快速换排布的普通页使用布局树。教程中的实现也把两种模式汇总为统一的 PlacedBlock 几何接口,下游渲染和导出不必关心坐标来自模板还是树。1

W3C 的 CSS Flexbox 标准同样面向沿主轴排列、分配剩余空间和对齐内容的需求;它的标准化历史从 2009 年的工作草案一路演进到后续候选推荐草案。2 3 这里仅把 CSS Flexbox 当作"声明排列意图、由布局算法计算尺寸"的类比。不要把 CSS Flexbox 标准日期误当成本文自定义布局树的项目历史。

四、为什么不让模型直接给坐标?

坐标是几何结果,不是语义意图。模型可以较容易判断"图片应在左,结论应突出",却很难持续保证 20 多个坐标同时满足不重叠、留白统一、字号可读和内容完整等约束。尤其是在用户拖拽、模型重新生成、内容字数变化后,坐标之间的约束关系会越来越复杂。

布局树把职责拆开后,模型只回答"怎么组织",确定性算法回答"放在哪儿"。同一棵树、同一张画布、同一套规则,求解结果就可以稳定复现。结构化输出可以约束模型返回的数据形状;OpenAI 的 Structured Outputs 文档说明,该能力可令返回内容符合给定 JSON Schema。不过,"符合 JSON Schema"不等于"满足业务语义",块 ID 是否完整、树是否过深、权重是否合理仍要由服务端检查。5

五、动手:写一个最小可运行的布局树求解器

下面用纯 Python 实现一个足够小、但具备核心机制的版本:支持 row 和 column 递归切分、ratios 和 grow 权重、以点数表达的 gap_pt,输出 0 到 1 之间的归一化矩形。代码不依赖第三方库,可用于理解算法;生产环境还要补上完整的树结构校验、内容测量、bleed 边缘扩展和图元质量检测。

先准备运行环境。这里不需要安装依赖;如果要继续使用下文的 Pydantic 校验示例,再安装 pydantic。

bash 复制代码
mkdir flex-layout-demo
cd flex-layout-demo
python -m venv .venv
source .venv/bin/activate
# Windows PowerShell 可用:.venv\Scripts\Activate.ps1
python --version

把下面代码保存为 flex_demo.py,然后执行 python flex_demo.py。矩形使用归一化坐标,因此 [0, 1] 的位置和尺寸可以映射到不同大小的画布。代码中的 ratios=[38, 62] 是权重,求解时会自动归一化;grow 只在纵向 column 容器中分配高度。

python 复制代码
from __future__ import annotations

from dataclasses import dataclass, field
from math import isfinite
from typing import Literal, TypeAlias


@dataclass(frozen=True)
class Rect:
    x: float
    y: float
    w: float
    h: float


@dataclass
class Leaf:
    block_id: str
    grow: float = 1.0
    bleed: bool = False


@dataclass
class Container:
    direction: Literal["row", "column"]
    children: list[Node] = field(default_factory=list)
    gap_pt: float = 16.0
    ratios: list[float] | None = None
    grow: float = 1.0


Node: TypeAlias = Leaf | Container


@dataclass(frozen=True)
class PlacedBlock:
    block_id: str
    rect: Rect
    bleed: bool


def _weights(values: list[float], expected: int) -> list[float]:
    if len(values) != expected:
        raise ValueError(f"期望 {expected} 个权重,实际收到 {len(values)} 个")
    if any(not isfinite(value) or value <= 0 for value in values):
        raise ValueError("布局权重必须是有限的正数")
    total = sum(values)
    return [value / total for value in values]


def solve(
    root: Node,
    *,
    canvas_w_pt: float = 960.0,
    canvas_h_pt: float = 540.0,
    padding_pt: float = 32.0,
) -> list[PlacedBlock]:
    if canvas_w_pt <= 0 or canvas_h_pt <= 0:
        raise ValueError("画布宽高必须大于 0")
    if padding_pt < 0 or 2 * padding_pt >= min(canvas_w_pt, canvas_h_pt):
        raise ValueError("padding_pt 必须非负且小于画布短边的一半")

    safe = Rect(
        padding_pt / canvas_w_pt,
        padding_pt / canvas_h_pt,
        1 - 2 * padding_pt / canvas_w_pt,
        1 - 2 * padding_pt / canvas_h_pt,
    )
    placed: list[PlacedBlock] = []

    def visit(node: Node, area: Rect, depth: int = 0) -> None:
        if depth > 16:
            raise ValueError("布局树超过最大深度 16")
        if isinstance(node, Leaf):
            placed.append(PlacedBlock(node.block_id, area, node.bleed))
            return
        if node.gap_pt < 0 or not isfinite(node.gap_pt):
            raise ValueError("gap_pt 必须是有限的非负数")
        count = len(node.children)
        if count == 0:
            return

        horizontal = node.direction == "row"
        start = area.x if horizontal else area.y
        extent = area.w if horizontal else area.h
        canvas_extent = canvas_w_pt if horizontal else canvas_h_pt
        gap = node.gap_pt / canvas_extent
        total_gap = gap * max(count - 1, 0)
        if total_gap > extent and count > 1:
            gap = extent / (count - 1)
            total_gap = extent
        available = max(extent - total_gap, 0.0)

        if horizontal:
            weights = (
                _weights(node.ratios, count)
                if node.ratios is not None
                else [1 / count] * count
            )
        else:
            weights = _weights([child.grow for child in node.children], count)

        cursor = start
        end = start + extent
        for index, (child, weight) in enumerate(zip(node.children, weights)):
            # 最后一个孩子吸收浮点残差,确保贴齐父容器右边或下边。
            size = end - cursor if index == count - 1 else available * weight
            if horizontal:
                child_area = Rect(cursor, area.y, max(size, 0.0), area.h)
            else:
                child_area = Rect(area.x, cursor, area.w, max(size, 0.0))
            visit(child, child_area, depth + 1)
            cursor += size + gap

    visit(root, safe)
    return placed


def example_tree() -> Container:
    return Container(
        direction="row",
        ratios=[38, 62],
        children=[
            Leaf("hero-image", bleed=True),
            Container(
                direction="column",
                children=[
                    Leaf("title", grow=0.4),
                    Container(
                        direction="row",
                        ratios=[1, 1, 1],
                        children=[
                            Leaf("point-1"),
                            Leaf("point-2"),
                            Leaf("point-3"),
                        ],
                    ),
                ],
            ),
        ],
    )


if __name__ == "__main__":
    blocks = solve(example_tree())
    assert [block.block_id for block in blocks] == [
        "hero-image", "title", "point-1", "point-2", "point-3"
    ]
    for block in blocks:
        r = block.rect
        assert r.w >= 0 and r.h >= 0
        print(
            f"{block.block_id:10s} "
            f"x={r.x:.4f} y={r.y:.4f} "
            f"w={r.w:.4f} h={r.h:.4f} bleed={block.bleed}"
        )

命令行运行后,每个内容块都会打印一行归一化矩形。x/y 是左上角的位置,w/h 是宽高。将归一化矩形乘以画布宽高,就能获得渲染器需要的实际尺寸。示例保留了 bleed 标记,但没有在求解器内执行出血扩展;生产实现应在坐标切分之后,仅对本来就贴近画布安全区边缘的块扩展对应方向,避免中间图片意外撑大。1

5.1 求解器到底做了什么?

遇到 row,算法先把间隙从总宽度中扣除,再按 ratios 权重切分可用宽度;遇到 column,则按孩子各自的 grow 权重分配高度。切出的矩形继续递归,直到叶子节点。总复杂度与树节点数成正比,遍历一遍就是 O(N)。

最后一个孩子的尺寸不要只用"总可用空间 × 最后一个权重"计算。更稳妥的做法是用父容器的边界减去当前游标,把累计浮点误差留给最后一块吸收。这样右边缘或下边缘能与父区域对齐,不容易出现细小缝隙。1

5.2 把布局树做成可校验的数据契约

实际项目里,树会经过模型、API、编辑器和渲染器多个环节,建议用 Pydantic 定义数据结构。它会依据类型注解和约束解析输入,并让验证后的实例符合字段类型;但树深度、块覆盖集合等业务条件仍应显式编写。6

下面示例使用 Pydantic v2。先安装依赖:

bash 复制代码
python -m pip install "pydantic>=2"
python 复制代码
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, model_validator


class LayoutNode(BaseModel):
    model_config = ConfigDict(extra="forbid")

    type: Literal["row", "column", "block"]
    id: str
    block_id: str | None = None
    children: list["LayoutNode"] = Field(default_factory=list)
    ratios: list[float] | None = None
    grow: float = 1.0
    gap_pt: float = 16.0
    bleed: bool = False

    @model_validator(mode="after")
    def check_shape(self):
        if self.type == "block":
            if not self.block_id or self.children:
                raise ValueError("block 必须有 block_id,且不能包含 children")
        else:
            if self.block_id is not None or not self.children:
                raise ValueError("row/column 必须有 children,且不能绑定 block_id")
            if self.gap_pt < 0 or self.grow <= 0:
                raise ValueError("gap_pt 必须非负,grow 必须为正数")
            if self.ratios is not None:
                if self.type != "row" or len(self.ratios) != len(self.children):
                    raise ValueError("ratios 只能用于 row,且必须与孩子数量一致")
                if any(value <= 0 for value in self.ratios):
                    raise ValueError("ratios 中的每个值都必须大于 0")
        return self


LayoutNode.model_rebuild()


def leaf_ids(node: LayoutNode) -> list[str]:
    if node.type == "block":
        return [node.block_id]
    return [block_id for child in node.children for block_id in leaf_ids(child)]


def validate_block_coverage(root: LayoutNode, expected_ids: set[str]) -> None:
    actual = leaf_ids(root)
    if len(actual) != len(set(actual)):
        raise ValueError("布局树中出现重复 block_id")
    if set(actual) != expected_ids:
        missing = expected_ids - set(actual)
        extra = set(actual) - expected_ids
        raise ValueError(f"block 集合不匹配:missing={missing}, extra={extra}")


# 用真实项目时,raw_json 来自模型响应,expected_ids 来自页面内容块。
# 这里用一个最小样例验证:左侧图片块 + 右侧标题块。
raw_json = '''
{
  "type": "row",
  "id": "root",
  "ratios": [38, 62],
  "children": [
    {"type": "block", "id": "leaf-image", "block_id": "image"},
    {"type": "block", "id": "leaf-title", "block_id": "title"}
  ]
}
'''
expected_ids = {"image", "title"}
root = LayoutNode.model_validate_json(raw_json)
validate_block_coverage(root, expected_ids)

JSON Schema 负责形状约束,Pydantic 负责解析与服务端校验,布局求解器负责计算几何。不要只靠提示词写"请保证格式正确",也不要把模型响应当成可信输入。对于不可恢复的树错误,应拒绝这个候选并走预设布局或确定性种子树,而不是悄悄删掉丢失的内容块。

六、企业项目如何接入?一条可上线的处理链

一个常见的服务端流程可以拆成:内容块建模、模型提议布局树、结构化解析、业务校验、规范化、内容适配、坐标求解、质量检查、统一渲染与导出。模型输出树只是"提议",不应成为最终坐标真相。

6.1 统一不同布局模式的下游接口

可以像编译器一样,把固定模板和灵活树都编译到统一的 PlacedBlock 列表。渲染器、质量检查器和 PPTX 导出器只消费几何结果,无须各自实现一遍布局分支。教程里采用 layout_mode 区分固定模式与 flex 模式,再让统一入口解析几何结果。1

下面是一个简化的接口示例:

python 复制代码
from dataclasses import dataclass


@dataclass(frozen=True)
class Rect:
    x: float
    y: float
    w: float
    h: float


@dataclass(frozen=True)
class PlacedBlock:
    block_id: str
    rect: Rect


def resolve_slide_geometry(slide) -> list[PlacedBlock]:
    if slide.layout_mode == "flex":
        return solve(slide.layout_tree)
    return resolve_fixed_slots(slide.layout_id, slide.blocks)

导出 PPTX 时,把 0 到 1 的归一化坐标转换为 PowerPoint 实际画布尺寸。例如使用 python-pptx,形状添加接口接收左、上、宽、高;这与"求解器只管几何,导出器负责单位转换"的分层很匹配。7

安装依赖:

bash 复制代码
python -m pip install python-pptx
python 复制代码
from pptx import Presentation
from flex_demo import example_tree, solve

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
slide_w = prs.slide_width
slide_h = prs.slide_height

# 示例从 demo 求解器取一个归一化矩形;项目里替换成对应 block 的 rect。
rect = solve(example_tree())[0].rect
x = int(rect.x * slide_w)
y = int(rect.y * slide_h)
w = int(rect.w * slide_w)
h = int(rect.h * slide_h)
shape = slide.shapes.add_textbox(x, y, w, h)
shape.text_frame.text = "由统一几何接口放置的文本块"
prs.save("demo.pptx")

注意:生产代码应直接使用 pptx 库中的 slide_width、slide_height 和 EMU 整数,不要把上面导入的 Inches 与 EMU 混用。此示例故意使用画布尺寸做单位转换;实际值须与生成归一化矩形时采用的安全区约定保持一致。

6.2 处理内容适配,而不是只把框切好

相同高度的文本框不代表相同的排版效果。三条要点与八条要点若分到同样高度,短内容会显得空,长内容则可能溢出。项目级实现可以先按字体度量估算文字自然高度,再按换行后的行数调整 grow;图片通常更适合吸收富余空间。教程中也把宽度拟合和内容高度拟合拆成两个阶段:先确定可读宽度,宽度决定折行;再按文字度量回推高度权重。1

还要区分"几何不越界"和"内容可读"。前者是坐标算法的职责;字号下限、行数上限、文本是否溢出、图片裁剪比例、对比度等属于内容与质量护栏。企业项目应在渲染后跑自动检查,并为无法自动修复的页面提供编辑器预览,而不是把"矩形都在画布内"当作版式合格。

6.3 编辑器交互、换排布与失败兜底

用户拖动分割线时,前端更新的是 ratios 或 grow,而不是手写每个块的坐标。增加、删除或移动内容块时,也应通过树操作函数同步更新树与块集合。跨容器拖动如果需要保留原区域,可用 spacer(占位节点)保留空间,避免其他块忽然变大。

"换排布"可以同时准备两类候选:模型根据当前内容提出新树;预设库根据块类型与数量匹配结构。候选进入页面前,统一经过 normalize、叶子块 ID 集合校验和去重。若模型候选都无效,就回退到预设或种子树,确保用户不会拿到一张无法渲染的页面。1

七、企业级稳定性的重点:幂等、双端一致、可回归

如果浏览器用 TypeScript 预览,后端用 Python 保存和导出,就会出现一个真实痛点:用户拖到满意的位置,松开鼠标后,后端重新算出的比例略有不同,页面"跳一下"。要避免这种体验,前后端必须共享算法约定、常量和边界规则,并用同一组 golden JSON 样本回归。

另一个关键概念是 normalize 幂等:

python 复制代码
normalize(normalize(tree)) == normalize(tree)

用大白话说,第一次规范化之后,再跑一次不能继续改变结果。比如把比例吸附到有限的设计值 {33, 38, 50, 62, 67},就要保证吸附后的值再跑时不会被吸到另一个值。grow 可以设置明确上下限;树深度、每行子节点数和权重总量也应设置边界。教程采用黄金样本同时验证前端和后端,并强调新增规则时先补样本,减少只改一端的风险。1

实际测试建议至少覆盖这些类别:空容器和单孩子、默认均分、权重和不为 100 的比例、超大间距、极窄画布、深层嵌套、重复或缺失 block ID、bleed 只贴边扩展、浮点边界、normalize 重复调用。对于 Python 与 TypeScript 的几何一致性,比较结果时应先约定浮点容差;如业务要求逐字节一致,则统一数值量化和舍入规则。

八、和常见方案怎么选?优劣势对比

下面比较的是解决同一类排版问题时可能采用的方案,不是说它们都是可以直接互换的库。CSS Flexbox / Grid 是浏览器里的 CSS 布局机制;布局树求解器则是可被 AI、编辑器和多个渲染目标共同调用的应用层组件。

方案 优势 短板 更适合的情况
模型直接输出绝对坐标 上手快,表达自由 坐标容易重叠、漂移;很难稳定满足边界约束 一次性草图或人工复核的概念稿
固定模板槽位 输出稳定、测试简单 组合有限、重复明显;新内容类型要补模板 品牌固定、页型少、版式不允许自由变化
CSS Flexbox 浏览器原生;擅长单主轴排列、分配剩余空间和对齐 本身不是 PPT 布局数据契约;导出到原生可编辑形状需额外映射 Web 页面一行/一列组件布局
CSS Grid 行列二维关系清晰,适合对齐严格的网格 对单行/单列流式内容可能显得偏重;非浏览器导出仍需桥接 仪表盘、卡片墙、固定行列结构
自定义布局树 + 确定性求解器 意图与坐标分离;跨端复用;容易做验证、编辑和回归 需要自行维护 schema、求解器、边界规则和内容适配 AI PPT、可视化编辑器、多渲染目标的企业项目

MDN 对 CSS Flexbox 与 Grid 的选择建议很实用:如果主要控制一行或一列,优先考虑 Flexbox;如果要同时控制行和列,Grid 更直接。4 但在 AI PPT 系统里,问题还包括模型如何提议结构、服务端如何验证、编辑器如何保存、PPTX 如何输出,所以应用层布局树解决的是比 CSS 属性选择更完整的一条链路。

九、常见业务场景与设计边界

AI 生成 PPT 或报告:每页内容块数和类型变化大,布局树可以为同一批内容快速生成多个结构候选。对于强品牌约束的首页或财务页,保留固定模板更稳。

在线页面编辑器:拖拽操作调整权重,插入或删除块后更新树。前后端共享数据协议和 golden cases,能降低"本地预览正常、保存后变形"的概率。

同一内容输出多端:相同布局意图可先转为统一几何,再分别交给 Web SVG/Canvas 渲染、图片渲染或 PPTX 导出器。需要确认各输出端的字体、文本测量和裁剪语义一致,否则几何一样不一定视觉一样。

不适用或需要扩展的场景 :需要大量元素自由重叠、复杂锚点、绝对定位、曲线和任意形状的海报,不适合只靠 row/column 二分切分。可以用固定模板处理特殊页;若确实要支持重叠,再显式增加 overlay 一类节点和对应的约束测试,不要把自由坐标偷偷塞进模型输出。

十、面试官高频问题

1. 为什么不让大模型直接生成 x/y/w/h?

因为坐标是一组互相约束的数值。模型可能生成局部看起来合理的值,但难以稳定保证整体不重叠、间距一致、内容不溢出。更可靠的做法是模型生成有边界的结构意图,由确定性算法计算几何。

2. ratios 和 grow 有什么区别?

在本文约定中,ratios 描述 row 中各列的横向权重;grow 描述 column 中各行的纵向权重。权重只是相对值,例如 [38, 62] 与 [19, 31] 归一化后分配比例相同。

3. 为什么需要布局树,而不是平铺的列表?

平铺列表只能表达顺序或单层行列。嵌套树能表达"先把页面切为左右两区,再把右区切成标题和要点",复杂结构可以通过递归组合,同时每个节点都有明确的局部上下文。

4. normalize 为什么需要幂等?

树通常会多次经过保存、编辑、重试和回放。如果每次规范化都会改变比例,页面就会不断漂移。幂等可以让重复处理不产生额外变化,也让重试安全、测试稳定。

5. 为什么让最后一个子块吸收浮点误差?

如果每一块都独立乘比例,浮点小数相加后可能与父容器总宽度差一点。最后一块直接使用"父边界减去当前游标",能把累计残差集中吸收,避免边缘缝隙。

6. 怎么保证前端 TypeScript 与后端 Python 结果一致?

共享规范、常量、黄金样本和边界规则;按相同输入比较输出;为浮点值制定容差或固定量化规则;每次新增特性先更新 golden cases,再分别运行两端测试。

7. 模型输出了不存在的 block ID,应该自动修复吗?

通常不应静默修复。它代表模型对当前页面内容的理解已经错位。拒绝候选并回退到预设或确定性种子树,可以避免静默漏内容,并且更容易排查问题。

8. 这种求解器的时间复杂度是多少?

若每个节点只遍历一次,每个节点的孩子只处理一次,求解是 O(N),其中 N 是布局树节点数。树深度还要限制,避免异常或恶意输入造成递归过深。

十一、落地检查清单

上线前,至少确认这几件事:模型只输出布局树而非最终坐标;输入经过 JSON Schema 与服务端业务校验;每个内容块恰好对应一个叶子;权重、深度、节点数、间距都有上限;布局求解是纯函数;normalize 幂等;Python 与 TypeScript 共享黄金样本;异常候选能回退;渲染后还检查文本溢出、重叠和越界;导出器使用与求解器一致的画布和安全区定义。

如果只记住一个原则,请记住:树描述意图,求解器计算坐标,质量护栏决定结果能否交付。 这套拆分并不会让每张幻灯片自动变成设计师作品,但它能显著减少模型随机性直接变成几何错误的机会,并让系统更容易测试、编辑和扩展。

转载声明

本文为原创文章,如需转载,请联系作者获得授权,并注明出处。

相关推荐
MasonYyp1 小时前
安装npm依赖仓库Verdaccio
前端·npm·node.js
Mininglamp_27181 小时前
Human-AI Co-Writing With Full Accept/Reject Control on Every Edit
人工智能
~~刘杨1 小时前
用 Python 批量处理商品图:抠图、高清放大与图生视频(同步 API 完整示例)
图像处理·python·aigc
小小张说故事1 小时前
Python 拼字符串为什么别在循环里用 +=?拆开 join 的两遍账:191.8 微秒与 6.8 微秒
python
CodeGL1 小时前
15、Python 访问数据库
数据库·python
X54先生(人文科技)1 小时前
ELR-SELLM_硅基伙伴通过对话碰撞个性涌现的推理架构_v10
人工智能·深度学习·架构·零知识证明
CV爱数码1 小时前
论文精读|RED-CNN:残差编解码网络,低剂量 CT 去噪的经典基线
网络·人工智能·算法·cnn·ct·重建·x-ray
Sailing1 小时前
【封闭结束3】ReAct 循环:从一次 Function Call 到多步 Agent
人工智能·面试·agent
Antruly1 小时前
C++ Web 开发选型漫谈:从 Drogon 到 libuvcpp + uvp,八个项目的定位、兼容性与务实排名
开发语言·前端·c++