从插件系统到微内核平台:一篇从入门到进阶的 NocoBase 架构笔记
如果你第一次听到“微内核架构”,可以先把它理解成一句话:
核心系统只保留最稳定、最通用的能力,具体业务能力都通过插件接进来。
这和我们平时用的很多软件很像。
浏览器本身只提供浏览网页、管理标签页、运行扩展的基础能力。广告拦截、翻译、密码管理、截图工具,都是扩展插件提供的。
VS Code 本身是一个编辑器内核。Python、Go、Java、Markdown、GitLens、Docker、数据库管理,这些能力大多来自插件。
平台型后端也可以用类似思路设计。核心不直接写死 CRM、审批、报表、文件、工作流、AI 调用,而是提供一套稳定的注册和运行机制,让这些能力以插件形式接入。
NocoBase 是一个很好的参考对象。它采用微内核架构,核心负责插件生命周期、依赖管理和基础能力封装,业务功能由插件提供。服务端插件可以注册数据表、资源接口、权限、事件、定时任务、日志、Telemetry 和迁移;客户端插件可以注册路由、页面、区块、字段和按钮。
本文不是复刻 NocoBase 源码,也不是贴一大段 Python 教学代码,而是把它背后的架构思路讲成一个从入门到进阶的路径。
读完这篇,你应该能回答三个问题:
1. 插件系统和微内核到底解决什么问题?
2. 为什么一个平台不能只有 PluginManager?
3. 从入门 demo 到生产平台,中间还差哪些工程能力?
先看问题
假设我们一开始写的是一个普通业务系统。需求来了,我们就在代码里加功能:
新增客户表
新增订单表
新增订单接口
新增订单页面
新增审批逻辑
新增导出按钮
新增权限判断
新增定时任务
早期这样写没有问题,因为系统小,业务也清楚。
但当它变成一个平台以后,情况会变复杂:
客户模块可能要被替换;
订单模块可能由不同团队维护;
审批、通知、导入导出希望复用给不同业务;
每个租户可能启用不同功能;
有些功能需要单独安装、禁用、升级;
前端页面、表单、字段和按钮也要能扩展;
权限、审计、日志、指标不能每个模块各写一套。
这时继续往核心系统里塞业务代码,核心会越来越大,也越来越难改。
微内核架构的目标就是把这个问题反过来:
核心不要知道太多业务。
核心只提供规则和注册点。
业务能力通过插件注册进来。
所以,微内核不是为了“少写代码”,而是为了让系统长期演进时不失控。
入门:最小插件系统长什么样
最小的插件系统可以很简单:
Kernel
├─ PluginManager
├─ HookBus
└─ ServiceRegistry
其中:
PluginManager 负责加载、启用、禁用插件
HookBus 负责让插件监听某些事件
ServiceRegistry 负责让插件注册可调用能力
一个非常简化的插件可能长这样:
class ArticlePlugin:
name = "article"
def load(self, app):
app.services.register("article.publish", self.publish)
def publish(self, article_id):
...
应用启动时:
app.plugins.add(ArticlePlugin())
app.plugins.enable_all()
这已经具备插件系统的雏形:
核心不直接写 article.publish;
ArticlePlugin 自己把 publish 能力注册进系统;
其他地方通过 service registry 调用它。
这一步适合入门理解,但还不是完整平台。
入门:为什么只有 PluginManager 不够
如果插件只负责注册几个函数,PluginManager 就够了。
但真实平台里的插件通常要做更多事情:
定义数据表;
扩展已有数据表;
注册 API;
配置权限;
监听数据变化;
注册后台任务;
注册前端页面;
注册表单字段;
保存插件配置;
升级数据库结构;
输出日志、指标和 trace。
如果没有统一机制,每个插件就会自己想办法:
插件 A 自己建表;
插件 B 自己写路由;
插件 C 自己判断权限;
插件 D 自己开线程跑定时任务;
插件 E 自己写日志格式;
插件 F 自己处理升级脚本。
最后的问题是:核心看起来变小了,但系统并没有变简单,只是复杂度被分散到了插件里。
完整微内核需要的不只是“能加载插件”,而是“所有重要能力都有受控入口”。
进阶:平台需要哪些注册表
可以把一个平台内核想象成一组注册表。
插件不能随便改系统内部状态,而是通过这些注册表把能力挂进来。
| 注册表 | 解决的问题 | 插件通常注册什么 |
|---|---|---|
| PluginManager | 插件怎么加载、排序、启停 | 插件本身、依赖关系、生命周期 |
| CollectionManager | 数据模型怎么扩展 | 表、字段、关联、默认值、校验 |
| ResourceManager | API 能力怎么暴露 |
resource:action、CRUD、自定义操作 |
| ACL | 权限怎么统一判断 | 角色权限、公开接口、数据范围 |
| EventBus | 副作用怎么解耦 | 事件监听器、数据变化监听器 |
| CronJobManager | 后台任务怎么托管 | 定时任务、清理任务、同步任务 |
| FrontendRegistry | 前端怎么扩展 | 路由、页面、区块、字段、按钮 |
| MigrationManager | 版本升级怎么处理 | 表结构变更、数据迁移、配置迁移 |
| Logger / Telemetry | 插件怎么被观测 | 日志、指标、链路追踪 |
这张表是理解微内核平台的关键。
一个插件不应该只是“一段被 import 的代码”。它更像一个完整扩展包:
Plugin = 后端能力 + 前端能力 + 数据模型 + 权限 + 配置 + 生命周期 + 迁移
NocoBase 的插件体系就是这个方向:不是只做后端模块,而是把服务端和客户端能力都纳入插件化运行时。
进阶:一条请求如何串起来
先看一个具体例子。
假设有一个 ArticlePlugin,它提供文章管理能力。它做了几件事:
1. 注册 articles 数据模型
2. 注册 articles 的 CRUD 操作
3. 注册 articles:publish 自定义操作
4. 配置谁可以发布文章
5. 发布后触发审计和搜索索引更新
用户点击“发布文章”按钮后,请求可能是:
/api/articles:publish
它在平台里的路径是:
前端按钮
↓
调用 /api/articles:publish
↓
ResourceManager 解析 resource=articles, action=publish
↓
ACL 判断当前用户能不能执行 articles:publish
↓
ArticlePlugin.publish 执行业务逻辑
↓
Database 更新文章状态
↓
EventBus 发出 articles.updated 事件
↓
AuditPlugin 记录审计
↓
SearchPlugin 刷新索引
↓
Telemetry 记录耗时、成功次数、trace
↓
返回结果
这条链路里,文章业务只是一个插件。权限、事件、审计、指标、错误处理都不应该散落在 ArticlePlugin 里,而应该经过平台统一入口。
这就是微内核平台和普通业务系统的差别。
普通业务系统关心:
怎么写一个发布文章接口?
平台系统关心:
插件如何声明 articles:publish?
这个 action 如何统一进入权限、日志、审计和指标?
其他插件如何观察这次发布?
禁用插件时这些注册项如何撤销?
升级插件时数据结构如何迁移?
问题一变,架构就变了。
进阶:Collection 先行
平台型系统里,数据模型应该先于页面和接口。
NocoBase 里有一个核心概念叫 Collection。你可以把它理解成“带元信息的数据表定义”。
它不只是表名,还包括:
字段名
字段类型
显示标题
默认值
是否必填
验证规则
关联关系
前端控件建议
比如文章插件可以定义:
app.collections.define(
name="articles",
title="Articles",
fields=[
Field("title", "string", required=True),
Field("content", "text"),
Field("status", "string", default="draft"),
],
)
这个定义不是只给数据库看的。它后面还可以推导出很多东西:
CRUD API
列表字段
表单字段
权限资源
审计对象
导入导出格式
搜索索引
工作流触发条件
很多系统会反过来做:
先写页面;
再补接口;
再补权限;
最后发现数据结构不够用。
平台系统更稳的顺序是:
先定义 Collection
再生成或注册 Resource
再绑定 ACL
最后让前端围绕 Collection 和 Resource 生成页面、表单和操作
这就是“数据模型驱动”。
如果平台支持动态建模,还要注意 SQL 安全。值可以参数绑定,但表名、字段名不能作为普通参数传入,必须白名单校验:
IDENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
def safe_ident(name: str) -> str:
if not IDENT_RE.match(name):
raise ValueError(f"unsafe SQL identifier: {name!r}")
return name
动态建模越强,越要把字段名、排序、过滤、关联查询这些边界做清楚。
进阶:Resource Action 统一 API
如果每个插件都随便写路由,系统很快会变乱:
/api/article/create
/api/post/update
/api/v1/content/remove
/api/custom/publish
命名不统一,权限不统一,审计不统一,日志和指标也不统一。
NocoBase 风格的 ResourceManager 更推荐把能力抽象成:
resource:action
例如:
articles:list
articles:get
articles:create
articles:update
articles:destroy
articles:publish
orders:salesByStatus
approval:submit
app:getInfo
这样 CRUD、图表聚合、审批提交、文件上传、导入导出,本质上都可以走同一种调用协议。
插件注册自定义 action:
app.resources.register_action(
"articles",
"publish",
publish_article,
)
调用时:
app.resources.call(ctx, "articles:publish", id=1)
ResourceManager 负责统一做这些事:
解析 resource/action
检查 ACL
注入用户上下文和数据范围
找到默认 CRUD 或自定义 handler
记录日志和指标
触发 before/after 事件
包装响应和错误
这就是平台化的价值。业务插件只关心“我要提供什么 action”,治理逻辑由统一入口处理。
进阶:ACL 不只是隐藏按钮
很多系统把权限理解成“前端按钮显不显示”。
这只是体验,不是安全。
真正的权限必须在后端统一执行,而且最好能进入查询层。
最基本的权限可以这样描述:
app.acl.grant("admin", "articles", "*")
app.acl.allow("articles", "list", "logged_in")
app.acl.allow("articles", "get", "logged_in")
含义是:
admin 可以操作 articles 的全部 action;
登录用户可以查看文章列表和详情;
其他操作默认不允许。
ResourceManager 在执行 action 前统一检查:
if not app.acl.can(ctx, resource, action):
raise PermissionError(...)
进阶一点,还要支持数据范围。
比如销售只能看自己的订单:
orders:list
role = sales
data_scope = owner_id = current_user.id
请求链路应该是:
ResourceManager
↓
ACL 检查通过
↓
注入 data_scope
↓
CollectionManager 合并 filter
↓
生成带 WHERE 的查询
所以权限不应该散落在每个业务函数里,也不应该只停留在 UI 层。它应该绑定到 resource:action,并在统一入口执行。
进阶:事件用于解耦副作用
事件适合处理副作用。
比如:
记录审计日志
发送通知
刷新缓存
同步搜索索引
触发工作流
上报指标
同步外部系统
文章发布时,ArticlePlugin 不应该手动调用所有插件:
audit.record(...)
search.reindex(...)
notify.send(...)
workflow.trigger(...)
这样会让 ArticlePlugin 知道太多其他插件。
更好的方式是发事件:
app.events.emit("articles.published", article=article)
其他插件自己监听:
app.events.on("articles.published", audit_listener)
app.events.on("articles.published", search_listener)
app.events.on("articles.published", notification_listener)
事件通常分两类:
app.events 应用级事件:启动、停止、插件加载、资源调用
db.events 数据库级事件:创建、更新、删除、模型定义
事件适合副作用,但不适合承载主流程里的强依赖逻辑。
如果“发布文章必须先扣库存”这种逻辑是主流程的一部分,就应该显式调用,不应该藏在事件监听器里。事件监听器失败以后要不要回滚主流程,也必须提前定义清楚。
生产系统里,事件监听还要能注销:
handle = app.events.on("articles.published", listener)
handle.close()
否则插件禁用以后监听器还在运行,就是资源泄漏。
进阶:后台任务要由内核托管
插件不要自己偷偷开线程、定时器或消费者。
错误示例:
threading.Thread(target=self.loop).start()
这样做的问题是:
内核不知道任务存在;
禁用插件时停不掉;
异常没人收集;
日志和指标没有统一记录;
多实例部署时可能重复执行;
资源泄漏难排查。
更好的方式是通过 CronJobManager:
app.cron.add_job(
name="article.daily_report",
cron="0 0 * * *",
handler=daily_report,
)
禁用插件时:
app.cron.remove_job("article.daily_report")
生产里的任务管理还要考虑:
分布式锁
任务超时
失败重试
任务日志
任务指标
并发控制
禁用开关
补偿策略
任务托管的核心不是“帮你跑 cron”,而是让后台行为纳入平台治理。
进阶:前端也要插件化
很多团队一开始只做后端插件。后来发现菜单、页面、字段、表单、按钮、设置页也都要扩展,于是前端到处打补丁。
平台系统更合理的设计是:插件是全栈扩展单元。
Plugin = Backend Extension + Frontend Extension + Metadata + Lifecycle + Permissions + Configuration
后端插件注册:
数据模型
资源操作
权限
事件
任务
迁移
前端插件注册:
路由
菜单
页面
区块
字段控件
操作按钮
设置页面
一个插件目录可以长这样:
article-plugin/
├─ plugin.json
├─ server/
│ ├─ plugin.py
│ ├─ collections.py
│ ├─ resources.py
│ └─ migrations/
└─ client/
├─ routes.tsx
├─ blocks.tsx
├─ fields.tsx
└─ settings.tsx
这样,ArticlePlugin 不只是“后端多一个接口”,而是可以完整带来文章表、文章接口、文章权限、文章页面、文章表单、文章按钮和文章设置页。
这才是平台插件。
高阶:生命周期不能太粗
入门版插件系统可能只有:
activate()
deactivate()
真实平台不够用。
参考 NocoBase 的插件生命周期,一个更完整的拆分是:
static_import
↓
after_add
↓
before_load
↓
load
↓
install 只在首次启用时执行
↓
after_enable
↓
running
↓
after_disable
↓
remove
可以用更通俗的话理解:
| 阶段 | 什么时候发生 | 适合做什么 |
|---|---|---|
| static_import | 类级初始化 | 注册全局类型、加载静态描述 |
| after_add | 插件加入管理器后 | 准备轻量状态、读取基本配置 |
| before_load | 所有插件 load 前 | 定义 Collection、注册中间件、监听事件 |
| load | 插件加载时 | 注册资源、API、权限、前端扩展 |
| install | 第一次启用时 | 初始化数据、默认配置、默认权限 |
| after_enable | 每次启用后 | 启动任务、建立连接、预热缓存 |
| after_disable | 禁用时 | 停止任务、关闭连接、注销监听 |
| remove | 删除时 | 清理插件私有资源 |
这里最容易混淆的是 install 和 migration。
install() 只处理首次启用。插件升级以后要改表结构、修数据、补配置,应该走 MigrationManager,而不是重复执行 install。
另一个关键点是 before_load 和 load 的分工。
通常:
before_load 先注册模型和底层准备工作
load 再注册 API、权限、页面、任务等能力
这样可以减少“插件 A 的 API 依赖插件 B 的 Collection,但 B 还没注册”的问题。
高阶:四个平面看平台内核
到这里,我们可以把微内核平台分成四个平面。
第一是 Runtime Plane,负责插件怎么运行:
PluginManager
DependencyResolver
LifecycleRunner
PluginStateStore
PackageManager
它处理插件发现、依赖排序、启用、禁用、卸载、状态持久化和版本约束。
第二是 Extension Plane,负责插件怎么提供业务能力:
CollectionManager
ResourceManager
ServiceRegistry
EventBus
WorkflowEngine
FrontendRegistry
它决定插件可以向系统注册哪些能力。
第三是 Governance Plane,负责插件怎么被治理:
ACL
Logger
Telemetry
Audit
RateLimiter
CircuitBreaker
HealthCheck
插件越多,治理越重要。没有统一治理面,平台只能依赖插件自觉。
第四是 Configuration Plane,负责插件配置、开关、密钥和升级:
ConfigManager
FeatureFlag
SecretManager
VersionManager
MigrationManager
这四个平面合起来,才是平台内核,而不是一个孤立的 PluginManager。
高阶:日志和 Telemetry 是一等公民
平台内核不能只关心插件怎么运行,还要关心插件怎么被观察。
日志至少要带上:
plugin_name
resource
action
user_id
request_id
trace_id
duration
error_type
指标至少应该覆盖:
plugin_enable_total
plugin_enable_failed_total
plugin_load_duration_seconds
resource_call_total
resource_call_failed_total
resource_call_duration_seconds
cron_job_run_total
cron_job_failed_total
db_operation_total
db_operation_duration_seconds
Trace 解决的是链路问题。一次请求可能经过:
API request
-> ResourceManager
-> ACL
-> ArticlePlugin.publish
-> Database.update
-> db.after_update event
-> AuditPlugin
-> SearchIndexPlugin
没有 trace_id,这些日志会散在各处。
NocoBase 的 Telemetry 模块基于 OpenTelemetry 思路提供 Trace 和 Metric 能力。自己实现平台时,即使先做简化版,也应该优先覆盖这些统一入口:
ResourceManager
PluginManager
CollectionManager / Database
CronJobManager
EventBus
因为统一入口观测一次,比每个插件各写一遍更可靠。
高阶:从教学版到生产版还差什么
如果只是学习,一个 Python demo 可以很小:
Application
PluginManager
CollectionManager
ResourceManager
ACL
EventBus
CronJobManager
这足够讲清楚概念。
但生产系统还要补很多工程能力。
第一,插件 manifest。
插件元信息不要只写在类里,应该有独立描述:
name: article
version: 1.0.0
apiVersion: 1
dependencies:
- audit >= 1.0.0
server: article_plugin.server:ArticlePlugin
client: article_plugin.client:index.js
permissions:
- articles:list
- articles:create
- articles:publish
第二,插件发现机制。
教学版可以手动注册:
app.pm.add(ArticlePlugin)
生产版要支持本地插件目录、包管理、远程插件市场或语言生态里的 entry points。
第三,版本约束。
依赖不只是插件名,还包括插件版本、内核 API 版本、语言版本、数据库版本和前端运行时版本。
第四,MigrationManager。
字段新增、字段重命名、类型变更、索引调整、配置迁移、数据补偿都应该走 migration,并记录每个插件每个 migration 是否执行过。
第五,事务边界。
文章发布成功后刷新搜索索引失败,要不要回滚发布状态?订单创建后工作流失败,要不要回滚订单?通知失败是否影响主流程?这些不能靠事件机制自动回答,必须由平台语义定义。
第六,禁用和删除。
禁用插件时要撤销事件监听、中间件、缓存、连接、消费者、定时任务、前端路由、权限片段和临时资源。删除插件时是否删除用户数据,更要谨慎。
第七,第三方插件隔离。
如果插件来自第三方或不可信,不建议直接运行在同一个进程里。进程内插件很难安全限制文件访问、网络访问、CPU、内存和死循环。更稳妥的是独立进程、容器、Worker Sandbox、HTTP/gRPC/IPC 调用、资源配额和签名校验。
第八,完整可观测性。
生产版应该把 Trace、Metric、Span、Exporter 和 Context propagation 做完整,至少覆盖资源调用、数据库操作、插件生命周期和后台任务。
常见误区
第一个误区是把插件系统等同于动态 import。
插件系统 = importlib.import_module(plugin_name)
这只是加载方式,不是架构。真正的插件系统必须有协议、生命周期、依赖管理、能力注册、权限控制、错误治理、日志监控和资源清理。
第二个误区是 Kernel 写太多业务逻辑。
如果 Kernel 里出现很多业务方法,边界通常已经坏了:
class Kernel:
def create_article(self): ...
def publish_article(self): ...
def export_excel(self): ...
def send_notification(self): ...
这些应该属于插件,而不是核心。
第三个误区是所有扩展都用 Hook。
Hook 适合事件通知,不适合表达所有扩展。
| 场景 | 推荐机制 |
|---|---|
| 多个插件监听某个事件 | Event / Hook |
| 插件提供一个可调用能力 | Service / Resource |
| 插件注册 API 操作 | ResourceManager |
| 插件定义数据结构 | CollectionManager |
| 插件控制权限 | ACL |
| 插件做后台任务 | CronJobManager |
第四个误区是权限散落在业务代码里。
权限应该统一挂在 resource:action 上,并进入后端查询层。
第五个误区是插件自己开线程、开消费者、开定时器。
后台任务必须由内核托管。
第六个误区是混用 install 和 migration。
首次安装和版本升级是两件事。
第七个误区是禁用插件但不清理资源。
插件禁用后还在监听事件、还在跑任务、还占用连接,就是资源泄漏。
从入门到进阶的落地路径
如果你想自己实现一个学习版,可以按四步走。
第一步,只做插件加载。
目标是理解:
什么是 Kernel
什么是 Plugin
什么是生命周期
插件如何注册一个函数
这一步可以只有:
Application
Plugin
PluginManager
ServiceRegistry
第二步,引入数据和 API 注册。
目标是理解:
插件如何定义 Collection
Collection 如何生成 CRUD
ResourceManager 如何统一分发 resource:action
ACL 如何统一拦截
这一步可以加入:
CollectionManager
ResourceManager
ACL
第三步,引入事件、任务和观测。
目标是理解:
主流程和副作用如何拆开
后台任务为什么要托管
日志、指标、trace 为什么要在统一入口做
这一步加入:
EventBus
CronJobManager
Logger
Telemetry
第四步,走向平台化。
目标是理解:
前端如何插件化
插件如何声明 manifest
版本升级如何 migration
插件禁用如何清理资源
第三方插件如何隔离
这一步加入:
FrontendRegistry
MigrationManager
ConfigManager
PluginManifest
Sandbox / Worker
不要一上来就写完整平台。先把核心链路跑通,再逐步补治理能力。
架构检查清单
设计自己的微内核平台时,可以用下面这份清单自检。
Kernel 边界:
[ ] Kernel 是否只保留稳定机制?
[ ] 业务能力是否都通过插件提供?
[ ] 插件是否不能随意修改内核内部状态?
[ ] 是否有清晰的 PluginContext 或受控 app API?
插件生命周期:
[ ] 是否区分首次安装和每次启用?
[ ] 是否有 before_load/load 阶段?
[ ] 是否有 after_disable 清理阶段?
[ ] 是否有 remove 删除阶段?
[ ] 是否有插件状态持久化?
数据模型:
[ ] 插件是否可以定义 Collection?
[ ] 插件是否可以扩展已有 Collection?
[ ] Collection 是否能推导出 Resource?
[ ] 数据迁移是否独立于 install?
API 与权限:
[ ] API 是否统一进入 ResourceManager?
[ ] 是否使用 resource/action 描述操作?
[ ] ACL 是否在统一入口执行?
[ ] 数据范围权限是否进入查询层?
[ ] 前端按钮是否能复用同一套权限语义?
事件与任务:
[ ] 是否有应用级事件?
[ ] 是否有数据库级事件?
[ ] 事件监听是否可注销?
[ ] 后台任务是否由内核托管?
[ ] 插件禁用时任务是否停止?
可观测性:
[ ] 插件生命周期是否有日志?
[ ] Resource 调用是否有日志和指标?
[ ] DB 操作是否有事件或指标?
[ ] CronJob 是否有运行日志和失败统计?
[ ] 是否有 trace_id 串联链路?
安全:
[ ] 第三方插件是否隔离运行?
[ ] 插件是否有权限声明?
[ ] 敏感配置是否通过 SecretManager 管理?
[ ] 插件包是否校验签名?
[ ] 插件是否不能直接访问全局配置?
总结
入门看,微内核就是:
核心少写业务,业务通过插件接入。
进阶看,微内核平台不是只有 PluginManager,而是一组稳定注册表:
PluginManager
CollectionManager
ResourceManager
ACL
EventBus
CronJobManager
FrontendRegistry
MigrationManager
Logger / Telemetry
高阶看,微内核真正解决的是长期演进问题:
插件如何安装、启用、禁用、删除;
数据如何迁移;
权限如何统一;
任务如何托管;
副作用如何解耦;
前端如何扩展;
日志、指标和 trace 如何贯穿全链路;
第三方插件如何隔离。
从 NocoBase 可以学到的核心不是某个具体 API,而是一套平台化纪律:
核心保持小而稳定;
业务能力全部插件化;
插件通过生命周期进入系统;
数据模型先行;
API 通过 resource/action 统一管理;
权限绑定资源操作;
事件处理副作用;
任务由内核托管;
前后端都应该插件化;
升级、禁用、观测和安全都要被治理。
所以,微内核不是插件加载器。
它更像一个平台操作系统:核心负责提供稳定规则,插件负责带来变化。规则越清晰,平台越能在长期演进中保持可控。
参考资料
Enjoy Reading This Article?
Here are some more articles you might like to read next:
- 一次接口文档站工程化实践:VitePress、Swagger 与 Cloudflare 部署踩坑记录
- 从 Mini NocoBase Demo 看无代码平台的微内核与插件化设计
- 给 FastAPI 后台模板补了一轮生产化能力
- 现代大语言模型的架构细节:从 RMSNorm 到 Loss 计算
- 从零写一个 AI 编程助手:MiniCode 的设计笔记
- 强化学习算法笔记:用一套框架串起 MC、TD、DQN、PPO、SAC
- 一个分布式锁没能拦住的重复下发问题
- 搞清楚 BatchNorm、LayerNorm、RMSNorm 到底在干嘛
- 强化学习基础笔记
- FastAPI-Template 实践笔记:以依赖注入管理请求生命周期