langchainrust:Rust 版 LangChain 框架(LLM+Agent+RAG)

langchainrust:Rust 版 LangChain 框架(LLM+Agent+RAG)

langchainrust 是一个 Rust 实现的 LLM 应用框架,提供 LLM 调用、Agent、RAG、向量存储、回调追踪等功能。

核心组件一览

组件 说明
LLM OpenAI 兼容接口,支持流式输出、function calling
Agents ReActAgent 智能代理,支持工具调用和多步推理
Prompts 提示词模板,支持变量替换
Memory 对话历史管理,支持多种记忆策略
Chains 链式调用,支持单步和多步流水线
RAG 文档分割、向量存储、语义检索的完整流程
Loaders PDF 和 CSV 文档加载器
Tools 内置工具:计算器、日期、数学运算、URL抓取
Callbacks 执行追踪和 LangSmith 集成
Tool Calling bind_tools() 工具绑定和结构化输出

安装

Cargo.toml:

toml 复制代码
[dependencies]
langchainrust = "0.2.3"
tokio = { version = "1.0", features = ["full"] }

启用 Qdrant 向量数据库(可选):

toml 复制代码
langchainrust = { version = "0.2.3", features = ["qdrant-integration"] }

一、LLM 调用

基础用法

创建 OpenAI 客户端并发起对话:

rust 复制代码
use langchainrust::{OpenAIChat, OpenAIConfig, BaseChatModel};
use langchainrust::schema::Message;

let config = OpenAIConfig {
    api_key: std::env::var("OPENAI_API_KEY")?,
    base_url: "https://api.openai.com/v1".to_string(),
    model: "gpt-3.5-turbo".to_string(),
    streaming: false,
    temperature: Some(0.7),
    max_tokens: Some(500),
    ..Default::default()
};

let llm = OpenAIChat::new(config);

let messages = vec![
    Message::system("你是一个助手"),
    Message::human("什么是 Rust 的所有权机制?"),
];

let response = llm.chat(messages, None).await?;
println!("{}", response.content);

说明:

  • Message::system() 设置系统提示,定义 AI 的角色和行为
  • Message::human() 用户消息
  • Message::ai() AI 响应消息(用于多轮对话时添加历史)
  • temperature 控制随机性,0.0 最确定,2.0 最随机

流式输出

逐 token 输出,适合长文本生成:

rust 复制代码
use futures_util::StreamExt;

let streaming_config = OpenAIConfig {
    streaming: true,
    ..Default::default()
};

let llm = OpenAIChat::new(streaming_config);
let stream = llm.stream(messages, None).await?;

while let Some(token) = stream.next().await {
    match token {
        Ok(text) => print!("{}", text),  // 实时打印
        Err(e) => eprintln!("Error: {}", e),
    }
}

使用其他模型

框架支持 OpenAI 兼容接口,可以对接 Qwen、DeepSeek、Azure 等:

rust 复制代码
// 通义千问
let config = OpenAIConfig {
    base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1".to_string(),
    model: "qwen-turbo".to_string(),
    api_key: std::env::var("DASHSCOPE_API_KEY")?,
    ..Default::default()
};

// DeepSeek
let config = OpenAIConfig {
    base_url: "https://api.deepseek.com/v1".to_string(),
    model: "deepseek-chat".to_string(),
    ..Default::default()
};

// 国内代理
let config = OpenAIConfig {
    base_url: "https://api.openai-proxy.com/v1".to_string(),
    ..Default::default()
};

配置项详解

字段 类型 说明
api_key String API 密钥,从环境变量读取
base_url String API 端点,默认 OpenAI,可换成其他兼容服务
model String 模型名称,如 gpt-3.5-turbo, gpt-4
streaming bool 是否流式输出
temperature Option 采样温度,0.0-2.0
max_tokens Option 最大生成 token 数,控制成本

二、提示词模板

PromptTemplate - 字符串模板

避免在代码里硬编码提示词,使用模板管理:

rust 复制代码
use langchainrust::prompts::PromptTemplate;
use std::collections::HashMap;

let template = PromptTemplate::new("你好,{name}!今天是{day}。");

let mut vars = HashMap::new();
vars.insert("name", "小明");
vars.insert("day", "星期一");

let prompt = template.format(&vars)?;
// 输出: "你好,小明!今天是星期一。"

