任务编排为什么要放后端:让流程可控、可变、可回放

任务编排为什么要放后端:让流程可控、可变、可回放 副标题 / 摘要 在多步骤、可中断、可回放的业务流程中,把“流程顺序与状态机”放在后端,是系统长期可演进的关键。本文从真实工程痛点出发,解释为什么前端不应硬编码流程顺序,并给出一套可落地的后端 Pipeline 编排思路与最小实现。 目标读者 正在设计多步骤流程 / 向导式产品的后端工程师 需要支撑 Web / App / Admin 多端一致流程的技术负责人 在 AI / LLM 产品中处理“模型自动 + 人工确认”混合流程的团队 背景 / 动机:问题通常是怎么爆出来的? 很多系统一开始都很简单: 前端:第 1 步 → 第 2 步 → 第 3 步 后端:校验 + 存数据 但随着业务演进,以下需求几乎一定会出现: 步骤 变多:从 3 步变成 10+ 步 步骤 可选:根据条件跳过 / 插入新步骤 步骤 可中断:需要用户确认、补充信息、人工审核 步骤 可重试 / 可回放:失败后从中间继续,而不是全部重来 步骤 多端一致:Web / App / 内部工具共享同一流程 如果此时流程顺序仍然写在前端: 每次流程变更 = 多端发版 出问题时无法准确回答:现在卡在哪一步? 想加监控、审计、回放,发现无从下手 根因只有一个: ...

2026年1月13日 · 2 分钟 · map[name:Jeanphilo]

算法与业务的关系:把不确定性变成可交付(以 LLM 事实抽取为例)

摘要 很多团队做 AI 应用时会陷入一种痛苦:业务逻辑写了一堆,却始终看不到“算法到底抽出了什么”。根因往往不是代码能力,而是把算法阶段的探索,过早塞进业务工程。本文用“投标写作工具里的事实抽取(FactMention)”作为例子,讲清楚算法与业务的边界、如何用 JupyterLab 快速验证、以及何时进入工程化。 目标读者 正在做 LLM/RAG/信息抽取的工程师(初级到中级) 负责 AI 产品落地的技术负责人 / 架构师 经常在“写了半天流程,结果不知道抽取效果如何”的同学 背景与动机:为什么这个问题重要? 在传统系统里,大家习惯把“算法”理解为一个函数或模型文件,把“业务”理解为接口与流程。但在 LLM 时代,这个界限变得更模糊: 算法不仅是模型,还包括 prompt、schema、抽取策略、规则归一、置信度与去重策略 算法输出往往是 不确定、需要人类直觉评估 的 如果你把这些“不确定”的东西直接嵌进业务链路(router/service/db/cache),你会遇到: 调试成本爆炸:只看到最后 response,不知道中间发生了什么 逻辑迭代极慢:改一行抽取策略,要跑完整流程 团队协作困难:大家在黑箱里争论“到底准不准” 因此,需要一个更清晰的边界:算法负责收敛中间态,业务负责稳定交付。 核心概念:算法与业务到底分别是什么? 1)算法(Algorithm)的工程定义 算法负责把“模糊世界”压缩成“可用的结构化中间态”。 关键词:不确定性、探索、需要“看结果”、需要收敛。 在投标写作场景中,算法阶段的问题长这样: LLM 抽取出来的 payload 字段应该有哪些? norm_key 怎么设计才代表“同一事实”? 同一人多条命中(mentions)要不要合并? confidence 到底有没有意义?怎么校准? 这些问题的共同特点是:你必须看中间结果才能判断对不对。 2)业务(Business)的工程定义 业务负责在中间态稳定之后,把事情编排起来:什么时候取什么数据、走哪条链路、如何返回给用户。 关键词:确定性、可维护、可测试、可复用。 在投标写作场景中,业务阶段的问题长这样: retrive_type = personnel 时走事实检索,否则走原文档检索 接口响应结构固定,前端按协议渲染 存储从 MemoryTable 换成 DB,不影响上层调用 这些问题的共同特点是:输入输出清晰,错误是边界情况,而不是“我也不知道会不会抽出来”。 一条“贴墙上”的分界线 凡是你还说不清“中间结果长什么样”的阶段 → 用 JupyterLab。 凡是你能画出输入/输出 JSON 形态并写出测试用例的阶段 → 进工程。 实践指南:什么时候用 JupyterLab,什么时候用工程代码? 阶段 1:事实抽取建模期(强制用 JupyterLab) 适用信号: ...

2026年1月7日 · 3 分钟 · map[name:Jeanphilo]

size_t 有什么用?为什么 C++ 循环更偏爱 size_t 而不是 int

