AI Agent学习:MetaGPT项目之debate.py

从debate函数开始看

python 复制代码
async def debate(idea: str, investment: float = 3.0, n_round: int = 5):
    """Run a team of presidents and watch they quarrel. :)"""
    Biden = Debator(name="Biden", profile="Democrat", opponent_name="Trump")
    Trump = Debator(name="Trump", profile="Republican", opponent_name="Biden")
    team = Team()
    team.hire([Biden, Trump])
    team.invest(investment)
    team.run_project(idea, send_to="Biden")  # send debate topic to Biden and let him speak first
    await team.run(n_round=n_round)


def main(idea: str, investment: float = 3.0, n_round: int = 10):
    """
    :param idea: Debate topic, such as "Topic: The U.S. should commit more in climate change fighting"
                 or "Trump: Climate change is a hoax"
    :param investment: contribute a certain dollar amount to watch the debate
    :param n_round: maximum rounds of the debate
    :return:
    """
    if platform.system() == "Windows":
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    asyncio.run(debate(idea, investment, n_round))


if __name__ == "__main__":
    fire.Fire(main)  

组件1:team

python 复制代码
class Team(BaseModel):
    """
    Team: Possesses one or more roles (agents), SOP (Standard Operating Procedures), and a env for instant messaging,
    dedicated to env any multi-agent activity, such as collaboratively writing executable code.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    env: Optional[Environment] = None
    investment: float = Field(default=10.0)
    idea: str = Field(default="")
    use_mgx: bool = Field(default=True)

    def __init__(self, context: Context = None, **data: Any):
        super(Team, self).__init__(**data)
        ctx = context or Context()
        if not self.env and not self.use_mgx:
            self.env = Environment(context=ctx)
        elif not self.env and self.use_mgx:
            self.env = MGXEnv(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"]

组件2:Environment

这里Environment和Context都是布置背景的内容,Context和Memory都是Envrionment初始化配置的内容。

这里区分MGXEnv和Env:

MGXEnv是一个团队协作环境中的消息发布系统 ,核心逻辑是TeamLeader(团队领导)作为消息中枢,管理不同角色之间的通信。我来详细解读:

核心逻辑架构

复制代码
用户/角色 → TeamLeader(Mike) → 目标角色
      ↑           ↓
    直接聊天     常规工作流

四种消息处理模式

1. 用户直接与特定角色聊天 (user_defined_recipient=True)

python

复制代码
if user_defined_recipient:
    # 用户直接私聊某个角色
    self.direct_chat_roles.add(role_name)  # 标记为直接聊天状态
    self._publish_message(message)  # 直接发送,不经过TL

场景:用户想和某个角色单独聊天(如:"@设计师,帮我设计个logo")

  • TL不参与,避免干扰

  • 其他角色也不会收到

2. 角色回复用户的直接聊天 (sent_from in direct_chat_roles)

python

复制代码
elif message.sent_from in self.direct_chat_roles:
    # 角色回复用户的私聊
    self.direct_chat_roles.remove(message.sent_from)  # 结束私聊状态
    if self.is_public_chat:
        self._publish_message(message)  # 只有在公开聊天时才广播

场景:设计师回复用户的私聊请求

  • 如果是在私聊环境,只有用户能看到回复

  • 如果切换到公开聊天,TL和其他角色才能看到

3. TeamLeader处理后的消息 (publicer == tl.profile)

python

复制代码
elif publicer == tl.profile:
    if message.send_to == {"no one"}:
        return True  # 跳过TL的虚拟消息
    # TL已处理,可以正式发布
    self._publish_message(message)

场景:TL完成消息分配/处理后发布

  • TL可能过滤、修改或分配消息

  • 这是常规工作流的最后一步

4. 常规工作流消息(默认情况)

python

复制代码
else:
    # 所有常规消息都先经过TL
    message.send_to.add(tl.name)  # 添加TL为收件人
    self._publish_message(message)  # 发送给TL处理

场景:角色A想和角色B沟通工作

复制代码
角色A → TL → 分配/处理 → 角色B

环境的作用和函数:

  1. observe 提供环境观察

  2. step action+obsevation

  3. publish_message 给参与的人分配信息

  4. run 运转所有任务

  5. 加入role 读取role

回到team

python 复制代码
class Team(BaseModel):
    def hire(self, roles: list[Role]):
        """Hire roles to cooperate"""
        self.env.add_roles(roles)

    ...

    def run_project(self, idea, send_to: str = ""):
        """Run a project from publishing user requirement."""
        self.idea = idea

        # Human requirement.
        self.env.publish_message(Message(content=idea))

    def start_project(self, idea, send_to: str = ""):
        """
        Deprecated: This method will be removed in the future.
        Please use the `run_project` method instead.
        """
        warnings.warn(
            "The 'start_project' method is deprecated and will be removed in the future. "
            "Please use the 'run_project' method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.run_project(idea=idea, send_to=send_to)

    @serialize_decorator
    async def run(self, n_round=3, idea="", send_to="", auto_archive=True):
        """Run company until target round or no money"""
        if idea:
            self.run_project(idea=idea, send_to=send_to)

        while n_round > 0:
            if self.env.is_idle:
                logger.debug("All roles are idle.")
                break
            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

那么 可以看到team主要也是队伍的成员、运行项目这些功能

debate.py 的debate函数 team.run_project

--> env.publish_message()

python 复制代码
    def publish_message(self, message: Message, peekable: bool = True) -> bool:
        """
        Distribute the message to the recipients.
        In accordance with the Message routing structure design in Chapter 2.2.1 of RFC 116, as already planned
        in RFC 113 for the entire system, the routing information in the Message is only responsible for
        specifying the message recipient, without concern for where the message recipient is located. How to
        route the message to the message recipient is a problem addressed by the transport framework designed
        in RFC 113.
        """
        logger.debug(f"publish_message: {message.dump()}")
        found = False
        # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113
        for role, addrs in self.member_addrs.items():
            if is_send_to(message, addrs):
                role.put_message(message)
                found = True
        if not found:
            logger.warning(f"Message no recipients: {message.dump()}")
        self.history.add(message)  # For debug

        return True

def is_send_to(message: "Message", addresses: set):  #Message default是配置MESSAGE_ROUTE_TO_ALL
    """Return whether it's consumer"""
    if MESSAGE_ROUTE_TO_ALL in message.send_to:
        return True

    for i in addresses:
        if i in message.send_to:
            return True
    return False

