MetaGPT例子解读与源码分析

官网例子1

实现例子

import asyncio #asyncio 模块,用于处理异步操作。
from metagpt.roles import (
    Architect,
    Engineer,
    ProductManager,
    ProjectManager,
)
from metagpt.team import Team

async def startup(idea: str):
    company = Team()
    company.hire(
        [
            ProductManager(),
            Architect(),
            ProjectManager(),
            Engineer(),
        ]
    )
    company.invest(investment=3.0)
    company.run_project(idea=idea)

    await company.run(n_round=5)# 调用 asyncio.run 方法启动事件循环,并运行 startup 函数

metagpt/team.py

初始化

def __init__(self, context: Context = None, **data: Any):
    super(Team, self).__init__(**data)
    ctx = context or Context()
    if not self.env:
        self.env = Environment(context=ctx)
    else:
        self.env.context = ctx  # The `env` object is allocated by deserialization
    if "roles" in data:
        self.hire(data["roles"])
    if "env_desc" in data:
        self.env.desc = data["env_desc"]

初始化方法中,在本例子中,上文导入了metagpt.roles,故将初始化self.hire(data["roles"])方法,一个 Role 能执行特定的 Action,拥有记忆、思考并采用各种策略行动。

hire
def hire(self, roles: list[Role]): self.env.add_roles(roles)

本质是将上述角色并添加到环境中。

invest
@property
    def cost_manager(self):
        """Get cost manager"""
        return self.env.context.cost_manager
def invest(self, investment: float):
    self.investment = investment
    self.cost_manager.max_budget = investment
    logger.info(f"Investment: ${investment}.")

将预算赋值给self.investment,并设置最大预算。预算根据每次请求返回到token数计算消耗的金额,进行扣减,当扣减到0元时,程序提前结束。

学习记录

@property装饰器用于将一个方法转换为属性。这意味着你可以像访问属性一样访问这个方法,而无需显式调用它。这对于需要计算或逻辑处理的属性非常有用,但你希望用户能够像访问普通属性一样使用它。

@classmethod装饰器用于定义类方法。类方法的第一个参数是类本身,而不是实例。这个参数通常命名为cls。类方法可以访问类的属性和方法,但不能访问实例属性。类方法通常用于创建工厂方法(例如,返回类的不同实例)。

run_project 

def run_project(self, idea, send_to: str = ""):
    self.idea = idea

    self.env.publish_message(
        Message(role="Human", content=idea, cause_by=UserRequirement, send_to=send_to or MESSAGE_ROUTE_TO_ALL),
        peekable=False,
    )
@serialize_decorator
async def run(self, n_round=3, idea="", send_to="", auto_archive=True):
    if idea:
        self.run_project(idea=idea, send_to=send_to)

    while n_round > 0:
        n_round -= 1
        self._check_balance()
        await self.env.run()

        logger.debug(f"max {n_round=} left.")
    self.env.archive(auto_archive)
    return self.env.history

RAG

定义

RAG(Retrieval-Augmented Generation)通过引用外部权威知识库来优化大型语言模型(LLM)的输出,增强其生成响应的能力。这种方法允许LLM在不重新训练的情况下,访问特定领域的知识,提高输出的相关性、准确性和实用性。

本文主要介绍当前MetaGPT所提供的RAG功能:

  1. 数据输入,支持多种格式文件(包括pdf/docx/md/csv/txt/ppt)、python对象

  2. 检索功能,支持faiss/bm25/chromadb/es,并支持混合检索

  3. 检索后处理,支持LLM Rerank/ColbertRerank,对上面检索出来的内容进行重排以得到更准确的数据

  4. 数据更新,增加文本与python对象

  5. 数据保存及恢复,不用每次都进行向

源码查看

from metagpt.rag.engines import SimpleEngine

init
class SimpleEngine(RetrieverQueryEngine):
    """SimpleEngine is designed to be simple and straightforward.

    It is a lightweight and easy-to-use search engine that integrates
    document reading, embedding, indexing, retrieving, and ranking functionalities
    into a single, straightforward workflow. It is designed to quickly set up a
    search engine from a collection of documents.
    """

    def __init__(
        self,
        retriever: BaseRetriever,
        response_synthesizer: Optional[BaseSynthesizer] = None,
        node_postprocessors: Optional[list[BaseNodePostprocessor]] = None,
        callback_manager: Optional[CallbackManager] = None,
        transformations: Optional[list[TransformComponent]] = None,
    ) -> None:
        super().__init__(
            retriever=retriever,
            response_synthesizer=response_synthesizer,
            node_postprocessors=node_postprocessors,
            callback_manager=callback_manager,
        )
        self._transformations = transformations or self._default_transformations()

下面三个在父类RetrieverQueryEngine有定义:

retriever: 一个检索器对象

response_synthesizer: 一个可选的响应合成器对象

callback_manager: 一个可选的回调管理器对象

学习记录:

1.为什么要用optional

在 Python 中,Optionaltyping 模块提供的一种类型提示工具,用于表示某个变量或者参数可以是某种类型,也可以是 None。这在函数参数或类属性可能为空值的情况下非常有用。eg:response_synthesizer: 可以是 BaseSynthesizer 的实例,也可以是 None

2.transformations作用是什么?默认的default_transformations()SentenceSplitter()是什么?

使用了LlamaIndex的库,

执行转换,将文档转换为节点

from_objs 类方法
    @classmethod
    def from_objs(
        cls,
        objs: Optional[list[RAGObject]] = None,
        transformations: Optional[list[TransformComponent]] = None,
        embed_model: BaseEmbedding = None,
        llm: LLM = None,
        retriever_configs: list[BaseRetrieverConfig] = None,
        ranker_configs: list[BaseRankerConfig] = None,
    ) -> "SimpleEngine":
        """From objs.

        Args:
            objs: List of RAGObject.
            transformations: Parse documents to nodes. Default [SentenceSplitter].
            embed_model: Parse nodes to embedding. Must supported by llama index. Default OpenAIEmbedding.
            llm: Must supported by llama index. Default OpenAI.
            retriever_configs: Configuration for retrievers. If more than one config, will use SimpleHybridRetriever.
            ranker_configs: Configuration for rankers.
        """
        objs = objs or []
        retriever_configs = retriever_configs or []

        if not objs and any(isinstance(config, BM25RetrieverConfig) for config in retriever_configs):
            raise ValueError("In BM25RetrieverConfig, Objs must not be empty.")

        nodes = [ObjectNode(text=obj.rag_key(), metadata=ObjectNode.get_obj_metadata(obj)) for obj in objs]
        #
        return cls._from_nodes(
            nodes=nodes,
            transformations=transformations,
            embed_model=embed_model,
            llm=llm,
            retriever_configs=retriever_configs,
            ranker_configs=ranker_configs,
        )
        if not objs and any(isinstance(config, BM25RetrieverConfig) for config in retriever_configs):
            raise ValueError("In BM25RetrieverConfig, Objs must not be empty.")
  • any(isinstance(config, BM25RetrieverConfig) for config in retriever_configs) 是一个生成器表达式,它遍历 retriever_configs 列表,并检查其中的每个元素是否是 BM25RetrieverConfig 类型。isinstance(config, BM25RetrieverConfig) 用于检查当前元素 config 是否是BM25RetrieverConfig 类型。BM25算法:

BM25icon-default.png?t=N7T8https://www.elastic.co/cn/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables

  • 对于 objs 列表中的每个 Player 对象,生成一个 ObjectNode 对象,并将这些 ObjectNode 对象组成一个列表赋值给 nodes

    • ObjectNode的类定义和初始化方法:

      class ObjectNode(TextNode):
          """RAG add object."""
      
          def __init__(self, **kwargs):
              super().__init__(**kwargs)
              self.excluded_llm_metadata_keys = list(ObjectNodeMetadata.model_fields.keys())
              self.excluded_embed_metadata_keys = self.excluded_llm_metadata_keys
    • ObjectNode 继承自 TextNode 类(llama_index.core.schema),并添加了特定的功能以支持在 RAG 系统中处理对象。

    • self.excluded_llm_metadata_keys:初始化一个列表,包含 ObjectNodeMetadata 的所有字段名。

    • self.excluded_embed_metadata_keys:将嵌入元数据排除键设置为与 excluded_llm_metadata_keys 相同的值。

静态方法 get_obj_metadata
    @staticmethod
    def get_obj_metadata(obj: RAGObject) -> dict:
        metadata = ObjectNodeMetadata(
            obj_json=obj.model_dump_json(), obj_cls_name=obj.__class__.__name__, obj_mod_name=obj.__class__.__module__
        )

        return metadata.model_dump()

@staticmethod:这是一个静态方法,它不依赖于类实例,可以直接通过类名调用。

get_obj_metadata(obj: RAGObject) -> dict:这个方法接受一个 RAGObject 类型的对象,并返回一个字典类型的元数据。

metadata = ObjectNodeMetadata(...)

  • 创建一个 ObjectNodeMetadata 实例,并填充以下字段:

    • obj_json:调用对象的 model_dump_json 方法,获取对象的 JSON 表示。

    • obj_cls_name:获取对象的类名。

    • obj_mod_name:获取对象类所在的模块名。

return metadata.model_dump()

  • 调用 metadatamodel_dump 方法,将 ObjectNodeMetadata 实例转换为字典,并返回这个字典。

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值