当前位置:   article > 正文

LLM多智能体AutoGen教程 7: 什么你还在自己查阅论文?

LLM多智能体AutoGen教程 7: 什么你还在自己查阅论文?

最近需要优化人脸姿态评估模型,往常我需要调研当前业界最新论文,在arxiv上查阅论文,然后到上查看相关算法benchmark上的排名,最后选定论文和模型。今天再deeplearning.ai的课程上看到使用AutoGen自动获取NVIDIA最近一年的股价并撰写一篇股票分析报告的实验,于是突发奇想,我为什么不用AutoGen写一个根据我的需求自动调研最近4年人脸姿态评估论文并撰写一个报告给我呢?这样至少给我节省不少时间,而且最终会给我输出一份中文报告岂不美哉?正所谓Talk is cheap, show me your code,开干!

1. 对话流程设计

要实现这样的任务,需要获取自动编码获取论文和摘要,然后根据获取到的论文摘要进行论文撰写报告。所以大概需要发布任务的Agent、规划任务步骤的Agent、编码获取信息的Agent、执行编码的Agent以及撰写报告的Agent。大致流程如下图所示

  • UserAgent发送任务给PlannerAgent
  • PlannerAgent开始规划任务
  • ProgrammingAgent 通过编写程序获取规划任务中的信息并发送给Code Executor
  • Code Executor执行编码
    • 如果程序运行出错,则反馈给ProgrammingAgent,其根据反馈调整代码,再次给到Code Executor
    • 如果程序运行成功,则输出结果给到WriterAgent
  • WriterAgent根据给定信息开始撰写报告,并发送给UserAgent审核
    • 如果审核通过,结束
    • 如果审核失败,则反馈给Writer让其优化。

2. 对话实现

想必大家已经熟悉了如何编写llm_config,如何实例化ConversableAgent,这里不再多说,其中system prompt较长有所删减,有需要的同学评论区留言。

user_proxy = autogen.ConversableAgent(
    name="Admin",
    system_message="Give the task, and send instructions to writer to refine the blog post.",
    code_execution_config=False,
    llm_config=llm_config,
    human_input_mode="ALWAYS",
)

planner = autogen.ConversableAgent(
    name="Planner",
    system_message="Given a task, please determine "
    "...",
    description="Given...",
    llm_config=llm_config,
)

engineer = autogen.AssistantAgent(
    name="Engineer",
    llm_config=llm_config,
    description="Write code based on the plan "
    "provided by the planner.",
)

writer = autogen.ConversableAgent(
    name="Writer",
    llm_config=llm_config,
    system_message="Writer. Please write blogs in markdown format (with relevant titles)",
    description="After all ...",
)

executor = autogen.ConversableAgent(
    name="Executor",
    description="Execute the code written by the engineer and report the result.",
    human_input_mode="NEVER",
    code_execution_config={
        "last_n_messages": 3,
        "work_dir": "coding",
        "use_docker": False,
    },
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

我们在[100行代码演绎AI版“狼人杀”-伟大的人类竟因展现出的战争哲学和领导力出局已经讲解了GroupChat,当时我们采用round robin轮询式的对话模式。AutoGen还支持自定义对话目标Agent,方法是通过设定GroupChat的参数。我们先了解一下GroupChat的参数都有哪些,它的参数比较多,我们捡重点的说明

  • agents - List[Agent] 一组对话的Agent
  • message - List[Dict] 需要传入groupchat的消息列表
  • max_round - 最大允许对话的次数
  • speaker_selection_methond - 支持字面量和可调用对象,默认是auto模式,由LLM自动根据Agent的描述选择
  • allowed_or_disallowed_speaker_transitions - Dict 这个参数命名真长,顾名思义就是话筒传递设定,接受一个dict,key为source agent也就是当前持有话筒的Agent,其value是List[Agent],可传递或者禁止传递话筒的目标Agent列表。
  • spearker_transitions_type 设定上面话筒传递列表是允许传递还是禁止传递方向,allowed或者disallowed
  • send_introductions - bool 默认为False,是否要群聊开始前各自介绍一下自己,方便自动转换。

所以按照第一节图中设计的对话顺序,实例化GroupChat如下

groupchat = autogen.GroupChat(
    agents=[user_proxy, engineer, writer, executor, planner],
    messages=[],
    max_round=10,
    allowed_or_disallowed_speaker_transitions={
        user_proxy: [writer, planner],
        engineer: [executor],
        writer: [user_proxy],
        executor: [engineer],
        planner: [engineer],
    },
    speaker_transitions_type="allowed",
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

群已经组好了,接下来需要实例化群管理员了GroupChatManager。GroupChatManager,继承自ConversableAgent,除了其父类的参数,还另外包含以下几个参数

  • groupchat - GroupChat 把群赋给它
  • name - Optional[str] 管理员名称

因此如下实例化GroupChatManager,并通过initial_chat发起对话。

manager = autogen.GroupChatManager(
    groupchat=groupchat, llm_config=llm_config
)

task = "使用arxiv获取2020-2024年期间所有人脸姿态识别的论文并写一篇报告"
groupchat_result = user_proxy.initiate_chat(
    manager,
    message=task,
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3. 运行

由于自动编码调试,无法输出稳定的结果。虽然它可以在engineer和executor之间不断调试代码,但是流程仍然是难以控制,无法稳定到输出结果给到Writer进行撰写报告。调试了一下午,只有一次通过爬虫获取到了部分论文的标题摘要,而且LLM并没有自动选择使用Writer撰写最后的报告。另外,个人认为使用LLM自动选择Agent来发言,是一个不太成熟的方法,对于LLM本身要求应该比较高,还是直接设定流程,会比较稳定一些。

image-20240614194956705

4. 优化

在整个环节中,因为自动编码无法稳定输出获取的论文,可以考虑尝试自己编写获取arxiv论文的代码,这样能够稳定输出论文,从而能够充分利用大语言模型自动撰写报告。

4.1 arXiv

arXiv 是一个开放给所有人的精选研究共享平台。作为数字开放获取的先锋,arXiv.org 现在托管着超过二百万篇学术文章,这些文章涵盖了八个学科领域,并由强大的志愿者社区进行筛选和管理。这8个领域包括:物理学、数学、计算机科学、定量生物学、定量金融、统计学、电气工程和系统科学、经济学。它开放了[API]方便获取,而在Python中我们可以通过arxiv的pip包进行检索或者获取论文。arxiv包较为简单,包含三个类型Client、Search和Result。其中Client用于指定可重用的获取结果的策略,Search用于指定查询条件,Result则是获取的结果,还包括一个下载论文的辅助方法。

  • 首先安装arxiv的pip包
pip install arxiv

  • 1
  • 2
  • 编写获取论文代码
import arxiv
client = arxiv.Client()
# 设定检索条件
search = arxiv.Search(
  query = "head pose estimation",
  max_results = 10,
  sort_by = arxiv.SortCriterion.SubmittedDate
)
# 获取返回结果
paper = next(client.results(search))
print(paper.title, paper.summary)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

检索论文就是如此方便,这里需要注意的client.results(search)返回的是Generator[Result, None, None],因此你需要使用generator来获取调用结果。接下来,我们尝试在上文《[26K Star!LLM多智能体AutoGen教程6:Python typing是如何自动生成工具调用请求参数的]》中讨论使用Python typing来注解这个函数。此外,由于arxiv没有时间过滤功能,要么自己获取大量论文后,手动按照时间过滤,这请求量会比较大,本文暂不考虑时间过滤,其次,考虑到论文的数量不宜过多,可能会超过LLM的Context Window,因此限制数量大小默认为10。后面学习LlamaIndex后,或许可以考虑RAG式的方法。

class Paper(TypedDict):
    title: str
    published: str
    summary: str

def search_arxiv(query: Annotated[str, "query string of arxiv"],
                 max_results: Annotated[Optional[int], "the max result from arxiv"] = 10) -> Annotated[List[Paper], "a List of paper contains paper's title, published and summary"]:
    import arxiv
    client = arxiv.Client()
    # 执行检索
    search = arxiv.Search(
        query=query,
        max_results=max_results,
        sort_by=arxiv.SortCriterion.Relevance
    )

    results = list(client.results(search))
    papers = []
    for result in results:
        papers.append(Paper(title=result.title, published=result.published.strftime("%Y-%M-%d"), summary=result.summary))
    return papers

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

4.2 编写对话流

在4.1中已经实现适合LLM函数调用的检索arxiv方法,在这一节我们将尝试使用ReAct式的流程来实现函数调用。根据官方教程中教程[ReAct]的用法,它的Prompt是根据Langchain中Prompt模板来设置的。ReAct能够推导当前要做什么Thought,Action是什么,然后执行Action,再把结果给到Observation,如果对话继续,Thought会根据Observation得出是否要继续下一步。以下是Prompt模板:

ReAct_prompt = """
Answer the following questions as best you can. You have access to tools provided.
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take
Action Input: the input to the action
Observation: the result of the action
... (this process can repeat multiple times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
"""

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

实例化用户和助手Agent,代码如下:

user_proxy = UserProxyAgent(
    name="User",
    is_termination_msg=lambda x: x.get("content", "") and x.get("content", "").rstrip().endswith("TERMINATE"),
    human_input_mode="ALWAYS",
    max_consecutive_auto_reply=10,
    code_execution_config={"work_dir": "coding", "last_n_messages": 3, "use_docker": False},
)

assistant = AssistantAgent(
    name="Assistant",
    system_message="Only use the tools you have been provided with. Reply TERMINATE when the task is done.",
    llm_config=llm_config,
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

将函数注册到两个Agent上:

register_function(
    search_arxiv,
    caller=assistant,
    executor=user_proxy,
    name="search_arxiv",
    description="Search the arxiv for the given query to get the paper",
)


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最后调用对话,其中自定义了message使用react_prompt_message方法构造Prompt输入。

def react_prompt_message(sender, recipient, context):
    return ReAct_prompt.format(input=context["question"])

task = "使用arxiv包获取2020-2024年期间所有头部姿态识别的论文并撰写一篇报告"
papers = user_proxy.initiate_chat(
    assistant,
    message=react_prompt_message,
    question=task,
)


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.3 运行

首先ReAct推导当前的需要做的事情,最终输入到LLM的参数如下,可以看到tools参数:

{'messages': [{'content': 'Only use the tools you have been provided with. Reply TERMINATE when the task is done.', 'role': 'system'}, {'content': '\nAnswer the following questions as best you can. You have access to tools provided.\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take\nAction Input: the input to the action\nObservation: the result of the action\n... (this process can repeat multiple times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin!\nQuestion: 使用arxiv包获取2020-2024年期间所有头部姿态识别的论文并撰写一篇报告\n', 'role': 'user'}], 'tools': [{'type': 'function', 'function': {'description': 'Search the arxiv for the given query to get the paper', 'name': 'search_arxiv', 'parameters': {'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'query string of arxiv'}, 'max_results': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'default': 10, 'description': 'the max result from arxiv'}}, 'required': ['query']}}}], 'model': 'qwen-max', 'temperature': 0.7}


  • 1
  • 2
  • 3

此时LLM给出推导Thought和Action以及Action的输入,并且字段tools告知AutoGen需要调用工具search_arxiv并且给出了参数:

ChatCompletionMessage(content='Thought: 首先,我需要使用search_arxiv函数来搜索相关的论文。由于直接查询可能无法精确限定年份和主题范围,我将首先尝试使用一个宽泛的查询字符串,然后在结果中筛选出符合2020-2024年且关于头部姿态识别的论文。不过,请注意,实际执行此任务时,我只能调用函数,无法直接撰写报告,但我可以提供如何根据检索结果撰写报告的指导和建议。\n\nAction: search_arxiv\nAction Input: {"query": "head pose estimation", "max_results": 50}', role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='', function=Function(arguments='{"query": "head pose estimation", "max_results": 50}', name='search_arxiv'), type='function')])


  • 1
  • 2
  • 3

调用search_arxiv函数返回如下50条论文信息,由于返回太多,考虑阅读体验,做了删减。此外,除非LLM的Token限制比较大,否则这里最好是给他反馈将参数调小,不然很容易报502。

[{"title": "Location-guided Head Pose Estimation for Fisheye Image", "published": "2024-33-28", "summary": "Camera with ..."}, ...}]


  • 1
  • 2
  • 3

既然工具调用已经结束,那么工具返回的结果,如何请求呢?从下面的调用请求上来看,这里ReAct Prompt里Observation是没有填写的,不似我们之前在文章[[LLM-Agents]万字长文深度解析Agent反思工作流框架Reflexion中篇:ReAct]解析Reflexion中所见那样,手动填写Observation。它是每一轮都从Thought、Action、Action Input走一遍,最后工具调用的结果放在角色为tool的消息中。

{'content': 'Only use the tools you have been provided with. Reply TERMINATE when the task is done.', 'role': 'system'}

{'content': '...Begin!\nQuestion: 使用arxiv包获取2020-2024年期间所有头部姿态识别的论文并撰写一篇报告\n', 'role': 'user'}

{'content': 'Thought: 首先,...。Action: search_arxiv\nAction Input: {"query": "head pose estimation", "max_results": 50}', 'tool_calls': [{'id': '', 'function': {'arguments': '{"query": "head pose estimation", "max_results": 50}', 'name': 'search_arxiv'}, 'type': 'function'}], 'role': 'assistant'}

{'role': 'tool', 'tool_call_id': '', 'content': 'USER INTERRUPTED'}

{'content': '最大结果数量改为10条', 'role': 'user'}

{'content': 'Thought: 由于用户希望减少最大结果数量以获取更精炼的信息,我将重新执行查询,这次将最大结果数量设置为10。\n\nAction: search_arxiv\nAction Input: {"query": "head pose estimation", "max_results": 10}', 'tool_calls': [{'id': '', 'function': {'arguments': '{"query": "head pose estimation", "max_results": 10}', 'name': 'search_arxiv'}, 'type': 'function'}], 'role': 'assistant'}

{'tool_call_id': '', 'role': 'tool', 'content': '[{"title": "Location-guided Head Pose Estimation for Fisheye Image", "published": "2024-33-28", "summary": "..."}, {"title": "FONT: Flow-guided One-shot Talking Head Generation with Natural Head Motions", "published": "2023-25-31", "summary": "..."}, {"title": "Face Alignment Assisted by Head Pose Estimation", "published": "2015-07-11", "summary": "..."}, {"title": "Human Head Pose Estimation by Facial Features Location", "published": "2015-15-09", "summary": "..."}, {"title": "Towards Head Motion Compensation Using Multi-Scale Convolutional Neural Networks", "published": "2018-57-10", "summary": "..."}, {"title": "Deep Entwined Learning Head Pose and Face Alignment Inside an Attentional Cascade with Doubly-Conditional fusion", "published": "2020-42-14", "summary": "..."}, {"title": "A Marker-free Head Tracker Using Vision-based Head Pose Estimation with Adaptive Kalman Filter", "published": "2021-20-24", "summary": "..."}, {"title": "Nose, eyes and ears: Head pose estimation by locating facial keypoints", "published": "2018-04-03", "summary": "..."}, {"title": "Semi-Supervised Unconstrained Head Pose Estimation in the Wild", "published": "2024-01-03", "summary": "...}."}]'}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从输入的消息来看,输入是包括了历史的输入和输出,因此最后的输出依然是重新推理要做的事情,并没有因为输入较多,导致LLM丢失要做的事情,他依然推导出下一步需要撰写报告。报告如下:

image-20240617145428010

5. 总结

本文尝试从查找论文入手,获取关于头部姿态评估的研究,并使用AutoGen自动编码来实现数据收集和报告撰写。我们设计了包括规划、工程、执行和写作等多个Agent,并通过群聊和自动选择Agent的应答和转换来协调工作。然而,发现该流程过于动态,对LLM的要求较高,失败几率较大。

最终,我们决定编写检索论文的代码,并采用ReAct Prompt范式来完成报告撰写。尽管ReAct在任务执行上高效,但也显示出AssitantAgent承担了过多职责。它的设定应尽量简单。我们应该坚持专人专事的原则,分别使用不同的Agent来获取arXiv论文和撰写文档。这种做法允许我们对撰写文档的Agent设定更专业的Prompt,从而提高工作效率和专业性。

在这里插入图片描述

如何学习AI大模型?

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/983844
推荐阅读
相关标签