从MetaGPT看如何高效的在安全领域运用GPT

引言

在近期对GPT在安全领域的应用中了解了部分高效Prompt和高效Agent流相关的基本原则,但这些概念都是偏理论的场景,偶然的机会了解到现阶段非常火热的一个开源GPT应用框架MetaGPT,这个框架以”软件公司“的形式非常专业的实现了高效Prompt及高效Agent流的落地,笔者以为这种落地思路会成为安全领域大模型落地的一个楔子,所以对框架的部分代码进行了分析,以此为依据,也为大模型在安全甲方的定位做了一个比较”虚“的畅享。

高效GPT概述

关于高效GPT,笔者收集到两种业内认可度比较好的基本原则,

第一套基本原则,CRIPSE,是关于如何写出一句高效Prompt的方案,只要描述的是Prompt的细节,更详细的案例可以从文章末尾的Reference中获取,

1
2
3
4
5
CR:Capacity and Role(能力与角色)。你希望 AI 扮演怎样的角色
I:Insight(洞察),提供背景信息和上下文
S:Statement(陈述),你希望 AI 做什么。
P:Personality(个性),你希望 AI 以什么风格或方式回答你。
E:Experiment(实验),要求 AI 为你提供多个答案

当拥有高效的Prompt编写能力后,如果通过进一步的与大模型交流,来提升结果质量,该内容源自吴恩达关于Agent工作流优化的分享

1
2
3
4
反思(Reflection):当让大模型帮助完成某项工作后,取得结果再次交给GPT,并补充反思性语句,如首先让GPT写出一段代码,然后补充到”这是XXX的代码,请仔细检测代码的正确性、健全性、效率和良好的结构。“
工具(Tools):不仅仅通过GPT同时通过大量API协同(如Massive APIs)来提升最终产出结果。比较有代表性的就是Gorilla。
规划(Planning):将GPT要执行的任务分步进行,告诉它第一步做什么,第二步做什么,依次类推。
多智体协同(Multi-agent):让GPT去扮演不同角色,如项目经理、架构师、产品经理、工程师等,彼此分工,讨论,得出最优结果。

MetaGPT具体实现解读

目录概览

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
├── __init__.py ddd
├── _compat.py
├── actions 每项具体工作,及工作怎么完成,包含大量Prompt
├── config
├── config2.py
├── configs
├── const.py
├── context.py
├── context_mixin.py
├── document.py
├── document_store
├── environment
├── ext
├── learn
├── llm.py
├── logs
├── logs.py
├── management
├── memory
├── metagpt
├── prompts
├── provider
├── rag
├── repo_parser.py
├── roles 在这里定义具体角色、每个角色负责的工作
├── schema.py
├── skills
├── software_company.py 主函数,在这里接收需求
├── startup.py
├── strategy
├── subscription.py
├── team.py
├── tools
├── utils
└── workspace

架构图如下,

启动参数概述

代码在下面给出,每个参数的意义均已经标明,比较常用的有code_review,用于代码review,提升代码质量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def startup(
idea: str = typer.Argument(None, help="Your innovative idea, such as 'Create a 2048 game.'"),
investment: float = typer.Option(default=3.0, help="Dollar amount to invest in the AI company."),
n_round: int = typer.Option(default=5, help="Number of rounds for the simulation."),
code_review: bool = typer.Option(default=True, help="Whether to use code review."),
run_tests: bool = typer.Option(default=False, help="Whether to enable QA for adding & running tests."),
implement: bool = typer.Option(default=True, help="Enable or disable code implementation."),
project_name: str = typer.Option(default="", help="Unique project name, such as 'game_2048'."),
inc: bool = typer.Option(default=False, help="Incremental mode. Use it to coop with existing repo."),
project_path: str = typer.Option(
default="",
help="Specify the directory path of the old version project to fulfill the incremental requirements.",
),
reqa_file: str = typer.Option(
default="", help="Specify the source file name for rewriting the quality assurance code."
),
max_auto_summarize_code: int = typer.Option(
default=0,
help="The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating "
"unlimited. This parameter is used for debugging the workflow.",
),
recover_path: str = typer.Option(default=None, help="recover the project from existing serialized storage"),
init_config: bool = typer.Option(default=False, help="Initialize the configuration file for MetaGPT."),
)

code_review

当开启–code-review的时候,体现在代码层面上,就是为这个项目添加一个合作角色Engineer,他的主要工作是WriteCodeReview

1
company.hire([Engineer(n_borg=5, use_code_review=code_review)])

关于Engineer这个类的第一个参数n_borg也非常有意思,n_borg代表博格人的数量,而这个博格人的官方解释如下

博格文明以蜂巢或集体思维方式运作,称为“集体”。每个博格个体都通过复杂的亚空间网络连接到集体,确保对每个成员的持续监督和指导。这种集体意识不仅使他们能够“共享相同的思想”,还使他们能够迅速适应新策略。虽然集体的个体成员很少交流,但有时集体的“声音”会在船上传输。
目前该参数虽然已经具备,但在代码中的使用还没有,所以我更偏向于这个参数的定位在未来也是去做类似于CRISPE框架中E的工作。

使用效果展示

MetaGpt在演示使用的时候,主要展示的是一个软件公司的工作流程,通过创造multi agent扮演产品经理、架构师、产品经理、工程师,为不同角色设定任务,实现了完整的Developing SOP流程

比如当我执行”写一个贪吃蛇程序“命令,

1
python3 software_company.py --code-review "写一个贪吃蛇程序"  

那么首先,这个任务会交给项目经理Agent,项目经理Agent基于已经编写好的模版会去找GPT生成PRD

紧接着,拿到PRD后,会把PRD交给团队的每个人,架构师Agent接过PRD,进行架构设计

架构设计完成后再交给产品经理进行定制任务

以此类推,接着交给工程师就行代码编写等等,直到任务完成。

核心代码解读-role

role提供三种工作模式,具体解释如下面给出的注释,粗略的可以解释为

  1. react模式:默认模式,也是现在最推崇的模式,让大模型明确自己的角色后,不断思考自己现在要做什么,接下来要做什么,再行动,完成需要做的事情,通过不断反思、行动,达成一个最佳结果。
  2. by_order模式:提前为角色定制好自己要做的事情,然后角色根据定制好的行动,依次交给大模型去完成。
  3. plan_and_act:首先让大模型思考自己要做哪些事情,然后依次让大模型去完成,相比于by_order模式,唯一的区别就是行动不再由代码提前设定好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""Set strategy of the Role reacting to observed Message. Variation lies in how
this Role elects action to perform during the _think stage, especially if it is capable of multiple Actions.

Args:
react_mode (str): Mode for choosing action during the _think stage, can be one of:
"react": standard think-act loop in the ReAct paper, alternating thinking and acting to solve the task, i.e. _think -> _act -> _think -> _act -> ...
Use llm to select actions in _think dynamically;
"by_order": switch action each time by order defined in _init_actions, i.e. _act (Action1) -> _act (Action2) -> ...;
"plan_and_act": first plan, then execute an action sequence, i.e. _think (of a plan) -> _act -> _act -> ...
Use llm to come up with the plan dynamically.
Defaults to "react".
max_react_loop (int): Maximum react cycles to execute, used to prevent the agent from reacting forever.
Take effect only when react_mode is react, in which we use llm to choose actions, including termination.
Defaults to 1, i.e. _think -> _act (-> return result and end)
"""

首先可以先理解一下think和act的时候,分别去干什么

  1. think的核心动作有两个,首先确定当前要做什么,然后确定接下来要去做什么
  2. act发生在think后,明确了要做什么,就把任务交给llm,让大模型去完成当前要做的事情

了解了基本动作,就可以去看一下MetaGPT中的三种工作模式,代码基本就是上面解释的翻译,感兴趣的可以看一下,不过多赘述细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 = Message(content="No actions taken yet", cause_by=Action) # will be overwritten after Role _act
while actions_taken < self.rc.max_react_loop:
# think
todo = await self._think()
if not 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

by_order模式源码如下

1
2
3
4
5
6
7
8
async def _act_by_order(self) -> Message:
"""switch action each time by order defined in _init_actions, i.e. _act (Action1) -> _act (Action2) -> ..."""
start_idx = self.rc.state if self.rc.state >= 0 else 0 # action to run from recovered state
rsp = Message(content="No actions taken yet") # return default message if actions=[]
for i in range(start_idx, len(self.states)):
self._set_state(i)
rsp = await self._act()
return rsp # return output from the last action

plan_and_act模式源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async def _plan_and_act(self) -> Message:
"""first plan, then execute an action sequence, i.e. _think (of a plan) -> _act -> _act -> ... Use llm to come up with the plan dynamically."""

# create initial plan and update it until confirmation
goal = self.rc.memory.get()[-1].content # retreive latest user requirement
await self.planner.update_plan(goal=goal)

# take on tasks until all finished
while self.planner.current_task:
task = self.planner.current_task
logger.info(f"ready to take on task {task}")

# take on current task
task_result = await self._act_on_task(task)

# process the result, such as reviewing, confirming, plan updating
await self.planner.process_task_result(task_result)

rsp = self.planner.get_useful_memories()[0] # return the completed plan as a response

self.rc.memory.add(rsp) # add to persistent memory

return rsp

核心目录解读-action

在metagpt的action目录下,有大量具体动作的代码,代码里主要保存的是每个动作对应的Prompt,在这里可以对该项目的Prompt做一个了解和学习,这里抽取大家比较熟悉的write_code行为进行展示,这里的Prompt的书写基本完全满足CRIPSE标准,可以作为CRIPSE的示例


PROMPT_TEMPLATE = """
NOTICE
Role: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code
Language: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".

# Context
## Design
{design}

## Task
{task}

## Legacy Code
1
{code}
## Debug logs
1
2
3
{logs}

{summary_log}
## Bug Feedback logs
1
{feedback}
# Format example ## Code: {filename}
1
2
## {filename}
...
# Instruction: Based on the context, follow "Format example", write code. ## Code: {filename}. Write code with triple quoto, based on the following attentions and context. 1. Only One file: do your best to implement THIS ONLY ONE FILE. 2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import. 4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. 5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. 6. Before using a external variable/module, make sure you import it first. 7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. """

甲方安全应用畅想

MetaGPT当前展示的主要是GPT在软件开发流程中的应用,因为核心角色是产品经理、架构师、项目经理、工程师、QA工程师,以接收到一个需求作为任务的起点,以该项目的完成作为终点,以该模版为参考,结合GPT形成一个全自动化的SDL流程,也应该具备可行性,在下面可以给出一个基于标准SDL的流程及角色任务定制。

流程

角色及任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
安全培训师:
任务一:基于团队定制的重点漏洞生成培训内容
任务二:基于上一次发布发生的问题,生成培训内容
任务三:基于本月热门漏洞,生成培训内容

安全评审:
任务:通过DepOps平台,了解每一个新需求的产生,在需求产生后分析需求可能发生的漏洞,生成开发前注意事项。

白盒审计人员:
任务一:在封版日前基于代码仓库代码变更,分析本次变更需求有哪些,出现了哪些风险函数调用。
任务二:基于白盒扫描内容进行风险二次反思,汇总本次白盒扫描出的风险。

黑盒测试人员:
任务一:基于白盒审计结果,告知黑盒重点测试内容。
任务二:基于黑盒扫描结果,对结果二次反思,决策出高危风险。

安全发布卡点人员:
任务:汇总上面流程发生的所有事件,汇总研发对于风险的反馈,决策此次发布风险。

引用

METAGPT: META PROGRAMMING FOR A MULTI-AGENT COLLABORATIVE FRAMEWORK

如何写出优雅的prompt? - 通用的万能框架