【AI Agent系列】【阿里AgentScope框架】AgentScope中的RAG实现思路(三)之 RAGAgent实现源码解读(可直接复用)

大家好,我是 同学小张,+v: jasper_8017 一起交流,持续学习C++进阶、OpenGL、WebGL知识技能AI大模型应用实战案例,持续分享,欢迎大家点赞+关注,共同学习和进步。


对于多智能体框架来说,RAG似乎并不是其框架内的内容,所以类似 AgentScope、LangGraph、MetaGPT 等框架,都没有提供特别明确的RAG实现流程。但RAG作为当前提高大模型能力、知识库问答等方向的主流方法,还是很重要的。所以这些框架也提供了实现RAG的一些示例。

前面文章我们分别在AgentScope中用 LangChain 和 LlamaIndex 封装了RAG的流程,本文来学习下AgentScope中如何使用这个RAG的流程:实现一个 RAG Agent。以后如果需要,可以直接复用。

文章目录

  • 1. 自定义Agent实现 - RAGAgentBase
    • 1.1 框架
    • 1.2 初始化
    • 1.3 reply 函数实现
  • 2. LlamaIndexAgent 实现
    • 2.1 LlamaIndexAgent 的初始化
    • 2.2 init_rag 函数实现
  • 3. LlamaIndexAgent 使用
    • 3.1 LlamaIndexAgent 配置
  • 4. 总结

1. 自定义Agent实现 - RAGAgentBase

在AgentScope中实现自定义的Agent,还是原来的套路:继承 AgentBase,实现 __init__ 函数和 reply 函数。

1.1 框架

示例代码中,首先是定义了一个 RAGAgentBase,这是为了后面方便使用不同的方式实现RAG基本流程。然后在此基础上,定义了 LlamaIndexAgent。

RAGAgentBase 就是我们实现的自定义Agent,它继承了 AgentBase。框架如下:

class RAGAgentBase(AgentBase, ABC):
    """
    Base class for RAG agents
    """

    def __init__(
        self,
        name: str,
        sys_prompt: str,
        model_config_name: str,
        emb_model_config_name: str,
        memory_config: Optional[dict] = None,
        rag_config: Optional[dict] = None,
    ) -> None:
        ......

    @abstractmethod
    def init_rag(self) -> RAGBase:
        ......

    def reply(
        self,
        x: dict = None,
    ) -> dict:
    ......

可以看到,除了基本的 初始化、reply 函数外,还定义了一个 init_rag 函数。这个函数是用来初始化 RAG 的,例如,决定是使用 LangChain 实现的RAG,还是使用 LlamaIndex 实现的RAG。

1.2 初始化

RAGAgentBase 的 __init__ 函数中,主要是初始化了一些基本变量。同时调用了其子类的 init_rag 函数,来初始化相应的 RAG。

def __init__(
    self,
    name: str,
    sys_prompt: str,
    model_config_name: str,
    emb_model_config_name: str,
    memory_config: Optional[dict] = None,
    rag_config: Optional[dict] = None,
) -> None:
    super().__init__(
        name=name,
        sys_prompt=sys_prompt,
        model_config_name=model_config_name,
        use_memory=True,
        memory_config=memory_config,
    )
    # setup embedding model used in RAG
    self.emb_model = load_model_by_config_name(emb_model_config_name)

    self.rag_config = rag_config or {}
    if "log_retrieval" not in self.rag_config:
        self.rag_config["log_retrieval"] = True

    # use LlamaIndexAgent OR LangChainAgent
    self.rag = self.init_rag()

1.3 reply 函数实现

reply 函数实现的功能是查询相关知识库文档,然后传给大模型生成回复。

首先,是query的准备。第一优先级,是从对话历史中拿最近的一条消息来作为query:

history = self.memory.get_memory(
    recent_n=self.rag_config.get("recent_n_mem", 1),
)

第二优先级才是用户输入的 x。

不知道这里为什么要这样处理?