size_t 有什么用?为什么 C++ 循环更偏爱 size_t 而不是 int 副标题 / 摘要 当你写 for 循环遍历容器时,size_t 往往比 int 更安全、更贴合语义。本文用 ACERS 结构讲清楚 size_t 的定义、使用理由、风险点与工程实践,适合写 C++ 的你快速落地。 元信息 阅读时长:8-10 分钟 标签:C++,size_t,类型系统,循环,STL SEO 关键词:size_t 用途,size_t 和 int 区别,C++ 循环初始化,size_t 下溢 元描述:解释 size_t 的定义与用途,说明为什么循环索引常用 size_t,并给出安全写法与工程场景。 目标读者 C++ 初学者:对 size_t、sizeof、容器 size() 的返回类型不熟悉 中级工程师:遇到过 -Wsign-compare 警告或下溢 bug 需要写跨平台/高性能 C++ 代码的人 背景 / 动机 在 C++ 代码里,你经常能看到这样的循环: for (size_t i = 0; i < vec.size(); ++i) { ... } 不少人疑惑: 为什么不用更“直观”的 int? size_t 到底是什么?为什么是无符号? 什么时候会踩坑? 这一篇把这些问题一次讲清楚。 ...

2025年12月30日 · 4 分钟 · map[name:Jeanphilo]

数据结构基础:好数对计数(Number of Good Pairs)哈希统计 ACERS 解析(LeetCode 1512)

副标题 / 摘要 这是“数据结构基础”系列第 2 题:好数对计数。通过“频次统计 + 组合计数”,把 O(n^2) 直接降到 O(n),并给出可直接迁移到工程的实现方式。 预计阅读时长:8~10 分钟 标签:哈希表、计数、数组 SEO 关键词:Good Pairs, 好数对, hash map, frequency, 计数 元描述:好数对计数的哈希表解法与工程化应用,含复杂度分析与多语言代码。 目标读者 刚开始学习哈希表与计数思想的初学者 希望把刷题方法迁移到业务统计的中级工程师 准备面试,想掌握基础计数模型的同学 背景 / 动机 “找出相同元素的两两组合数量”是一个常见的计数类问题。 在数据去重、行为分析、错误归因等场景里,这类问题通常会被反复遇到。 若用双重循环计算,复杂度是 O(n^2);一旦数据规模扩大就会变慢。 因此需要一个可线性扩展的方案。 A — Algorithm(题目与算法) 题目还原 给你一个整数数组 nums。 如果一组数字 (i, j) 满足 nums[i] == nums[j] 且 i < j,就称为一个 好数对。 返回好数对的数目。 输入输出 名称 类型 描述 nums int[] 整数数组 返回 int 好数对数量 基础示例 nums 输出 说明 [1, 2, 3, 1, 1, 3] 4 (0,3) (0,4) (3,4) (2,5) [1, 1, 1, 1] 6 C(4,2) = 6 [1, 2, 3] 0 无重复 直观图示(示例 1) ...

2025年12月30日 · 5 分钟 · map[name:Jeanphilo]

Hot100:Two Sum 两数之和哈希表一遍扫描与 ACERS 工程化解析(LeetCode 1)

副标题 / 摘要 Two Sum(两数之和)是最经典的数组哈希题:用“补数 + 哈希表”把 O(n^2) 降到 O(n)。本文按 ACERS 结构拆解题意、原理与工程迁移,并给出多语言可运行实现。 预计阅读时长:10~12 分钟 标签:Hot100、哈希表、数组、补数、面试高频 SEO 关键词:Two Sum, 两数之和, hash map, 补数, O(n), LeetCode 1, Hot100 元描述:两数之和的哈希表解法与工程应用解析,含复杂度对比与多语言代码。 目标读者 刚开始刷题,希望建立“补数 + 哈希表”基本模型的初学者 需要把算法思路迁移到工程问题的中级开发者 准备面试、想快速掌握高频题的求职者 背景 / 动机 “在一堆数字里找出两数之和”等价于一个快速配对问题,常见于对账、预算、风控、推荐等场景。 朴素暴力法虽然简单,但在数据量上来后会直接超时;哈希表一遍扫描能把复杂度从 O(n^2) 降到 O(n),是最工程可行的做法之一。 A — Algorithm(题目与算法) 题目还原 给定一个整数数组 nums 和一个整数目标值 target,请在该数组中找出和为目标值的 两个 整数,并返回它们的数组下标。 每种输入只会对应一个答案,并且你不能使用两次相同的元素。答案可以按任意顺序返回。 输入输出 名称 类型 描述 nums int[] 整数数组 target int 目标和 返回 int[] 满足 nums[i] + nums[j] == target 的下标 基础示例 nums target 输出 [2, 7, 11, 15] 9 [0, 1] [3, 2, 4] 6 [1, 2] 补数图示(示例 1) ...

2025年12月28日 · 5 分钟 · map[name:Jeanphilo]

XOR 与 RC5:从原理到 Go 实战(含安全替代建议)

