Skip to content

Latest commit

 

History

History
765 lines (612 loc) · 17.9 KB

File metadata and controls

765 lines (612 loc) · 17.9 KB

Sage Agent 优化建议报告

执行摘要

基于对 Sage Agent 代码库的全面分析,项目整体架构良好,但存在一些可以优化的领域。本报告提供了按优先级排序的具体优化建议。

代码库规模:

  • 292,320 行 Rust 代码
  • 967 个文件
  • 4 个主要 crate (sage-core, sage-tools, sage-cli, sage-sdk)

整体评估: ✅ 架构优秀,需要改进测试覆盖率和错误处理


🔴 高优先级优化 (立即执行)

1. 修复重复类型定义 (RS-05 违规)

问题: 发现 9 个重复的类型定义,违反了 DRY 原则并可能导致维护问题。

重复类型列表:

1. LspTool
   - crates/sage-tools/src/tools/code_intelligence/lsp/mod.rs:26
   - crates/sage-tools/src/tools/diagnostics/lsp.rs:40

2. ProviderConfig
   - crates/sage-core/src/config/provider/config.rs:13
   - docs/swe/timeout-configuration-example.rs:200

3. Theme
   - crates/sage-cli/src/ui/rnk_app/theme.rs:18
   - crates/sage-core/src/ui/bridge/state.rs:180

4. TimeoutConfig
   - crates/sage-core/src/types/provider.rs:32
   - docs/swe/timeout-configuration-example.rs:14

5. AppState
   - crates/sage-cli/src/signal_handler.rs:20
   - crates/sage-core/src/ui/bridge/state.rs:11

6. SseEvent
   - crates/sage-core/src/llm/sse_decoder/event.rs:5
   - crates/sage-core/src/llm/streaming.rs:308

7. Session
   - crates/sage-core/src/session/types/session.rs:20
   - crates/sage-core/src/session/types/unified/header.rs:162

8. RateLimiter
   - crates/sage-core/src/llm/rate_limiter/bucket.rs:23
   - crates/sage-core/src/recovery/rate_limiter/limiter.rs:11

9. MockEventSink
   - crates/sage-core/src/ui/traits/event_sink.rs:62
   - crates/sage-core/src/ui/traits/mod.rs:69

