当前位置: 首页 > news >正文

Rust Axum 快速上手指南(静态网页和动态网页2024版)

本文基于 Axum 0.7.5(当前稳定版)、tower-http 0.5.2MiniJinja 0.7.2 编写,涵盖生产环境核心场景:tower-http Layer 叠加与数据传递静态网页服务MiniJinja 动态模板渲染,并重点解析请求 / 应答在多 Layer 中的流转逻辑。

一、环境准备:依赖配置

首先在 Cargo.toml 中添加最新依赖,确保组件兼容性(Axum 0.7+ 需搭配 tower-http 0.5+):

[package]
name = "axum-demo"
version = "0.1.0"
edition = "2021"[dependencies]
# Axum 核心(含路由、Handler、State 等)
axum = { version = "0.7.5", features = ["json", "macros"] }
# 异步运行时(Axum 依赖 tokio)
tokio = { version = "1.35.1", features = ["full"] }
# HTTP 中间件生态(Layer 核心)
tower-http = { version = "0.5.2", features = ["trace",    # 日志追踪"compression-br",  # Brotli 压缩"cors",     # 跨域支持"serve-dir",# 静态文件服务"request-body-limit", # 请求大小限制"fs",       # 文件系统操作
] }
# 日志格式化(配合 tower-http::trace)
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
# 动态模板引擎
minijinja = "0.7.2"
# 路径处理
std-fs = "0.1.4"

二、核心概念铺垫

在深入 Layer 之前,需明确 Axum 生态的 3 个核心组件:

  1. Handler:处理 HTTP 请求的函数(如 async fn hello() -> &'static str),是请求处理的「终点」。
  2. Router:路由分发器,将请求匹配到对应的 Handler,支持嵌套和挂载。
  3. Layer:中间件抽象,用于拦截 / 修改请求(Request)或应答(Response),可叠加使用(如日志、压缩、跨域)。

关键逻辑:Layer 会「包装」Router 或下一层 Layer,形成一个「洋葱模型」—— 请求从外层 Layer 流向内层 Handler,应答从内层 Handler 流回外层 Layer。


三、tower-http Layer 叠加与数据传递

3.1 Layer 核心规则:洋葱模型

Layer 的执行顺序遵循 「请求外→内,应答内→外」,即:

  • 请求阶段:先添加的 Layer 先处理请求(如先日志 → 再压缩 → 最后到 Handler)。
  • 应答阶段:先添加的 Layer 后处理应答(如 Handler 生成应答 → 压缩 → 日志 → 客户端)。

下图直观展示多 Layer 数据流转:

客户端 → [TraceLayer(日志)] → [CompressionLayer(压缩)] → [CorsLayer(跨域)] → Router → Handler↑                                  ↓
客户端 ← [TraceLayer(日志)] ← [CompressionLayer(压缩)] ← [CorsLayer(跨域)] ← Router ← Handler

3.2 生产环境常用 Layer 配置

以下是现实项目中必选的 Layer 组合,按「外层到内层」顺序添加(优化性能和安全性):

步骤 1:初始化日志(TraceLayer)

用于记录请求方法、路径、状态码、耗时等,是调试和监控的核心。

步骤 2:跨域处理(CorsLayer)

解决浏览器跨域问题,需明确允许的 Origin、Method、Header。

步骤 3:请求大小限制(RequestBodyLimitLayer)

防止超大请求攻击(如上传恶意文件),生产环境建议限制 10MB 以内。

步骤 4:压缩(CompressionLayer)

减少响应体积,支持 Brotli、Gzip(Brotli 压缩率更高,优先启用)。

代码实现:Layer 叠加
use axum::{Router, Server};
use tower_http::{compression::CompressionLayer,cors::{Any, CorsLayer},request_body_limit::RequestBodyLimitLayer,trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer},
};
use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt};#[tokio::main]
async fn main() {// 1. 初始化日志(必须先启动,否则 Layer 日志不生效)tracing_subscriber::registry().with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "axum_demo=debug,tower_http=debug".into()),).with(tracing_subscriber::fmt::layer()).init();// 2. 构建核心路由(后续添加静态/动态路由)let app_router = Router::new();// 3. 叠加 Layer(顺序:外层→内层,影响请求/应答处理顺序)let app = app_router// Layer 1:日志追踪(最外层,优先记录完整请求).layer(TraceLayer::new_for_http().make_span_with(DefaultMakeSpan::new().include_headers(true)) // 记录请求头.on_response(DefaultOnResponse::new().include_headers(true)), // 记录响应头)// Layer 2:跨域(外层,先验证跨域,避免后续无用处理).layer(CorsLayer::new().allow_origin(Any) // 生产环境替换为具体域名(如 "https://example.com").allow_methods(Any).allow_headers(Any),)// Layer 3:请求大小限制(10MB).layer(RequestBodyLimitLayer::new(10 * 1024 * 1024))// Layer 4:压缩(内层,靠近 Handler,减少数据传输).layer(CompressionLayer::new().br(true).gzip(true));// 启动服务Server::bind(&([127, 0, 0, 1], 3000).into()).serve(app.into_make_service()).await.unwrap();
}