XOR 与 RC4:从原理到 Go 实战(含安全替代建议) 副标题 / 摘要 用最少的数学解释 XOR 与 RC4 的工作机制,给出可运行的 Go 示例,并说明 RC4 的安全问题与替代方案。 目标读者 想读懂遗留 RC4 代码的后端工程师 想区分“编码”与“加密”的初学者 需要建立流密码心智模型的中级开发者 背景 / 动机 很多系统仍遗留 RC4 或“自研解密”的逻辑。常见误区是把 Base64 当作加密,或忽视“完整性校验”。理解 XOR 与 RC4,有助于正确评估安全性,并避免把旧方案复制到新系统。 核心概念 XOR(异或):按位运算,可逆 流密码:用伪随机密钥流与明文逐字节 XOR RC4:经典流密码,但已不推荐 Base64:编码,不是加密 完整性:仅加密不等于防篡改 实践指南 / 步骤 接收 Base64 字符串(通常是 RC4 输出) Base64 解码得到原始字节 用共享密钥初始化 RC4 将密钥流与字节逐字节 XOR 把输出按 UTF-8 转为字符串(若是文本) 可运行示例(Go) package main import ( "crypto/rc4" "encoding/base64" "fmt" ) func rc4XOR(key string, data []byte) ([]byte, error) { c, err := rc4.NewCipher([]byte(key)) if err != nil { return nil, err } out := make([]byte, len(data)) c.XORKeyStream(out, data) return out, nil } func encryptToBase64RC4(key, plaintext string) (string, error) { out, err := rc4XOR(key, []byte(plaintext)) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(out), nil } func decryptBase64RC4(key, encoded string) (string, error) { raw, err := base64.StdEncoding.DecodeString(encoded) if err != nil { return "", err } out, err := rc4XOR(key, raw) if err != nil { return "", err } return string(out), nil } func main() { key := "demo-key" plaintext := "hello rc4" enc, _ := encryptToBase64RC4(key, plaintext) dec, _ := decryptBase64RC4(key, enc) fmt.Println(enc) fmt.Println(dec) } 运行: ...

2025年12月16日 · 2 分钟 · map[name:Jeanphilo]

Python 抽象基类 ABC vs ABCMeta:什么时候用哪个?

副标题 / 摘要 ABC 用来“写抽象接口并阻止未实现的类被实例化”;ABCMeta 用来“在创建类时施加规则(自动注入、校验、注册)”。本文用最短可运行例子帮你在两者之间做选择。 目标读者 Python 初学者:了解抽象类怎么用、为啥会报 TypeError 中级开发者:在“接口约束”和“元类自动化”之间做取舍 需要做插件/框架能力的人:统一约束子类结构、自动补齐类级属性 背景 / 动机 你可能遇到过这些痛点: 想规定“子类必须实现某些方法”,但团队里总有人忘写 想让一批子类都有统一的类属性(比如 plugin_name),不想每个子类手写一遍 看到别人写 metaclass=ABCMeta,不确定是不是“更高级/更正确” 结论先说:大多数业务代码只需要 ABC;只有当你真的需要“类创建期的自动化规则”时,才考虑直接使用 ABCMeta(或在它上面做扩展)。 核心概念 1)抽象方法(@abstractmethod) 被标记为抽象的方法/属性,表示“必须由子类提供实现”。只要类里还有抽象成员未实现,它就不能被实例化。 2)抽象基类(ABC, Abstract Base Class) 用于定义一组接口约束:能继承、能被 isinstance/issubclass 判断,并能阻止不完整实现的类被实例化。 3)元类(metaclass) 普通类的“类”是 type;元类决定“类是怎么被创建出来的”。你可以在元类里: 在类创建时自动添加/修改类属性 校验子类是否符合规则(命名、属性、方法签名等) 统一注册子类到某个 registry ABCMeta 就是 abc 模块提供的元类:它把“抽象基类能力”实现为一套类创建/实例化规则。 实践指南 / 步骤 步骤 1:只需要“接口约束”——用 ABC 如果你只关心“子类必须实现哪些方法”,直接继承 ABC 是最简洁的写法。 步骤 2:需要“类创建期自动化规则”——用(或继承)ABCMeta 当你希望“子类不用手写,也能按规则自动拥有某些类属性/被校验/被注册”,再考虑元类。 可运行示例 示例 A:用 ABC 做接口约束(推荐默认选项) from abc import ABC, abstractmethod class Repo(ABC): @abstractmethod def save(self, obj) -> None: ... class MemoryRepo(Repo): def save(self, obj) -> None: print("saved:", obj) # Repo() # 取消注释会抛 TypeError:抽象类不能实例化 MemoryRepo().save({"id": 1}) 你得到的是:强约束(没实现抽象方法就不能实例化),且写法清晰。 ...

