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
}))
}
}
说明:
name和description会被 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"
资源链接
- GitHub: https://github.com/atliliw/langchainrust
- crates.io: https://crates.io/crates/langchainrust
- API 文档: https://docs.rs/langchainrust
- 示例代码: examples/ 目录
运行示例:
bash
cargo run --example hello_llm
cargo run --example agent_with_tools
cargo run --example rag_demo