3.3 请求 / 应答在多 Layer 中的数据传递细节

每个 Layer 本质是一个「Service 包装器」,通过 Service::call 方法传递请求,具体流程:

  1. 请求阶段(Request Flow)

    • 客户端发送 HTTP 请求 → 进入最外层 Layer(如 TraceLayer)。
    • TraceLayer 记录请求开始时间、方法、路径 → 调用下一层 Layer(CorsLayer)的 call 方法,传递修改后的 Request(或原 Request)。
    • CorsLayer 检查请求的 Origin/Method → 若合法,调用下一层(RequestBodyLimitLayer)→ 否则直接返回 403 应答。
    • RequestBodyLimitLayer 检查请求体大小 → 若超限,返回 413 应答 → 否则传递给 CompressionLayer。
    • CompressionLayer 不修改请求(仅处理应答)→ 传递给 Router → Router 匹配 Handler → Handler 处理请求并生成 Response。
  2. 应答阶段(Response Flow)

    • Handler 生成 Response → 回传给 CompressionLayer。
    • CompressionLayer 检查 Response 的 Content-Type(如文本、HTML)→ 若支持压缩,对响应体进行 Brotli/Gzip 压缩 → 添加上 Content-Encoding 头 → 回传给 RequestBodyLimitLayer。
    • RequestBodyLimitLayer 不处理应答 → 回传给 CorsLayer。
    • CorsLayer 为 Response 添加 Access-Control-Allow-* 头 → 回传给 TraceLayer。
    • TraceLayer 记录应答的状态码、耗时 → 将最终 Response 发送给客户端。

3.4 自定义 Layer 示例(直观理解流转)

若需验证 Layer 执行顺序,可自定义一个打印日志的 Layer,观察请求 / 应答的处理时机:

use axum::body::Body;
use http::{Request, Response};
use tower::{Layer, Service};
use std::task::{Context, Poll};// 自定义 Layer(无状态,仅打印日志)
#[derive(Clone, Copy, Default)]
struct LogLayer;impl<S> Layer<S> for LogLayer {type Service = LogService<S>;fn layer(&self, inner: S) -> Self::Service {LogService { inner }}
}// Layer 对应的 Service(实际处理逻辑)
struct LogService<S> {inner: S,
}impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for LogService<S>
whereS: Service<Request<ReqBody>, Response = Response<ResBody>>,S::Error: std::fmt::Display,
{type Response = S::Response;type Error = S::Error;type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {self.inner.poll_ready(cx)}fn call(&mut self, req: Request<ReqBody>) -> Self::Future {// 请求阶段:打印请求信息(外层 Layer 先执行)println!("[LogLayer] 收到请求: {} {}", req.method(), req.uri().path());// 保存 inner 的引用(因 async 闭包无法捕获 &mut self)let mut inner = self.inner.clone();Box::pin(async move {// 调用下一层 Service(传递请求)let resp = inner.call(req).await?;// 应答阶段:打印应答信息(内层 Layer 先执行)println!("[LogLayer] 返回应答: {}", resp.status());Ok(resp)})}
}// 在 main 中添加自定义 Layer(放在 TraceLayer 之后,观察顺序)
// let app = app_router
//     .layer(TraceLayer::new_for_http(...))
//     .layer(LogLayer)  // 自定义 Layer
//     .layer(CorsLayer::new(...))
//     ...;

运行后,请求 GET / 会输出:

[LogLayer] 收到请求: GET /  # 请求阶段:先 Trace → 再 Log → 再 Cors...
[LogLayer] 返回应答: 200 OK  # 应答阶段:先 Handler → 再 Compression → 再 Log → 再 Trace...

四、Serve 静态网页

使用 tower-http::serve_dir::ServeDir 实现静态文件服务(如 HTML、CSS、JS、图片),支持路径映射和 404 处理。

4.1 基础静态服务(映射本地目录)

假设本地有 static 目录,结构如下:

static/index.html  # 首页css/style.css # 样式文件img/logo.png  # 图片

代码实现:挂载 /static 路径到本地 static 目录:

use axum::Router;
use tower_http::serve_dir::ServeDir;// 在 main 中构建路由
let app_router = Router::new()// 挂载静态文件:请求 /static/xxx → 读取 static/xxx.nest_service("/static", ServeDir::new("static"))// 根路径(/)重定向到 /static/index.html.route("/", axum::routing::get(|| async {axum::response::Redirect::permanent("/static/index.html")}));

启动服务后,访问以下路径会返回对应文件

  • http://127.0.0.1:3000/ → 重定向到 index.html
  • http://127.0.0.1:3000/static/css/style.css → 返回样式文件
  • http://127.0.0.1:3000/static/img/logo.png → 返回图片

4.2 高级配置:自定义 404 页面

当请求的静态文件不存在时,默认返回 404 空白页,可自定义 404 页面:

use axum::{response::IntoResponse, http::StatusCode};
use tower_http::serve_dir::ServeDir;// 自定义 404 响应(HTML 格式)
async fn not_found() -> impl IntoResponse {(StatusCode::NOT_FOUND,axum::response::Html("<h1>404 - 页面不存在</h1><p>请检查路径是否正确</p>"),)
}// 构建路由时,用 `fallback` 处理 404
let app_router = Router::new().nest_service("/static", ServeDir::new("static").not_found_service(// 静态文件不存在时,调用 not_found Handleraxum::routing::get(not_found))).route("/", axum::routing::get(|| async {axum::response::Redirect::permanent("/static/index.html")}))// 其他路径(非 /static)也返回 404.fallback(not_found);

4.3 生产环境优化:添加缓存头

为静态文件添加 Cache-Control 头,减少重复请求(使用 tower-http::cache_control::CacheControlLayer):

use tower_http::cache_control::{CacheControlLayer, CacheControl};// 在 Layer 叠加中添加缓存控制(放在 CompressionLayer 之后,靠近静态服务)
let app = app_router.layer(TraceLayer::new_for_http(...)).layer(CorsLayer::new(...)).layer(RequestBodyLimitLayer::new(10 * 1024 * 1024)).layer(CompressionLayer::new().br(true).gzip(true))// 静态文件缓存:设置 max-age=3600(1 小时).layer(CacheControlLayer::new().with_cache_control(CacheControl::new().max_age(std::time::Duration::from_secs(3600)))// 仅对静态文件路径生效.on_route(|route| route.starts_with("/static/")),);

五、MiniJinja 模板动态网页

MiniJinja 是轻量、安全的模板引擎,支持变量、循环、条件判断、模板继承,适合生成动态 HTML(如用户中心、列表页)。

5.1 模板目录结构

先创建 templates 目录,存放模板文件,推荐结构:

templates/base.html    # 基础模板(公共头部、尾部)index.html   # 首页(继承 base.html)users.html   # 用户列表页(继承 base.html)

5.2 初始化 MiniJinja 模板环境

模板环境(TemplateEnvironment)需全局共享(通过 Axum 的 State 传递),避免重复初始化:

use axum::{Router, extract::State, response::Html};
use minijinja::{Environment, Template};
use std::sync::Arc;// 定义全局状态(包装 MiniJinja 环境)
#[derive(Clone)]
struct AppState {template_env: Arc<Environment<'static>>,
}// 初始化模板环境
fn init_template_env() -> Environment<'static> {let mut env = Environment::new();// 添加模板目录(加载 .html 文件)env.add_template_dir("templates").expect("Failed to add template directory");// 可选:添加自定义过滤器(如日期格式化)env.add_filter("upper", |s: &str| s.to_uppercase());env
}#[tokio::main]
async fn main() {// 初始化模板环境并包装为全局状态let template_env = Arc::new(init_template_env());let app_state = AppState { template_env };// 构建路由(通过 with_state 传递全局状态)let app_router = Router::new().route("/", axum::routing::get(render_index)).route("/users", axum::routing::get(render_users)).with_state(app_state); // 传递全局状态// 叠加 Layer 并启动服务(同前)// ...
}

5.3 编写模板文件

1. 基础模板(base.html)

使用 {% block %} 定义可替换的区块,供子模板继承:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>{% block title %}默认标题{% endblock %}</title><link rel="stylesheet" href="/static/css/style.css">
</head>
<body><header><h1>Axum MiniJinja 示例</h1><nav><a href="/">首页</a> | <a href="/users">用户列表</a></nav></header><!-- 子模板内容区域 --><main>{% block content %}{% endblock %}</main><footer><p>© 2024 Axum 开发指南</p></footer>
</body>
</html>
2. 首页模板(index.html)

继承 base.html,填充 title 和 content 区块:

{% extends "base.html" %}{% block title %}首页 - Axum 示例{% endblock %}{% block content %}<h2>欢迎访问首页</h2><p>当前时间:{{ current_time }}</p><p>用户名:{{ username | upper }}</p> <!-- 使用自定义 upper 过滤器 -->
{% endblock %}
3. 用户列表模板(users.html)

使用 {% for %} 循环渲染列表:

{% extends "base.html" %}{% block title %}用户列表 - Axum 示例{% endblock %}{% block content %}<h2>用户列表</h2>{% if users.is_empty() %}<p>暂无用户</p>{% else %}<ul>{% for user in users %}<li>{{ user.id }}: {{ user.name }} ({{ user.age }} 岁)</li>{% endfor %}</ul>{% endif %}
{% endblock %}

5.4 编写 Handler 渲染模板

通过 State 提取全局模板环境,传递上下文数据(如当前时间、用户列表),渲染模板并返回 HTML:

use axum::{extract::State, response::Html};
use chrono::Local; // 需要添加依赖:chrono = "0.4.31"
use std::sync::Arc;// 定义用户结构体(用于传递到模板)
#[derive(Debug, serde::Serialize)] // MiniJinja 需要 Serialize  trait
struct User {id: u32,name: String,age: u8,
}// 渲染首页
async fn render_index(State(state): State<AppState>) -> Html<String> {// 1. 准备上下文数据(需实现 Serialize)let context = minijinja::context! {current_time => Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),username => "alice"};// 2. 加载并渲染模板let template = state.template_env.get_template("index.html").unwrap();let html = template.render(&context).unwrap();Html(html)
}// 渲染用户列表
async fn render_users(State(state): State<AppState>) -> Html<String> {// 1. 模拟从数据库获取用户数据let users = vec![User { id: 1, name: "Alice".into(), age: 25 },User { id: 2, name: "Bob".into(), age: 30 },User { id: 3, name: "Charlie".into(), age: 28 },];// 2. 传递上下文let context = minijinja::context! { users => users };// 3. 渲染模板let template = state.template_env.get_template("users.html").unwrap();let html = template.render(&context).unwrap();Html(html)
}

注意:需添加 chrono 依赖(用于时间格式化)和 serde 依赖(serde = { version = "1.0.193", features = ["derive"] }),因为 MiniJinja 要求上下文数据实现 serde::Serialize

5.5 模板预编译(生产环境优化)

模板默认是运行时加载,生产环境可预编译模板到二进制中,避免文件 IO 开销:

// 预编译模板(在 init_template_env 中)
fn init_template_env() -> Environment<'static> {let mut env = Environment::new();// 预编译 base.htmlenv.add_template("base.html", include_str!("../templates/base.html")).expect("Failed to compile base.html");// 预编译 index.htmlenv.add_template("index.html", include_str!("../templates/index.html")).expect("Failed to compile index.html");// 预编译 users.htmlenv.add_template("users.html", include_str!("../templates/users.html")).expect("Failed to compile users.html");env.add_filter("upper", |s: &str| s.to_uppercase());env
}

说明:include_str! 是 Rust 宏,编译时将文件内容嵌入二进制,运行时无需读取本地文件。


六、综合示例:完整应用

将上述所有功能整合,最终的 main.rs 如下:

use axum::{extract::State,http::StatusCode,response::{Html, IntoResponse, Redirect},Router, Server,
};
use chrono::Local;
use minijinja::{context::Context, Environment};
use serde::Serialize;
use std::sync::Arc;
use tower_http::{cache_control::{CacheControl, CacheControlLayer},compression::CompressionLayer,cors::{Any, CorsLayer},request_body_limit::RequestBodyLimitLayer,serve_dir::ServeDir,trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer},
};
use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt};// 全局状态
#[derive(Clone)]
struct AppState {template_env: Arc<Environment<'static>>,
}// 用户结构体
#[derive(Debug, Serialize)]
struct User {id: u32,name: String,age: u8,
}// 初始化模板环境
fn init_template_env() -> Environment<'static> {let mut env = Environment::new();// 预编译模板env.add_template("base.html", include_str!("../templates/base.html")).expect("Failed to compile base.html");env.add_template("index.html", include_str!("../templates/index.html")).expect("Failed to compile index.html");env.add_template("users.html", include_str!("../templates/users.html")).expect("Failed to compile users.html");// 自定义过滤器env.add_filter("upper", |s: &str| s.to_uppercase());env
}// 404 处理
async fn not_found() -> impl IntoResponse {(StatusCode::NOT_FOUND,Html("<h1>404 - 页面不存在</h1><p>请检查路径是否正确</p>"),)
}// 渲染首页
async fn render_index(State(state): State<AppState>) -> Html<String> {let context = context! {current_time => Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),username => "alice"};let template = state.template_env.get_template("index.html").unwrap();let html = template.render(&context).unwrap();Html(html)
}// 渲染用户列表
async fn render_users(State(state): State<AppState>) -> Html<String> {let users = vec![User { id: 1, name: "Alice".into(), age: 25 },User { id: 2, name: "Bob".into(), age: 30 },User { id: 3, name: "Charlie".into(), age: 28 },];let context = context! { users => users };let template = state.template_env.get_template("users.html").unwrap();let html = template.render(&context).unwrap();Html(html)
}#[tokio::main]
async fn main() {// 初始化日志tracing_subscriber::registry().with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "axum_demo=debug,tower_http=debug".into()),).with(tracing_subscriber::fmt::layer()).init();// 初始化模板环境和全局状态let template_env = Arc::new(init_template_env());let app_state = AppState { template_env };// 构建路由let app_router = Router::new()// 动态路由(模板渲染).route("/", axum::routing::get(render_index)).route("/users", axum::routing::get(render_users))// 静态路由(文件服务).nest_service("/static",ServeDir::new("static").not_found_service(axum::routing::get(not_found)),)// 404 处理.fallback(not_found)// 传递全局状态.with_state(app_state);// 叠加 Layerlet app = app_router// 1. 日志追踪.layer(TraceLayer::new_for_http().make_span_with(DefaultMakeSpan::new().include_headers(true)).on_response(DefaultOnResponse::new().include_headers(true)),)// 2. 跨域.layer(CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any),)// 3. 请求大小限制(10MB).layer(RequestBodyLimitLayer::new(10 * 1024 * 1024))// 4. 压缩.layer(CompressionLayer::new().br(true).gzip(true))// 5. 静态文件缓存(1 小时).layer(CacheControlLayer::new().with_cache_control(CacheControl::new().max_age(std::time::Duration::from_secs(3600))).on_route(|route| route.starts_with("/static/")),);// 启动服务Server::bind(&([127, 0, 0, 1], 3000).into()).serve(app.into_make_service()).await.expect("Failed to start server");
}