2025年12月12日 · 2 分钟 · map[name:Jeanphilo]

从参数直传到Pipeline: 一次可复现可观测的数据处理管线改造实践

从参数直传到 Pipeline:一次可复现、可观测的数据处理管线改造实践 副标题: 为什么当处理链变得越来越长时,“配置驱动 + 上下文 + 外部存储”的 Pipeline 模式会比“参数直传”更适合工作? 标签: Python / Pipeline / ETL / 数据工程 / 架构设计 适读人群: 后端开发、数据工程师、做文档处理/Embedding/索引构建的同学 阅读时间: 10–15 min 摘要: 本文记录我从“领域模型传来传去”的后端式写法,迁移到“配置驱动 Pipeline”模式的过程,总结落地要点、踩过的坑,以及为什么这种模式更适合复杂的数据加工链。 🧭 写这篇文章的动机 做后端时,我长期习惯一种简单粗暴的风格: 需要什么参数就一直往下传,函数链一路 call 下去。 很多业务都是这么写的: 输入是个模型/DTO 处理完传下一个函数 大对象在链路里飘来飘去 但当我开始做 文档处理、Embedding、实体提取、索引构建 这种“多步骤、可重跑、需观测”的任务时,这种写法很快失效: 需要重跑某一步时必须重建整条调用链 中间产物无法落地检查 改一个策略需要改一堆函数签名 并发 / 异常恢复都难处理 后来接触到构建数据处理 Workflow / ETL Pipeline 的方式,发现它的核心思路完全不一样: 配置驱动策略 → 上下文承载运行态 → 外部存储承载数据流。 这套体系让多步处理链突然变得可插拔、能恢复、能观测、能重放。 于是就有了这篇文章,把我的心智迁移过程与实践要点记录下来。 ⚡ TL;DR(你只看这一屏也能理解本文核心) 配置驱动: 路径、模型、超参都写 config,而不是塞进函数参数里。 上下文 context: 统一管理 I/O 句柄、缓存、回调、统计、运行时标志。 外部存储: 步骤间不传大对象,读写约定表名:documents → text_units → entities → index。 可插拔 Pipeline: “步骤名 → 函数指针”的顺序列表,可一键切换 Standard/Fast 等方案。 幂等与恢复: 中间表持久化,可覆盖或版本化,崩溃后能断点续跑。 观测与回调: start/end/进度统一上报,产出 stats.json,定位问题更快。 异步友好: 步骤 await 执行,内部可分片并发或调用 LLM。 取舍: 成本是心智负担增加;收益是可观察、可重跑、可替换、低耦合。 👥 目标读者 适合以下同学阅读: ...

2025年12月10日 · 3 分钟 · map[name:Jeanphilo]

Pydantic vs dataclass vs TypedDict:谁负责什么,怎么组合?

标题 Pydantic vs dataclass vs TypedDict:谁负责什么,怎么组合? 副标题 / 摘要 承接《别让 Pydantic 占领你的整个项目》,这一篇用对比视角把 Pydantic、dataclass、TypedDict 的定位、取舍和组合方式讲清楚:谁用于 API 校验、谁承载业务状态、谁只做类型提示。 目标读者 FastAPI / Pydantic 用户,想搞清楚“数据类”该放在哪一层 有 0–5 年经验、在做服务端建模的 Python 工程师 已读过前一篇分层文章,想进一步对比具体工具 背景 / 动机:为什么要区分三者? 在上一篇里,我们强调“Pydantic 应该停留在 API/外围”。很多同学随后会问: “那 Python 原生 dataclass 呢?和 Pydantic 有什么差?” “TypedDict 是不是又一个‘数据类’,要不要取代 dataclass?” “什么时候该用 Pydantic dataclasses,什么时候用标准库?” 不区分清楚,常见后果有: 用 TypedDict 写业务逻辑,测试时才发现它根本不做运行时校验; 用 Pydantic BaseModel 传来传去,导致 Domain 强绑定外部依赖; dataclass 和 Pydantic 混用,序列化和校验边界越来越模糊。 核心概念:一句话定位 Pydantic BaseModel:运行时校验 + 类型转换 + JSON 友好;属于“对外/边界”。 dataclass(标准库):轻量数据载体,可承载业务方法;不做自动校验,属于“领域/内部”。 TypedDict:仅提供静态类型提示,运行时就是普通 dict;属于“静态约束/外部协议”。 主要差异: ...

2025年12月9日 · 3 分钟 · map[name:Jeanphilo]

排序专题(终篇):选型实战——按规模、稳定性、内存与分布选择排序算法

以实战视角整理排序选型:给出规模/分布/稳定性/内存维度的决策表、工程场景示例、测试清单与常见坑,快速落地前七篇内容。

2025年12月9日 · 2 分钟 · map[name:Jeanphilo]