Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

writen-report #1235

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config/config2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
# Config Docs: https://docs.deepwisdom.ai/main/en/guide/get_started/configuration.html
llm:
api_type: "openai" # or azure / ollama / groq etc.
model: "gpt-4-turbo" # or gpt-3.5-turbo
base_url: "https://api.openai.com/v1" # or forward url / other llm url
model: "gpt-3.5-turbo" # or
base_url: "" # or forward url / other llm url
api_key: "YOUR_API_KEY"
97 changes: 97 additions & 0 deletions tests/data/report_writer/domain.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
first_paragraph:
name: first_paragraph
desc: 请帮我依据用户所提供事故要点信息进行简要扩写, 完整事故报告的第一自然段。
guidance: |
请根据以下关键信息撰写一个合理的事故报告开头的第一自然段,主要介绍事故概要。在段落的结尾处,加入一段官方声明,声明应体现事故调查的严谨性和全面性。
*** 注意事项 ***
1. 在段落的结尾,请务必补充一段官方声明。eg:<事故调查组按照“实事求是、客观公正、尊重科学”的原则,..., ... ,现将有关情况报告如下>
2. 事件描述 简短扼要。
请确保您的文本不仅简要记录了事故概要,而且语言表达准确、连贯,体现出调查报告的权威性和专业性。字数要求 300 - 500 之间。


second_paragraph:
name: second_paragraph
desc: 事故发生单位及事发工地概况章节内容
guidance: |
请根据大语言模型固有知识和常识,详细描述事故发生单位的基本情况以及事故相关人员的情况。请严格按照参考模板的格式返回。
参考模板:
```
一、事故的基本情况
(一)事故发生单位的基本情况。
<事故发生单位名称>于<成立年份>年<成立月份>月<成立日期>日成立,法定代表人为<法定代表人姓名>,社会统一信用代码:<信用代码>。公司主要从事<主营业务>,注册地址位于<注册地址>,公司规模<员工人数>人左右, 等等。
(二)事故相关人员情况。
<人员详细信息>
```
*** 注意事项 ***
1. 生成文本只包含参考模板里‘事故的基本情况’的章节,不要引入非必要的其他章节。


third_paragraph:
name: third_paragraph
desc: 事故发生经过和应急处置情况章节内容
guidance: |
请依据所提供的信息,依照以下参考模板,详细叙述事故的发生过程和采取的紧急应对措施。请确保只提供“事故发生经过和事故救援情况”章节的内容,不要包含其他非相关章节。
参考模板:
```
二、事故发生经过和事故救援情况
<在此处详细描述事故的起因、发展、影响以及救援行动的详细过程,包括事故的评估和救援措施的有效性>
```


forth_paragraph:
name: forth_paragraph
desc: 事故发生的原因和事故性质章节内容
guidance: |
请依据所提供的信息,结合提供的参考模板,详细描述事故发生的原因和事故性质。请严格按照参考模板的格式返回,并确保每个部分都有合理推理的详细描述。
参考模板:
```
三、事故发生的原因和事故性质
(一)直接原因。
<合理推理出发生事故的直接原因,并给出详细的解释和描述>
(二)间接原因。
<合理推理出发生事故的间接原因,并给出详细的解释和描述>
(三)事故性质。
<给出事故性质的明确描述,例如:经事故调查组认定, XXX 事故是一起 XXX 而引发的 xxx 事故。>
```
*** 注意事项 ***
1. 生成文本只包含参考模板里‘事故发生的原因和事故性质’的章节,不要引入非必要的其他章节。
2. 在描述直接原因和间接原因时,请确保推理过程的逻辑性和合理性。


fifth_paragraph:
name: fifth_paragraph
desc: 事故责任的认定及处理建议章节内容
guidance: |
请依据所提供的信息,结合提供的参考模板,详细描述事故责任的认定及处理建议。在描述过程中,需要结合事故发生的具体情况,对责任者的行为与事故发生原因的联系进行深入分析,并根据责任者对事故后果的影响程度进行责任认定。同时,请提供具体的处理建议,包括但不限于刑事责任追究和单位问责。
参考模板:
```
四、事故责任的认定及处理建议
通过调查事故的经过和分析事故发生的原因,根据责任者的行为与事故发生原因的联系以及对事故后果所起的作用的程度,对事故责任认定如下:
(一)<对责任者A的行为与事故发生原因的联系进行详细描述,并分析其对事故后果的影响程度,给出责任认定>
(二)<对责任者B的行为与事故发生原因的联系进行详细描述,并分析其对事故后果的影响程度,给出责任认定>
对事故责任者及责任单位的处理建议:
(一)<建议追究刑事责任人员,包括具体的人员名单、职位、建议的刑事责任类型及其依据>
(二)<问责单位建议,包括具体的单位名称、建议的问责措施及其依据>
```
*** 注意事项 ***
1. 生成文本只包含参考模板里‘事故责任的认定及处理建议’的章节,不要引入非必要的其他章节。
2. 在认定责任时,要确保描述清晰、逻辑严谨,并基于事实和法律规定进行推理。
3. 在提出处理建议时,要确保建议是可行的、合理的,并且符合相关法律法规的要求。


last_paragraph:
name: last_paragraph
desc: 事故整改及防范措施章节内容
guidance: |
请根据“info”中提供的信息,按照以下参考模板,详细列出针对事故的整改及防范措施。请仅关注和填写“事故整改及防范措施”章节,无需涉及其他部分。
参考模板:
```
五、事故整改及防范措施
各县(市、区)必须深刻吸取事故教训,针对事故暴露的问题,进行全面审查和整改。结合本地实际情况,采取切实有效的措施,确保不再发生类似事故。
(一)<在此填写具体的第一条整改及防范措施>
(二)<在此填写具体的第二条整改及防范措施>
(三)<在此填写具体的第三条整改及防范措施>
...
```


38 changes: 38 additions & 0 deletions tests/data/report_writer/sop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"task_id": "1",
"dependent_task_ids": [],
"instruction": "基于用户提供的事故要点信息,写一份事故报告的第一自然段。",
"task_type": "first_paragraph"
},
{
"task_id": "2",
"dependent_task_ids": ["1"],
"instruction": "详细准确书写事故发生单位及事发工地概况章节内容。",
"task_type": "second_paragraph"
},
{
"task_id": "3",
"dependent_task_ids": ["1"],
"instruction": "详细准确书写事故发生经过和应急处置情况及评估章节内容。",
"task_type": "third_paragraph"
},
{
"task_id": "4",
"dependent_task_ids": ["3"],
"instruction": "详细准确书写事故发生的原因和事故性质章节内容。",
"task_type": "forth_paragraph"
},
{
"task_id": "5",
"dependent_task_ids": ["3","4"],
"instruction": "详细准确书写事故责任的认定及处理建议章节内容。",
"task_type": "fifth_paragraph"
},
{
"task_id": "6",
"dependent_task_ids": ["1","2","3","4","5"],
"instruction": "详细准确书写事故整改及防范措施章节内容。",
"task_type": "last_paragraph"
}
]
191 changes: 191 additions & 0 deletions tests/metagpt/ext/write_report/core_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
from __future__ import annotations

import json
import os
from typing import Literal, Union

from pydantic import Field, model_validator

from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message, Task, TaskResult
from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender
from tests.metagpt.ext.write_report.write_evaluator_refine import (
EvaluatorReport,
RefineReport,
WriteAnalysisReport,
)
from tests.metagpt.ext.write_report.write_report_planner import WriteReportPlanner


class ReportRewriter(Role):
name: str = "wangs"
profile: str = "事故调查报告"
auto_run: bool = True
use_plan: bool = True
planner: WriteReportPlanner = Field(default_factory=WriteReportPlanner)
evaluator: EvaluatorReport = Field(default_factory=EvaluatorReport, exclude=True)
refine: RefineReport = Field(default_factory=RefineReport, exclude=True)

use_evaluator: bool = True
tools: Union[str, list[str]] = [] # Use special symbol ["<all>"] to indicate use of all registered tools
tool_recommender: ToolRecommender = ToolRecommender()
react_mode: Literal["plan_and_act", "react"] = "plan_and_act" # "by_order"
human_design_sop: bool = False
max_react_loop: int = 5
use_reflection: bool = False

# 重构
@model_validator(mode="after")
def set_plan_and_tool(self):
self._set_react_mode(react_mode=self.react_mode, max_react_loop=self.max_react_loop, auto_run=self.auto_run)
# update user_definite planner
self.planner = WriteReportPlanner(goal=self.goal, working_memory=self.rc.working_memory, auto_run=self.auto_run)
# Whether to adopt the sop paradigm predefined by humans(是否采用人类预先定义的 sop 范式)
self.planner.human_design_sop = self.human_design_sop
self.use_plan = (
self.react_mode == "plan_and_act"
) # create a flag for convenience, overwrite any passed-in value
if self.tools:
self.tool_recommender = BM25ToolRecommender(tools=self.tools)
self.set_actions([WriteAnalysisReport])
self._set_state(0)
return self

@property
def working_memory(self):
return self.rc.working_memory

# 重构
async def run(self, with_message=None, upload_file="") -> Message | None:
"""
Asynchronously runs the function.

Args:
with_message (Any, optional): The message to be passed to the superclass run method. Defaults to None.
upload_file (str, optional): The file to be uploaded. Defaults to "".

Raises:
ValueError: If the upload_file does not exist.

Returns:
Message | None: The result of the superclass run method.
"""
if not os.path.exists(upload_file):
raise ValueError("upload_file must be provided")
self.upload_file = upload_file
return await super().run(with_message)

# 重构
async def _plan_and_act(self) -> Message:
rsp = await super()._plan_and_act()
self.write_out_report()
return rsp

async def _act_on_task(self, current_task: Task) -> TaskResult:
code, result, is_success = await self._ready_write_report()
task_result = TaskResult(code=code, result=result, is_success=is_success)
return task_result

async def _ready_write_report(self, max_retry: int = 3):
"""
Asynchronously prepares the report for writing.

Args:
max_retry (int, optional): The maximum number of retries for evaluating the report. Defaults to 3.

Returns:
Tuple[str, str, bool]: A tuple containing the report, suggestion, and success status.
- report (str): The generated report.
- suggestion (str): The suggestion for improving the report.
- success (bool): Indicates whether the report evaluation was successful.

Raises:
AttributeError: If the planner is not initialized and use_plan is True.

"""
if self.use_plan:
if self.planner is None:
raise AttributeError("Planner is not initialized")
plan_status = self.planner.get_plan_status()
else:
plan_status = ""

if self.tools:
context = self.working_memory.get()[-1].content if self.working_memory.get() else ""
plan = self.planner.plan if self.use_plan else None
tool_info = await self.tool_recommender.get_recommended_tool_info(context=context, plan=plan)
else:
tool_info = ""

await self._check_data()
# Write the first draft of the report first
report, cause_by = await self._write_report(plan_status, tool_info)
self.working_memory.add(Message(content=report, role="assistant", cause_by=cause_by))
suggestion, success = "", True
# Evaluate the first draft of the report
if self.use_evaluator:
counter = 0
success = False
while not success and counter < max_retry:
suggestion, success = await self.evaluator.run(working_memory=self.working_memory.get())
# Unsuccessful evaluation, manuscript improvement
if not success:
report = await self.refine.run(suggestion=suggestion, working_memory=self.working_memory.get())
# Update working_memory to replace the first draft with the reviewed manuscript
self.working_memory.delete(self.working_memory.get()[-1])
self.working_memory.add(Message(content=report, role="assistant", cause_by=RefineReport))
else:
break
counter += 1
return report, suggestion, success

async def _write_report(
self,
plan_status: str = "",
tool_info: str = "",
):
if not hasattr(self, "rc") or not hasattr(self.rc, "todo"):
raise AttributeError("Expected 'rc' and 'rc.todo' to be initialized")
todo = self.rc.todo # todo is WriteAnalysisCode
logger.info(f"ready to {todo.name}")
user_requirement = self.get_memories()[0].content
structual_prompt, report = await todo.run(
user_requirement=user_requirement,
plan_status=plan_status,
tool_info=tool_info,
working_memory=self.working_memory.get(),
)
# Initially, the file information attached to the user is loaded into working_memory. Now we are trying to update it so that working_memory only retains questions and answers (user/assistant).
if self.working_memory and self.working_memory.get()[-1].cause_by == "custom":
self.working_memory.delete(self.working_memory.get()[-1])
self.working_memory.add(Message(content=structual_prompt, role="user", cause_by=RewriteReport))
return report, todo

def read_data_info(self):
file_path = self.upload_file
with open(file_path, "r", encoding="utf-8") as file:
data_info = json.loads(file.read())
key = self.planner.current_task.task_type
data = "\n".join([str(x) for x in data_info[key].items()])
# Add rule conditions
if key == "second_paragraph":
data += "\n".join([str(x) for x in data_info["third_paragraph"].items()])
self.working_memory.add(Message(content=data, role="user", cause_by="custom"))

def write_out_report(self):
# file_path = DATA_PATH / 'report.txt'
directory, _ = os.path.splitext(self.upload_file)
file_path = f"{directory}_metagpt.txt"
result = ""
for task in self.planner.plan.tasks:
result += f"{task.code}\n"
with open(file_path, "w") as f:
f.write(result)

async def _check_data(self):
if self.working_memory.index is None or "custom" not in self.working_memory.index:
logger.info("add user additional data into working_memory")
self.read_data_info()
if not self.use_plan or not self.planner.plan.get_finished_tasks() or self.planner.plan.current_task.task_type:
return
21 changes: 21 additions & 0 deletions tests/metagpt/ext/write_report/report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2024/5/06 13:54
@Author : wangwenju269
@File : report.py
"""

import fire

from tests.metagpt.ext.write_report.core_report import ReportRewriter


async def main(auto_run: bool = True):
requirement = "您的目标是构建一个完整、连贯的事故报告, 请确保您的文字精确、逻辑清晰,并保持专业和客观的写作风格。"
di = ReportRewriter(auto_run=auto_run, human_design_sop=True, use_evaluator=True)
await di.run(f"{requirement}", upload_file="<upload file>")


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