同一份 15 个流程 JSON,第六种语言也跑通了:工作流引擎 Rust 移植实录

作者:mldong日期:2026/9/2

一、第六格不是"翻译"

这个系列写过五门语言的工作流引擎:Java、Go、Python、Node、PHP。五门语言读的是同一份东西——引擎仓库里 flows/ 目录下的 15 个流程 JSON:简单审批、多任务、条件路由、fork-join、三种会签、退回发起人、混合模式……六语言 demo 共读这一个目录,改一处,六边验证。

第六种语言是 Rust。

先说清楚一件事,免得标题被误读:Rust 里不缺"工作流引擎"——做任务编排(durable execution)的有 Temporal、workflow-rs、wfaas 一大票;解析 BPMN 的有 snurr、wfrs;连外部集群的客户端有 zeebe-rs、camunda-client。这些是编排型和 BPMN 型,解决的是"长任务怎么可靠地跑完"。

缺的是另一类:嵌入式审批流引擎——不解析 BPMN、不连外部集群、带会签/抄送/委托/退回发起人这套 OA 语义、嵌进业务系统里用的那种。Java 里这块是 Flowable 的地盘,Rust 里基本没人做。jeeflow 的 Rust 实现填的就是这个格。

所以这篇不是"Rust 没有工作流引擎,我们造了一个"——那是假话。它是:同一份契约,在执行模型完全不同的第六种语言里重新落地。没有 GC、没有反射、没有 ORM、async/await、借用检查器——Java 里一行 new ProcessTask(...) 背后的一整套运行时习惯,在 Rust 里全要重新想。

移植不是翻译。被钉死的是契约:code=0/msg 响应信封、submitType 提交类型、五张表(define/instance/task/task_actor/cc_instance)、22 个合规场景。"跑通"的定义不是 demo 能点,是 22 个场景的行为逐一对齐。

这篇是实录:四个坑,四个解法。前两个是 Rust 特有的,后两个是任何语言都会踩、只是 Rust 这次踩得最疼的。


二、移植前:被钉死的东西

先看这 15 个 JSON 长什么样。01-simple.json,最朴素的请假审批:发起 → 上级审批 → 结束。

1// jeeflow-rust/flows/01-simple.json(节选)
2{
3  "name": "simple",
4  "displayName": "简单审批流程",
5  "type": "approval",
6  "nodes": [
7    { "id": "start", "type": "snaker:start", "text": { "value": "开始" } },
8    {
9      "id": "apply",
10      "type": "snaker:task",
11      "properties": { "form": "apply-form", "assignee": "applicant", "taskType": 0, "performType": 0 },
12      "text": { "value": "发起申请" }
13    },
14    {
15      "id": "task1",
16      "type": "snaker:task",
17      "properties": { "form": "leave-form", "assignee": "leader", "taskType": 0, "performType": 0 },
18      "text": { "value": "上级审批" }
19    },
20    { "id": "end", "type": "snaker:end", "text": { "value": "结束" } }
21  ],
22  "edges": [
23    { "id": "e0", "sourceNodeId": "start", "targetNodeId": "apply" },
24    { "id": "e_apply_1", "sourceNodeId": "apply", "targetNodeId": "task1" },
25    { "id": "e2", "sourceNodeId": "task1", "targetNodeId": "end" }
26  ]
27}
28

节点、边、表达式——就是第 4 篇拆过的那套 LogicFlow JSON。这 15 个文件的唯一编辑源是 Java 仓,各语言仓持有一份镜像副本(维护者机器上自动镜像、保持同步,读取点只读本仓副本;JEFFLOW_FLOWS_DIR 可覆盖)。改一处,六边验证——"共享"共享的是内容,不是文件句柄。

契约的另一半是行为。spec/08 定义了 22 个合规场景:线性流程、多任务、条件分支、fork-join、三种会签、退回发起人、权限校验、拦截器事件、抄送、加签……移植验收 = 22 个场景在 Rust 引擎里的可观察行为与参考实现逐一一致。

