从LangChain到LangGraph:Agent工作流编排与状态管理实战

去年年中,我的团队决定把几个稳定的业务Agent全部从LangChain迁到LangGraph。当时不少人觉得这是折腾——毕竟LangChain用得好好的,Agent也能跑。结果迁移完第一个复杂Agent之后,所有人的态度都变了:原来那种靠“链”硬拗出来的流程,在LangGraph里被拆得清清楚楚,状态流转、分支条件、人工介入全部可视化,调试效率直接翻倍。

这个系列已经写到第四篇,前几篇分别讲了LangChain的起步、工具调用和Prompt工程,这次我想把LangGraph整个核心知识体系拉通一次,做一个从LangChain过渡到LangGraph的全景式梳理。不管你是刚接触Agent开发,还是已经在用LangChain写了一些简单Agent,这篇文章都适合你。我尽量不堆概念,而是结合我们实际迁移过程中踩过的坑、验证过的用法来写。

1. LangChain的链式思维:能跑,但很拧巴

要理解LangGraph为什么出现,得先回到LangChain。LangChain最核心的抽象是Chain,本质上是一条固定流水线:Prompt模板接LLM,LLM接输出解析器,或者再接下一个Prompt。这个抽象在文档问答、文本翻译这类线性流程里非常好用,封装干净,写起来也快。

但Agent场景一出现,Chain就开始暴露短板。Agent的本质是“模型自己决定调用什么工具、按什么顺序调用”,这需要流程具有分支、循环、条件跳转的能力。用Chain来实现这种能力,做法通常是什么?我见过不少:

  • 把模型输出里的意图判断结果塞进一个Router Chain,按路由结果选择不同的子链执行;
  • 或者暴力一点,把循环直接嵌在Python函数里,在函数内部循环调用同一个Chain,直到满足退出条件。

这两种做法都能跑,但维护成本很高。第一个做法,当工具数量超过五个,路由分支变成网状,代码就变成一团面条,你根本说不清某个网络请求是从哪个分支触发的。第二个做法,循环逻辑和业务逻辑混在同一个函数里,状态完全靠外部变量传递,一旦并发一上来,变量被互相覆盖,Bug惨不忍睹。

我在一篇内部技术总结里写过一句话:LangChain的Chain适合“从左到右的一条路”,而Agent需要的是一张“可以走回头路的地图”。LangGraph就是把这张地图做成了正规军。

LangGraph并没有推翻LangChain。它保留了LangChain的Prompt模板、模型接口、工具封装这些好用的东西,只是在“流程编排”这一层换了一套引擎。流程图上的每一个节点,可以是普通的Python函数,也可以是LangChain的Runnable;节点的输入输出,共享一个全局状态对象。你过去在LangChain里写下的Prompt、Tool定义、Model调用,在LangGraph里基本可以直接复用,真正的增量是图结构和状态管理。

2. 四大基础概念:State、Node、Edge、编译

LangGraph的知识体系,可以压缩成四个词:State、Node、Edge、Compile。这四个概念全部吃透,整个框架就没有秘密了。

2.1 State:Agent的全局记忆与数据流转中枢

很多从LangChain转过来的人,上手LangGraph第一脚踩坑就踩在State上。State就是整个图运行期间共享的一个数据对象,所有节点读它、改它、再传给它,循环往复直到图结束。

State的定义方式有两种:TypedDict和Pydantic。我的建议是能用Pydantic就用Pydantic,尤其是字段较多的场景,IDE提示和运行时校验都能帮你省时间。

from typing import TypedDict, Annotated import operator class AgentState(TypedDict): messages: Annotated[list, operator.add] current_step: str tool_results: dict

注意上面这个Annotated[list, operator.add],这是LangGraph状态机制的精髓,叫做Reducer。默认情况下,节点返回的字典会覆盖State里同名字段,但如果你把某个字段声明成Annotated[list, operator.add],那每次更新就是“追加”而不是“覆盖”。

这一点在Agent里极其常用。因为Agent的典型状态就是对话消息列表,每执行一步,需要把模型回复、工具结果都追加到同一份消息列表里。如果没有Reducer,用一个新列表覆盖老列表,前一轮对话上下文立刻就丢了。

我建议所有LangGraph新手都手动跑一遍Reducer生效和没生效的对比实验,眼见为实,这个机制的理解程度直接决定后面写复杂Agent的幸福感。

2.2 Node:把一切操作变成图上的节点