有了 query 之后,拿这个query去检索知识库,得到检索结果:self.rag.retrieve

之后组装 Prompt,包括 system_prompt、query、检索结果。

最后调用大模型生成回复。注意最后返回的还是 Msg 对象。

def reply(
    self,
    x: dict = None,
) -> dict:
    retrieved_docs_to_string = ""
    # record the input if needed
    if self.memory:
        self.memory.add(x)
        # in case no input is provided (e.g., in msghub),
        # use the memory as query
        history = self.memory.get_memory(
            recent_n=self.rag_config.get("recent_n_mem", 1),
        )
        query = (
            "/n".join(
                [msg["content"] for msg in history],
            )
            if isinstance(history, list)
            else str(history)
        )
    elif x is not None:
        query = x["content"]
    else:
        query = ""

    if len(query) > 0:
        retrieved_docs = self.rag.retrieve(query, to_list_strs=True)
        ......

    # prepare prompt
    prompt = self.model.format(
        Msg(
            name="system",
            role="system",
            content=self.sys_prompt,
        ),
        self.memory.get_memory(
            recent_n=self.rag_config.get("recent_n_mem", 1),
        ),
        Msg(
            name="user",
            role="user",
            content="Context: " + retrieved_docs_to_string,
        ),
    )

    # call llm and generate response
    response = self.model(prompt).text
    msg = Msg(self.name, response)

    ......

    return msg

2. LlamaIndexAgent 实现

上面实现了 RAGAgentBase,在此基础上实现 LlamaIndexAgent。LlamaIndexAgent 继承了 RAGAgentBase,并实现了 init_rag 函数。

class LlamaIndexAgent(RAGAgentBase):

2.1 LlamaIndexAgent 的初始化

这里的初始化,直接将参数传给父类,对父类进行初始化。

def __init__(
    self,
    name: str,
    sys_prompt: str,
    model_config_name: str,
    emb_model_config_name: str = None,
    memory_config: Optional[dict] = None,
    rag_config: Optional[dict] = None,
) -> None:
    super().__init__(
        name=name,
        sys_prompt=sys_prompt,
        model_config_name=model_config_name,
        emb_model_config_name=emb_model_config_name,
        memory_config=memory_config,
        rag_config=rag_config,
    )

2.2 init_rag 函数实现

init_rag 函数实现的功能是初始化相应的RAG,例如前面文章我们实现的 LlamaIndexRAG 或 LangChainRAG。这里的例子我们使用了 LlamaIndexRAG。

rag = LlamaIndexRAG(
    model=self.model,
    emb_model=self.emb_model,
    config=self.rag_config,
)

然后使用这个 rag 来加载数据:docs = rag.load_data(**load_data_args),其中这里的 load_data_args 是从配置文件中读取的。

然后使用这个 rag 来存储和索引数据:rag.store_and_index(docs, **store_and_index_args),其中这里的 store_and_index_args 是从配置文件中读取的。

def init_rag(self) -> LlamaIndexRAG:
    rag = LlamaIndexRAG(
        model=self.model,
        emb_model=self.emb_model,
        config=self.rag_config,
    )
    ......
    docs = rag.load_data(**load_data_args)

    ......
    rag.store_and_index(docs, **store_and_index_args)

    return rag

3. LlamaIndexAgent 使用

下面来看下如何使用 LlamaIndexAgent。

3.1 LlamaIndexAgent 配置

上面在 init_rag 函数中,我们使用了配置文件中的参数。下面是一个示例配置:

{
    "class": "LlamaIndexAgent",
    "args": {
        "name": "AgentScope Tutorial Assistant",
        "sys_prompt": "You're a helpful assistant. You need to generate answers based on the provided context.",
        "model_config_name": "qwen_config",
        "emb_model_config_name": "qwen_emb_config",
        "rag_config": {
            "load_data": {
                "loader": {
                    "create_object": true,
                    "module": "llama_index.core",
                    "class": "SimpleDirectoryReader",
                    "init_args": {
                        "input_dir": "../../docs/sphinx_doc/en/source/tutorial/",
                        "required_exts": [".md"]
                    }
                }
            },
            "chunk_size": 2048,
            "chunk_overlap": 40,
            "similarity_top_k": 10,
            "log_retrieval": false,
            "recent_n_mem": 1
        }
    }
},