这里埋个伏笔:移植初期,Rust 的测试没有按 22 场景成建制——测试用手写 JSON 片段,场景零散覆盖,流程 JSON 也不读共享目录。这条 issue(#87)后来闭环,测试按 test_c01~test_c22 编号重建,JSON 驱动源改成直读共享 flows 目录——测试数据从此和五语言同源。"怎么验收"的故事放到第八节,先说架构。


三、四个 crate:把架构 Rust 化

模块布局对照 Java 侧(jeeflow-java 的 core / repository-jdbc / starter / facade):

crate对应 Java职责
jeeflow-corejeeflow-core引擎核心:零第三方依赖,纯 stdlib
jeeflow-repository-sqlxjeeflow-repository-jdbcsqlx 0.8 MySQL 仓储(含 DDL)
jeeflow-persistpersist 扩展ARCHIVE/SYNC 动态表写入(元数据驱动)
jeeflow-facadefacade统一门面:一个入口转发全部 action
jeeflow-demo-salvodemo-boot4Salvo 演示服务(不发布)

前四个发 crates.io,1.0.5 于 2026-08-25 首发,当前 1.0.6。

最硬的一个工程事实:jeeflow-core 的 Cargo.toml——

1# jeeflow-core/Cargo.toml
2[dependencies]
3# Zero third-party dependencies - core is pure Rust stdlib only
4

[dependencies] 段是空的。serde_json 和 tokio 全在 [dev-dependencies]——只给测试用。grep 整个 core 的生产代码,外部 crate import 数为零。

这意味着什么?Java 侧的核心是"98KB、只依赖 slf4j-api";Rust 侧更极端——连日志库都没有。引擎不感知 JSON 库、不感知数据库、不感知 HTTP,全靠 SPI 注入(ProcessRepository / UserProvider / IdGenerator)。

那 async fn 怎么来的?Rust 的 async fn 是语言特性,不需要 tokio——引擎只有 5 个生产 async fn(start / execute / 三种 jump),定义的是"可挂起的计算",运行时(tokio runtime)由调用方注入。SPI 的 ProcessRepository trait 本身是同步的(Send + Sync),sqlx 仓储在内部做 sync-over-async。

这个"core 同步、门面 async"的分层,就是下面第一个坑的架构根源。先看正常路径长什么样——README 快速开始(真实代码):

1use std::sync::Arc;
2use std::collections::HashMap;
3
4use jeeflow_core::{
5    context::ServiceContext,
6    id_gen::AtomicIdGenerator,
7    memory::MemoryRepository,
8    spi::{ProcessRepository, UserProvider},
9};
10use jeeflow_facade::JeeflowFacade;
11
12// 1. 装配上下文:仓储 + 用户 Provider + ID 生成器
13let repo = Arc::new(MemoryRepository::new());
14let ctx = ServiceContext::new()
15    .with_repository(repo.clone() as Arc<dyn ProcessRepository>)
16    .with_user_provider(Arc::new(my_user_provider()))
17    .with_id_generator(Arc::new(AtomicIdGenerator::new(1)));
18
19// 2. 注册流程定义(LogicFlow JSON,与其他五语言共享同一套 flows 文件)
20//    repo.save_define(&mut define);  ← 略,见 README
21
22// 3. 统一门面:一个入口转发全部 action
23let facade = JeeflowFacade::new(ctx);
24
25let start_args: HashMap<String, serde_json::Value> = [
26    ("name", serde_json::json!("leave")),
27    ("operator", serde_json::json!("user1")),
28    ("days", serde_json::json!("3")),
29].into_iter().collect();
30
31// 发起(startAndExecute:发起并自动完成发起节点)
32let resp = facade.flow("processDefine/startAndExecute", &start_args).await;
33

一个 facade.flow(action, args) 进去,{"code":0,"data":{...}} 出来——和另外五门语言的门面同形。flow 的签名是:

1// jeeflow-facade/src/lib.rs
2pub async fn flow(&self, action: &str, args: &HashMap<String, Json>) -> Json
3

注意它是 async fn。第一版不是——第一版是同步函数,内部 block_on 调引擎。这就是坑一。


四、坑一:嵌套 block_on panic(Rust 特有)

症状

demo 起来,第一个请求打过去:

1POST /processDefine/startAndExecute
2→ worker 线程 panic + Mutex poisoned
3

不是业务报错,是运行时直接 panic。

根因

门面要同时服务两类调用方:框架控制器(同步上下文)和未来可能的 async 调用方。第一版的设计是——flow() 保持同步签名,内部用 block_on 把 async 引擎"压平":

1// 第一版(错的)
2pub fn flow(&self, action: &str, args: &HashMap<String, Json>) -> Json {
3    // ...
4    let resp = tokio::task::block_in_place(|| {
5        Handle::current().block_on(self.engine.start_async(...))
6    });
7    // ...
8}
9

问题在 Handle::current().block_on(...):demo 用的是 Salvo,handler 本身就跑在 tokio runtime 的 worker 线程里——你已经在 runtime 里,还要 block_on 一个 future,这就是 tokio 明令禁止的嵌套阻塞,直接 panic。

Java/Go/Python/Node 没有这个概念:它们的门面要么天然 async(协程),要么同步且没有"runtime 内再阻塞"这回事。这是**"同步门面 + async 引擎"组合独有的坑**,台账里归为 Rust 实现缺口。

为什么 170 个测试全绿没抓住

facade 的测试全是同步 #[test]——仓内当时零个 #[tokio::test]。同步测试跑在没有 runtime 的上下文里,代码走了"新建 runtime"的兜底分支,永远 happy path。170 个测试全绿,全绿得理直气壮。

修复

  • flow() 改成 async fn,内部直接 .await 引擎,不再 block;
  • facade 全部测试改 #[tokio::test],在真实 runtime 里跑。

一句话教训:测试必须和生产在同类运行时里跑。你的生产代码跑在 tokio 里,测试却在裸线程里 block_on 新建 runtime——两个世界,测试绿得不算数。


五、坑二:聚合根水合缺失(DDD 在 Rust)

症状

发起正常,办理第一步就报错:

1Task 100002 not found in instance 100001
2

根因

第 5 篇讲过 jeeflow 的 DDD 设计:ProcessInstance 是聚合根,ProcessTask 是它的子实体,任务挂在实例的 tasks 列表上。移植时这个结构被忠实地搬进了 Rust——ProcessInstance { tasks: Vec<ProcessTask>, ... } 原样。

坑不在设计,在水合责任。MemoryRepository(内存仓储,测试和 demo 都用它)把 instance 和 tasks 分开存:find_instance_by_id 只查实例主记录,不填充 tasks。于是 execute_task_async 加载出来的实例,tasks 是空的——complete_task 在空列表里找任务,当然找不到。

Java 侧同样的"分开存"由仓储实现层统一水合,调用方无感;Rust 侧移植初期,水合这行责任落在了调用路径上,而三条执行路径(execute_task_async / execute_and_jump_async / execute_and_jump_to_end_async)全都漏了。

修复

三条路径在 find_instance_by_id 之后补上水合:

1// 加载实例后,补全聚合根的子实体
2instance.tasks = self.repo().find_history_tasks(instance.instance_id)?;
3

影响范围:所有"加载 instance 后操作 task"的路径。一行修复,但它标出了移植 DDD 聚合时的通用陷阱:聚合结构可以照搬,聚合的加载语义(谁负责把子实体装回来)必须逐个仓储实现核实。


六、坑三:测试全绿,契约还是错的

前两个坑是"功能坏了",这个坑更阴:功能看着是好的,测试是全绿的,但响应契约错了。而且是两个同族的错,都是全链路 curl 实测才暴露的。

6.1 execute 响应的 taskIds 是 [0]

demo :8091 全链路实测:

1execute {"id":"100001","operator":"superAdmin"}
2→ {"code":0,"data":{"taskIds":[0]}}          ← 新任务 id 是 0?
3
4todoList operator=user2
5→ rows[0].id = "100002"                       ← 新任务真实 id 在这
6

引擎存储是对的——新任务确实落库了、id 是 100002、todoList 能查到。错的只有响应:办理推进出新任务时,响应里的 taskIds 是 [0]。

调用方要是拿 execute 响应里的新任务 id 直接做下一步(比如接着 execute 下一个任务),拿到 0 就炸。

根因是一条很隐蔽的克隆时序:

  1. 节点执行时,新任务以 id=0 收集进 exec.new_tasks;
  2. persist_tasks 落库时,对克隆体赋真实 id 再存——let mut t = task.clone(); t.task_id = self.next_id(); save_task(&mut t);
  3. 但 execute_task_async 返回的 new_tasks,是 persist 之前克隆的那份——原片 id 还是 0。

对照:start 路径(assign_ids)在 persist 前就对原片赋 id,所以 startAndExecute 的 taskIds 是对的([100001])——同一条响应契约,两条路径,一条对一条错。这种"一条路径对"的 bug,最容易骗过测试:测试通常只测走得通的那条。

6.2 出口 stringifier 漏了复数 Ids

第二个同族错在 facade 出口。联邦契约要求 id 字符串化(防止大数字跨语言丢精度),facade 出口有个 stringify_ids 递归改写响应里的 id 字段。实测:

1startAndExecute → {"data":{"taskIds":[100001], ...}}   ← 数组里是裸 number,不是 "100001"
2

根因在键匹配:

1// jeeflow-facade/src/lib.rs(第一版)
2if k == "id" || k.ends_with("Id") || k.ends_with("_id") { ... }
3

taskIds / roleIds / actorIds 以 s 结尾——三个条件全不命中,数组原样放行。

为什么"测试 id 小看不出来"?因为测试用的 id 是 100001 这种小数字,裸 number 过 JSON 没问题。但生产是雪花 id——19 位,超过 2^53,JavaScript 拿到直接丢精度,前端算出来的 id 和后端对不上。测试数据没暴露生产形态,这是契约测试的经典死法。

为什么仓内测试全绿没抓住

两个 bug 各自的漏网原因(台账原文核对):

  • 6.1:facade 测试断言 taskIds != 0 的"跨调用一致性"从来没写过——只断"非空"或干脆没断这条路径;
  • 6.2:stringifier 的测试只测单数键(id / taskId / user_id);"出口审计无 >2^53 的 number"这条审计标准本身也漏了"复数 Ids 键下裸 number 数组"这个形态——小 id 的裸 number 根本过得了审计。

怎么发现的

都不是仓内测试抓的,是全链路 curl 仲裁:起 demo(:8091),发起 → 办理 → 再查 todoList,跨调用对照响应里的 id。6.1 的修复验收断言就按这个写的:execute 响应的 taskIds 不仅非 0,还要和 todoList 查到的新任务 id 一致。

两个 bug 同一个修复 commit(dfea338)闭环,测试补齐后全绿。

这一节的通用教训

  • 测试全绿 ≠ 契约正确。断言要跨调用(这次响应的 id,下次查询对得上),不能只断形状;
  • 测试数据形态要覆盖生产形态。雪花 id 的精度问题,小 id 测试永远测不出来——出口审计必须按"生产 id 会多大"设计;
  • 呼应本系列反复出现的三场景测试纪律:正向 + 负向还不够,全链路仲裁是第四道门。

七、坑四:串行会签"一次建全"——还发现了一段死代码

症状

06-countersign-sequential.json——串行会签,userA、userB 依次审批:

1// jeeflow-rust/flows/06-countersign-sequential.json(节选)
2{
3  "id": "task1",
4  "type": "snaker:task",
5  "properties": {
6    "assignee": "userA,userB",
7    "performType": 1,
8    "countersignType": "SEQUENTIAL"
9  },
10  "text": { "value": "串行会签" }
11}
12

数据侧清清楚楚:两人、串行。契约语义(第 8 篇讲过):串行 = 完成一个建一个,任意时刻只有 1 个 DOING 任务。

实测:

1start + apply 后:DOING = 2(userA/userB 一次建全)
2userA execute 后:实例 state = 20(直接 FINISHED!),userB 任务留在 DOING(孤儿待办)
3

任意一人完成 → 整单定稿 → 其余成员变孤儿待办。

根因:比"一次建全"更糟——是死代码

挖下去,发现比台账最初描述的"只数人头"更糟:

  • 创建侧:create_countersign_tasks 对所有 actor 一次建全,actor_ids.iter().map(...) 不区分串行/并行;
  • 推进侧:负责会签完成判定的 CountersignHandler(含 SEQUENTIAL 分支的 is_complete)是全仓零调用的死代码——会签 Task 节点在 execute_task_async 完成后无条件沿出边流转,没有任何完成门控。

台账里原话:

台账"只数人头"的描述是对这段死代码的误读,实际比它说的更糟。

——排查时先按死代码的注释理解行为,实测才发现那段代码根本没被执行过。Rust 编译期不删死代码(trait impl 不产生警告压力),一段逻辑可以安安静静躺在仓库里几个月。

为什么测试没抓住

test_c06_countersign_sequential 的断言:

1let tasks = repo.find_doing_tasks(iid, &[]).unwrap();
2assert!(!tasks.is_empty(), "c06: sequential countersign should create first task");
3// 只断言"非空",未断言"恰好 1 个"
4

断言过软:一次建全了 2 个,"非空"照样绿。

这个坑的特殊位置

它是 Java/PHP 的 issue/93 的同类缺陷(五语言对齐里那批),但 Rust 当时未发版——台账的处理方式值得引用:

Rust 未正式发版,故单独立项跟踪,不并入五语言对齐,避免污染发版基线。

没为了"五语言一起修"的整齐把没发版的 Rust 塞进对齐批次,而是独立跟踪、独立闭环。

修复(六步)

  1. 串行逐个创建:SEQUENTIAL 分支只建 actors[0],operatorList/loopCounter 写进 instance 变量,完成一人建下一人;
  2. 会签完成门控(新增前置):execute_task_async 沿出边流转前插一道 gate,条件不满足直接 return;
  3. 一票否决条件化 + 软拒绝 + merged 废弃残留:submitType=20 默认软拒绝,节点配 ONE_VOTE_VETO 才阻断;merged 后废弃该节点剩余 DOING 任务,不留孤儿;
  4. 死代码处置:删除 CountersignHandler 的 IHandler impl 和旧测试,重构为纯函数 check_merge;
  5. # 前缀变量解析(比例表达式);
  6. countersignDisagreeFlag 注入。

验收:测试全绿,test_c28 重写为 08 流程(会签 → leader 审批 → end)全链硬断言 4 步——每步都断 DOING 数 + actor + 实例状态,不再"只断非空"。


八、怎么验收:22 个场景 + 15 个 JSON + 真 MySQL

四个坑讲完,回到"移植到底怎么算完成"。Rust 侧的验收是三道门:

第一道:仓内快测(T0)。22 个合规场景按 spec/08 成建制编号——

1test_c01_simple_linear          test_c04_parallel_fork_join
2test_c02_multi_task             test_c05_countersign_parallel
3test_c03_decision_branch        test_c06_countersign_sequential
4test_c07_countersign_ratio      test_c08_reject_to_applicant
5test_c09_permission_check       ... 直到 test_c22
6

流程 JSON 不手写片段,测试直读 flows/ 目录的 15 个共享文件(JEFFLOW_FLOWS_DIR 可覆盖)——测试数据和 Java 侧永远是同一份。

第二道:真 MySQL 冒烟(T1)。连隔离库跑全流程 / persist / 抄送 / 委托 / 事务回滚。移植初期这道门是缺失的(issue #86:全仓零个连库测试,sqlx 的 16 个测试全是 DDL 语法断言——"连库测试"被错误地豁免成了"第二步"),后来补上 M1–M4。

第三道:全链路 curl 仲裁。demo 起在 :8091,发起 → 待办 → 办理 → 查新任务,跨调用对照。坑三的两个 bug 都是这一道门抓的。

数字收口(crates.io 1.0.6 基线):

合规场景22 个(spec/08,test_c01~test_c22 成建制)
仓内测试252 个
共享流程 JSON15 个(六语言共读同一目录)
发布 crate4 个(core / repository-sqlx / persist / facade)
首发1.0.5,2026-08-25,crates.io

其余一批小坑不展开,各有一条台账、各自闭环:字符串 id 双容错({"id":"100000"} 和 {"id":100000} 都得收,#84)、m_{alias}_{op}_{column} 三段式查询参数解析(#85)、demo 静默吞 body 解析错误改显式报错(#88)。


结语:六语言到底是什么

回头看这第六格,"六语言"这三个字的准确含义就清楚了:

六语言 ≠ 六份一样的代码拷贝,= 一份契约 + 15 个 JSON + 六层薄实现。

Java 用反射和 Spring,Go 用 goroutine,Python 用 asyncio,Node 用 event loop,PHP 用同步过程式,Rust 用 async/await + 借用检查器——执行模型没有两门是相同的,但 22 个合规场景的行为逐一一致,15 个流程 JSON 一处定义六边共读,五张表的列一个不差。Rust 这一格恰恰证明了这件事:连"core 零第三方依赖、纯 stdlib、SPI 同步 + 门面 async"这种和 Java 完全不同的架构切法,契约也能把行为钉住。

而这篇实录真正的教训,比"Rust 难"更深一层:

  • 测试全绿不是验收标准。 170 个测试全绿时,第一个请求就打崩了运行时;单测全绿时,响应里的 id 还是 0——这两个 bug 都是全链路 curl 对照才抓到的。测试要和生产同运行时、断言要跨调用、数据要覆盖生产形态(雪花 id);
  • 死代码比 bug 危险。 bug 会报错,死代码会沉默——你以为它在工作,它一行都没执行过;
  • 移植的验收单位不是"功能",是"契约"。 22 个场景逐一对齐,才算这一格补上。

前五种语言各走各的快,Rust 走得最慢——它逼着契约把没说清的地方全说清了。这可能就是第六种语言最大的价值。


参考资料


《同一份 15 个流程 JSON,第六种语言也跑通了:工作流引擎 Rust 移植实录》 是转载文章,点击查看原文。


相关推荐


OpenTelemetry Java 扩展:无需分叉 agent 即可自定义追踪
Elasticsearch2026/8/25

作者:来自 Elastic Sylvain Juge 一个 JAR 文件,在 OpenTelemetry Java agent 启动时加载,就可以过滤健康检查、重命名 span、添加资源属性以及控制采样,而无需修改应用程序代码。 你刚刚为一个 Java 应用设置了自动插桩。无需修改任何代码,追踪数据就开始流向你的可观测性平台。几分钟后,你发现健康检查端点正在大量充斥你的追踪视图,而且事务名称反映的是通用的框架模式,而不是你的业务领域操作。 分叉 agent 可以解决这个问题,但这样一来,你就需


把 Agent 做成一家公司,真比通用提示词好用吗?
苏灿烤鱼2026/8/12

它卖的不是一个万能 Agent,而是一套可挑选、可安装的 AI 专业分工。 ⚡️ 30 秒速读:msitarzewski/agency-agents 以 143,086 星、今日 +971 登上 GitHub Trending #1,连续 3 天在榜,排名从 #3、#2 升到 #1。它把不同专业角色写成独立 Agent 文件,每个文件包含身份与性格、核心任务与工作流、带代码示例的技术交付物、成功指标和沟通风格;可用原生桌面应用或 Shell 脚本安装到 Claude Code、Cursor、


Vite 8.1 深度拆解:Rolldown 统一打包器如何终结前端构建的「双引擎时代」
NutShell Wang2026/8/2

2026 年 3 月 12 日,Vite 8.0 正式发布,将 Rolldown——一个用 Rust 编写的打包器——作为唯一打包引擎引入,取代了此前 esbuild(开发)+ Rollup(生产)的双引擎架构。这被官方称为「自 Vite 2 以来最重大的架构变更」。三个月后的 Vite 8.1(6 月 23 日发布)进一步推出了实验性打包开发模式,在 10,000 个 React 组件的测试中实现了约 15 倍的启动加速。与此同时,Rolldown 本身也在快速迭代——1.0 正式版于 5 月


线程栈与TLS和线程互斥
keyipatience2026/7/25

线程栈 主线程(进程 main 栈)特性: (1)来源:fork 复制父进程栈 (2)可动态自动扩容 (3)缺页容错特殊:允许访问未映射页、不一定直接段错误的栈 线程栈 mem = mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); 标志MAP_STACK:专门标记这块内存用作线程栈;默认固定 8MB 大小(一般够用),不支持动态扩容,空间用完直接栈溢出崩溃;属于进程虚拟地址里一块独


如何用 AI 协助解决陌生技术问题:拆解-分析-熟悉-解决四步法
dozenyaoyida2026/7/17

你接过陌生项目吗?那种打开 IDE,几千个文件铺开,光看目录名就头大,盯着屏幕两小时一行代码没写的感觉。 或者更常见的,线上突然报了个错,涉及一个你从没读过的模块,老板在群里 @ 你,你点开文件,密密麻麻的调用链,不知道从哪开始查。 我做了十年开发,最近两年重度用 AI 辅助。最大的体会是,面对陌生问题,卡住你的从来不是"难",是"乱"。你不知道从哪下手,不知道自己不知道什么,于是在原地打转。 下面这套方法我用了几十次,核心就四个字:拆解、分析、熟悉、解决。AI 在每个阶段扮演的角色不一样,你介


【从零开始大模型开发与微调:基于PyTorch与ChatGLM】(基于PyTorch卷积层的MNIST分类实战:从卷积直觉到高效卷积设计)
承渊政道2026/7/9

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》 《MySQL数据库学习》 《Python知识内容》 ✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介: 前面使用多层感知机完成了MNIST分类实战的演示.多层感知机是一种对目标数据进行整体分类的计算方法.虽然从演示效果来看,多层感知机可以较好地完成项目


开源「仓颉.Skill」2.0,你现在可以蒸馏任何视频!
AI袋鼠帝2026/7/1

大家好,我是袋鼠帝。 没想到cangjie-skill在4月开源,中间没怎么推,两个月还慢慢涨到了1.3K Star,有点出乎我的意料。 而且现在每天都还在增涨,感谢大家支持~ github.com/kangarookin… 说明大家对蒸馏书是有需求的(可以理解为人工智能拆书)。 也并不是像评论区一些人说的:“所有书AI都学过了,你这个是脱了裤子放屁。”那样不堪。 对一些大众非常熟悉的书,可能不太需要这个方式来蒸馏。但是有很多比较小众的书,AI不一定记得清楚,甚至还有很多新书是AI没有训练的。


AI Agent(六)- Dify 自定义工具实战 - 基于百度天气 API 搭建天气查询 Agent(天气智查助手)
BigDataMagician2026/6/22

文章目录 一、前言二、整体实现思路三、申请百度地图开放平台 AK1. 注册百度地图开放平台2. 登录百度地图开放平台3. 创建应用并获取AK4. 查看国内天气查询接口开发文档5. 接口测试 四、创建自定义工具1. OpenAPI 规范配置内容及说明1.1 OpenAPI 规范配置内容1.2 OpenAPI 规范配置说明 2. 配置OpenAPI 规范3. 工具测试 六、搭建天气智查助手Agent1. 创建Agent2. System Prompt(系统提示词)3. 调用工具4.


MyBatis魔法堂:结果集映射
独泪了无痕2026/6/14

一、ResultMap 的定义   在当今的软件开发领域,MyBatis 作为一款优秀的持久层框架,以其简洁的配置和强大的功能,深受广大开发者的喜爱。然而,在实际的项目开发中,我们常常会遇到数据模型与数据库表结构不一致的情况,这时就需要 MyBatis 的 resultMap 功能来帮助我们实现复杂的映射关系。想象一下,一个典型的业务场景:一个电商系统中的订单表,其字段包括订单ID、用户ID、商品ID、订单金额等。然而,在业务逻辑层,我们可能需要将订单信息与对应的用户信息和商品信息结合起来,以便


不用 Mac 也可以 Windows下管理iOS描述文件的非Xcode完整指南
程序员不说人话2026/6/7

很多开发者第一次接触 iOS 描述文件(Provisioning Profile)时,看到的教程基本都围绕 Xcode 和钥匙串。 但实际开发里,有一类项目并不是在 Mac 上完成的、uni-app、Flutter、React Native、HBuilderX 云打包、Windows 开发环境,这时问题会变成.mobileprovision 文件到底怎么管理? 尤其项目一多之后,开发者会开始遇到 描述文件和证书不匹配、Bundle ID 混乱、测试设备漏加、文件过期后无法安装、不同电脑之间无法同

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读