rucora-core 0.1.2

Core abstractions and types for the rucora LLM agent framework
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
//! Agent(智能体)核心抽象模块
//!
//! # 概述
//!
//! 本模块定义了 Agent 的抽象接口。Agent 是能够思考、决策和行动的自主实体。
//!
//! # 核心概念
//!
//! ## 决策与执行分离
//!
//! - **Agent trait**: 负责思考、决策、规划(大脑)
//! - **AgentExecutor trait**: 负责执行、调用、编排(身体)
//!
//! Agent 通过 `think()` 方法返回决策,`AgentExecutor` 或其他执行器负责执行。

pub mod types;

/// 运行时适配器抽象
pub mod runtime_adapter;

use async_trait::async_trait;
use futures_util::stream::BoxStream;
use serde_json::Value;

use crate::channel::types::ChannelEvent;

/// Agent 决策结果。
///
/// Agent 通过 `think()` 方法返回决策,Runtime 或其他执行器负责执行。
#[derive(Debug, Clone)]
pub enum AgentDecision {
    /// 调用 LLM 进行对话。
    Chat {
        /// 对话请求。
        request: Box<crate::provider::types::ChatRequest>,
    },
    /// 调用工具。
    ToolCall {
        /// 工具名称。
        name: String,
        /// 工具输入参数。
        input: Value,
    },
    /// 直接返回结果。
    Return(Value),
    /// 需要更多思考(继续循环)。
    ThinkAgain,
    /// 停止执行。
    Stop,
}

/// Agent 上下文。
///
/// 包含 Agent 思考所需的所有信息。
#[derive(Debug, Clone)]
pub struct AgentContext {
    /// 用户原始输入。
    pub input: AgentInput,
    /// 对话历史。
    pub messages: Vec<crate::provider::types::ChatMessage>,
    /// 工具调用结果。
    pub tool_results: Vec<ToolResult>,
    /// 当前步骤数。
    pub step: usize,
    /// 最大步骤数。
    pub max_steps: usize,
}

impl AgentContext {
    /// 创建新的上下文。
    pub fn new(input: AgentInput, max_steps: usize) -> Self {
        Self {
            input,
            messages: Vec::new(),
            tool_results: Vec::new(),
            step: 0,
            max_steps,
        }
    }

    /// 添加消息到历史。
    pub fn add_message(&mut self, message: crate::provider::types::ChatMessage) {
        self.messages.push(message);
    }

    /// 添加工具调用结果。
    pub fn add_tool_result(&mut self, tool_name: String, result: Value) {
        self.tool_results.push(ToolResult { tool_name, result });
    }

    /// 创建默认的对话请求。
    ///
    /// 所有 LLM 参数(temperature 等)默认为 None,使用模型默认值。
    /// 可通过 `default_chat_request_with()` 传入自定义参数。
    pub fn default_chat_request(&self) -> crate::provider::types::ChatRequest {
        crate::provider::types::ChatRequest {
            messages: self.messages.clone(),
            model: None,
            tools: None,
            temperature: None,
            max_tokens: None,
            response_format: None,
            metadata: None,
            top_p: None,
            top_k: None,
            frequency_penalty: None,
            presence_penalty: None,
            stop: None,
            extra: None,
        }
    }

    /// 创建带 LLM 参数的对话请求。
    pub fn default_chat_request_with(
        &self,
        params: &crate::provider::types::LlmParams,
    ) -> crate::provider::types::ChatRequest {
        let mut request = self.default_chat_request();
        params.apply_to(&mut request);
        request
    }
}

/// 工具调用结果。
#[derive(Debug, Clone)]
pub struct ToolResult {
    /// 工具名称。
    pub tool_name: String,
    /// 工具返回结果。
    pub result: Value,
}

/// Agent 输入。
///
/// 用于向 Agent 传递用户请求。
///
/// # 使用示例
///
/// ```rust
/// use rucora_core::agent::AgentInput;
///
/// // 简单文本输入
/// let input = AgentInput::new("你好");
///
/// // 使用 builder 模式
/// let input = AgentInput::builder("帮我查询天气")
///     .with_context("user_location", "北京")
///     .build();
/// ```
#[derive(Debug, Clone)]
pub struct AgentInput {
    /// 文本输入。
    pub text: String,
    /// 额外上下文数据。
    pub context: serde_json::Value,
}

impl AgentInput {
    /// 从文本创建输入。
    pub fn new(text: impl Into<String>) -> Self {
        Self {
            text: text.into(),
            context: serde_json::Value::Object(serde_json::Map::new()),
        }
    }