其中重点看这几个参数:

  • sys_prompt:RAG的系统提示

  • model_config_name:模型配置名称

  • emb_model_config_name:嵌入模型配置名称

  • rag_config:RAG 配置,包括使用的 loader、知识库目录、chunk_size、chunk_overlap、similarity_top_k 等。

从文件中读取配置,传给 LlamaIndexAgent:

with open("./agent_config.json", "r", encoding="utf-8") as f:
        agent_configs = json.load(f)
tutorial_agent = LlamaIndexAgent(**agent_configs[0]["args"])

后面就可以使用 tutorial_agent 来进行对话了。

使用部分,最主要的是这个配置文件的内容。为了读这个配置文件,代码中还要配备解析这个配置文件的相应函数。

4. 总结

本文介绍了在AgentScope中自定义RAGAgent的流程及源码。源码解析包括 RAGAgentBase 和 LlamaIndexAgent 的实现。RAGAgentBase 实现了基本的Agent流程,LlamaIndexAgent 继承了 RAGAgentBase,并实现了 init_rag 函数来实现差异化的RAG框架。

所有的参数都在配置文件中配置,虽然方便,但也导致了代码的可读性变差,新手很容易被一大堆的配置吓退。其实这些配置参数都可以直接当作参数传给类。

完整代码可参考:https://github.com/modelscope/agentscope/tree/main/examples/conversation_with_RAG_agents/rag

如果觉得本文对你有帮助,麻烦点个赞和关注呗 ~~~


  • 大家好,我是 同学小张,持续学习C++进阶、OpenGL、WebGL知识技能AI大模型应用实战案例
  • 欢迎 点赞 + 关注 👏,持续学习持续干货输出
  • +v: jasper_8017 一起交流💬,一起进步💪。
  • 微信公众号搜同学小张 🙏

本站文章一览:

在这里插入图片描述

  • 10
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现大模型的RAG(Retrieval Augmented Generation)主要包括**数据准备阶段和应用阶段**两个关键环节。具体步骤如下: 1. **数据准备阶段**: - **数据提取**:首先需要确定并提取适用于特定领域的私域数据,这些数据可以是PDF文件、数据库内容或其他形式的私有知识库。 - **文本分割**:将提取出的文档进行分块处理,以便于后续的处理和检索。 - **向量化**:对分割后的文本块进行向量化操作,即将文本转换为机器能够高效处理的数值表示形式。 - **数据入库**:处理好的数据需要构建索引并存入向量数据库,为接下来的检索任务做准备。 2. **应用阶段**: - **用户提问**:当用户提出问题时,同样需要将这个查询向量化。 - **数据检索**:利用向量数据库的检索能力,找出与用户提问相似度最高的k个文档片段。 - **注入Prompt**:将检索到的结果结合用户的原始提问,按照一定的Prompt模板组装成一个完整的输入提示给大语言模型。 - **LLM生成答案**:大语言模型根据提供的Prompt生成最终的回答。 此外,还需要考虑如何优化数据的准备过程,比如选择适合的向量化技术(如使用词嵌入模型)以及如何设计高效的检索算法来快速准确地从大量数据找到相关信息。同时,在应用阶段,需要精心设计Prompt模板,以便大模型能更好地理解问题和检索到的信息,从而给出更准确的回答。 值得一提的是,RAG架构的优势在于它结合了大模型的强大语言理解和生成能力以及向量检索系统的高效信息获取能力,使得大模型能够在专业场景或行业细分领域提供更加精准和丰富的回答。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

同学小张

如果觉得有帮助,欢迎给我鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值