MESSAGE_ROUTE_TO_ALL :Message default是配置MESSAGE_ROUTE_TO_ALL

所以普通 环境里 消息传给所有人

这里member_addrs 是加入成员的时候就会设置 这是以防名字在不同环境下重复,要区分

组件3:role

然后就到role了

python 复制代码
class Role(BaseRole, SerializationMixin, ContextMixin, BaseModel):
    """Role/Agent"""

    model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")

    name: str = ""
    profile: str = ""
    goal: str = ""
    constraints: str = ""
    desc: str = ""
    is_human: bool = False
    enable_memory: bool = (
        True  # Stateless, atomic roles, or roles that use external storage can disable this to save memory.
    )

    role_id: str = ""
    states: list[str] = []

    # scenarios to set action system_prompt:
    #   1. `__init__` while using Role(actions=[...])
    #   2. add action to role while using `role.set_action(action)`
    #   3. set_todo while using `role.set_todo(action)`
    #   4. when role.system_prompt is being updated (e.g. by `role.system_prompt = "..."`)
    # Additional, if llm is not set, we will use role's llm
    actions: list[SerializeAsAny[Action]] = Field(default=[], validate_default=True)
    rc: RoleContext = Field(default_factory=RoleContext)
    addresses: set[str] = set()
    planner: Planner = Field(default_factory=Planner)

    # builtin variables
    recovered: bool = False  # to tag if a recovered role
    latest_observed_msg: Optional[Message] = None  # record the latest observed message when interrupted
    observe_all_msg_from_buffer: bool = False  # whether to save all msgs from buffer to memory for role's awareness

    __hash__ = object.__hash__  # support Role as hashable type in `Environment.members`

    def put_message(self, message):
        """Place the message into the Role object's private message buffer."""
        if not message:
            return
        self.rc.msg_buffer.push(message)