解决方案:

  1. 将共享类型移到 crates/sage-core/src/types/ 模块
  2. 在原位置使用 pub use crate::types::TypeName; 重新导出
  3. 如果类型语义不同,重命名以区分(如 LlmRateLimiter vs RecoveryRateLimiter
  4. 对于文档示例中的重复,添加到 .vibeguard-duplicate-types-allowlist

预期收益:

  • 消除维护负担
  • 防止类型不一致
  • 提高代码可读性

2. 减少嵌套锁模式 (RS-01 违规)

问题: 发现 25 个函数存在多次锁获取,可能导致死锁和性能问题。

严重案例:

1. crates/sage-cli/src/ui/rnk_app/mod.rs:49 fn app
   - 15 次锁获取 ⚠️ 极高风险

2. crates/sage-cli/src/ui/rnk_app/executor/command_loop.rs:18 fn executor_loop
   - 6 次锁获取

3. examples/interrupt_demo.rs:14 fn main
   - 5 次锁获取

4. crates/sage-cli/src/signal_handler.rs:54 fn start
   - 4 次锁获取

解决方案:

方案 A: 合并状态到单个锁

// 之前: 多个独立的锁
struct App {
    state: Arc<RwLock<State>>,
    config: Arc<RwLock<Config>>,
    session: Arc<RwLock<Session>>,
}

// 之后: 单个组合状态
struct AppState {
    state: State,
    config: Config,
    session: Session,
}

struct App {
    state: Arc<RwLock<AppState>>,
}

方案 B: 缩小锁作用域

// 之前: 长时间持有锁
let guard = state.read().await;
process_data(&guard);
do_something_else(&guard);

// 之后: 克隆后立即释放
let data = {
    let guard = state.read().await;
    guard.clone()
};
process_data(&data);
do_something_else(&data);

方案 C: 使用消息传递代替共享状态

// 使用 tokio::sync::mpsc 通道
enum Command {
    UpdateState(State),
    GetState(oneshot::Sender<State>),
}

// 单个任务管理状态,无需锁
async fn state_manager(mut rx: mpsc::Receiver<Command>) {
    let mut state = State::new();
    while let Some(cmd) = rx.recv().await {
        match cmd {
            Command::UpdateState(new_state) => state = new_state,
            Command::GetState(tx) => { let _ = tx.send(state.clone()); }
        }
    }
}

预期收益:

  • 消除死锁风险
  • 提高并发性能
  • 简化代码逻辑

3. 替换 unwrap/expect 为正确的错误处理 (RS-03 违规)

问题: 发现 1,105 个 unwrap/expect 调用,可能导致 panic。

统计:

  • sage-core: 740 个 .clone() 调用
  • sage-core: 1,105 个 .unwrap()/.expect() 调用

高风险区域:

// 配置加载
crates/sage-core/src/settings/locations.rs:16 处 unwrap
crates/sage-core/src/settings/loader.rs:16 处 unwrap

// 工具执行
crates/sage-core/src/tools/background_registry.rs:12 处 unwrap

// 会话管理
crates/sage-core/src/session/manager.rs:31 处 unwrap
crates/sage-core/src/session/file_tracker.rs:23  unwrap

解决方案:

模式 1: 返回 Result

// 之前
fn load_config() -> Config {
    let path = get_config_path().unwrap();
    let content = fs::read_to_string(path).unwrap();
    serde_json::from_str(&content).unwrap()
}

// 之后
fn load_config() -> Result<Config, ConfigError> {
    let path = get_config_path()
        .ok_or(ConfigError::PathNotFound)?;
    let content = fs::read_to_string(path)
        .map_err(ConfigError::IoError)?;
    serde_json::from_str(&content)
        .map_err(ConfigError::ParseError)
}

模式 2: 使用 Option 和默认值

// 之前
let value = map.get("key").unwrap();

// 之后
let value = map.get("key").unwrap_or(&default_value);
// 或
let value = map.get("key").ok_or(Error::KeyNotFound)?;

模式 3: 在测试中保留 unwrap

// 测试代码中可以使用 unwrap
#[cfg(test)]
mod tests {
    #[test]
    fn test_something() {
        let result = function().unwrap(); // OK in tests
        assert_eq!(result, expected);
    }
}

预期收益:

  • 消除生产环境 panic
  • 提供更好的错误信息
  • 提高系统稳定性

4. 拆分大文件 (>450 行)

问题: 10 个文件超过 450 行,违反了项目规范。

需要拆分的文件:

479 lines - crates/sage-tools/src/tools/infrastructure/terraform.rs
471 lines - crates/sage-tools/src/tools/infrastructure/cloud.rs
470 lines - crates/sage-tools/src/tools/database/mongodb.rs
460 lines - crates/sage-core/src/sandbox/policy/path_policy.rs
456 lines - crates/sage-core/src/prompts/registry.rs
455 lines - crates/sage-core/src/output/formatter.rs
454 lines - crates/sage-core/src/prompts/template.rs
452 lines - crates/sage-core/src/prompts/builder.rs

重构建议:

示例 1: terraform.rs (479 行)

terraform.rs (当前 479 行)
  ↓ 拆分为
terraform/
  ├── mod.rs (主入口, ~50 行)
  ├── commands.rs (命令执行, ~150 行)
  ├── state.rs (状态管理, ~100 行)
  ├── validation.rs (验证逻辑, ~100 行)
  └── types.rs (类型定义, ~80 行)

示例 2: prompts/ 模块重构

当前结构:
prompts/
  ├── registry.rs (456 行) ⚠️
  ├── template.rs (454 行) ⚠️
  ├── builder.rs (452 行) ⚠️
  └── ...

建议结构:
prompts/
  ├── registry/
  │   ├── mod.rs (核心注册逻辑, ~200 行)
  │   ├── cache.rs (缓存管理, ~150 行)
  │   └── discovery.rs (提示发现, ~100 行)
  ├── template/
  │   ├── mod.rs (模板主逻辑, ~200 行)
  │   ├── parser.rs (解析器, ~150 行)
  │   └── renderer.rs (渲染器, ~100 行)
  └── builder/
      ├── mod.rs (构建器核心, ~200 行)
      ├── sections.rs (节构建, ~150 行)
      └── validation.rs (验证, ~100 行)

预期收益:

  • 提高代码可维护性
  • 更清晰的职责分离
  • 更容易进行单元测试

🟡 中优先级优化 (1-2 个月内)

5. 提高测试覆盖率

当前状态:

  • 87 个测试文件 / 967 个源文件 = 9% 测试文件比率
  • 323 个文件有 #[cfg(test)] 块 = 33% 覆盖率
  • 无性能基准测试

目标: 将测试覆盖率提高到 60%+

缺失的测试:

A. 工具测试

// 需要测试的工具
crates/sage-tools/src/tools/infrastructure/terraform.rs - 无测试
crates/sage-tools/src/tools/infrastructure/cloud.rs - 无测试
crates/sage-tools/src/tools/database/mongodb.rs - 无测试

B. 集成测试

// 建议添加的集成测试
tests/
├── agent_execution_tests.rs (端到端代理执行)
├── session_lifecycle_tests.rs (会话生命周期)
├── multi_tool_workflow_tests.rs (多工具工作流)
├── mcp_integration_tests.rs (MCP 集成)
└── error_recovery_tests.rs (错误恢复)

C. 属性测试

// 使用 proptest 进行属性测试
#[cfg(test)]
mod property_tests {
    use proptest::prelude::*;
    
    proptest! {
        #[test]
        fn session_roundtrip(session in any::<Session>()) {
            let serialized = serde_json::to_string(&session)?;
            let deserialized: Session = serde_json::from_str(&serialized)?;
            prop_assert_eq!(session, deserialized);
        }
    }
}

D. 性能基准测试

// 使用 criterion 添加基准测试
benches/
├── tool_execution.rs
├── llm_parsing.rs
├── session_storage.rs
└── context_management.rs

// 示例
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn bench_tool_execution(c: &mut Criterion) {
    c.bench_function("bash_tool_execute", |b| {
        b.iter(|| {
            // 基准测试代码
        });
    });
}

实施计划:

  1. 第 1 周: 为所有工具添加单元测试
  2. 第 2-3 周: 添加集成测试套件
  3. 第 4 周: 添加属性测试
  4. 第 5-6 周: 添加性能基准测试
  5. 第 7-8 周: 设置 CI 覆盖率报告

6. 减少不必要的克隆

问题: 740 个 .clone() 调用可能导致性能开销。

优化策略:

策略 1: 使用引用代替克隆

// 之前
fn process_config(config: Config) {
    // 使用 config
}
let config = load_config();
process_config(config.clone()); // 不必要的克隆

// 之后
fn process_config(config: &Config) {
    // 使用 config
}
let config = load_config();
process_config(&config); // 无克隆

策略 2: 使用 Cow 处理条件所有权

use std::borrow::Cow;

// 之前
fn format_message(prefix: &str, msg: &str) -> String {
    if prefix.is_empty() {
        msg.to_string() // 总是克隆
    } else {
        format!("{}: {}", prefix, msg)
    }
}

// 之后
fn format_message<'a>(prefix: &str, msg: &'a str) -> Cow<'a, str> {
    if prefix.is_empty() {
        Cow::Borrowed(msg) // 无克隆
    } else {
        Cow::Owned(format!("{}: {}", prefix, msg))
    }
}