LangGraph里的节点就是普通Python函数,签名统一:接收State,返回State的更新字典。你可以把模型调用写进节点,把工具执行写进节点,把自定义逻辑写进节点,然后用add_node注册进图里。

from langgraph.graph import StateGraph def call_model(state: AgentState) -> dict: response = llm.invoke(state["messages"]) return {"messages": [response]} graph = StateGraph(AgentState) graph.add_node("call_model", call_model)

注意几点实操经验:

  • 节点函数里不要持有全局可变状态。同一个节点可能被并发执行多次,如果函数内部用了一个模块级字典缓存东西,数据串了极难排查。
  • 节点返回的字典只包含你要更新的字段,不需要把整个State原样返回。LangGraph会自动做合并处理,返回了多余的字段反而容易出错。
  • 节点函数默认是同步的。如果某个操作是IO密集型的(比如调用外部API),你可以定义成async def,但注意一旦图里出现异步节点,整个图要用异步接口驱动(后面会细讲)。

节点是整个图的基本执行单元,它的粒度怎么切,取决于你想在什么地方能插入逻辑。我自己的习惯是:模型调用一个节点,每个工具单独一个节点,状态处理的纯函数逻辑单独一个节点。粒度分得细,后面做人工介入、排查问题都会方便很多。

2.3 Edge:控制流的连接纽带

光有节点没有边,图只是一堆孤立的函数。Edge定义了节点之间的流转关系。LangGraph的边分为两类:普通边和条件边。

普通边就是无条件连接,一个节点执行完必定进入下一个节点,适合顺序执行的场景。

条件边就厉害了,它是实现Agent自主决策的核心。条件边的路由逻辑由你定义的一个函数决定,这个函数读取State,然后返回下一个要执行的节点名字。

def route_after_model(state: AgentState) -> str: last_message = state["messages"][-1] if last_message.tool_calls: return "tool_node" return "end" graph.add_conditional_edges("call_model", route_after_model, { "tool_node": "tool_node", "end": END })

这里的第三参数字典是可选的,如果你不传,LangGraph默认认为返回值就是目标节点名。我建议还是写清楚映射字典,代码可读性会好很多。

条件边数量不受限制,你可以给同一个节点接多个条件边,返回不同的目标。这就像地铁换乘,进站之后你根据目的地选择不同线路。Agent的思考-行动-观察循环,就是靠这条条件边撑起来的:模型判断需要调用工具就去执行工具节点,不需要就直接结束。

2.4 编译:从图定义到可执行对象

所有节点和边都定义好之后,还需要最后一步:编译。注意这里的编译不是传统意义的编译成机器码,LangGraph的compile()会把你的图结构封装成一个可执行对象,同时绑定检查点存储、中断配置等运行时能力。

app = graph.compile() result = app.invoke({"messages": [{"role": "user", "content": "今天北京适合穿什么?"}]})

编译后的对象有三种调用方式:

  • invoke:同步执行整个图,返回最终State;
  • stream:同步逐节点执行,每次产生输出就返回给调用方,适合看执行过程;
  • astream:异步流式版本,配合async for使用。

实际开发里,我几乎都用stream而不是invoke,原因很实际:Agent执行链路长,一旦某个节点阻塞住,你至少知道卡在哪一步。直接invoke的话,用户只能看到转圈图标,后台日志如果不完整,排查问题全靠猜。

第一次在LangGraph里跑通一个两节点的图时,我心里冒出的想法是:这不就是给LangChain装了一套“状态机操作系统”吗?事实证明这个类比挺准确。

3. 设计意图:从地铁换乘图到Agent的执行路线

说到地铁换乘,我是真心觉得这个类比用来理解LangGraph太贴切了。你从A站上车,到B站需要换乘,这时你会看换乘标识,判断走哪条通道去几号线。对应到LangGraph里:

  • 车站就是节点;
  • 你手里持有的地铁卡余额就是State;
  • 换乘站的指示牌就是条件边;
  • 刷卡出站就是END。

普通Chain只能让你一条线坐到终点,想换线?你得自己写个判断逻辑去手动“下车再买票”。LangGraph则把整个地铁网络图显式铺在眼前,你可以标注哪条线是主线、哪个换乘点可以人工介入(打断流程等人工确认)。

