Spaces:
Runtime error
Runtime error
#!/usr/bin/env python | |
""" | |
@Modified By: mashenquan, 2023/8/22. A definition has been provided for the return value of _think: returning false indicates that further reasoning cannot continue. | |
""" | |
import asyncio | |
from pydantic import BaseModel | |
from metagpt.actions import CollectLinks, ConductResearch, WebBrowseAndSummarize | |
from metagpt.actions.research import get_research_system_text | |
from metagpt.const import RESEARCH_PATH | |
from metagpt.logs import logger | |
from metagpt.roles import Role | |
from metagpt.schema import Message | |
class Report(BaseModel): | |
topic: str | |
links: dict[str, list[str]] = None | |
summaries: list[tuple[str, str]] = None | |
content: str = "" | |
class Researcher(Role): | |
def __init__( | |
self, | |
name: str = "David", | |
profile: str = "Researcher", | |
goal: str = "Gather information and conduct research", | |
constraints: str = "Ensure accuracy and relevance of information", | |
language: str = "en-us", | |
**kwargs, | |
): | |
super().__init__(name, profile, goal, constraints, **kwargs) | |
self._init_actions([CollectLinks(name), WebBrowseAndSummarize(name), ConductResearch(name)]) | |
self.language = language | |
if language not in ("en-us", "zh-cn"): | |
logger.warning(f"The language `{language}` has not been tested, it may not work.") | |
async def _think(self) -> bool: | |
if self._rc.todo is None: | |
self._set_state(0) | |
return True | |
if self._rc.state + 1 < len(self._states): | |
self._set_state(self._rc.state + 1) | |
else: | |
self._rc.todo = None | |
return False | |
async def _act(self) -> Message: | |
logger.info(f"{self._setting}: ready to {self._rc.todo}") | |
todo = self._rc.todo | |
msg = self._rc.memory.get(k=1)[0] | |
if isinstance(msg.instruct_content, Report): | |
instruct_content = msg.instruct_content | |
topic = instruct_content.topic | |
else: | |
topic = msg.content | |
research_system_text = get_research_system_text(topic, self.language) | |
if isinstance(todo, CollectLinks): | |
links = await todo.run(topic, 4, 4) | |
ret = Message("", Report(topic=topic, links=links), role=self.profile, cause_by=type(todo)) | |
elif isinstance(todo, WebBrowseAndSummarize): | |
links = instruct_content.links | |
todos = (todo.run(*url, query=query, system_text=research_system_text) for (query, url) in links.items()) | |
summaries = await asyncio.gather(*todos) | |
summaries = list((url, summary) for i in summaries for (url, summary) in i.items() if summary) | |
ret = Message("", Report(topic=topic, summaries=summaries), role=self.profile, cause_by=type(todo)) | |
else: | |
summaries = instruct_content.summaries | |
summary_text = "\n---\n".join(f"url: {url}\nsummary: {summary}" for (url, summary) in summaries) | |
content = await self._rc.todo.run(topic, summary_text, system_text=research_system_text) | |
ret = Message("", Report(topic=topic, content=content), role=self.profile, cause_by=type(self._rc.todo)) | |
self._rc.memory.add(ret) | |
return ret | |
async def _react(self) -> Message: | |
while True: | |
await self._think() | |
if self._rc.todo is None: | |
break | |
msg = await self._act() | |
report = msg.instruct_content | |
self.write_report(report.topic, report.content) | |
return msg | |
def write_report(self, topic: str, content: str): | |
if not RESEARCH_PATH.exists(): | |
RESEARCH_PATH.mkdir(parents=True) | |
filepath = RESEARCH_PATH / f"{topic}.md" | |
filepath.write_text(content) | |
if __name__ == "__main__": | |
import fire | |
async def main(topic: str, language="en-us"): | |
role = Researcher(topic, language=language) | |
await role.run(topic) | |
fire.Fire(main) | |