策略 3: 使用 Arc 共享所有权

// 之前: 多次克隆大型配置
struct Agent {
    config: Config, // 每个 agent 都克隆
}

// 之后: 共享配置
struct Agent {
    config: Arc<Config>, // 所有 agent 共享
}

热点路径分析:

// 需要分析的高频路径
1. 工具执行路径 (tools/executor.rs)
2. 会话序列化 (session/manager.rs)
3. LLM 消息构建 (agent/unified/message_builder.rs)
4. 配置加载 (config/loader/)

预期收益:

  • 减少内存分配
  • 提高执行速度
  • 降低 GC 压力

7. 优化锁竞争

问题: 605 个锁操作可能存在竞争。

分析工具:

# 使用 tokio-console 分析异步任务
cargo install tokio-console
# 在 Cargo.toml 中启用
tokio = { version = "1", features = ["full", "tracing"] }

# 运行应用并连接 console
tokio-console

优化模式:

模式 1: 读写锁优化

// 之前: 使用 Mutex (独占锁)
struct Registry {
    tools: Arc<Mutex<HashMap<String, Tool>>>,
}

// 之后: 使用 RwLock (读多写少)
struct Registry {
    tools: Arc<RwLock<HashMap<String, Tool>>>,
}

// 读操作不会互相阻塞
let tools = registry.tools.read().await;

模式 2: 使用 DashMap (无锁并发 HashMap)

// 之前: 锁保护的 HashMap
struct Cache {
    data: Arc<RwLock<HashMap<String, Value>>>,
}

// 之后: DashMap (内部分片锁)
use dashmap::DashMap;

struct Cache {
    data: Arc<DashMap<String, Value>>,
}

// 无需显式锁
cache.data.insert(key, value);
let value = cache.data.get(&key);

模式 3: 原子操作

// 之前: 锁保护的计数器
struct Counter {
    count: Arc<Mutex<u64>>,
}

// 之后: 原子计数器
use std::sync::atomic::{AtomicU64, Ordering};

struct Counter {
    count: Arc<AtomicU64>,
}

impl Counter {
    fn increment(&self) {
        self.count.fetch_add(1, Ordering::Relaxed);
    }
}

预期收益:

  • 提高并发性能
  • 减少锁等待时间
  • 提升吞吐量

🟢 低优先级优化 (3-6 个月内)

8. 模块重组

当前问题: sage-core 有 32 个顶层模块,可以更好地组织。

建议重组:

当前:
sage-core/src/
├── agent/
├── llm/
├── tools/
├── config/
├── settings/
├── session/
├── trajectory/
├── memory/
├── learning/
├── ... (32 个模块)

建议:
sage-core/src/
├── execution/        (执行层)
│   ├── agent/
│   ├── llm/
│   └── tools/
├── configuration/    (配置层)
│   ├── config/
│   └── settings/
├── persistence/      (持久化层)
│   ├── session/
│   ├── trajectory/
│   └── storage/
├── intelligence/     (智能层)
│   ├── memory/
│   └── learning/
├── integration/      (集成层)
│   ├── mcp/
│   ├── plugins/
│   └── hooks/
└── types/           (共享类型)

9. 工具开发框架

问题: 工具实现有大量样板代码。

解决方案: 创建派生宏

// 使用派生宏简化工具定义
#[derive(Tool)]
#[tool(
    name = "bash",
    description = "Execute bash commands",
    category = "system"
)]
struct BashTool {
    #[tool_param(required = true, description = "Command to execute")]
    command: String,
    
    #[tool_param(default = "120000")]
    timeout: u64,
}

#[async_trait]
impl ToolExecute for BashTool {
    async fn execute(&self, ctx: &ToolContext) -> ToolResult {
        // 实现逻辑
    }
}

生成的代码:

  • 自动实现 Tool trait
  • 自动生成参数验证
  • 自动生成 JSON schema
  • 自动注册到工具注册表

10. 性能监控和分析

添加性能追踪:

// 使用 tracing 进行性能分析
use tracing::{instrument, info_span};

#[instrument(skip(self))]
async fn execute_tool(&self, tool: &Tool) -> Result<Output> {
    let _span = info_span!("tool_execution", tool = %tool.name()).entered();
    // 执行逻辑
}

添加指标收集:

// 使用 metrics crate
use metrics::{counter, histogram, gauge};

counter!("tool.executions", 1, "tool" => tool.name());
histogram!("tool.duration_ms", duration.as_millis() as f64);
gauge!("active_sessions", session_count as f64);

📊 优化优先级矩阵

优化项 影响 难度 优先级 预计时间
修复重复类型 🔴 P0 2-3 天
减少嵌套锁 🔴 P0 1-2 周
替换 unwrap 🔴 P0 2-3 周
拆分大文件 🔴 P0 1 周
提高测试覆盖率 🟡 P1 6-8 周
减少克隆 🟡 P1 2-3 周
优化锁竞争 🟡 P1 2-3 周
模块重组 🟢 P2 4-6 周
工具框架 🟢 P2 4-6 周
性能监控 🟢 P2 2-3 周

🎯 实施路线图

第 1 个月 (高优先级)

  • ✅ 周 1: 修复重复类型定义
  • ✅ 周 2: 拆分大文件
  • ✅ 周 3-4: 开始替换 unwrap/expect

第 2-3 个月 (中优先级)

  • ✅ 周 5-6: 减少嵌套锁模式
  • ✅ 周 7-10: 提高测试覆盖率到 40%
  • ✅ 周 11-12: 减少不必要的克隆

第 4-6 个月 (低优先级)

  • ✅ 周 13-16: 模块重组
  • ✅ 周 17-20: 创建工具开发框架
  • ✅ 周 21-24: 添加性能监控

📈 预期收益

性能提升

  • 内存使用: 减少 20-30% (通过减少克隆)
  • 并发性能: 提升 30-50% (通过优化锁)
  • 启动时间: 减少 10-15% (通过优化配置加载)

代码质量

  • 测试覆盖率: 从 33% 提升到 60%+
  • 代码重复: 减少 15-20%
  • 维护性: 显著提升

稳定性

  • Panic 风险: 减少 80%+ (通过替换 unwrap)
  • 死锁风险: 减少 90%+ (通过优化锁模式)
  • 错误处理: 全面改进

🛠️ 工具和自动化

持续集成检查

# .github/workflows/quality.yml
name: Code Quality

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run VibeGuard
        run: make guard-strict
      - name: Run Clippy
        run: cargo clippy -- -D warnings
      - name: Check test coverage
        run: cargo tarpaulin --out Xml
      - name: Upload coverage
        uses: codecov/codecov-action@v2

Pre-commit Hooks

# .git/hooks/pre-commit
#!/bin/bash
make guard-strict || exit 1
cargo clippy -- -D warnings || exit 1
cargo test || exit 1

📚 参考资源

Rust 性能优化

测试最佳实践

并发模式


结论

Sage Agent 是一个架构良好的项目,具有坚实的基础。通过实施这些优化建议,可以显著提升代码质量、性能和可维护性。

关键要点:

  1. 优先修复高风险问题(重复类型、嵌套锁、unwrap)
  2. 逐步提高测试覆盖率
  3. 持续监控和优化性能
  4. 保持代码库的清洁和组织

下一步行动:

  1. 审查并批准此优化计划
  2. 创建 GitHub Issues 跟踪每个优化项
  3. 分配资源和时间表
  4. 开始实施高优先级优化

报告生成时间: 2026-02-23 分析工具: VibeGuard, Cargo Clippy, 自定义分析脚本