我们在生产环境里有一个工单自动分类Agent,它需要读取工单内容,判断类型,再决定调用哪个知识库检索工具。用LangChain写的时候,分类逻辑和检索逻辑分散在三个Chain里,中间用Router串,整个执行路径不直观。改成LangGraph之后,执行路线一目了然:

  • read_ticket节点读取工单;
  • classify节点判断类型,分类结果写入State;
  • 条件边从classify出发,根据类型进入不同的检索工具节点;
  • 最后汇总进answer节点生成回复。

这个图上墙做Review,团队里任何人都能看懂流程。这就是“把控制流变成数据结构”的威力。

4. 实操一个可运行的ReAct Agent

理论讲再多不如直接跑一个Demo。下面我写的例子是一个支持天气查询和计算器的信息助手Agent,完全按照ReAct模式实现,即:思考 → 调用工具 → 观察结果 → 再思考,直到答案生成。

4.1 第一步:定义状态结构和工具

from typing import TypedDict, Annotated, Sequence import operator from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage from langgraph.graph import StateGraph, END class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], operator.add]

工具我就简单实现了两个:一个是查天气的模拟函数,一个是计算器。这里为了演示,我直接用@tool装饰器把它们声明成LangChain工具。

from langchain_core.tools import tool @tool def get_weather(city: str) -> str: """获取指定城市当前天气""" weather_data = {"北京": "晴,28℃", "上海": "小雨,24℃"} return weather_data.get(city, "暂无数据") @tool def calculate(expression: str) -> str: """计算简单的四则运算表达式""" try: return str(eval(expression)) except Exception: return "表达式错误"

4.2 第二步:构建图

from langchain_openai import ChatOpenAI from langgraph.prebuilt import ToolExecutor tools = [get_weather, calculate] tool_executor = ToolExecutor(tools) llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) llm_with_tools = llm.bind_tools(tools) def agent_node(state: AgentState) -> dict: response = llm_with_tools.invoke(state["messages"]) return {"messages": [response]} def tool_node(state: AgentState) -> dict: last_message = state["messages"][-1] tool_calls = last_message.tool_calls outputs = [] for call in tool_calls: tool_result = tool_executor.invoke(call) outputs.append(ToolMessage(content=str(tool_result), tool_call_id=call["id"])) return {"messages": outputs} def should_continue(state: AgentState) -> str: last_message = state["messages"][-1] return "tools" if last_message.tool_calls else "end" graph = StateGraph(AgentState) graph.add_node("agent", agent_node) graph.add_node("tools", tool_node) graph.set_entry_point("agent") graph.add_conditional_edges("agent", should_continue, {"tools": "tools", "end": END}) graph.add_edge("tools", "agent") app = graph.compile()

4.3 第三步:跑起来

result = app.invoke({ "messages": [HumanMessage(content="北京今天多少度?顺便算一下 (23+45)*2")], }) for msg in result["messages"]: print(msg.type, ":", msg.content)

执行过程简单描述一下:

  • 用户发消息,入口节点agent把消息喂给绑定了工具的LLM;
  • 模型判断这两个问题都要调工具,于是输出两个tool_calls;
  • 条件边检测到tool_calls非空,路由到tools节点;
  • tools节点逐个执行工具,把结果包装成ToolMessage追加进State;
  • 回到agent节点,模型看到工具结果后生成最终回答;
  • 条件边发现没有新的tool_calls,走end边,图结束。

这个循环就是Agent的灵魂。模型不是一次性输出答案,而是“边看结果边决定下一步”,LangGraph用一个条件边把整个循环撑了起来,代码量不多,但结构非常清晰。

5. 记忆、持久化与人工介入:生产级Agent的必备能力

跑通Demo只是第一步。真要放到生产环境,有三个能力跑不掉:记忆、持久化、人工介入。

5.1 记忆与Thread

Agent一轮对话之后,如果你直接invoke下一轮,State是全新的,模型根本不记得刚才聊了什么。LangGraph给出的方案是Checkpoint。

在编译图的时候指定一个CheckpointSaver,再给每次会话指定一个thread_id,LangGraph就会自动把每一步的State存下来。下一次用同一个thread_id继续invoke,图会从最近一次状态接着走。

from langgraph.checkpoint.memory import MemorySaver saver = MemorySaver() app = graph.compile(checkpointer=saver) config = {"configurable": {"thread_id": "user_123"}} result1 = app.invoke({"messages": [HumanMessage(content="我叫小明")]}, config=config) result2 = app.invoke({"messages": [HumanMessage(content="我叫什么名字?")]}, config=config) # 模型能回答出“小明”

注意MemorySaver是存内存里的,进程重启就没了。真正生产环境请用持久化方案,比如数据库存储的Checkpointer,具体根据你的技术栈选型。