    /// 从文本和上下文创建输入。
    pub fn with_context(text: impl Into<String>, context: serde_json::Value) -> Self {
        Self {
            text: text.into(),
            context,
        }
    }

    /// 创建 builder。
    pub fn builder(text: impl Into<String>) -> AgentInputBuilder {
        AgentInputBuilder::new(text)
    }

    /// 获取文本内容。
    pub fn text(&self) -> &str {
        &self.text
    }

    /// 获取上下文数据。
    pub fn context(&self) -> &serde_json::Value {
        &self.context
    }
}

/// AgentInput 构建器。
pub struct AgentInputBuilder {
    text: String,
    context: serde_json::Value,
}

impl AgentInputBuilder {
    /// 创建新的构建器。
    pub fn new(text: impl Into<String>) -> Self {
        Self {
            text: text.into(),
            context: serde_json::Value::Object(serde_json::Map::new()),
        }
    }

    /// 添加上下文键值对。
    pub fn with_context(
        mut self,
        key: impl Into<String>,
        value: impl Into<serde_json::Value>,
    ) -> Self {
        if let serde_json::Value::Object(ref mut map) = self.context {
            map.insert(key.into(), value.into());
        }
        self
    }

    /// 设置完整上下文。
    pub fn context(mut self, context: serde_json::Value) -> Self {
        self.context = context;
        self
    }

    /// 构建输入。
    pub fn build(self) -> AgentInput {
        AgentInput {
            text: self.text,
            context: self.context,
        }
    }
}

impl From<String> for AgentInput {
    fn from(text: String) -> Self {
        Self::new(text)
    }
}

impl From<&str> for AgentInput {
    fn from(text: &str) -> Self {
        Self::new(text)
    }
}

/// Agent 输出。
///
/// 包含 Agent 执行的结果和相关信息。
///
/// # 字段说明
///
/// - `value`: 主要输出内容(通常是 JSON 格式)
/// - `messages`: 对话历史
/// - `tool_calls`: 工具调用记录
///
/// # 使用示例
///
/// ```rust
/// use rucora_core::agent::AgentOutput;
///
/// // 访问输出内容
/// let output: AgentOutput = get_output();
///
/// // 提取文本内容
/// if let Some(content) = output.value.get("content").and_then(|v| v.as_str()) {
///     println!("回复:{}", content);
/// }
///
/// // 访问对话历史
/// println!("对话轮数:{}", output.messages.len());
///
/// // 访问工具调用
/// println!("工具调用次数:{}", output.tool_calls.len());
/// ```
#[derive(Debug, Clone)]
pub struct AgentOutput {
    /// 主要输出内容(通常是 JSON 格式,包含 `content` 字段)。
    pub value: Value,
    /// 对话历史。
    pub messages: Vec<crate::provider::types::ChatMessage>,
    /// 工具调用记录。
    pub tool_calls: Vec<ToolCallRecord>,
    /// Token 使用统计(累计所有 LLM 调用的 usage)。
    pub usage: Option<crate::provider::types::Usage>,
}

impl AgentOutput {
    /// 创建新的输出。
    pub fn new(value: Value) -> Self {
        Self {
            value,
            messages: Vec::new(),
            tool_calls: Vec::new(),
            usage: None,
        }
    }

    /// 创建带历史的输出。
    pub fn with_history(
        value: Value,
        messages: Vec<crate::provider::types::ChatMessage>,
        tool_calls: Vec<ToolCallRecord>,
    ) -> Self {
        Self {
            value,
            messages,
            tool_calls,
            usage: None,
        }
    }

    /// 创建带 usage 的输出。
    pub fn with_usage(
        value: Value,
        messages: Vec<crate::provider::types::ChatMessage>,
        tool_calls: Vec<ToolCallRecord>,
        usage: Option<crate::provider::types::Usage>,
    ) -> Self {
        Self {
            value,
            messages,
            tool_calls,
            usage,
        }
    }

    /// 获取文本内容(如果存在)。
    pub fn text(&self) -> Option<&str> {
        self.value.get("content").and_then(|v| v.as_str())
    }

    /// 获取文本内容,如果不存在则返回空字符串。
    pub fn text_unwrap(&self) -> &str {
        self.text().unwrap_or("")
    }

    /// 获取文本内容,如果不存在则返回默认值。
    pub fn text_or<'a>(&'a self, default: &'a str) -> &'a str {
        self.text().unwrap_or(default)
    }

    /// 消费自身,获取文本内容的所有权。
    pub fn into_text(self) -> String {
        self.text().map(String::from).unwrap_or_default()
    }

    /// 获取对话历史长度。
    pub fn message_count(&self) -> usize {
        self.messages.len()
    }