说明 : 用 {变量名} 定义占位符,调用时传入值替换。

ChatPromptTemplate - 聊天模板

管理多角色对话的模板:

rust 复制代码
use langchainrust::prompts::ChatPromptTemplate;
use langchainrust::schema::Message;

let template = ChatPromptTemplate::new(vec![
    Message::system("你是一个{role},专精于{domain}。"),
    Message::human("你好,我是{name}。"),
    Message::human("{question}"),
]);

let mut vars = HashMap::new();
vars.insert("role", "Rust 专家");
vars.insert("domain", "系统编程");
vars.insert("name", "小红");
vars.insert("question", "解释所有权机制");

let messages = template.format(&vars)?;
// 直接传给 llm.chat()
let response = llm.chat(messages, None).await?;

说明: 模板中的每个 Message 都支持变量替换,适合构建可复用的对话模板。


三、Agent 与工具调用

Agent 能自主调用工具完成复杂任务。

基础 Agent 用法

rust 复制代码
use langchainrust::{
    ReActAgent, AgentExecutor, BaseAgent, BaseTool,
    Calculator, DateTimeTool, SimpleMathTool,
};
use std::sync::Arc;

// 注册工具
let tools: Vec<Arc<dyn BaseTool>> = vec![
    Arc::new(Calculator::new()),
    Arc::new(DateTimeTool::new()),
    Arc::new(SimpleMathTool::new()),
];

// 创建 Agent
let agent = ReActAgent::new(llm, tools.clone(), None);

// 创建执行器,设置最大迭代次数防止无限循环
let executor = AgentExecutor::new(
    Arc::new(agent) as Arc<dyn BaseAgent>,
    tools
).with_max_iterations(5);

// 执行任务
let result = executor.invoke("计算 37 * 48,告诉我今天是星期几".to_string()).await?;
println!("结果: {}", result);

说明:

  • Agent 会自动分解任务,调用合适的工具
  • max_iterations 防止 Agent 卡在循环里
  • ReActAgent 采用 Reasoning + Acting 模式,先思考再行动

Agent 执行过程示例

用户输入: "计算 37 * 48"

Agent 内部过程(自动完成):

复制代码
Thought: 需要计算乘法
Action: Calculator
Input: 37 * 48
Observation: 1776
Thought: 我有答案了
Final Answer: 37 * 48 = 1776

自定义工具

实现 BaseTool trait 创建自己的工具:

rust 复制代码
use langchainrust::{BaseTool, ToolDefinition, ToolResult};
use async_trait::async_trait;

pub struct WeatherTool;

#[async_trait]
impl BaseTool for WeatherTool {
    fn name(&self) -> &str {
        "weather"
    }
    
    fn description(&self) -> &str {
        "查询指定城市的天气。输入城市名称,返回天气信息。"
    }
    
    fn definition(&self) -> ToolDefinition {
        ToolDefinition {
            name: self.name().to_string(),
            description: self.description().to_string(),
            parameters: serde_json::json!({
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如北京、上海"
                    }
                },
                "required": ["city"]
            }),
        }
    }
    
    async fn call(&self, input: serde_json::Value) -> ToolResult {
        let city = input["city"].as_str().unwrap_or("unknown");
        
        // 调用真实天气 API
        let weather = fetch_weather(city).await?;
        
        Ok(serde_json::json!({
            "city": city,
            "temperature": weather.temp,
            "condition": weather.condition
        }))
    }
}

说明:

  • namedescription 会被 Agent 看到,决定是否调用
  • parameters 定义输入参数的 schema,支持 JSON Schema 格式
  • call 是实际执行逻辑

内置工具列表

工具 说明
Calculator 数学表达式计算
DateTimeTool 获取当前日期时间
SimpleMathTool sin, cos, sqrt, factorial 等
URLFetchTool 抓取网页内容

四、Tool Calling

v0.2.3 支持 OpenAI 的 function calling,让 LLM 直接调用预定义函数。

bind_tools() 绑定工具

rust 复制代码
use langchainrust::ToolDefinition;

let tools = vec![
    ToolDefinition {
        name: "get_user_info".to_string(),
        description: "查询用户信息".to_string(),
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "user_id": {
                    "type": "string",
                    "description": "用户 ID"
                }
            },
            "required": ["user_id"]
        }),
    },
];

// 绑定工具到 LLM
let llm_with_tools = llm.bind_tools(tools);

let messages = vec![
    Message::human("查询用户 abc123 的信息"),
];

let response = llm_with_tools.chat(messages, None).await?;

// 检查是否有工具调用请求
if let Some(tool_calls) = response.tool_calls {
    for call in tool_calls {
        println!("工具: {}", call.name);        // "get_user_info"
        println!("参数: {}", call.arguments);   // {"user_id": "abc123"}
        
        // 执行工具逻辑
        let result = execute_tool(&call.name, call.arguments)?;
        
        // 将结果返回给 LLM 继续对话
        let follow_up = vec![
            Message::human("查询用户 abc123 的信息"),
            Message::tool_call(call.id.clone(), call.name.clone(), call.arguments),
            Message::tool_result(call.id.clone(), result),
        ];
        
        let final_response = llm_with_tools.chat(follow_up, None).await?;
        println!("{}", final_response.content);
    }
}

说明:

  • bind_tools() 告诉 LLM 有哪些工具可用
  • LLM 返回 tool_calls 表示想调用工具
  • 需要执行工具并把结果通过 Message::tool_result() 返回给 LLM

结构化输出

强制 LLM 返回特定格式的数据:

rust 复制代码
use schemars::schema_for;

#[derive(serde::Deserialize, schemars::JsonSchema)]
struct UserInfo {
    name: String,
    age: u32,
    interests: Vec<String>,
}

// 生成 schema
let schema = schema_for!(UserInfo);

// 配置 LLM 强制返回该格式
let llm_structured = llm.with_structured_output(schema);

let messages = vec![
    Message::human("分析这个用户:张三,25岁,喜欢编程和音乐"),
];

let response = llm_structured.chat(messages, None).await?;

// 直接反序列化
let info: UserInfo = serde_json::from_str(&response.content)?;
println!("姓名: {}", info.name);
println!("年龄: {}", info.age);
println!("兴趣: {:?}", info.interests);

说明 : 用 schemars 生成 JSON Schema,LLM 会严格按照 schema 返回数据。


五、对话记忆

ChatMessageHistory - 历史记录

rust 复制代码
use langchainrust::{ChatMessageHistory, Message};

let mut history = ChatMessageHistory::new();

// 添加消息
history.add_message(Message::human("你好!"));
history.add_message(Message::ai("你好!有什么可以帮你?"));
history.add_message(Message::human("什么是 Rust?"));
history.add_message(Message::ai("Rust 是一门系统编程语言..."));

// 获取所有历史
for msg in history.messages() {
    println!("{:?}: {}", msg.message_type, msg.content);
}

// 清空
history.clear();

ConversationBufferMemory - 缓冲记忆

自动管理对话历史:

rust 复制代码
use langchainrust::{ConversationBufferMemory, Memory};

let memory = ConversationBufferMemory::new();

// 保存对话
memory.save_context(HashMap::from([
    ("input", "什么是所有权?"),
    ("output", "所有权是 Rust 的核心概念,确保内存安全...")
]))?;

// 加载历史
let vars = memory.load_memory_variables()?;
println!("历史: {:?}", vars);

// 用于多轮对话
let messages_with_history = memory.get_history();
let full_messages = messages_with_history.into_iter()
    .chain(vec![Message::human("能否详细解释?")])
    .collect();
let response = llm.chat(full_messages, None).await?;

ConversationBufferWindowMemory - 窗口记忆

只保留最近 K 条,控制上下文长度:

rust 复制代码
use langchainrust::memory::ConversationBufferWindowMemory;

// 只保留最近 5 条消息
let window_memory = ConversationBufferWindowMemory::new(5);

// 旧的消息会自动丢弃
// 适合控制 token 使用量

说明: 窗口记忆适合长对话场景,避免历史太长超出模型限制。


六、Chain 工作流

LLMChain - 单步链

rust 复制代码
use langchainrust::{LLMChain, BaseChain};

let chain = LLMChain::new(llm, "分析以下主题: {topic}");

let mut inputs = HashMap::new();
inputs.insert("topic", "Rust 语言的特点");

let result = chain.invoke(inputs).await?;
println!("{}", result);

SequentialChain - 多步流水线

串联多个 Chain,上一个的输出作为下一个的输入:

rust 复制代码
use langchainrust::{SequentialChain, LLMChain, BaseChain};
use std::sync::Arc;
use serde_json::Value;

// 步骤1: 分析
let chain1 = LLMChain::new(llm1, "分析: {topic}");
let chain1 = Arc::new(chain1);

// 步骤2: 总结
let chain2 = LLMChain::new(llm2, "根据以下分析生成总结: {analysis}");
let chain2 = Arc::new(chain2);

// 步骤3: 建议
let chain3 = LLMChain::new(llm3, "基于以下总结给出行动建议: {summary}");
let chain3 = Arc::new(chain3);

// 组装流水线
let pipeline = SequentialChain::new()
    // chain1: 输入 topic,输出 analysis
    .add_chain(chain1, vec!["topic"], vec!["analysis"])
    // chain2: 输入 analysis,输出 summary
    .add_chain(chain2, vec!["analysis"], vec!["summary"])
    // chain3: 输入 summary,输出 suggestions
    .add_chain(chain3, vec!["summary"], vec!["suggestions"]);

// 执行
let inputs = HashMap::from([
    ("topic", Value::String("Rust 在 AI 领域的应用".to_string()))
]);

let results = pipeline.invoke(inputs).await?;

println!("分析: {}", results["analysis"]);
println!("总结: {}", results["summary"]);
println!("建议: {}", results["suggestions"]);

说明:

  • add_chain 定义输入输出变量名
  • 前一个 chain 的输出变量自动传给下一个
  • 最终返回所有变量的值

七、RAG 检索增强生成

RAG 流程: 加载文档 → 分割 → 向量化 → 存储 → 检索 → 生成回答

文档分割

长文档需要分割成小块:

rust 复制代码
use langchainrust::{
    Document, RecursiveCharacterSplitter, TextSplitter
};

let doc = Document::new("Rust 是一门系统编程语言,专注于安全性...");

// 200 字一块,50 字重叠(防止信息断裂)
let splitter = RecursiveCharacterSplitter::new(200, 50);

let chunks = splitter.split_document(&doc);

for (i, chunk) in chunks.iter().enumerate() {
    println!("片段 {}: {}", i, chunk.content);
}

说明:

  • chunk_size 每块最大长度
  • chunk_overlap 相邻块重叠长度,避免句子被切断

向量存储与检索

rust 复制代码
use langchainrust::{
    InMemoryVectorStore, OpenAIEmbeddings, SimilarityRetriever,
    RetrieverTrait, Document
};
use std::sync::Arc;

// 创建 Embeddings(生成向量)
let embeddings = Arc::new(OpenAIEmbeddings::new(OpenAIEmbeddingsConfig::default()));

// 创建向量存储
let store = Arc::new(InMemoryVectorStore::new());

// 创建检索器
let retriever = SimilarityRetriever::new(store.clone(), embeddings);

// 添加文档(自动生成向量并存储)
let docs = vec![
    Document::new("Rust 由 Mozilla 研发,2015 年发布 1.0。"),
    Document::new("所有权是 Rust 的核心特性,确保内存安全。"),
    Document::new("Cargo 是 Rust 的构建系统和包管理器。"),
];

retriever.add_documents(docs).await?;

// 检索
let question = "Rust 什么时候发布的?";
let results = retriever.retrieve(question, 3).await?;

for result in results {
    println!("相关度: {}", result.score);
    println!("内容: {}", result.document.content);
}

完整 RAG 流程

rust 复制代码
async fn rag_chat(question: &str) -> Result<String, Box<dyn std::error::Error>> {
    // 1. 检索相关文档
    let results = retriever.retrieve(question, 3).await?;
    
    // 2. 构建上下文
    let context = results.iter()
        .map(|r| r.document.content.as_str())
        .collect::<Vec<_>>()
        .join("\n");
    
    // 3. 构建提示词
    let messages = vec![
        Message::system("根据上下文回答问题。如果上下文没有相关信息,说明不知道。"),
        Message::human(&format!(
            "上下文:\n{}\n\n问题: {}", 
            context, question
        )),
    ];
    
    // 4. LLM 生成回答
    let response = llm.chat(messages, None).await?;
    
    Ok(response.content)
}

let answer = rag_chat("什么是所有权?").await?;
println!("{}", answer);

八、向量数据库

InMemoryVectorStore

内存存储,适合开发测试:

rust 复制代码
use langchainrust::vector_stores::InMemoryVectorStore;

let store = InMemoryVectorStore::new();

特点:

  • 无需配置,直接使用
  • 极快查询速度
  • 数据不持久化
  • 适合测试和小规模数据

QdrantVectorStore

生产级向量数据库:

rust 复制代码
use langchainrust::vector_stores::{
    QdrantVectorStore, QdrantConfig, QdrantDistance
};

let config = QdrantConfig::new("http://localhost:6334", "knowledge_base")
    .with_vector_size(1536)           // OpenAI embedding 维度
    .with_distance(QdrantDistance::Cosine);

let store = QdrantVectorStore::new(config).await?;

特点:

  • 支持百万级向量
  • 数据持久化
  • 支持分布式部署
  • 生产环境首选

启动 Qdrant:

bash 复制代码
docker run -p 6333:6333 -p 6334:6334 \
    -v $(pwd)/qdrant_storage:/qdrant/storage \
    qdrant/qdrant

两种存储对比

特性 InMemory Qdrant
持久化
容量 内存限制 百万级
配置 无需 需启动服务
适用场景 测试/原型 生产

九、文档加载器

PDF 加载

rust 复制代码
use langchainrust::retrieval::{PDFLoader, DocumentLoader};

let loader = PDFLoader::new("docs/manual.pdf");
let documents = loader.load().await?;

for doc in documents {
    println!("页 {}: {}", doc.metadata["page"], doc.content);
}

CSV 加载

rust 复制代码
use langchainrust::retrieval::{CSVLoader, DocumentLoader};

// 指定内容列
let loader = CSVLoader::new("data/products.csv", "description");
let documents = loader.load().await?;

for doc in documents {
    println!("产品: {}", doc.content);
    println!("行数据: {:?}", doc.metadata);
}

十、Callbacks 回调系统

追踪执行过程,集成 LangSmith。

StdOutHandler - 控制台输出

rust 复制代码
use langchainrust::callbacks::{CallbackManager, StdOutHandler};

let callback_manager = CallbackManager::new()
    .add_handler(StdOutHandler::new());

let llm = OpenAIChat::new(config)
    .with_callbacks(callback_manager);

// 执行时会打印:
// [LLM Start] Calling gpt-3.5-turbo
// [LLM End] Response received
// [Tool Start] Calculator
// [Tool End] Result: 1776

LangSmith 集成

LangSmith 是 LangChain 官方的追踪平台:

rust 复制代码
use langchainrust::callbacks::{LangSmithHandler, LangSmithConfig};

let config = LangSmithConfig {
    api_key: std::env::var("LANGCHAIN_API_KEY")?,
    project_name: "my-project".to_string(),
    endpoint: "https://api.smith.langchain.com".to_string(),
};

let handler = LangSmithHandler::new(config);
let callback_manager = CallbackManager::new().add_handler(handler);

let llm = OpenAIChat::new(llm_config)
    .with_callbacks(callback_manager);

执行轨迹会自动上传到 LangSmith,可以查看:

  • 完整调用链路
  • Token 使用统计
  • 每步耗时
  • 错误追踪

自定义回调处理器

rust 复制代码
use langchainrust::callbacks::{BaseCallbackHandler, CallbackEvent};
use async_trait::async_trait;

pub struct MetricsHandler {
    call_count: AtomicU64,
}

#[async_trait]
impl BaseCallbackHandler for MetricsHandler {
    async fn on_llm_start(&self, event: CallbackEvent) {
        self.call_count.fetch_add(1, Ordering::SeqCst);
        println!("LLM 调用开始,累计: {}", self.call_count.load(Ordering::SeqCst));
    }
    
    async fn on_llm_end(&self, event: CallbackEvent) {
        println!("LLM 调用结束");
    }
    
    async fn on_tool_start(&self, event: CallbackEvent) {
        println!("工具调用开始: {}", event.tool_name);
    }
}

十一、项目结构

复制代码
src/
├── core/                    # 核心 trait 定义
│   ├── language_models/     # BaseChatModel trait
│   ├── runnables/           # Runnable trait
│   └── tools/               # BaseTool trait
│
├── language_models/         # LLM 实现
│   └── openai/              # OpenAI 客户端
│
├── embeddings/              # 文本嵌入
│   ├── openai.rs            # OpenAI Embeddings
│   └── mock.rs              # Mock(测试用)
│
├── vector_stores/           # 向量数据库
│   ├── memory.rs            # 内存存储
│   └── qdrant.rs            # Qdrant
│
├── retrieval/               # RAG 组件
│   ├── retriever.rs         # 检索器
│   ├── splitter.rs          # 文档分割
│   └── loaders/             # 文档加载器
│       ├── pdf.rs
│       └── csv.rs
│
├── agents/                  # Agent 框架
│   └── react/               # ReActAgent
│
├── prompts/                 # 提示词模板
│
├── memory/                  # 对话记忆
│
├── chains/                  # Chain 工作流
│
├── tools/                   # 内置工具
│
├── callbacks/               # 回调系统
│
└── schema/                  # 数据结构定义

十二、测试

运行测试

bash 复制代码
# 单元测试(不需要 API Key)
cargo test --lib

# 显示输出
cargo test -- --nocapture

# 运行特定测试
cargo test test_prompt_template -- --nocapture

# 集成测试(需要 API Key)
export OPENAI_API_KEY="your-key"
cargo test --test integration_agent

# Qdrant 相关测试
cargo test --features qdrant-integration

测试分类

类型 目录 特点
单元测试 tests/unit/ 无需 API Key
集成测试 tests/integration/ 需要 API Key
E2E 测试 tests/e2e/ 完整流程

十三、常见场景示例

场景1: 多轮对话

rust 复制代码
let mut history = ChatMessageHistory::new();

loop {
    let user_input = read_user_input();
    
    // 构建完整历史
    let messages: Vec<Message> = history.messages()
        .into_iter()
        .chain(vec![Message::human(&user_input)])
        .collect();
    
    let response = llm.chat(messages, None).await?;
    
    // 保存历史
    history.add_message(Message::human(&user_input));
    history.add_message(Message::ai(&response.content));
    
    println!("AI: {}", response.content);
}

场景2: 知识库问答

rust 复制代码
// 启动时构建知识库
let knowledge_docs = load_documents("knowledge/");
retriever.add_documents(knowledge_docs).await?;

// 查询时
let question = "如何配置环境变量?";
let results = retriever.retrieve(question, 5).await?;
let context = build_context(&results);
let answer = llm.chat(build_rag_prompt(context, question), None).await?;

场景3: Agent 自动处理

rust 复制代码
let executor = AgentExecutor::new(agent, tools)
    .with_max_iterations(10);

// Agent 自动决定调用哪些工具
let result = executor.invoke("帮我分析这篇文章,计算统计数据").await?;

十四、环境配置

bash 复制代码
# 必须配置
export OPENAI_API_KEY="your-api-key"

# 可选配置
export OPENAI_BASE_URL="https://api.openai.com/v1"

# LangSmith 追踪
export LANGCHAIN_API_KEY="your-langsmith-key"
export LANGCHAIN_PROJECT="my-project"

资源链接

运行示例:

bash 复制代码
cargo run --example hello_llm
cargo run --example agent_with_tools
cargo run --example rag_demo
相关推荐
趙卋傑2 小时前
测试开发场景下常见的 MCP 服务
开发语言·python·测试工具·ai编程
阿里嘎多学长2 小时前
2026-04-11 GitHub 热点项目精选
开发语言·程序员·github·代码托管
yugi9878382 小时前
基于最大信息熵的粒子群优化算法图像分割(MATLAB实现)
开发语言·算法·matlab
yaoxin5211232 小时前
376. Java IO API - 使用 Globbing 和自定义 Filter 过滤目录内容
java·开发语言·python
飞翔的SA2 小时前
全程 Python:无需离开 Python 即可实现光速级 CUDA 加速,无需c++支持
开发语言·c++·python·nvidia·cuda
冰暮流星2 小时前
javascript之dom访问css
开发语言·javascript·css
北风toto2 小时前
java进制转换方法
java·开发语言·python
楼田莉子2 小时前
设计模式:创建型设计模式简介
服务器·开发语言·c++·设计模式
好家伙VCC2 小时前
**基于Colab的高效Python深度学习开发流程:从环境配置到模型部署全流程实战**在当前人工智
java·开发语言·python·深度学习