七、生产环境注意事项

  1. Layer 顺序优化

    • 安全相关 Layer(CORS、请求大小限制)放外层,避免无效处理。
    • 日志 Layer 放最外层,记录完整请求 / 应答。
    • 压缩 Layer 放内层,减少数据传输量。
  2. 静态文件安全

    • 禁用目录列表(ServeDir 默认禁用,勿开启)。
    • 限制静态文件类型(如仅允许 text/*image/*)。
  3. 模板安全

    • 禁用 MiniJinja 的 eval 和 exec 功能(默认禁用),防止注入攻击。
    • 对用户输入的内容使用 {{ user_input | escape }} 转义(MiniJinja 默认转义 HTML)。
  4. 性能优化

    • 预编译模板到二进制。
    • 为静态文件添加缓存头。
    • 使用 tokio 的 release 模式编译(cargo build --release)。


文章转载自:

http://BaAjsEdw.hrrmb.cn
http://YRBEdxlb.hrrmb.cn
http://q7KrkCA9.hrrmb.cn
http://Tt4SiUBN.hrrmb.cn
http://TFZIqChV.hrrmb.cn
http://WVxerJ7V.hrrmb.cn
http://I2SzNBCQ.hrrmb.cn
http://BS0njDTJ.hrrmb.cn
http://BAnBqqHU.hrrmb.cn
http://nE9I7XwE.hrrmb.cn
http://nif7lRIU.hrrmb.cn
http://pfFFgxfo.hrrmb.cn
http://Znfj4ZFW.hrrmb.cn
http://IAdUOBI0.hrrmb.cn
http://9TmGocwP.hrrmb.cn
http://PCZ7dKvX.hrrmb.cn
http://SuctIMvp.hrrmb.cn
http://QOxxLYtz.hrrmb.cn
http://MxjFk1WC.hrrmb.cn
http://pfwdPsK4.hrrmb.cn
http://phSEp5aZ.hrrmb.cn
http://sUTa5FHe.hrrmb.cn
http://9Nks8akt.hrrmb.cn
http://E4KQdTjj.hrrmb.cn
http://TEcb30SS.hrrmb.cn
http://Zg1uRK9p.hrrmb.cn
http://nMLGQyoO.hrrmb.cn
http://dfkUl9HJ.hrrmb.cn
http://qs4RbNpl.hrrmb.cn
http://5ese8iAo.hrrmb.cn
http://www.dtcms.com/a/369013.html

相关文章:

  • CVPR 2025|无类别词汇的视觉-语言模型少样本学习
  • 9月14日 不见不散|2025年华望M-Design v2软件线上发布会
  • 疯狂星期四文案网第61天运营日记
  • 还在堆模型、拼算力?醒醒吧!你的AI项目99%会死于数据
  • DL3382P6平替RClamp3382P.TCT
  • 硬件基础:串口通信
  • 华为在国内搞的研发基地有多野?标杆游学带你解锁“研发界顶流”
  • LangChain关于提示词的几种写法
  • openharmony之AV_CodeC音视频编解码模块详解(二)
  • 【明道云】[工作表控件9] 子表控件:一对多数据建模实战指南
  • Linux/UNIX系统编程手册笔记:DAEMON、编写安全的特权程序、能力、登录记账
  • Docker部署PanSou 一款开源网盘搜索项目,集成前后端,一键部署
  • 【服务器】英伟达M40显卡风冷方案心得
  • Process Explorer 学习笔记(第三章3.3.1):DLL和句柄
  • 解锁无限创意:Tldraw+cpolar如何通过内网穿透技术打破空间限制
  • 安全沙箱配置针对海外vps容器隔离的验证方法
  • SQL-DML
  • 数据库原理及应用_数据库基础_第4章关系模型的基本理论_触发器
  • RWA点亮新能源的数字未来
  • css margin外边距重叠/塌陷问题
  • 【Python - 基础 - 规范】(01)Python命名规范...
  • 高级RAG策略学习(四)——上下文窗口增强检索RAG
  • 如何通过AI进行数据资产梳理
  • 跨平台超低延迟RTSP播放器技术设计探究
  • 一文了解大模型推理优化
  • 嵌入式单片机---串口通信及相关通信技术
  • k8s基础练习环境搭建
  • AiPPT生成的PPT内容质量怎么样?会不会出现逻辑混乱或数据错误?
  • 系统架构思考20241204
  • GPU版Pytorch的安装