metagpt中ActionNode的用法

devtools/2024/12/21 22:54:18/

目录

    • 整体流程
      • 1. 基础组件:
      • 2. SIMPLE_THINK_NODE 和 SIMPLE_CHECK_NODE:
      • 3. THINK_NODES 类:
      • 4. ThinkAction 类:
      • 5. SimplePrint 类:
      • 6. Printer 类:
      • 7. main 函数:
      • 总结:
      • 主要执行流程:
    • 代码
    • 参考链接:

整体流程

实现一个基于节点 (Node) 结构和思维推理的系统,用于生成和打印数字列表。它使用了 ActionNode 和 Action 类来定义任务和节点,并且通过异步任务执行逻辑来实现工作流。

1. 基础组件:

ActionNode 和 Action 类:

  • ActionNode 是一个继承自 ActionNode 类的对象,负责执行特定任务的节点。每个节点可以有子节点,形成树形结构。
  • Action 是执行动作的基础类,ThinkAction 和 SimplePrint 继承自 Action。

2. SIMPLE_THINK_NODE 和 SIMPLE_CHECK_NODE:

这两个节点负责“思考”和“检查”数字列表的生成:

  • SIMPLE_THINK_NODE:这个节点的任务是思考生成一个数字列表的过程,传入的 instruction 说明需要生成什么样的数字列表(例如 Fibonacci 数列的前 10 个数字)。
  • SIMPLE_CHECK_NODE:这个节点负责接收数字列表,并确保返回的格式严格符合要求,返回的格式必须是像 [1,2,3,4] 这样的数组。

3. THINK_NODES 类:

THINK_NODES 类继承自 ActionNode,代表一个包含子节点的节点(SIMPLE_THINK_NODE 和 SIMPLE_CHECK_NODE)。
它有一个 simple_fill 方法,负责根据给定的 context 生成思考内容,并用 LLM(Large Language Model)生成数字列表。子节点的返回内容会被依次传递,最终父节点返回的内容是最后一个子节点的输出。

4. ThinkAction 类:

该类继承自 Action,用于“思考”任务,目的是根据传入的 instruction(例如,提供一个斐波那契数列的任务),生成一个数字列表。
它使用 THINK_NODES 类来生成数字列表,并且通过正则表达式(find_in_brackets 方法)从返回的字符串中提取数字列表。
如果提取到有效的数字列表,会将其返回,否则返回空列表。

5. SimplePrint 类:

该类继承自 Action,其任务是简单地打印数字。它接受一个 input_num 参数,并在 run 方法中打印这个数字。

6. Printer 类:

Printer 类代表一个角色(Role),它将多个动作(如 ThinkAction 和 SimplePrint)组合成一个完整的工作流。
它有多个方法来执行这些动作:

  • _think:确定下一个要执行的动作(基于角色的当前状态)。
  • _prepare_print:准备打印数字列表。
  • _act:执行实际的动作(例如,运行 ThinkAction,生成数字列表,然后调用 SimplePrint 打印数字)。
  • _react:在运行过程中根据状态决定执行顺序,直到所有任务完成。

7. main 函数:

main 函数是程序的入口点。在此函数中,首先定义了一个任务(例如,生成斐波那契数列的前 10 个数字),然后创建了一个 Printer 角色,运行任务并记录日志。

总结:

使用面向对象的设计,创建不同的 Action 和 ActionNode 来管理工作流。
ThinkAction 负责生成数字列表,SimplePrint 负责打印结果。
Printer 类作为角色,协调执行 ThinkAction 和 SimplePrint。
通过异步方法(async/await)实现了非阻塞的执行流程,使得每个任务都能并发执行。

主要执行流程:

Printer 类 调用 ThinkAction,根据指令生成数字列表。
生成的数字列表被 Printer 通过 SimplePrint 输出。
整个过程是异步的,通过 asyncio 来运行和协调多个任务。

最终,Printer 角色根据任务要求生成并打印一个数字列表,例如斐波那契数列。

代码

import asyncio
import refrom metagpt.actions.action import Action, ActionNode
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message# 将思考斐波那契数列的10个数字作为prompt输入,在这里我们将“思考需要生成的数字列表”作为命令(instruction)写入
# 将期望返回格式(expected_type)设置为str,无需设置例子(example)
SIMPLE_THINK_NODE = ActionNode(key="Simple Think Node",expected_type=str,instruction="""Think about what list of numbers you need to generate""",example="",
)# 在这里通过命令(instruction)来规定需要生成的数字列表格式,提供例子(example)来帮助LLM理解
SIMPLE_CHECK_NODE = ActionNode(key="Simple CHECK Node",expected_type=str,instruction="""Please provide the number list for me, strictly following the following requirements:1. Answer strictly in the list format like [1,2,3,4]2. Do not have extra spaces or line breaks.Return the list here:""",example="[1,2,3,4]" "[4,5,6]",
)class THINK_NODES(ActionNode):def __init__(self, name="Think Nodes", expected_type=str, instruction="", example=""):super().__init__(key="",expected_type=expected_type,instruction=instruction,example=example,)self.add_children([SIMPLE_THINK_NODE, SIMPLE_CHECK_NODE])  # 初始化过程,将上面实现的两个子节点加入作为THINK_NODES类的子节点async def simple_fill(self,schema,mode,exclude=None,):prompt = self.compile(context=self.context, schema=schema, mode=mode, exclude=exclude)print(f"actionnode:{prompt}")if schema != "raw":mapping = self.get_mapping(mode, exclude=exclude)class_name = f"{self.key}_AN"content, scontent = await self._aask_v1(prompt,class_name,mapping,images=None,schema=schema,timeout=5,)self.content = contentself.instruct_content = scontentelse:self.content = await self.llm.aask(prompt)self.instruct_content = Nonereturn selfasync def fill(self, context, llm, schema="raw", mode="auto", strgy="complex"):self.set_llm(llm)self.set_context(context)if self.schema:schema = self.schemaif strgy == "simple":return await self.simple_fill(schema=schema, mode=mode)elif strgy == "complex":# 这里隐式假设了拥有childrenchild_context = context  # 输入context作为第一个子节点的contextfor _, i in self.children.items():i.set_context(child_context)  # 为子节点设置contextchild = await i.simple_fill(schema=schema, mode=mode)child_context = (child.content)  # 将返回内容(child.content)作为下一个子节点的contextself.content = child_context  # 最后一个子节点返回的内容设置为父节点返回内容(self.content)return selfclass SimplePrint(Action):"""Action that print the num inputted"""def __init__(self, name="SimplePrint", input_num: int = 0):super().__init__()self.input_num = input_numasync def run(self, **kwargs):print(str(self.input_num) + "\n")return "0"class ThinkAction(Action):"""Action that think"""def __init__(self, name="ThinkAction", context=None, llm=None):super().__init__()self.node = (THINK_NODES())  # 初始化Action时,初始化一个THINK_NODE实例并赋值给self.nodeasync def run(self, instruction) -> list:PROMPT = """You are now a number list generator, follow the instruction {instruction} and generate a number list to be printed please."""prompt = PROMPT.format(instruction=instruction)print(f"thinkaction: {prompt}")rsp_node = await self.node.fill(context=prompt, llm=self.llm, schema="raw", strgy="complex")  # 运行子节点,获取返回(返回格式为ActionNode)(注意设置 schema="raw" )rsp = rsp_node.content  # 获取返回的文本内容rsp_match = self.find_in_brackets(rsp)  # 按列表格式解析返回的文本内容,定位“[”与“]”之间的内容try:rsp_list = list(map(int, rsp_match[0].split(",")))  # 按列表格式解析返回的文本内容,按“,”对内容进行分割,并形成一个python语法中的列表return rsp_listexcept:return []@staticmethoddef find_in_brackets(s):pattern = r"\[(.*?)\]"match = re.findall(pattern, s)return matchclass Printer(Role):def __init__(self, name="Jerry", profile="Printer", goal="Print the number", constraints=""):super().__init__()self.set_actions([ThinkAction])# self.num_list = list()async def _think(self) -> None:"""Determine the action"""# logger.info(self.rc.state)if self.rc.todo is None:self._set_state(0)returnif self.rc.state + 1 < len(self.states):self._set_state(self.rc.state + 1)else:self.rc.todo = Noneasync def _prepare_print(self, num_list: list) -> Message:"""Add actions"""actions = list()for num in num_list:actions.append(SimplePrint(input_num=num))self.set_actions(actions)self.rc.todo = Nonereturn Message(content=str(num_list))async def _act(self) -> Message:"""Action"""todo = self.rc.todoif type(todo) is ThinkAction:msg = self.rc.memory.get(k=1)[0]self.goal = msg.contentresp = await todo.run(instruction=self.goal)# logger.info(resp)return await self._prepare_print(resp)resp = await todo.run()# logger.info(resp)return Message(content=resp, role=self.profile)async def _react(self) -> Message:""""""while True:await self._think()if self.rc.todo is None:breakmsg = await self._act()return msgasync def main():msg = "Provide the first 10 numbers of the Fibonacci series"role = Printer()logger.info(msg)result = await role.run(msg)logger.info(result)asyncio.run(main())

输出:

(metagpt) D:\llm\MetaGPT> d: && cd d:\llm\MetaGPT && cmd /C "d:\soft\anaconda\envs\metagpt\python.exe c:\Users\32564\.vscode\extensions\ms-python.debugpy-2024.14.0-win32-x64\bundled\libs\debugpy\adapter/../..\debugpy\launcher 55967 -- D:\llm\MetaGPT\notebook\TEST12.PY "
2024-12-17 17:44:27.438 | INFO     | metagpt.const:get_metagpt_package_root:21 - Package root set to d:\llm\metagpt
2024-12-17 17:44:33.566 | INFO     | __main__:main:224 - Provide the first 10 numbers of the Fibonacci series
thinkaction:You are now a number list generator, follow the instruction Provide the first 10 numbers of the Fibonacci series andgenerate a number list to be printed please.actionnode:You are now a number list generator, follow the instruction Provide the first 10 numbers of the Fibonacci series andgenerate a number list to be printed please.## Actions
Language: Please use the same language as Human INPUT.Think about what list of numbers you need to generateThe Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, usually starting with 0 and 1. The first 10 numbers of the Fibonacci series are:0, 1, 1, 2, 3, 5, 8, 13, 21, 34Here is the number list generated:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
2024-12-17 17:44:38.394 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model GLM-4-flash not found in TOKEN_COSTS.
actionnode:The Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, usually starting with 0 and 1. The first 10 numbers of the Fibonacci series are:0, 1, 1, 2, 3, 5, 8, 13, 21, 34Here is the number list generated:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]## Actions
Language: Please use the same language as Human INPUT.Please provide the number list for me, strictly following the following requirements:1. Answer strictly in the list format like [1,2,3,4]2. Do not have extra spaces or line breaks.Return the list here:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
2024-12-17 17:44:41.264 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model GLM-4-flash not found in TOKEN_COSTS.
01123581321342024-12-17 17:44:41.314 | INFO     | __main__:main:226 - : 0

参考链接:

https://deepwisdom.feishu.cn/wiki/KhCcweQKmijXi6kDwnicM0qpnEf


http://www.ppmy.cn/devtools/144200.html

相关文章

04、Vue与Ajax

4.1 发送AJAX异步请求的方式 发送AJAX异步请求的常见方式包括&#xff1a; 4.1.1. 原生方式 使用浏览器内置的JS对象XMLHttpRequest const xhr new XMLHttpRequest() xhr.open() xhr.send() xhr.onreadystatechange function(){} 4.1.2. 原生方式 使用浏览器内置的JS函…

基于阿里云日志服务的程序优化策略与实践

背景 我们的服务端程序日志现已全面迁移至阿里云&#xff0c;这一举措极大地便利了我们通过阿里云的日志工具来深入洞察接口的调用状况。 content是个json对象&#xff0c;request和path是content对象下的字段。我的需求是统计每个请求一分钟调用次数。以此为依据考虑优化的方…

RTC实时时钟

BKP&#xff08;备份寄存器&#xff09; 1. 什么是BKP&#xff1f; 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份域里&#xff0c;当VDD电 源被切断&#xff0c;他们仍然由VBAT维持供电。当系统在待机模式下被唤醒&#xff…

RabbitMQ个人理解与基本使用

目录 一. 作用&#xff1a; 二. RabbitMQ的5中队列模式&#xff1a; 1. 简单模式 2. Work模式 3. 发布/订阅模式 4. 路由模式 5. 主题模式 三. 消息持久化&#xff1a; 消息过期时间 ACK应答 四. 同步接收和异步接收&#xff1a; 应用场景 五. 基本使用 &#xff…

css代码加密

CSS代码加密是一种手段&#xff0c;目的是保护CSS代码不被轻易查看或修改。然而&#xff0c;需要注意的是&#xff0c;这种加密并不是绝对安全的&#xff0c;因为它可以被解密。以下是一种简单的CSS加密方法&#xff0c;使用了简单的替换加密&#xff1a; function encryptCSS…

全脐点曲面当且仅当平面或者球面的一部分

S 是全脐点曲面当且仅当 S 是平面或者球面的一部分。 S_\text{ 是全脐点曲面当且仅当 }{S_\text{ 是平面或者球面的一部分。}} S 是全脐点曲面当且仅当 ​S 是平面或者球面的一部分。​ 证&#xff1a; 充分性显然&#xff0c;下证必要性。 若 r ( u , v ) r(u,v) r(u,v)是…

基于单片机的医院盒装药品自动分拣系统(论文+源码)

1. 系统设计 本系统供电部分采用5V供电&#xff0c;通过电机驱动模块驱动步进电机完成传送带的制作&#xff0c;在工作时由工作人员将盒装药品放置在传送带上&#xff0c;由传送带将盒装药品送至识别模块下端&#xff0c;利用射频识别模块识别贴在药盒上的射频标签&#xff0c…

Web安全攻防入门教程——hvv行动详解

Web安全攻防入门教程 Web安全攻防是指在Web应用程序的开发、部署和运行过程中&#xff0c;保护Web应用免受攻击和恶意行为的技术与策略。这个领域不仅涉及防御措施的实现&#xff0c;还包括通过渗透测试、漏洞挖掘和模拟攻击来识别潜在的安全问题。 本教程将带你入门Web安全攻防…