【AI的未来 - AI Agent系列】【MetaGPT】5. 更复杂的Agent实战 - 实现技术文档助手

在 【AI的未来 - AI Agent系列】【MetaGPT】2. 实现自己的第一个Agent 中,我们已经实现了一个简单的Agent,实现的功能就是顺序打印数字。

文章目录

    • 0. 本文实现内容
    • 1. 实现思路
    • 2. 完整代码及细节注释

0. 本文实现内容

今天我们来实现一个有实际意义的Agent - 实现一个技术文档助手,用户只需要输入技术文档的标题,例如“Git教程”,Agent自动将Git教程写成文档,分目录,分块,条理清晰,并有代码示例。

先看下要实现的效果(全程用户只需要输入“Git 教程”):

  • MarkDown格式
  • 分目录,一级标题、二级标题
  • 有代码示例

在这里插入图片描述

1. 实现思路

因为token限制的原因,我们先通过 LLM 大模型生成教程的目录,再对目录按照二级标题进行分块,对于每块目录按照标题生成详细内容,最后再将标题和内容进行拼接,解决 LLM 大模型长文本的限制问题。

整体流程如下(下图来自《MetaGPT智能体开发入门》):

分析上述流程图,我们需要实现:

  • 生成文档大纲的Action:WriteDirectory
  • 子任务的Action:WriteContent
  • 在得到文档大纲之后,要对大纲进行拆分(本例按目录进行拆分),然后根据拆分内容动态添加子任务Action,让子任务去根据目录写技术文档的内容
  • 将子任务Action生成的内容最后做拼接,形成最终的MarkDown文档

2. 完整代码及细节注释

直接放出完整代码,代码中添加了一些细节注释来帮助你理解,用的MetaGPT 0.5.2版本。建议你一定要实操一遍,因为不实操,你永远不知道自己会遇到多少坑…

代码并不复杂

  • WriteDirectory的实现:基本就是我们把自己的需求放入我们准备好的提示词模板里,询问大模型得到结果,然后我们对得到的内容做一个解析。(数据格式化)
  • WriteContent的实现:直接根据传入的子标题内容调用大模型生成回答
# 加载 .env 到环境变量
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())from datetime import datetime
from typing import Dict
import asyncio
from metagpt.actions.write_tutorial import WriteDirectory, WriteContent
from metagpt.const import TUTORIAL_PATH
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.utils.file import File
import fire
import timefrom typing import Dictfrom metagpt.actions import Action
from metagpt.prompts.tutorial_assistant import DIRECTORY_PROMPT, CONTENT_PROMPT
from metagpt.utils.common import OutputParser## 1. 生成文档大纲目录
class WriteDirectory(Action):"""Action class for writing tutorial directories.Args:name: The name of the action.language: The language to output, default is "Chinese"."""def __init__(self, name: str = "", language: str = "Chinese", *args, **kwargs):super().__init__(name, *args, **kwargs)self.language = languageasync def run(self, topic: str, *args, **kwargs) -> Dict:COMMON_PROMPT = """You are now a seasoned technical professional in the field of the internet. We need you to write a technical tutorial with the topic "{topic}"."""DIRECTORY_PROMPT = COMMON_PROMPT + """Please provide the specific table of contents for this tutorial, strictly following the following requirements:1. The output must be strictly in the specified language, {language}.2. Answer strictly in the dictionary format like {{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}, {{"dir 2": ["sub dir 3", "sub dir 4"]}}]}}.3. The directory should be as specific and sufficient as possible, with a primary and secondary directory.The secondary directory is in the array.4. Do not have extra spaces or line breaks.5. Each directory title has practical significance."""prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language)resp = await self._aask(prompt=prompt)return OutputParser.extract_struct(resp, dict) ## 1.1 对结果进行校验,必须符合Dict结构,否则报错## 2. 子任务Action,这里是根据拆分的目录标题写技术文档内容
class WriteContent(Action):"""Action class for writing tutorial content.Args:name: The name of the action.directory: The content to write.language: The language to output, default is "Chinese"."""def __init__(self, name: str = "", directory: str = "", language: str = "Chinese", *args, **kwargs):super().__init__(name, *args, **kwargs)self.language = languageself.directory = directoryasync def run(self, topic: str, *args, **kwargs) -> str:COMMON_PROMPT = """You are now a seasoned technical professional in the field of the internet. We need you to write a technical tutorial with the topic "{topic}"."""CONTENT_PROMPT = COMMON_PROMPT + """Now I will give you the module directory titles for the topic. Please output the detailed principle content of this title in detail. If there are code examples, please provide them according to standard code specifications. Without a code example, it is not necessary.The module directory titles for the topic is as follows:{directory}Strictly limit output according to the following requirements:1. Follow the Markdown syntax format for layout.2. If there are code examples, they must follow standard syntax specifications, have document annotations, and be displayed in code blocks.3. The output must be strictly in the specified language, {language}.4. Do not have redundant output, including concluding remarks.5. Strict requirement not to output the topic "{topic}"."""prompt = CONTENT_PROMPT.format(topic=topic, language=self.language, directory=self.directory)return await self._aask(prompt=prompt)## 3. 技术文档角色,用来执行Action
class TutorialAssistant(Role):def __init__(self,name: str = "Stitch",profile: str = "Tutorial Assistant",goal: str = "Generate tutorial documents",constraints: str = "Strictly follow Markdown's syntax, with neat and standardized layout",language: str = "Chinese",):super().__init__(name, profile, goal, constraints)self._init_actions([WriteDirectory(language=language)]) ## 3.1 初始化时,先只添加WriteDirectory Action,生成目录。WriteContent Action后面根据目录动态添加,这里你也不知道要添加多少个,添加的内容是什么。self.topic = ""self.main_title = "" ## 3.2 记录文章题目self.total_content = "" ## 3.3 生成的所有内容,拼接到这里self.language = languageasync def _think(self) -> None:"""Determine the next action to be taken by the role."""if self._rc.todo is None:self._set_state(0) ## 3.4 转到第一个Action执行returnif self._rc.state + 1 < len(self._states):self._set_state(self._rc.state + 1) ## 3.5 将要执行下一个Actionelse:self._rc.todo = None## 3.6 根据生成的目录,拆分出一级标题和二级标题,动态添加到WriteContent Action中,输入的titles必须是Dict类型,这就要求WriteDirectory的输出必须能按Dict类型解析,否则报错,程序无法继续执行。async def _handle_directory(self, titles: Dict) -> Message:self.main_title = titles.get("title")directory = f"{self.main_title}\n"self.total_content += f"# {self.main_title}"actions = list()for first_dir in titles.get("directory"):actions.append(WriteContent(language=self.language, directory=first_dir)) ## 3.7 动态添加 WriteContent Action,将一级目录内容传入key = list(first_dir.keys())[0]directory += f"- {key}\n"for second_dir in first_dir[key]:directory += f"  - {second_dir}\n"self._init_actions(actions) ## 3.8 执行了这一句,此时动作列表全是WriteContent了self._rc.todo = Nonereturn Message(content=directory)async def _act(self) -> Message:"""Perform an action as determined by the role.Returns:A message containing the result of the action."""time.sleep(20) ## 3.9 这是为了避免OpenAI接口调用频率限制,不是办法的办法todo = self._rc.todoif type(todo) is WriteDirectory: msg = self._rc.memory.get(k=1)[0] ## 3.10 获取记忆,这里是获取用户输入,因为任何动作都还没执行,所以只有用户输入self.topic = msg.contentresp = await todo.run(topic=self.topic) ## 3.11 根据用户输入生成目录logger.info(resp)return await self._handle_directory(resp)resp = await todo.run(topic=self.topic) ## 3.12 走到这里的都是WriteContent Action。 这里的self.topic还是用户输入,因为并没有其它地方更新该值。这里传入的目的是让WriteContent写的内容以这个为范围限制logger.info(resp)if self.total_content != "":self.total_content += "\n\n\n"self.total_content += resp ## 3.13 拼接数据return Message(content=resp, role=self.profile)async def _react(self) -> Message:"""Execute the assistant's think and actions.Returns:A message containing the final result of the assistant's actions."""while True:await self._think()if self._rc.todo is None:breakmsg = await self._act()## 3.14 全部Action执行完毕,写文件root_path = TUTORIAL_PATH / datetime.now().strftime("%Y-%m-%d_%H-%M-%S")await File.write(root_path, f"{self.main_title}.md", self.total_content.encode('utf-8'))return msgasync def main():msg = "Git 教程"role = TutorialAssistant()logger.info(msg)result = await role.run(msg)logger.info(result)asyncio.run(main())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/635186.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【华为 ICT HCIA eNSP 习题汇总】——题目集4

1、&#xff08;多选&#xff09;网络中出现故障后&#xff0c;管理员通过排查发现某台路由器的配置被修改了&#xff0c;那么管理员应该采取哪些措施来避免这种状况再次发生&#xff1f; A、管理员应该通过配置 ACL 来扩展只有管理员能够登录设备 B、管理员应该在路由的管理端…

宋仕强论道之再混华强北(三十五)

我是2012年重新回到华强北的&#xff0c;宋仕强说来深圳市第一份工作就在华强北担任一名工程师&#xff0c;和华强北有深厚的感情。我回来后经常混华强北的上层圈子跟老板老板娘们吹牛逼&#xff0c;最初大家看我穿的衣冠楚楚人模狗样的但态度吊儿郎当&#xff0c;理论一套一套…

File 类的用法和 InputStream, OutputStream 的用法

1.File类的用法 下面就用几个简单的代码案例来熟悉File类里面函数的用法&#xff1a; public class IODemo1 {public static void main(String[] args) throws IOException {File f new File("./test2.txt");//File f new File("C:/User/1/test.txt");S…

新手也能看懂的【前端自动化测试入门】!

前言 最近在网上搜索前端自动化测试相关的文档&#xff0c;但是发现网上的文章都是偏使用&#xff0c;没有把一些基础概念说清楚&#xff0c;导致后续一口气遇到一些karma、Jasmine、jest、Mocha、Chai、BDD等词汇的时候很容易一头雾水&#xff0c;这次一方面整理一下收获的知…

SOCKET编程和TCP通信案例三次握手四次挥手

文章目录 一、SOCKET1、网络套接字SOCKET2、网络字节序2.1、小端法2.2、大端法2.3、字节序转换3、IP地址转换函数3.1、本地字节序转网络字节序3.1.1、函数原型&#xff1a;3.1.2、返回值3.2、网络字节序转本地字节序3.2.1、函数原型3.2.2、返回值4、sockaddr地址结构&#xff0…

【蓝桥杯EDA设计与开发】立创开源社区分享的关于蓝桥被EDA真题与仿真题的项目分析

立创开源社区内有几个项目分享了往年 EDA 设计题目与仿真题&#xff0c;对此展开了学习。 【本人非科班出身&#xff0c;以下对项目的学习仅在我的眼界范围内发表意见&#xff0c;如有错误&#xff0c;请指正。】 项目一 来源&#xff1a;第十四届蓝桥杯EDA赛模拟题一 - 嘉立…

网络爬虫采集工具

在当今数字化的时代&#xff0c;获取海量数据对于企业、学术界和个人都至关重要。网络爬虫成为一种强大的工具&#xff0c;能够从互联网上抓取并提取所需的信息。本文将专心分享关于网络爬虫采集数据的全面指南&#xff0c;深入探讨其原理、应用场景以及使用过程中可能遇到的挑…

C++无锁队列的原理与实现

目录 1.无锁队列原理 1.1.队列操作模型 1.2.无锁队列简介 1.3.CAS操作 2.无锁队列方案 2.1.boost方案 2.2.ConcurrentQueue 2.3.Disruptor 3.无锁队列实现 3.1.环形缓冲区 3.2.单生产者单消费者 3.3.多生产者单消费者 3.4.RingBuffer实现 3.5.LockFreeQueue实现 …

实现仿ChatGPT光标跟随效果

先看效果 实现效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>光标闪烁效果</title>…

网络安全需要对网络风险有独特的理解

迷失在翻译中&#xff1a;网络风险解释的脱节现实 在古印度的一个经典故事中&#xff0c;几个蒙住眼睛的人接近一头大象&#xff0c;每个人检查不同的部位。有人触摸树干&#xff0c;认为它像一条蛇。另一个摸到了一条腿&#xff0c;认为它是一棵树。还有一个拿着象牙的人&…

Java中打印图案最常用的25个图案程序

Java是公认的最流行的编程语言&#xff0c;因为它的简单性和多功能性。还可以使用它开发各种应用程序&#xff0c;包括Web、移动和桌面应用程序。此外&#xff0c;Java为开发人员提供了强大的工具来轻松高效地创建复杂的程序。Java最有前途的特性之一是它能够创建可以以特定格式…

《向量数据库指南》——为什么说向量数据库是更适合AI体质的“硬盘”

其“AI原生”的体质,具体表现在几个方面: 1.更高的效率。 AI算法,要从图像、音频和文本等海量的非结构化数据中学习,提取出以向量为表示形式的“特征”,以便模型能够理解和处理。因此,向量数据库比传统基于索引的数据库有明显优势。 2.更低的成本。 大模型要从一种新…

【stm32】hal库学习笔记-GPIO按键控制LED和蜂鸣器(超详细!)

【stm32】hal库学习笔记-GPIO按键控制LED和蜂鸣器 注&#xff1a;本学习笔记基于stm32f4系列 使用的开发板为正点原子stmf407ZGT6探索者开发板 GPIO引脚使用时&#xff0c;可输入或输出数字信号 例如: 检测按键输入信号&#xff08;Read_Pin&#xff09;输出信号&#xff08;W…

flink operator 拉取阿里云私有镜像(其他私有类似)

创建 k8s secret kubectl --namespace flink create secret docker-registry aliyun-docker-registry --docker-serverregistry.cn-shenzhen.aliyuncs.com --docker-usernameops_acr1060896234 --docker-passwordpasswd --docker-emailDOCKER_EMAIL注意命名空间指定你使用的 我…

Linux:多线程

目录 1.线程的概念 1.1线程的理解 1.2进程的理解 1.3线程如何看待进程内部的资源? 1.4进程 VS 线程 2.线程的控制 2.1线程的创建 2.2线程的等待 2.3线程的终止 2.4线程ID 2.5线程的分离 3.线程的互斥与同步 3.1相关概念 3.2互斥锁 3.2.1概念理解 3.2.2操作理解…

分类预测 | Matlab实现WOA(海象)-XGboost分类【24年新算法】基于海象优化算法(WOA)优化XGBoost的数据分类预测

分类预测 | Matlab实现WOA(海象)-XGboost分类【24年新算法】基于海象优化算法(WOA)优化XGBoost的数据分类预测 目录 分类预测 | Matlab实现WOA(海象)-XGboost分类【24年新算法】基于海象优化算法(WOA)优化XGBoost的数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本…

模型的召回率(Recall)

召回率&#xff08;Recall&#xff09;&#xff0c;也称为灵敏度&#xff08;Sensitivity&#xff09;或真正例率&#xff08;True Positive Rate&#xff09;&#xff0c;是用于评估二分类模型性能的指标之一。召回率衡量了模型正确识别正例的能力&#xff0c;即在所有实际正例…

ctfshow php特性(web89-web101)

目录 web89 web90 web91 web92 web93 web94 web95 web96 web97 web98 web99 web100 web101 php特性(php基础知识) web89 <?php include("flag.php"); highlight_file(_FILE_);if(isset($_GET[num])){$num$_GET[num];if(preg_match("/[0-9]/&…

Docker项目部署()

1.创建文件夹tools mkdir tools 配置阿里云 Docker Yum 源 : yum install - y yum - utils device - mapper - persistent - data lvm2 yum - config - manager -- add - repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker - ce.repo 更新 yum 缓存 yum makec…

Kafka-消费者-KafkaConsumer分析-PartitionAssignor

Leader消费者在收到JoinGroupResponse后&#xff0c;会按照其中指定的分区分配策略进行分区分配&#xff0c;每个分区分配策略就是一个PartitionAssignor接口的实现。图是PartitionAssignor的继承结构及其中的组件。 PartitionAssignor接口中定义了Assignment和Subscription两个…