5.2 人工介入

人工介入用的是interrupt机制,本质是在图执行到某个节点之前挂起,等人工确认后再继续。我们有个Code Review Agent就用了这个能力:模型生成代码建议之后,图会挂起,Senior工程师看一遍,点击确认或修改后,图才继续后续的消息发送。

from langgraph.types import interrupt, Command def review_node(state: AgentState) -> dict: approval = interrupt({ "question": "是否批准这条自动回复?", "content": state["messages"][-1].content, }) return {"approval": approval}

实现思路就是在需要打断的节点函数里调用interrupt,LangGraph会把状态存到检查点,然后把控制权交回给调用方。后续通过Command(resume=value)恢复执行。

这个能力在自动化程度高的业务里非常关键。它让AI出力的同时,人在关键节点保留否决权,而不是完完全全把流程交给模型。

6. 常见问题与排查技巧实录

最后一块,我把这半年迁移过程中遇到的高频问题整理成清单,条条都是真金白银的经验。

6.1 State字段不更新

现象:节点返回了某个字段,但下一节点读取到的还是旧值。

排查思路:

  • 先看目标字段是否被声明成了Reducer(比如Annotated[list, operator.add]),Reducer的语义是追加,不是覆盖,如果你期望覆盖,别给它声明Reducer;
  • 再看节点返回值里字段名是否和State定义里的完全一致,一个字母打错,LangGraph不会报错,只是静默忽略。

6.2 条件边死循环

现象:Agent在agent → tools之间反复跳,停不下来,甚至整个图超时。

我遇到的多数情况是工具结果里包含了模型判定为“还要继续调用工具”的关键词。比如工具返回的文本里有一段示例代码,里面恰好长得很像工具调用指令。处理方法是:在工具节点里给ToolMessage增加一个标记,明确来源是工具结果,模型权重的提示词里也要强调“不要对工具返回的示例再发起调用”。

另外,给条件边加一个最大步数保护。比如状态里加一个step_count,每次循环自增,超过阈值直接路由到END。这在调试和线上都是保命手段。

6.3 并发修改同一状态键

现象:消息列表偶尔出现重复内容,或者部分消息丢失。

原因通常是Reducer机制应对并发更新时的冲突。LangGraph允许多个节点并行执行,如果两个并行节点同时往同一个字段追加内容,有可能出现竞态。解决方法是给Reducer传一个自定义合并函数,或者在你的业务逻辑里避免“多个路径同时更新同一个字段”。

6.4 异步节点驱动方式错误

现象:图里定义了async def节点,但用app.invoke(...)去调用,直接报错。

LangGraph对异步的支持是值得顺带吐槽一下的。如果你的图里有任何异步节点,整个图必须用await app.ainvoke(...)或async for ... astream(...)来驱动。我们在一个爬虫类Agent里踩过这个坑,最后统一所有节点改成同步函数,避免混用。

6.5 回调跟踪

查问题时,我习惯在节点函数里临时加一行print打印关键State字段。LangGraph官网也有带界面的调试工具,可以直观看到每一步的状态变化和节点执行顺序。但注意:使用调试工具时不要在生产环境开启,会拖慢执行速度。

7. 后面还能怎么扩展?

LangGraph这套知识体系,打通之后扩展性非常强。方向包括:

  • 多Agent协作:不同Agent作为不同的图节点,Agent之间通过State传递任务,LangGraph天然支持这种拓扑;
  • 递归循环:处理需要反复优化的任务(比如写代码多轮测试修复),只需要调整条件边即可;
  • 子图封装:把复杂流程拆成子图,在大图里作为一个节点调用,模块化程度更高;
  • 自适应工具选择:根据用户的意图动态加载不同的工具集,工具本身不进图,工具列表放进State。

我个人实际使用下来,LangGraph最大的价值不是性能,而是把“Agent会怎么做决策”这件事变得完全可解释。以前用LangChain写Agent,模型的决策路径藏在回调日志里,出了问题只能一遍遍翻输出。现在State里每一轮的工具调用、每一步的状态变化都在同一份数据里,任何一步都能回放。这个体验,用过的团队基本都回不去了。

如果你做完这篇文章里的所有示例,再回去看那些用LangChain硬写出来的Agent,大概率也会产生“这个流程其实可以更清晰”的感觉。那就对了,说明你开始适应图的思维方式了。

相关新闻

