【MRAG】使用RAG技术增强AI回复的实时性和准确性

概要

我们用2周时间,使用Rust+Python开发了这个名为MRAG的知识库系统。

体验地址:https://mrag.coderbox.cn/

支持私有化部署。

技术栈

  • 知识库:使用向量数据库milvus,Python的API。目前我们主要维护了一个高考知识库,其中收集整理了近5年的高考相关内容,包含录取分数线、学校信息、专业信息、各专业在各个省份的录取线等,仍在完善中。我们通过对这些非结构化的数据归一化为统一的特征向量,来增强模型的知识能力。其他知识库正在补充中。
  • 服务端:使用Rust(Rocket+Rbatis)。首次尝试使用Rust进行完整的服务端开发,也遇到了不小的阻力。我们使用默认的配置,在x86_64-unknown-linux-musl编译后,可执行文件仅有29M,运行内存稳定在70M左右,相比于Java小了很多。
  • 前端:仍然使用Vue + Element UI。
  • 模型:使用第三方模型,如Deepseek、Qwen等

遇到的问题

ORM框架的抉择

我们最初尝试了diesel,diesel性能确实很不错,但是鉴于不能很好的支持复杂和动态SQL,于是我们选择了Rbatis,一是因为它和MyBatis比较像,可以很快的切换过来,二是因为支持动态SQL,可以很方便的写复杂SQL,虽然性能比diesel差一点点(大概慢5~10ms左右),但是利于开发。

Rbatis事务问题

使用Rbatis时,目前需要手动提交事务,有时候会忘记提交,所以我们简单封装了一个事务函数,当业务逻辑执行成功后自动提交事务,否则回滚。

rust 复制代码
pub async fn tx<'a, F, R, RV>(exec: F) -> AppResult<RV>
where
    F: Fn(RBatisTxExecutor) -> R,
    R: Future<Output = AppResult<RV>>,
{
    let tx = match Pool::get()?.acquire_begin().await {
        Ok(tx) => tx,
        Err(e) => {
            log::error!("事务异常: {}", e);
            return Err(db_error!(e));
        }
    };

    let result = exec(tx.clone()).await;

    match result {
        Ok(result) => {
            match tx.commit().await {
                Ok(_) => log::debug!("事务提交成功,事务ID:{}", tx.tx_id),
                Err(e) => {
                    log::error!("事务提交失败,事务ID:{}, 原因: {}", tx.tx_id, e);
                    return Err(db_error!(e));
                }
            };
            Ok(result)
        }
        Err(e) => {
            log::debug!("事务闭包执行失败,即将回滚,错误原因: {}", e);
            match tx.rollback().await {
                Ok(_) => {
                    log::debug!("事务回滚成功,事务ID:{}", tx.tx_id);
                    Err(e)
                }
                Err(e) => {
                    log::error!("事务回滚失败,事务ID:{}, 原因: {}", tx.tx_id, e);
                    Err(db_error!(e))
                }
            }
        }
    }
}

Rocket的SSE连接及恢复

Web框架我们使用的是Rocket,Rocket对SSE的支持比较简单,不能向SpringBoot那样直接拿到连接对象,所以我们自己封装了一个SSE的连接池,并支持了连接恢复,使得页面刷新后仍然能接收流式输出。这个问题,在这里也有提到。

让人烦躁的错误处理

在Rust中,无法抛出一个异常,也无法捕获一个异常,需要我们手动处理每一个异常,因此不得不让人思考应该返回什么错误,如何处理这个错误。我们可以使用anyhow传递大部分错误,但是有些错误我们不希望传递到前端展示,比如数据库错误等,以防泄露一些信息,因为我们需要对错误进行分类。

我们对Error分为了以下几种类型:

rust 复制代码
#[derive(Debug, thiserror::Error)]
pub enum AppError {
    /// 初始化错误
    #[error("init error: {0}")]
    InitError(String),
    /// 数据库错误
    #[error("db error: {0}")]
    DbError(rbatis::Error),
    /// 系统错误
    #[error("system error: {0}")]
    SystemError(anyhow::Error),
    /// 业务错误。改错误不会传递到前端,仅内部科可见。所有的业务逻辑处理均使用该错误。
    #[error("business error: {0}")]
    BusinessError(anyhow::Error),
    /// 提示类错误。该错误会传递到前端,前端显示给用户,且不会输出到日志。
    #[error("{0}")]
    MessageError(String),
    /// 提示类错误。该错误会传递到前端,并指定一个错误编码,前端显示给用户,且不会输出到日志。
    #[error("{1}")]
    MessageCodeError(i32, String),
}

其中只有MessageError错误需要传递到前端展示。

为此我们也提供了一些宏来简化错误处理,例如:

rust 复制代码
#[macro_export]
macro_rules! message_error {
    ($e:expr) => {
        AppError::MessageError($e.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        AppError::MessageError(format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! message_code_error {
    ($code:expr, $e:expr) => {
        AppError::MessageCodeError($code, $e.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        AppError::MessageCodeError($code, format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! business_error {
    ($e:expr) => {
        AppError::BusinessError(anyhow!($e))
    };
    ($fmt:expr, $($arg:tt)*) => {
        AppError::BusinessError(anyhow!(format!($fmt, $($arg)*)))
    };
}

#[macro_export]
macro_rules! db_error {
    ($e:expr) => {
        AppError::DbError($e)
    };
}

于是对于大部分错误,我们可以将原始的错误类型包装为AppError,例如:map_err(|e| db_error!(e))?

移动端UI适配

移动端做的比较少,在UI适配时花了不少时间,但效果仍然不理想。可能是因为移动端也使用的时ElementUI,而ElementUI对移动端的支持不是很好的缘故吧。

Rust服务端项目架构问题

前些年使用Java开发后端服务均使用微服务架构,而现在使用Rust开发时时常回想需不需要微服务架构,目前来看是不需要的,一是因为数据量不大,二是因为Rust的性能较好,且比较稳定,可能不需要拆分服务,三是因为Cargo的强大的包管理能力,也很利于分模块或分团队开发,能够在不拆分服务的情况下降低系统耦合度。所以目前来看不需要微服务的,如果向提高并行处理能力,多节点部署即可。

最后

我们后续将开发更简单易用的RAG产品,让所有人都能够建立自己的个性化知识库。

相关推荐
muyouking113 小时前
4.Rust+Axum Tower 中间件实战:从集成到自定义
开发语言·中间件·rust
Source.Liu4 小时前
【TeamFlow】4.2 Yew库详细介绍
rust·yew
techdashen7 小时前
性能比拼: Rust vs Zig vs Go
开发语言·golang·rust
muyouking1110 小时前
9.Rust+Axum 测试驱动开发与性能优化全攻略
驱动开发·性能优化·rust
阿阳热爱前端11 小时前
BongoCat 桌宠全新升级!开源 × 跨平台,快来撸猫!
前端·rust·app
余俊晖13 小时前
再看开源多模态RAG的视觉文档(OCR-Free)检索增强生成方案-VDocRAG
多模态·rag
阿廖沙102416 小时前
前端开发工程师的Rust入门
rust
pumpkin845141 天前
学习笔记十七——Rust 支持面向对象编程吗?
笔记·学习·rust
Source.Liu1 天前
【TeamFlow】3 Rust 与 WebAssembly (Wasm) 深度应用指南
rust·wasm