存message到每个role的memory里

整个过程怎么运转的呢?

来看examples/debate.py

python 复制代码
async def debate(idea: str, investment: float = 3.0, n_round: int = 5):
    """Run a team of presidents and watch they quarrel. :)"""
    Biden = Debator(name="Biden", profile="Democrat", opponent_name="Trump")
    Trump = Debator(name="Trump", profile="Republican", opponent_name="Biden")
    team = Team()
    team.hire([Biden, Trump])
    team.invest(investment)
    team.run_project(idea, send_to="Biden")  # send debate topic to Biden and let him speak first
    await team.run(n_round=n_round)

就是team.run(n_round=n_round)这句维持运转

然后跳转到metagpt\team.py

python 复制代码
    @serialize_decorator
    async def run(self, n_round=3, idea="", send_to="", auto_archive=True):
        """Run company until target round or no money"""
        if idea:
            self.run_project(idea=idea, send_to=send_to)

        while n_round > 0:
            if self.env.is_idle:
                logger.debug("All roles are idle.")
                break
            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

然后就是self.env.run ()

跳转到metagpt\environment\base_env.py

python 复制代码
    async def run(self, k=1):
        """处理一次所有信息的运行
        Process all Role runs at once
        """
        for _ in range(k):
            futures = []
            for role in self.roles.values():
                if role.is_idle:
                    continue
                future = role.run()
                futures.append(future)

            if futures:
                await asyncio.gather(*futures)
            logger.debug(f"is idle: {self.is_idle}")

role.run() 跳转到metagpt\roles\role.py

python 复制代码
@role_raise_decorator
    async def run(self, with_message=None) -> Message | None:
        """Observe, and think and act based on the results of the observation"""
        if with_message:
            msg = None
            if isinstance(with_message, str):
                msg = Message(content=with_message)
            elif isinstance(with_message, Message):
                msg = with_message
            elif isinstance(with_message, list):
                msg = Message(content="\n".join(with_message))
            if not msg.cause_by:
                msg.cause_by = UserRequirement
            self.put_message(msg)
        if not await self._observe():
            # If there is no new information, suspend and wait
            logger.debug(f"{self._setting}: no news. waiting.")
            return

        rsp = await self.react()

        # Reset the next action to be taken.
        self.set_todo(None)
        # Send the response message to the Environment object to have it relay the message to the subscribers.
        self.publish_message(rsp)
        return rsp

每次就是读消息 存消息 然后观察_observe()整理信息 react

observe

python 复制代码
async def _observe(self) -> int:
        """Prepare new messages for processing from the message buffer and other sources."""
        # Read unprocessed messages from the msg buffer.
        news = []
        if self.recovered and self.latest_observed_msg:
            news = self.rc.memory.find_news(observed=[self.latest_observed_msg], k=10)
        # 从消息缓冲区读取未处理的消息消息来源可能是其他角色发送的,或者是系统产生的。使用pop_all()确保每条消息只被一个角色处理一次
        if not news:
            news = self.rc.msg_buffer.pop_all()
        # Store the read messages in your own memory to prevent duplicate processing.
        old_messages = [] if not self.enable_memory else self.rc.memory.get()
        # Filter in messages of interest.
        self.rc.news = [
            n for n in news if (n.cause_by in self.rc.watch or self.name in n.send_to) and n not in old_messages  ## 关注特定来源   消息是发给我的
        ]
        if self.observe_all_msg_from_buffer:
            # save all new messages from the buffer into memory, the role may not react to them but can be aware of them
            self.rc.memory.add_batch(news)
        else:
            # only save messages of interest into memory
            self.rc.memory.add_batch(self.rc.news)
        self.latest_observed_msg = self.rc.news[-1] if self.rc.news else None  # record the latest observed msg

        # Design Rules:
        # If you need to further categorize Message objects, you can do so using the Message.set_meta function.
        # msg_buffer is a receiving buffer, avoid adding message data and operations to msg_buffer.
        news_text = [f"{i.role}: {i.content[:20]}..." for i in self.rc.news]
        if news_text:
            logger.debug(f"{self._setting} observed: {news_text}")
        return len(self.rc.news)
复制代码
消息缓冲区
   ↓
[收件箱] ← 新消息流入
   ↓
🔍 观察者筛选:
   ├── 消息是发给我的吗? → 是 → 加入rc.news
   ├── 是我关注的事件吗? → 是 → 加入rc.news
   └── 都不是 → 忽略
   ↓
💾 记忆存储 (仅存储筛选后的消息)
   ↓
📝 记录最后观察的消息 (latest_observed_msg)

关键技术细节

1. 恢复机制

python

复制代码
if self.recovered and self.latest_observed_msg:
    news = self.rc.memory.find_news(observed=[self.latest_observed_msg], k=10)
  • 当角色从崩溃/中断中恢复时,避免消息丢失

  • 基于最后观察的消息,从记忆中找到后续的10条新消息

2. 两种观察模式

python

复制代码
if self.observe_all_msg_from_buffer:
    self.rc.memory.add_batch(news)  # 模式A:知道所有消息
else:
    self.rc.memory.add_batch(self.rc.news)  # 模式B:只记相关消息
  • 全知模式:了解所有消息内容(用于团队领导等需要全局视野的角色)

  • 专注模式:只关注与自己直接相关的内容(用于专业角色)

3. 去重机制

python

复制代码
[n for n in news if ... and n not in old_messages]
  • 避免重复处理同一消息

  • 提升系统效率

然后react

python 复制代码
    async def _react(self) -> Message:
        """Think first, then act, until the Role _think it is time to stop and requires no more todo.
        This is the standard think-act loop in the ReAct paper, which alternates thinking and acting in task solving, i.e. _think -> _act -> _think -> _act -> ...
        Use llm to select actions in _think dynamically
        """
        actions_taken = 0
        rsp = AIMessage(content="No actions taken yet", cause_by=Action)  # will be overwritten after Role _act
        while actions_taken < self.rc.max_react_loop:
            # think
            has_todo = await self._think()
            if not has_todo:
                break
            # act
            logger.debug(f"{self._setting}: {self.rc.state=}, will do {self.rc.todo}")
            rsp = await self._act()
            actions_taken += 1
        return rsp  # return output from the last action

_think

python 复制代码
async def _think(self) -> bool:
        """Consider what to do and decide on the next course of action. Return false if nothing can be done."""
        if len(self.actions) == 1:
            # If there is only one action, then only this one can be performed
            self._set_state(0)

            return True

        if self.recovered and self.rc.state >= 0:
            self._set_state(self.rc.state)  # action to run from recovered state
            self.recovered = False  # avoid max_react_loop out of work
            return True

        if self.rc.react_mode == RoleReactMode.BY_ORDER:
            if self.rc.max_react_loop != len(self.actions):
                self.rc.max_react_loop = len(self.actions)
            self._set_state(self.rc.state + 1)
            return self.rc.state >= 0 and self.rc.state < len(self.actions)

        prompt = self._get_prefix()
        prompt += STATE_TEMPLATE.format(
            history=self.rc.history,
            states="\n".join(self.states),
            n_states=len(self.states) - 1,
            previous_state=self.rc.state,
        )

        next_state = await self.llm.aask(prompt)
        next_state = extract_state_value_from_output(next_state)
        logger.debug(f"{prompt=}")

        if (not next_state.isdigit() and next_state != "-1") or int(next_state) not in range(-1, len(self.states)):
            logger.warning(f"Invalid answer of state, {next_state=}, will be set to -1")
            next_state = -1
        else:
            next_state = int(next_state)
            if next_state == -1:
                logger.info(f"End actions with {next_state=}")
        self._set_state(next_state)
        return True

def extract_state_value_from_output(content: str) -> str:
    """
    For openai models, they will always return state number. But for open llm models, the instruction result maybe a
    long text contain target number, so here add a extraction to improve success rate.

    Args:
        content (str): llm's output from `Role._think`
    """
    content = content.strip()  # deal the output cases like " 0", "0\n" and so on.
    pattern = (
        r"(?<!-)[0-9]"  # TODO find the number using a more proper method not just extract from content using pattern
    )
    matches = re.findall(pattern, content, re.DOTALL)
    matches = list(set(matches))
    state = matches[0] if len(matches) > 0 else "-1"
    return state 

对于think

模式1:唯一选择(默认路径)

python

复制代码
if len(self.actions) == 1:
    self._set_state(0)  # 只有一个动作,没得选
    return True

场景:角色只有一个能力时(如"翻译员"只能做翻译)。

模式2:恢复执行

python

复制代码
if self.recovered and self.rc.state >= 0:
    self._set_state(self.rc.state)  # 从上次中断的状态继续
    return True

场景:系统崩溃后重启,角色从上次中断的地方继续工作。

模式3:顺序执行

python

复制代码
if self.rc.react_mode == RoleReactMode.BY_ORDER:
    self._set_state(self.rc.state + 1)  # 简单地执行下一个动作

场景:需要严格按步骤执行的流程(如"需求分析→设计→开发→测试")。

模式4:LLM智能决策(最复杂)

当需要灵活决策时,让大语言模型来决定。


🤖 模式4详解:LLM如何"思考"

这是最核心的部分,展示了如何让AI像人一样思考下一步行动:

第1步:构建思考提示词

python

复制代码
prompt = STATE_TEMPLATE.format(
    history=self.rc.history,        # 过往对话历史
    states="\n".join(self.states),  # 所有可能动作的描述
    n_states=len(self.states) - 1,  # 可选动作编号范围
    previous_state=self.rc.state,    # 上一个动作
)

生成的Prompt示例

text

复制代码
你是一个设计师,刚刚完成了UI草图。
历史对话:PM说"需要更简洁的设计",你回复"好的,我调整一下"。

可选动作:
0. 细化当前设计方案
1. 与PM确认需求细节  
2. 开始设计下一个页面
3. 任务完成,结束工作

请基于历史对话,选择下一步最合适的动作编号(0-3),如果应该结束则选-1。

第2步:LLM推理与回答

python

复制代码
next_state = await self.llm.aask(prompt)  # LLM返回如"1"或"我认为应该选1"

第3步:解析LLM的回答(关键!)

python

复制代码
def extract_state_value_from_output(content: str) -> str:
    # LLM可能返回"我建议选择动作1"或"答案是:1"等非标准格式
    pattern = r"(?<!-)[0-9]"  # 匹配数字但不匹配负号
    matches = re.findall(pattern, content)
    state = matches[0] if matches else "-1"  # 提取不到则默认结束
    return state

解析示例

LLM原始回答 提取结果 说明
"1" "1" 标准回答,直接使用
"选择动作1" "1" 从文本中提取数字
"我觉得应该结束" "-1" 无数字,默认结束
"选项2不错" "2" 提取到数字2

第4步:验证与执行

python

复制代码
if int(next_state) not in range(-1, len(self.states)):
    next_state = -1  # 无效选择则结束
self._set_state(next_state)  # 设置下一步动作

然后到act think+act=react

python 复制代码
    async def _act(self) -> Message:
        logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
        response = await self.rc.todo.run(self.rc.history)
        if isinstance(response, (ActionOutput, ActionNode)):
            msg = AIMessage(
                content=response.content,
                instruct_content=response.instruct_content,
                cause_by=self.rc.todo,
                sent_from=self,
            )
        elif isinstance(response, Message):
            msg = response
        else:
            msg = AIMessage(content=response or "", cause_by=self.rc.todo, sent_from=self)
        self.rc.memory.add(msg)

        return msg

就这样走完轮回就跑完过程

同时角色在初始化的时候 会初始化action和state 不详写

组件4:Action

metagpt\roles\role.py的_act 函数--> self.rc.todo.run

metagpt\actions\action.py

python 复制代码
    async def _run_action_node(self, *args, **kwargs):
        """Run action node"""
        msgs = args[0]
        context = "## History Messages\n"
        context += "\n".join([f"{idx}: {i}" for idx, i in enumerate(reversed(msgs))])
        return await self.node.fill(req=context, llm=self.llm)

    async def run(self, *args, **kwargs):
        """Run action"""
        if self.node:
            return await self._run_action_node(*args, **kwargs)
        raise NotImplementedError("The run method should be implemented in a subclass.")

然后就到actionnode

python 复制代码
@exp_cache(serializer=ActionNodeSerializer())
    async def fill(
        self,
        *,
        req,
        llm,
        schema="json",
        mode="auto",
        strgy="simple",
        images: Optional[Union[str, list[str]]] = None,
        timeout=USE_CONFIG_TIMEOUT,
        exclude=[],
        function_name: str = None,
    ):
        """Fill the node(s) with mode.

        :param req: Everything we should know when filling node.
        :param llm: Large Language Model with pre-defined system message.
        :param schema: json/markdown, determine example and output format.
         - raw: free form text
         - json: it's easy to open source LLM with json format
         - markdown: when generating code, markdown is always better
        :param mode: auto/children/root
         - auto: automated fill children's nodes and gather outputs, if no children, fill itself
         - children: fill children's nodes and gather outputs
         - root: fill root's node and gather output
        :param strgy: simple/complex
         - simple: run only once
         - complex: run each node
        :param images: the list of image url or base64 for gpt4-v
        :param timeout: Timeout for llm invocation.
        :param exclude: The keys of ActionNode to exclude.
        :return: self
        """
        self.set_llm(llm)
        self.set_context(req)
        if self.schema:
            schema = self.schema

        if mode == FillMode.CODE_FILL.value:
            result = await self.code_fill(context, function_name, timeout)
            self.instruct_content = self.create_class()(**result)
            return self

        elif mode == FillMode.XML_FILL.value:
            context = self.xml_compile(context=self.context)
            result = await self.xml_fill(context, images=images)
            self.instruct_content = self.create_class()(**result)
            return self

        elif mode == FillMode.SINGLE_FILL.value:
            result = await self.single_fill(context, images=images)
            self.instruct_content = self.create_class()(**result)
            return self

        if strgy == "simple":
            return await self.simple_fill(schema=schema, mode=mode, images=images, timeout=timeout, exclude=exclude)
        elif strgy == "complex":
            # 这里隐式假设了拥有children
            tmp = {}
            for _, i in self.children.items():
                if exclude and i.key in exclude:
                    continue
                child = await i.simple_fill(schema=schema, mode=mode, images=images, timeout=timeout, exclude=exclude)
                tmp.update(child.instruct_content.model_dump())
            cls = self._create_children_class()
            self.instruct_content = cls(**tmp)
            return self

核心概念:ActionNode 是一棵树

想象一下项目管理中的工作分解结构(WBS),ActionNode 就是它的动态、智能化版本:

text

复制代码
🌲 根节点(Root ActionNode)
├── 📝 子任务 A(Child ActionNode)
├── 🔧 子任务 B(Child ActionNode)
│   ├── ⚙️ 子子任务 B.1
│   └── ⚙️ 子子任务 B.2
└── 📊 子任务 C(Child ActionNode)

每个节点都负责完成一个定义清晰的小任务,所有节点组合起来完成一个大目标。


🎯 fill方法:节点的"执行引擎"

fill 方法是让这棵"任务树"动起来的关键。它根据不同的 mode(模式)和 strgy(策略),智能地决定如何填充(完成)节点内容。

三种核心填充模式

模式 触发条件 工作原理 典型场景
auto(自动) 默认模式 检查是否有子节点: • 有 → 填充所有子节点 • 无 → 填充当前节点 通用任务处理
children(仅子节点) 明确指定时 只填充子节点,忽略当前节点内容 协调者角色(如项目经理)
root(仅根节点) 明确指定时 只填充当前根节点,不处理子节点 简单、原子性任务

两种执行策略

策略 工作原理 适用场景
simple(简单) 整个节点树一次性交给LLM处理,让LLM自己理解结构并输出所有结果 任务结构简单、子任务间关联性强
complex(复杂) 分别处理每个子节点,逐个调用LLM,最后合并结果 任务复杂、子任务相对独立、需要精确控制

python

复制代码
# simple策略:一次性处理整个树
result = await root_node.simple_fill("设计一个电商网站")

# complex策略:逐个处理子节点
for child in root_node.children:
    await child.simple_fill(f"设计电商网站的{child.key}部分")

⚙️ 高级填充模式详解

除了基本模式,还有三种针对特定场景的优化模式:

1. CODE_FILL(代码填充)

python

复制代码
# 专门用于生成代码
await node.fill(mode="code_fill", function_name="calculate_price")
# LLM会专注于生成特定函数的代码实现

2. XML_FILL(XML填充)

python

复制代码
# 使用XML格式进行结构化填充
context = node.xml_compile("用户需求描述")
result = await node.xml_fill(context)
# 适合需要严格结构化的输出

3. SINGLE_FILL(单节点填充)

python

复制代码
# 只填充当前单个节点,不涉及子节点
result = await node.single_fill("翻译这句话")
# 最简单的原子操作

这里配置会配置到actionnode的content和instruct_content然后

返回到role.py来看 metagpt\roles\role.py

python 复制代码
 async def _act(self) -> Message:
        logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
        response = await self.rc.todo.run(self.rc.history)
        if isinstance(response, (ActionOutput, ActionNode)):
            msg = AIMessage(
                content=response.content,
                instruct_content=response.instruct_content,
                cause_by=self.rc.todo,
                sent_from=self,
            )
        elif isinstance(response, Message):
            msg = response
        else:
            msg = AIMessage(content=response or "", cause_by=self.rc.todo, sent_from=self)
        self.rc.memory.add(msg)

        return msg

有content和instruct_content就能出aimessage 到此就完成action

相关推荐
做cv的小昊2 小时前
计算机图形学:【Games101】学习笔记04——着色(光照与基本着色模型,着色频率、图形管线、纹理映射)
笔记·学习·3d·图形渲染·光照贴图·计算机图形学
秋深枫叶红2 小时前
嵌入式第三十三篇——linux系统编程——文件IO
linux·学习·文件io
第二层皮-合肥2 小时前
50天学习FPGA第21天-verilog的时序与延迟
学习·fpga开发
知识分享小能手2 小时前
CentOS Stream 9入门学习教程,从入门到精通,CentOS Stream 9 中大数据 —语法详解与实战案例(15)
大数据·学习·centos
石像鬼₧魂石2 小时前
Hydra 弱口令爆破的详细命令模板
linux·windows·学习·ubuntu
暗然而日章3 小时前
C++基础:Stanford CS106L学习笔记 9 类模板(Class Templates)
c++·笔记·学习
m0_689618283 小时前
拓扑变换让机器人抓得又稳、又柔、又灵活
人工智能·笔记·学习·机器人
车载测试工程师3 小时前
CAPL学习-SOME/IP交互层-底层API函数
学习·tcp/ip·以太网·capl·canoe
代码游侠3 小时前
学习笔记——Linux内核链表
linux·运维·笔记·学习·算法·链表