哈希表从原理到实现:链地址法、开放寻址法与C++代码详解

哈希表从原理到实现:链地址法、开放寻址法与C++代码详解

哈希表可以说是C进阶路上绕不开的一块硬骨头。很多人在刷题时用过unordered_map,但一问到它底层的“哈希桶结构”到底是什么、开放寻址法和链地址法有什么区别、扩容的时候发生了什么,就说不清楚了。这篇内容我想从原理到底层实现,把哈希表的…

2026/10/20 18:51:53 阅读更多 →
JVM内存模型与垃圾回收实战解剖:从原理到调优

JVM内存模型与垃圾回收实战解剖:从原理到调优

1. 这不是笔记,是JVM内存运行的“人体解剖图”你有没有过这种体验:代码写得飞起,一上线就OOM;GC日志满屏飘红,却像看天书;调优时改了-XX:UseG1GC,结果吞吐量不升反降,连自己写的对象…

2026/10/20 18:51:53 阅读更多 →
用Claude Code Skill打造终端AI决策顾问:历史案例与心学智慧结合

用Claude Code Skill打造终端AI决策顾问:历史案例与心学智慧结合

做决策这件事,我一度觉得自己被它耗干了。小到要不要换工作,大到要不要选一个方向全力投入,每次都是搜集一堆资料、问一圈人、做一页利弊表,最后还是在两个选项之间反复横跳。后来我做了一个很实际的事情:把Claude Cod…

2026/10/20 18:50:52 阅读更多 →

最新新闻

AI论文汇总如何变成技术雷达:从arXiv时间切片提取工程信号

AI论文汇总如何变成技术雷达:从arXiv时间切片提取工程信号

1. 这不是论文目录,而是一份AI研究动态的“施工日志”你点开一个标着“[arxiv-cs.AI] 汇总-2026.10.07-人工智能论文”的文件夹,第一反应可能是:又一份需要硬啃的文献清单?别急——我过去三年里,几乎每周都整理类似标题…

2026/10/20 19:31:02 阅读更多 →
深度学习水果识别实战:从CNN训练到Flask部署全流程

深度学习水果识别实战:从CNN训练到Flask部署全流程

1. 项目定位与整体设计思路先聊聊这个项目的来龙去脉。基于深度学习的水果识别,本质上是图像分类任务,也就是让计算机按照像素内容判断“这张图属于哪个类”。很多同学会选择它做毕业设计,原因很直接:它属于计算机视觉领域的经典方…

2026/10/20 19:31:02 阅读更多 →
Agent框架插件化设计:把扩展能力做成第一公民

Agent框架插件化设计:把扩展能力做成第一公民

最近把DeepSeek开源的那套Agent框架源码整体拆了一遍,说实话,拆完心里有点复杂——很多设计我以前自己折腾过,但没有想到可以做得这么系统。这套框架给我最大的启发,不是“Agent还能这么写”,而是“插件化居然可以做到…

2026/10/20 19:31:02 阅读更多 →
C#轻量级OPC UA客户端:5分钟连PLC读温度

C#轻量级OPC UA客户端:5分钟连PLC读温度

简介:本资源是一个面向工业自动化与.NET开发者的C# OPC客户端实战项目,聚焦于使用OPC Net Api Chs库实现与OPC服务器的连接、数据读写、事件订阅及异常处理等核心通信功能,适用于具备基础C#和WinForms开发能力的中初级工程师快速掌握工业协议…

2026/10/20 19:31:02 阅读更多 →
用QClaw打造微信聊天分析助手:意图识别与情绪解读的Skill实践

用QClaw打造微信聊天分析助手:意图识别与情绪解读的Skill实践

一个多月前,我把 QClaw 上常用的几个小技能整理成了自己的私藏工具箱,其中有一个人起初当玩笑写的 Skill,最后却成了我打开次数最多的一个——“恋爱大师”。它的作用听起来很玄乎:把微信里那些容易让人内耗的对话,拆成…

2026/10/20 19:31:02 阅读更多 →
iOS自定义相机开发:AVCaptureSession底层控制与实战优化

iOS自定义相机开发:AVCaptureSession底层控制与实战优化

1. 项目概述:为什么需要一个“自定义相机”而不是直接调用系统相册 在 iOS 开发中,“调用系统相机”这句看似简单的指令,背后藏着大量被封装掉的决策权。当你使用 UIImagePickerController 拍照时,你拿到的是一张 JPEG 或 PNG—…

2026/10/20 19:29:59 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →