mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-02 12:22:39 +02:00
Merge pull request #606 from better629/feat_basemodel
Feat add basemodel of other roles/actions and fix examples
This commit is contained in:
commit
41a174399e
23 changed files with 253 additions and 198 deletions
|
|
@ -55,16 +55,13 @@ class CreateAgent(Action):
|
|||
|
||||
|
||||
class AgentCreator(Role):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Matrix",
|
||||
profile: str = "AgentCreator",
|
||||
agent_template: str = MULTI_ACTION_AGENT_CODE_EXAMPLE,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
name: str = "Matrix"
|
||||
profile: str = "AgentCreator"
|
||||
agent_template: str = MULTI_ACTION_AGENT_CODE_EXAMPLE
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([CreateAgent])
|
||||
self.agent_template = agent_template
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}({self._rc.todo.name})")
|
||||
|
|
@ -86,10 +83,6 @@ if __name__ == "__main__":
|
|||
|
||||
creator = AgentCreator(agent_template=agent_template)
|
||||
|
||||
# msg = """Write an agent called SimpleTester that will take any code snippet (str)
|
||||
# and return a testing code (str) for testing
|
||||
# the given code snippet. Use pytest as the testing framework."""
|
||||
|
||||
msg = """
|
||||
Write an agent called SimpleTester that will take any code snippet (str) and do the following:
|
||||
1. write a testing code (str) for testing the given code snippet, save the testing code as a .py file in the current working directory;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ import subprocess
|
|||
import fire
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.roles.role import Role, RoleReactMode
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
|
|
@ -23,8 +22,7 @@ class SimpleWriteCode(Action):
|
|||
your code:
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "SimpleWriteCode", context=None, llm: LLM = None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "SimpleWriteCode"
|
||||
|
||||
async def run(self, instruction: str):
|
||||
prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
|
||||
|
|
@ -44,8 +42,7 @@ class SimpleWriteCode(Action):
|
|||
|
||||
|
||||
class SimpleRunCode(Action):
|
||||
def __init__(self, name: str = "SimpleRunCode", context=None, llm: LLM = None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "SimpleRunCode"
|
||||
|
||||
async def run(self, code_text: str):
|
||||
result = subprocess.run(["python3", "-c", code_text], capture_output=True, text=True)
|
||||
|
|
@ -55,13 +52,11 @@ class SimpleRunCode(Action):
|
|||
|
||||
|
||||
class SimpleCoder(Role):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Alice",
|
||||
profile: str = "SimpleCoder",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
name: str = "Alice"
|
||||
profile: str = "SimpleCoder"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([SimpleWriteCode])
|
||||
|
||||
async def _act(self) -> Message:
|
||||
|
|
@ -76,15 +71,13 @@ class SimpleCoder(Role):
|
|||
|
||||
|
||||
class RunnableCoder(Role):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Alice",
|
||||
profile: str = "RunnableCoder",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
name: str = "Alice"
|
||||
profile: str = "RunnableCoder"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([SimpleWriteCode, SimpleRunCode])
|
||||
self._set_react_mode(react_mode="by_order")
|
||||
self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value)
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}({self._rc.todo.name})")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import re
|
|||
import fire
|
||||
|
||||
from metagpt.actions import Action, UserRequirement
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.schema import Message
|
||||
|
|
@ -28,9 +27,7 @@ class SimpleWriteCode(Action):
|
|||
Return ```python your_code_here ``` with NO other texts,
|
||||
your code:
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "SimpleWriteCode", context=None, llm: LLM = None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "SimpleWriteCode"
|
||||
|
||||
async def run(self, instruction: str):
|
||||
prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
|
||||
|
|
@ -43,13 +40,11 @@ class SimpleWriteCode(Action):
|
|||
|
||||
|
||||
class SimpleCoder(Role):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Alice",
|
||||
profile: str = "SimpleCoder",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
name: str = "Alice"
|
||||
profile: str = "SimpleCoder"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._watch([UserRequirement])
|
||||
self._init_actions([SimpleWriteCode])
|
||||
|
||||
|
|
@ -62,8 +57,7 @@ class SimpleWriteTest(Action):
|
|||
your code:
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "SimpleWriteTest", context=None, llm: LLM = None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "SimpleWriteTest"
|
||||
|
||||
async def run(self, context: str, k: int = 3):
|
||||
prompt = self.PROMPT_TEMPLATE.format(context=context, k=k)
|
||||
|
|
@ -76,13 +70,11 @@ class SimpleWriteTest(Action):
|
|||
|
||||
|
||||
class SimpleTester(Role):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Bob",
|
||||
profile: str = "SimpleTester",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
name: str = "Bob"
|
||||
profile: str = "SimpleTester"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([SimpleWriteTest])
|
||||
# self._watch([SimpleWriteCode])
|
||||
self._watch([SimpleWriteCode, SimpleWriteReview]) # feel free to try this too
|
||||
|
|
@ -106,8 +98,7 @@ class SimpleWriteReview(Action):
|
|||
Review the test cases and provide one critical comments:
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "SimpleWriteReview", context=None, llm: LLM = None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "SimpleWriteReview"
|
||||
|
||||
async def run(self, context: str):
|
||||
prompt = self.PROMPT_TEMPLATE.format(context=context)
|
||||
|
|
@ -118,13 +109,11 @@ class SimpleWriteReview(Action):
|
|||
|
||||
|
||||
class SimpleReviewer(Role):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Charlie",
|
||||
profile: str = "SimpleReviewer",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
name: str = "Charlie"
|
||||
profile: str = "SimpleReviewer"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([SimpleWriteReview])
|
||||
self._watch([SimpleWriteTest])
|
||||
|
||||
|
|
@ -147,7 +136,7 @@ async def main(
|
|||
)
|
||||
|
||||
team.invest(investment=investment)
|
||||
team.start_project(idea)
|
||||
team.run_project(idea)
|
||||
await team.run(n_round=n_round)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from metagpt.roles.invoice_ocr_assistant import InvoiceOCRAssistant
|
||||
from metagpt.roles.invoice_ocr_assistant import InvoiceOCRAssistant, InvoicePath
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ async def main():
|
|||
|
||||
for path in absolute_file_paths:
|
||||
role = InvoiceOCRAssistant()
|
||||
await role.run(Message(content="Invoicing date", instruct_content={"file_path": path}))
|
||||
await role.run(Message(content="Invoicing date", instruct_content=InvoicePath(file_path=path)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ async def main():
|
|||
# Serper API
|
||||
# await Searcher(engine=SearchEngineType.SERPER_GOOGLE).run(question)
|
||||
# SerpAPI
|
||||
# await Searcher(engine=SearchEngineType.SERPAPI_GOOGLE).run(question)
|
||||
await Searcher(engine=SearchEngineType.SERPAPI_GOOGLE).run(question)
|
||||
# Google API
|
||||
await Searcher(engine=SearchEngineType.DIRECT_GOOGLE).run(question)
|
||||
# await Searcher(engine=SearchEngineType.DIRECT_GOOGLE).run(question)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions.write_code import WriteCode
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.highlight import highlight
|
||||
|
||||
|
|
@ -27,8 +31,9 @@ def run(*args) -> pd.DataFrame:
|
|||
|
||||
|
||||
class CloneFunction(WriteCode):
|
||||
def __init__(self, name="CloneFunction", context: list[Message] = None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "CloneFunction"
|
||||
context: list[Message] = []
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
def _save(self, code_path, code):
|
||||
if isinstance(code_path, str):
|
||||
|
|
|
|||
|
|
@ -5,12 +5,20 @@
|
|||
@Author : alexanderwu
|
||||
@File : design_api_review.py
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
|
||||
|
||||
class DesignReview(Action):
|
||||
def __init__(self, name, context=None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "DesignReview"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
async def run(self, prd, api_design):
|
||||
prompt = (
|
||||
|
|
|
|||
|
|
@ -5,13 +5,19 @@
|
|||
@Author : femto Zheng
|
||||
@File : execute_task.py
|
||||
"""
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class ExecuteTask(Action):
|
||||
def __init__(self, name="ExecuteTask", context: list[Message] = None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
name: str = "ExecuteTask"
|
||||
context: list[Message] = []
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -21,5 +21,7 @@ class GenerateQuestions(Action):
|
|||
"""This class allows LLM to further mine noteworthy details based on specific "##TOPIC"(discussion topic) and
|
||||
"##RECORD" (discussion records), thereby deepening the discussion."""
|
||||
|
||||
name: str = "GenerateQuestions"
|
||||
|
||||
async def run(self, context):
|
||||
return await QUESTIONS.fill(context=context, llm=self.llm)
|
||||
|
|
|
|||
|
|
@ -12,17 +12,21 @@ import os
|
|||
import zipfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
from paddleocr import PaddleOCR
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.const import INVOICE_OCR_TABLE_PATH
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.prompts.invoice_ocr import (
|
||||
EXTRACT_OCR_MAIN_INFO_PROMPT,
|
||||
REPLY_OCR_QUESTION_PROMPT,
|
||||
)
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.utils.common import OutputParser
|
||||
from metagpt.utils.file import File
|
||||
|
||||
|
|
@ -36,8 +40,9 @@ class InvoiceOCR(Action):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "", *args, **kwargs):
|
||||
super().__init__(name, *args, **kwargs)
|
||||
name: str = "InvoiceOCR"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
@staticmethod
|
||||
async def _check_file_type(file_path: Path) -> str:
|
||||
|
|
@ -125,9 +130,10 @@ class GenerateTable(Action):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "", language: str = "ch", *args, **kwargs):
|
||||
super().__init__(name, *args, **kwargs)
|
||||
self.language = language
|
||||
name: str = "GenerateTable"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
language: str = "ch"
|
||||
|
||||
async def run(self, ocr_results: list, filename: str, *args, **kwargs) -> dict[str, str]:
|
||||
"""Processes OCR results, extracts invoice information, generates a table, and saves it as an Excel file.
|
||||
|
|
@ -169,9 +175,10 @@ class ReplyQuestion(Action):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, name: str = "", language: str = "ch", *args, **kwargs):
|
||||
super().__init__(name, *args, **kwargs)
|
||||
self.language = language
|
||||
name: str = "ReplyQuestion"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
language: str = "ch"
|
||||
|
||||
async def run(self, query: str, ocr_result: list, *args, **kwargs) -> str:
|
||||
"""Reply to questions based on ocr results.
|
||||
|
|
|
|||
|
|
@ -19,5 +19,7 @@ Attention: Provide as markdown block as the format above, at least 10 questions.
|
|||
|
||||
|
||||
class PrepareInterview(Action):
|
||||
name: str = "PrepareInterview"
|
||||
|
||||
async def run(self, context):
|
||||
return await QUESTIONS.fill(context=context, llm=self.llm)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Callable
|
||||
from typing import Callable, Optional, Union
|
||||
|
||||
from pydantic import parse_obj_as
|
||||
from pydantic import Field, parse_obj_as
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.tools.search_engine import SearchEngine
|
||||
from metagpt.tools.web_browser_engine import WebBrowserEngine, WebBrowserEngineType
|
||||
from metagpt.utils.common import OutputParser
|
||||
|
|
@ -78,17 +80,12 @@ above. The report must meet the following requirements:
|
|||
class CollectLinks(Action):
|
||||
"""Action class to collect links from a search engine."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "",
|
||||
*args,
|
||||
rank_func: Callable[[list[str]], None] | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, *args, **kwargs)
|
||||
self.desc = "Collect links from a search engine."
|
||||
self.search_engine = SearchEngine()
|
||||
self.rank_func = rank_func
|
||||
name: str = "CollectLinks"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
desc: str = "Collect links from a search engine."
|
||||
search_engine: SearchEngine = Field(default_factory=SearchEngine)
|
||||
rank_func: Union[Callable[[list[str]], None], None] = None
|
||||
|
||||
async def run(
|
||||
self,
|
||||
|
|
@ -178,20 +175,20 @@ class CollectLinks(Action):
|
|||
class WebBrowseAndSummarize(Action):
|
||||
"""Action class to explore the web and provide summaries of articles and webpages."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
browse_func: Callable[[list[str]], None] | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(*args, **kwargs)
|
||||
name: str = "WebBrowseAndSummarize"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
desc: str = "Explore the web and provide summaries of articles and webpages."
|
||||
browse_func: Union[Callable[[list[str]], None], None] = None
|
||||
web_browser_engine: WebBrowserEngine = WebBrowserEngine(
|
||||
engine=WebBrowserEngineType.CUSTOM if browse_func else None,
|
||||
run_func=browse_func,
|
||||
)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if CONFIG.model_for_researcher_summary:
|
||||
self.llm.model = CONFIG.model_for_researcher_summary
|
||||
self.web_browser_engine = WebBrowserEngine(
|
||||
engine=WebBrowserEngineType.CUSTOM if browse_func else None,
|
||||
run_func=browse_func,
|
||||
)
|
||||
self.desc = "Explore the web and provide summaries of articles and webpages."
|
||||
|
||||
async def run(
|
||||
self,
|
||||
|
|
@ -247,8 +244,12 @@ class WebBrowseAndSummarize(Action):
|
|||
class ConductResearch(Action):
|
||||
"""Action class to conduct research and generate a research report."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
name: str = "ConductResearch"
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if CONFIG.model_for_researcher_report:
|
||||
self.llm.model = CONFIG.model_for_researcher_report
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,13 @@ This script uses the 'fire' library to create a command-line interface. It gener
|
|||
the specified docstring style and adds them to the code.
|
||||
"""
|
||||
import ast
|
||||
from typing import Literal
|
||||
from typing import Literal, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.utils.common import OutputParser
|
||||
from metagpt.utils.pycst import merge_docstring
|
||||
|
||||
|
|
@ -157,9 +161,9 @@ class WriteDocstring(Action):
|
|||
desc: A string describing the action.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.desc = "Write docstring for code."
|
||||
desc: str = "Write docstring for code."
|
||||
context: Optional[str] = None
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
async def run(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@
|
|||
"""
|
||||
from typing import List
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.actions.action_node import ActionNode
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
|
||||
REVIEW = ActionNode(
|
||||
key="Review",
|
||||
|
|
@ -33,5 +37,8 @@ WRITE_REVIEW_NODE = ActionNode.from_children("WRITE_REVIEW_NODE", [REVIEW, LGTM]
|
|||
class WriteReview(Action):
|
||||
"""Write a review for the given context."""
|
||||
|
||||
name: str = "WriteReview"
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
|
||||
async def run(self, context):
|
||||
return await WRITE_REVIEW_NODE.fill(context=context, llm=self.llm, schema="json")
|
||||
|
|
|
|||
|
|
@ -9,8 +9,12 @@
|
|||
|
||||
from typing import Dict
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.prompts.tutorial_assistant import CONTENT_PROMPT, DIRECTORY_PROMPT
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.utils.common import OutputParser
|
||||
|
||||
|
||||
|
|
@ -22,9 +26,9 @@ class WriteDirectory(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 = language
|
||||
name: str = "WriteDirectory"
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
language: str = "Chinese"
|
||||
|
||||
async def run(self, topic: str, *args, **kwargs) -> Dict:
|
||||
"""Execute the action to generate a tutorial directory according to the topic.
|
||||
|
|
@ -49,10 +53,10 @@ class WriteContent(Action):
|
|||
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 = language
|
||||
self.directory = directory
|
||||
name: str = "WriteContent"
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
directory: dict = dict()
|
||||
language: str = "Chinese"
|
||||
|
||||
async def run(self, topic: str, *args, **kwargs) -> str:
|
||||
"""Execute the action to write document content according to the directory and topic.
|
||||
|
|
|
|||
|
|
@ -29,8 +29,4 @@ class CustomerService(Sales):
|
|||
name: str = "Xiaomei"
|
||||
profile: str = "Human customer service"
|
||||
desc: str = DESC
|
||||
|
||||
store: Optional[str] = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
|
|
|||
|
|
@ -7,14 +7,35 @@
|
|||
@File : invoice_ocr_assistant.py
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
from pydantic import BaseModel
|
||||
|
||||
from metagpt.actions.invoice_ocr import GenerateTable, InvoiceOCR, ReplyQuestion
|
||||
from metagpt.prompts.invoice_ocr import INVOICE_OCR_SUCCESS
|
||||
from metagpt.roles import Role
|
||||
from metagpt.roles.role import Role, RoleReactMode
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
class InvoicePath(BaseModel):
|
||||
file_path: Path = ""
|
||||
|
||||
|
||||
class OCRResults(BaseModel):
|
||||
ocr_result: str = "[]"
|
||||
|
||||
|
||||
class InvoiceData(BaseModel):
|
||||
invoice_data: list[dict] = []
|
||||
|
||||
|
||||
class ReplyData(BaseModel):
|
||||
content: str = ""
|
||||
|
||||
|
||||
class InvoiceOCRAssistant(Role):
|
||||
"""Invoice OCR assistant, support OCR text recognition of invoice PDF, png, jpg, and zip files,
|
||||
generate a table for the payee, city, total amount, and invoicing date of the invoice,
|
||||
|
|
@ -28,21 +49,19 @@ class InvoiceOCRAssistant(Role):
|
|||
language: The language in which the invoice table will be generated.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Stitch",
|
||||
profile: str = "Invoice OCR Assistant",
|
||||
goal: str = "OCR identifies invoice files and generates invoice main information table",
|
||||
constraints: str = "",
|
||||
language: str = "ch",
|
||||
):
|
||||
super().__init__(name, profile, goal, constraints)
|
||||
name: str = "Stitch"
|
||||
profile: str = "Invoice OCR Assistant"
|
||||
goal: str = "OCR identifies invoice files and generates invoice main information table"
|
||||
constraints: str = ""
|
||||
language: str = "ch"
|
||||
filename: str = ""
|
||||
origin_query: str = ""
|
||||
orc_data: Optional[list] = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([InvoiceOCR])
|
||||
self.language = language
|
||||
self.filename = ""
|
||||
self.origin_query = ""
|
||||
self.orc_data = None
|
||||
self._set_react_mode(react_mode="by_order")
|
||||
self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value)
|
||||
|
||||
async def _act(self) -> Message:
|
||||
"""Perform an action as determined by the role.
|
||||
|
|
@ -54,7 +73,8 @@ class InvoiceOCRAssistant(Role):
|
|||
todo = self._rc.todo
|
||||
if isinstance(todo, InvoiceOCR):
|
||||
self.origin_query = msg.content
|
||||
file_path = msg.instruct_content.get("file_path")
|
||||
invoice_path: InvoicePath = msg.instruct_content
|
||||
file_path = invoice_path.file_path
|
||||
self.filename = file_path.name
|
||||
if not file_path:
|
||||
raise Exception("Invoice file not uploaded")
|
||||
|
|
@ -69,17 +89,23 @@ class InvoiceOCRAssistant(Role):
|
|||
|
||||
self._rc.todo = None
|
||||
content = INVOICE_OCR_SUCCESS
|
||||
resp = OCRResults(ocr_result=json.dumps(resp))
|
||||
msg = Message(content=content, instruct_content=resp)
|
||||
self._rc.memory.add(msg)
|
||||
return await super().react()
|
||||
elif isinstance(todo, GenerateTable):
|
||||
ocr_results = msg.instruct_content
|
||||
resp = await todo.run(ocr_results, self.filename)
|
||||
ocr_results: OCRResults = msg.instruct_content
|
||||
resp = await todo.run(json.loads(ocr_results.ocr_result), self.filename)
|
||||
|
||||
# Convert list to Markdown format string
|
||||
df = pd.DataFrame(resp)
|
||||
markdown_table = df.to_markdown(index=False)
|
||||
content = f"{markdown_table}\n\n\n"
|
||||
resp = InvoiceData(invoice_data=resp)
|
||||
else:
|
||||
resp = await todo.run(self.origin_query, self.orc_data)
|
||||
content = resp
|
||||
resp = ReplyData(content=resp)
|
||||
|
||||
msg = Message(content=content, instruct_content=resp)
|
||||
self._rc.memory.add(msg)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
the `cause_by` value in the `Message` to a string to support the new message distribution feature.
|
||||
"""
|
||||
|
||||
|
||||
import asyncio
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -13,7 +12,7 @@ from metagpt.actions import Action, CollectLinks, ConductResearch, WebBrowseAndS
|
|||
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.roles.role import Role, RoleReactMode
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
|
|
@ -25,21 +24,20 @@ class Report(BaseModel):
|
|||
|
||||
|
||||
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._set_react_mode(react_mode="by_order")
|
||||
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.")
|
||||
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"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions(
|
||||
[CollectLinks(name=self.name), WebBrowseAndSummarize(name=self.name), ConductResearch(name=self.name)]
|
||||
)
|
||||
self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value)
|
||||
if self.language not in ("en-us", "zh-cn"):
|
||||
logger.warning(f"The language `{self.language}` has not been tested, it may not work.")
|
||||
|
||||
async def _act(self) -> Message:
|
||||
logger.info(f"{self._setting}: to do {self._rc.todo}({self._rc.todo.name})")
|
||||
|
|
@ -107,7 +105,7 @@ if __name__ == "__main__":
|
|||
import fire
|
||||
|
||||
async def main(topic: str, language="en-us"):
|
||||
role = Researcher(topic, language=language)
|
||||
role = Researcher(language=language)
|
||||
await role.run(topic)
|
||||
|
||||
fire.Fire(main)
|
||||
|
|
|
|||
|
|
@ -208,8 +208,7 @@ class Role(BaseModel):
|
|||
if "actions" in kwargs:
|
||||
self._init_actions(kwargs["actions"])
|
||||
|
||||
if "watch" in kwargs:
|
||||
self._watch(kwargs["watch"])
|
||||
self._watch(kwargs.get("watch") or [UserRequirement])
|
||||
|
||||
def __init_subclass__(cls, **kwargs: Any) -> None:
|
||||
super().__init_subclass__(**kwargs)
|
||||
|
|
@ -482,6 +481,7 @@ class Role(BaseModel):
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -7,13 +7,19 @@
|
|||
@Modified By: mashenquan, 2023-11-1. In accordance with Chapter 2.2.1 and 2.2.2 of RFC 116, utilize the new message
|
||||
distribution feature for message filtering.
|
||||
"""
|
||||
|
||||
from pydantic import Field
|
||||
from semantic_kernel import Kernel
|
||||
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
|
||||
from semantic_kernel.planning import SequentialPlanner
|
||||
from semantic_kernel.planning.action_planner.action_planner import ActionPlanner
|
||||
from semantic_kernel.planning.basic_planner import BasicPlanner
|
||||
from semantic_kernel.planning.basic_planner import BasicPlanner, Plan
|
||||
|
||||
from metagpt.actions import UserRequirement
|
||||
from metagpt.actions.execute_task import ExecuteTask
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.provider.base_gpt_api import BaseGPTAPI
|
||||
from metagpt.roles import Role
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.make_sk_kernel import make_sk_kernel
|
||||
|
|
@ -30,27 +36,33 @@ class SkAgent(Role):
|
|||
constraints (str): Constraints for the SkAgent.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Sunshine",
|
||||
profile: str = "sk_agent",
|
||||
goal: str = "Execute task based on passed in task description",
|
||||
constraints: str = "",
|
||||
planner_cls=BasicPlanner,
|
||||
) -> None:
|
||||
name: str = "Sunshine"
|
||||
profile: str = "sk_agent"
|
||||
goal: str = "Execute task based on passed in task description"
|
||||
constraints: str = ""
|
||||
|
||||
plan: Plan = None
|
||||
planner_cls: BasicPlanner = BasicPlanner
|
||||
planner: BasicPlanner = Field(default_factory=BasicPlanner)
|
||||
llm: BaseGPTAPI = Field(default_factory=LLM)
|
||||
kernel: Kernel = Field(default_factory=Kernel)
|
||||
import_semantic_skill_from_directory: str = ""
|
||||
import_skill: dict[str, SKFunctionBase] = dict()
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
"""Initializes the Engineer role with given attributes."""
|
||||
super().__init__(name, profile, goal, constraints)
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([ExecuteTask()])
|
||||
self._watch([UserRequirement])
|
||||
self.kernel = make_sk_kernel()
|
||||
|
||||
# how funny the interface is inconsistent
|
||||
if planner_cls == BasicPlanner:
|
||||
self.planner = planner_cls()
|
||||
elif planner_cls in [SequentialPlanner, ActionPlanner]:
|
||||
self.planner = planner_cls(self.kernel)
|
||||
if self.planner_cls == BasicPlanner:
|
||||
self.planner = self.planner_cls()
|
||||
elif self.planner_cls in [SequentialPlanner, ActionPlanner]:
|
||||
self.planner = self.planner_cls(self.kernel)
|
||||
else:
|
||||
raise f"Unsupported planner of type {planner_cls}"
|
||||
raise Exception(f"Unsupported planner of type {self.planner_cls}")
|
||||
|
||||
self.import_semantic_skill_from_directory = self.kernel.import_semantic_skill_from_directory
|
||||
self.import_skill = self.kernel.import_skill
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from typing import Dict
|
|||
from metagpt.actions.write_tutorial import WriteContent, WriteDirectory
|
||||
from metagpt.const import TUTORIAL_PATH
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.roles.role import Role, RoleReactMode
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.file import File
|
||||
|
||||
|
|
@ -28,21 +28,20 @@ class TutorialAssistant(Role):
|
|||
language: The language in which the tutorial documents will be generated.
|
||||
"""
|
||||
|
||||
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)])
|
||||
self.topic = ""
|
||||
self.main_title = ""
|
||||
self.total_content = ""
|
||||
self.language = language
|
||||
self._set_react_mode(react_mode="by_order")
|
||||
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"
|
||||
|
||||
topic = ""
|
||||
main_title = ""
|
||||
total_content = ""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([WriteDirectory(language=self.language)])
|
||||
self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value)
|
||||
|
||||
async def _handle_directory(self, titles: Dict) -> Message:
|
||||
"""Handle the directories for the tutorial document.
|
||||
|
|
|
|||
|
|
@ -21,12 +21,14 @@ def make_sk_kernel():
|
|||
if CONFIG.openai_api_type == "azure":
|
||||
kernel.add_chat_service(
|
||||
"chat_completion",
|
||||
AzureChatCompletion(CONFIG.deployment_name, CONFIG.openai_base_url, CONFIG.openai_api_key),
|
||||
AzureChatCompletion(
|
||||
deployment_name=CONFIG.deployment_name, endpoint=CONFIG.openai_base_url, api_key=CONFIG.openai_api_key
|
||||
),
|
||||
)
|
||||
else:
|
||||
kernel.add_chat_service(
|
||||
"chat_completion",
|
||||
OpenAIChatCompletion(CONFIG.openai_api_model, CONFIG.openai_api_key),
|
||||
OpenAIChatCompletion(model_id=CONFIG.openai_api_model, api_key=CONFIG.openai_api_key),
|
||||
)
|
||||
|
||||
return kernel
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@
|
|||
@File : test_invoice_ocr_assistant.py
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from metagpt.roles.invoice_ocr_assistant import InvoiceOCRAssistant
|
||||
from metagpt.roles.invoice_ocr_assistant import InvoiceOCRAssistant, InvoicePath
|
||||
from metagpt.schema import Message
|
||||
|
||||
|
||||
|
|
@ -55,8 +56,8 @@ async def test_invoice_ocr_assistant(
|
|||
):
|
||||
invoice_path = Path.cwd() / invoice_path
|
||||
role = InvoiceOCRAssistant()
|
||||
await role.run(Message(content=query, instruct_content={"file_path": invoice_path}))
|
||||
await role.run(Message(content=query, instruct_content=InvoicePath(file_path=invoice_path)))
|
||||
invoice_table_path = Path.cwd() / invoice_table_path
|
||||
df = pd.read_excel(invoice_table_path)
|
||||
dict_result = df.to_dict(orient="records")
|
||||
assert dict_result == expected_result
|
||||
assert json.dumps(dict_result) == json.dumps(expected_result)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue