feat(llm): 添加 LLM 调用周期核心模块

新增 LLM 调用生命周期引擎,包含 Provider 抽象、OpenAI 兼容实现、
可重试机制及 Token 用量追踪。移除原有的占位测试代码。
添加所需的 Rust 依赖(tokio、reqwest、serde 等)。
This commit is contained in:
徐涛
2026-05-12 06:06:24 +08:00
parent b21e163be0
commit 91d32a6a82
10 changed files with 788 additions and 13 deletions
+149
View File
@@ -0,0 +1,149 @@
mod retry;
pub mod usage;
pub use retry::RetryConfig;
pub use usage::{CostTracker, Usage};
use crate::llm::cycle::retry::should_retry;
use crate::llm::error::LlmError;
use crate::llm::provider::LlmProvider;
use crate::llm::types::{ChatRequest, ChatResponse, ContentBlock, Message, Role, ToolDefinition};
/// LLM 生命周期引擎的配置。
pub struct CycleConfig {
/// 使用的模型名称。
pub model: String,
/// 最大输出 token 数。
pub max_tokens: Option<u32>,
/// 采样温度。
pub temperature: Option<f32>,
/// 最大对话轮数(预留,暂未使用)。
pub max_turns: Option<u32>,
/// 重试策略配置。
pub retry: RetryConfig,
}
impl Default for CycleConfig {
fn default() -> Self {
Self {
model: String::from("gpt-4o"),
max_tokens: None,
temperature: None,
max_turns: None,
retry: RetryConfig::default(),
}
}
}
/// LLM 调用生命周期引擎。
///
/// 管理一次多轮交互的完整生命周期,包括:
/// - 消息历史维护
/// - Token 用量追踪
/// - 自动重试
pub struct LlmCycle {
provider: Box<dyn LlmProvider>,
config: CycleConfig,
usage: CostTracker,
messages: Vec<Message>,
system_prompt: Option<String>,
}
impl LlmCycle {
/// 创建新的生命周期引擎。
pub fn new(provider: Box<dyn LlmProvider>, config: CycleConfig) -> Self {
Self {
provider,
config,
usage: CostTracker::default(),
messages: Vec::new(),
system_prompt: None,
}
}
/// 设置系统提示词(Builder 模式)。
pub fn with_system_prompt(mut self, prompt: String) -> Self {
self.system_prompt = Some(prompt);
self
}
/// 获取 Token 用量追踪器引用。
pub fn usage(&self) -> &CostTracker {
&self.usage
}
/// 获取当前消息历史。
pub fn messages(&self) -> &[Message] {
&self.messages
}
/// 清空消息历史。
pub fn clear_messages(&mut self) {
self.messages.clear();
}
/// 重置 Token 用量统计。
pub fn reset_usage(&mut self) {
self.usage.reset();
}
/// 提交一条用户消息并获取模型响应。
///
/// 流程:
/// 1. 将用户消息追加到消息历史
/// 2. 构建 ChatRequest
/// 3. 使用重试循环调用 provider.chat()
/// 4. 将助手回复追加到消息历史
/// 5. 累计 token 用量
/// 6. 返回 ChatResponse
pub async fn submit(
&mut self,
prompt: String,
tools: Vec<ToolDefinition>,
) -> Result<ChatResponse, LlmError> {
self.messages.push(Message {
role: Role::User,
content: vec![ContentBlock::Text { text: prompt }],
});
let mut attempts = 0;
loop {
let request = self.build_request(&tools);
match self.provider.chat(request).await {
Ok(response) => {
self.messages.push(Message {
role: Role::Assistant,
content: response.message.content.clone(),
});
self.usage.add(&response.usage);
return Ok(response);
}
Err(e) if should_retry(&e) && attempts < self.config.retry.max_retries => {
attempts += 1;
let delay = self.config.retry.compute_delay(attempts);
tokio::time::sleep(delay).await;
}
Err(e) => {
return Err(e);
}
}
}
}
/// 根据当前状态构建 ChatRequest。
fn build_request(&self, tools: &[ToolDefinition]) -> ChatRequest {
ChatRequest {
model: self.config.model.clone(),
messages: self.messages.clone(),
system_prompt: self.system_prompt.clone(),
tools: tools.to_vec(),
max_tokens: self.config.max_tokens,
temperature: self.config.temperature,
extra_body: None,
}
}
}