    /// 获取工具调用次数。
    pub fn tool_call_count(&self) -> usize {
        self.tool_calls.len()
    }

    /// 获取 Token 使用统计。
    pub fn usage(&self) -> Option<&crate::provider::types::Usage> {
        self.usage.as_ref()
    }

    /// 获取总 Token 数。
    pub fn total_tokens(&self) -> u32 {
        self.usage.as_ref().map_or(0, |u| u.total_tokens)
    }

    /// 获取提示词 Token 数。
    pub fn prompt_tokens(&self) -> u32 {
        self.usage.as_ref().map_or(0, |u| u.prompt_tokens)
    }

    /// 获取输出 Token 数。
    pub fn completion_tokens(&self) -> u32 {
        self.usage.as_ref().map_or(0, |u| u.completion_tokens)
    }

    /// 格式化 Token 使用信息。
    pub fn usage_summary(&self) -> String {
        match &self.usage {
            Some(u) => format!(
                "Tokens: {} total ({} prompt + {} completion)",
                u.total_tokens, u.prompt_tokens, u.completion_tokens
            ),
            None => "Tokens: N/A".to_string(),
        }
    }
}

impl std::fmt::Display for AgentOutput {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.text() {
            Some(text) => write!(f, "{text}"),
            None => Ok(()),
        }
    }
}

/// 工具调用记录。
#[derive(Debug, Clone)]
pub struct ToolCallRecord {
    /// 工具名称。
    pub name: String,
    /// 输入参数。
    pub input: Value,
    /// 返回结果。
    pub result: Value,
}

/// Agent trait - 智能体的抽象接口。
///
/// Agent 负责思考、决策和规划。它可以:
/// - 独立运行(处理简单任务)
/// - 使用内置执行能力(处理复杂任务)
/// - 调用 Tool/MCP/Skill/A2A 等外部能力
///
/// # 设计原则
///
/// Agent trait 只定义决策接口(`think`),执行能力(`run`/`run_stream`)由具体实现提供。
///
/// ## 决策与执行分离
///
/// - **决策层** (`think`): 每个 Agent 类型有不同的思考策略
/// - **执行层** (`run`/`run_stream`): 所有 Agent 共享相同的执行能力
///
/// ## 使用方式
///
/// ```rust,no_run
/// use rucora_core::agent::{Agent, AgentContext, AgentDecision, AgentInput, AgentOutput};
/// use async_trait::async_trait;
///
/// struct MyAgent;
///
/// #[async_trait]
/// impl Agent for MyAgent {
///     async fn think(&self, context: &AgentContext) -> AgentDecision {
///         // 自定义决策逻辑
///         AgentDecision::Return(serde_json::json!({"content": "Hello"}))
///     }
///
///     fn name(&self) -> &str { "my_agent" }
/// }
/// ```
///
/// # 内置执行能力
///
/// 如果 Agent 需要工具调用、流式输出等能力,可以组合 `DefaultExecution`:
///
/// ```rust,no_run
/// use rucora::agent::execution::DefaultExecution;
/// use rucora_core::agent::Agent;
///
/// struct MyAgent {
///     execution: DefaultExecution,
///     // ... 其他字段
/// }
///
/// impl Agent for MyAgent {
///     // ... 实现 think 方法
///     
///     // DefaultExecution 提供默认的 run/run_stream 实现
/// }
/// ```
#[async_trait]
pub trait Agent: Send + Sync {
    /// 思考:分析当前情况,决定下一步行动。
    ///
    /// 这是 Agent 的核心方法,返回决策结果。
    async fn think(&self, context: &AgentContext) -> AgentDecision;

    /// 获取 Agent 名称。
    fn name(&self) -> &str;

    /// 获取 Agent 描述(可选)。
    ///
    /// 返回 Agent 的简短描述,用于调试和日志。
    fn description(&self) -> Option<&str> {
        None
    }

    /// 运行 Agent(非流式)。
    ///
    /// 默认实现适用于简单场景(直接返回结果)。
    /// 需要工具调用等复杂能力的 Agent 应该使用 `run_with()` 方法配合 `AgentExecutor`。
    ///
    /// # 默认行为
    ///
    /// 默认实现会循环调用 `think()` 直到返回 `Return` 或 `Stop`。
    /// 如果返回 `Chat` 或 `ToolCall`,会返回错误(需要 Runtime 支持)。
    ///
    /// # 配置
    ///
    /// 默认最大步骤数为 20。如果需要自定义,请使用 `run_with()` 方法。
    ///
    /// # 示例
    ///
    /// ```rust,no_run
    /// use rucora_core::agent::{Agent, AgentInput};
    ///
    /// # async fn example(agent: &dyn Agent) -> Result<(), Box<dyn std::error::Error>> {
    /// let output = agent.run(AgentInput::new("你好")).await?;
    /// # Ok(())
    /// # }
    /// ```
    async fn run(&self, input: AgentInput) -> Result<AgentOutput, AgentError> {
        // 默认最大步骤数:20
        const DEFAULT_MAX_STEPS: usize = 20;

        let mut context = AgentContext::new(input.clone(), DEFAULT_MAX_STEPS);

        loop {
            let decision = self.think(&context).await;

            match decision {
                AgentDecision::Return(value) => {
                    return Ok(AgentOutput::with_history(
                        value,
                        context.messages,
                        Vec::new(),
                    ));
                }
                AgentDecision::Stop => {
                    return Ok(AgentOutput::with_history(
                        Value::Null,
                        context.messages,
                        Vec::new(),
                    ));
                }
                AgentDecision::ThinkAgain => {
                    context.step += 1;
                    if context.step >= context.max_steps {
                        return Err(AgentError::MaxStepsExceeded {
                            max_steps: context.max_steps,
                        });
                    }
                }
                AgentDecision::Chat { request: _ } => {
                    // Chat 决策需要 LLM 调用,默认实现无法处理
                    return Err(AgentError::RequiresRuntime);
                }
                AgentDecision::ToolCall { .. } => {
                    // ToolCall 决策需要工具执行,默认实现无法处理
                    return Err(AgentError::RequiresRuntime);
                }
            }
        }
    }

    /// 运行 Agent(流式)。
    ///
    /// 默认实现返回错误(需要具体实现提供流式能力)。
    ///
    /// # 示例
    ///
    /// ```rust,no_run
    /// use rucora_core::agent::{Agent, AgentInput};
    /// use futures_util::StreamExt;
    ///
    /// # async fn example(agent: &dyn Agent) -> Result<(), Box<dyn std::error::Error>> {
    /// let mut stream = agent.run_stream(AgentInput::new("你好"));
    /// while let Some(event) = stream.next().await {
    ///     match event? {
    ///         rucora_core::channel::types::ChannelEvent::TokenDelta(delta) => {
    ///             print!("{}", delta.delta);
    ///         }
    ///         _ => {}
    ///     }
    /// }
    /// # Ok(())
    /// # }
    /// ```
    fn run_stream(
        &self,
        _input: AgentInput,
    ) -> BoxStream<'static, Result<ChannelEvent, AgentError>> {
        use futures_util::stream;
        Box::pin(stream::once(async {
            Err(AgentError::Message("此 Agent 不支持流式输出".to_string()))
        }))
    }

    /// 运行 Agent(使用执行器)。
    ///
    /// 此方法允许使用外部执行器来运行 Agent。
    /// 这是实现 dyn 兼容的关键方法。
    ///
    /// # 参数
    ///
    /// - `executor`: 执行器,负责实际的运行逻辑
    /// - `input`: 用户输入
    ///
    /// # 示例
    ///
    /// ```rust,no_run
    /// use rucora_core::agent::{Agent, AgentInput, AgentExecutor};
    ///
    /// # async fn example(agent: &impl Agent, executor: &dyn AgentExecutor) -> Result<(), Box<dyn std::error::Error>> {
    /// let output = agent.run_with(executor, AgentInput::new("你好")).await?;
    /// # Ok(())
    /// # }
    /// ```
    async fn run_with(
        &self,
        executor: &dyn AgentExecutor,
        input: AgentInput,
    ) -> Result<AgentOutput, AgentError>
    where
        Self: Sized,
    {
        executor.run(self, input).await
    }
}

/// Agent 执行器 trait
///
/// 用于执行 Agent 的运行逻辑,支持工具调用、流式输出等。
/// 这是实现 dyn 兼容的关键。
#[async_trait]
pub trait AgentExecutor: Send + Sync {
    /// 运行 Agent
    async fn run(&self, agent: &dyn Agent, input: AgentInput) -> Result<AgentOutput, AgentError>;

    /// 流式运行 Agent
    ///
    /// 注意:由于生命周期限制,此方法不支持 Agent 决策。
    /// 它只执行简单的工具调用循环。
    fn run_stream(&self, input: AgentInput)
    -> BoxStream<'static, Result<ChannelEvent, AgentError>>;
}

// 重新导出统一的 AgentError 定义
pub use crate::error::AgentError;

/// 重新导出运行时适配器相关类型
pub use runtime_adapter::{
    LogLevel, NativeRuntimeAdapter, RestrictedRuntimeAdapter, RuntimeAdapter, RuntimeCapabilities,
    RuntimeError, RuntimePlatform, ShellResult,
};