mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-24 14:15:17 +02:00
feat: +software tools
This commit is contained in:
parent
e1b3bd3869
commit
7d92e3eb12
4 changed files with 261 additions and 8 deletions
|
|
@ -24,6 +24,7 @@ from metagpt.logs import logger
|
|||
from metagpt.roles import Architect
|
||||
from metagpt.roles.di.team_leader import TeamLeader
|
||||
from metagpt.schema import AIMessage, UserMessage
|
||||
from metagpt.strategy.experience_retriever import TRDToolExpRetriever
|
||||
from metagpt.utils.common import aread, to_markdown_code_block
|
||||
|
||||
app = typer.Typer(add_completion=False)
|
||||
|
|
@ -40,7 +41,7 @@ class EnvBuilder(BaseModel):
|
|||
def build(self) -> Environment:
|
||||
env = MGXEnv(context=self.context)
|
||||
team_leader = TeamLeader()
|
||||
architect = Architect()
|
||||
architect = Architect(experience_retriever=TRDToolExpRetriever())
|
||||
|
||||
# Prepare context
|
||||
use_case_actors = "".join([f"- {v}: {k}\n" for k, v in self.actors.items()])
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ from metagpt.actions.requirement_analysis.trd import (
|
|||
WriteTRD,
|
||||
)
|
||||
from metagpt.roles.di.role_zero import RoleZero
|
||||
from metagpt.tools.libs.software_development import (
|
||||
extract_external_interfaces,
|
||||
write_framework,
|
||||
write_trd,
|
||||
)
|
||||
from metagpt.utils.common import tool2name
|
||||
|
||||
|
||||
|
|
@ -42,16 +47,24 @@ class Architect(RoleZero):
|
|||
|
||||
instruction: str = """Use WriteDesign tool to write a system design document if a system design is required; Use WriteTRD tool to write a TRD if a TRD is required;"""
|
||||
max_react_loop: int = 1 # FIXME: Read and edit files requires more steps, consider later
|
||||
# tools: list[str] = [
|
||||
# "Editor:write,read,write_content",
|
||||
# "RoleZero",
|
||||
# "WriteDesign",
|
||||
# "CompressExternalInterfaces",
|
||||
# "DetectInteraction",
|
||||
# "EvaluateTRD",
|
||||
# "WriteTRD",
|
||||
# "WriteFramework",
|
||||
# "EvaluateFramework",
|
||||
# ]
|
||||
tools: list[str] = [
|
||||
"Editor:write,read,write_content",
|
||||
"RoleZero",
|
||||
"WriteDesign",
|
||||
"CompressExternalInterfaces",
|
||||
"DetectInteraction",
|
||||
"EvaluateTRD",
|
||||
"WriteTRD",
|
||||
"WriteFramework",
|
||||
"EvaluateFramework",
|
||||
extract_external_interfaces.__name__,
|
||||
write_trd.__name__,
|
||||
write_framework.__name__,
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,64 @@ class ExpRetriever(BaseModel):
|
|||
class DummyExpRetriever(ExpRetriever):
|
||||
"""A dummy experience retriever that returns empty string."""
|
||||
|
||||
def retrieve(self, context: str = "") -> str:
|
||||
return self.EXAMPLE
|
||||
|
||||
EXAMPLE: str = ""
|
||||
|
||||
|
||||
class TRDToolExpRetriever(ExpRetriever):
|
||||
"""A TRD-related experience retriever that returns empty string."""
|
||||
|
||||
def retrieve(self, context: str = "") -> str:
|
||||
return self.EXAMPLE
|
||||
|
||||
EXAMPLE: str = """
|
||||
## example 1
|
||||
User Requirement: Given some user requirements, write a TRD, and implement the TRD within a software framework.
|
||||
Explanation: Given a complete user requirement, to write the TRD (Technical Requirements Document), follow these steps: 1. Call `extract_external_interfaces` to extract information about external interfaces from the acknowledgment; 2. Call `write_trd` to generate the TRD; 3. Call `write_framework` to generate the software framework code.
|
||||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "extract_external_interfaces",
|
||||
"args": {
|
||||
"task_id": "1",
|
||||
"dependent_task_ids": [],
|
||||
"instruction": "Execute `extract_external_interfaces` to extract external interfaces information from acknowledgement.",
|
||||
"acknowledge": "## Interfaces\n balabala..."
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_name": "write_trd",
|
||||
"args": {
|
||||
"task_id": "2",
|
||||
"dependent_task_ids": ["1"],
|
||||
"instruction": "Execute `write_trd` to write the TRD based on user requirements",
|
||||
"user_requirements": "This is user requirement balabala...",
|
||||
"use_case_actors": "These are actors involved in the use case, balabala...",
|
||||
"external_interfaces": "<external_interfaces> returned by `extract_external_interfaces`",
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_name": "write_framework",
|
||||
"args": {
|
||||
"task_id": "3",
|
||||
"dependent_task_ids": ["2"],
|
||||
"instruction": "Execute `write_framework` to write the framework based on the TRD",
|
||||
"use_case_actors": "These are actors involved in the use case, balabala...",
|
||||
"trd": "<trd> returned by `write_trd`",
|
||||
"acknowledge": "## Interfaces\n balabala...",
|
||||
"additional_technical_requirements": "These are additional technical requirements, balabala..."
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
class TRDExpRetriever(ExpRetriever):
|
||||
"""A TRD-related experience retriever that returns empty string."""
|
||||
|
||||
def retrieve(self, context: str = "") -> str:
|
||||
return self.EXAMPLE
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,24 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from metagpt.actions.requirement_analysis.framework import (
|
||||
EvaluateFramework,
|
||||
WriteFramework,
|
||||
save_framework,
|
||||
)
|
||||
from metagpt.actions.requirement_analysis.trd import (
|
||||
CompressExternalInterfaces,
|
||||
DetectInteraction,
|
||||
EvaluateTRD,
|
||||
WriteTRD,
|
||||
)
|
||||
from metagpt.const import ASSISTANT_ALIAS
|
||||
from metagpt.logs import ToolLogItem, log_tool_output
|
||||
from metagpt.context import Context
|
||||
from metagpt.logs import ToolLogItem, log_tool_output, logger
|
||||
from metagpt.tools.tool_registry import register_tool
|
||||
from metagpt.utils.cost_manager import CostManager
|
||||
|
||||
|
||||
async def import_git_repo(url: str) -> Path:
|
||||
|
|
@ -42,3 +57,169 @@ async def import_git_repo(url: str) -> Path:
|
|||
log_tool_output(output=outputs, tool_name=import_git_repo.__name__)
|
||||
|
||||
return ctx.repo.workdir
|
||||
|
||||
|
||||
@register_tool(tags=["system design", "Extracts and compresses the information about external system interfaces"])
|
||||
async def extract_external_interfaces(acknowledge: str) -> str:
|
||||
"""
|
||||
Extracts and compresses information about external system interfaces from a given acknowledgement text.
|
||||
|
||||
Args:
|
||||
acknowledge (str): A natural text of acknowledgement containing details about external system interfaces.
|
||||
|
||||
Returns:
|
||||
str: A compressed version of the information about external system interfaces.
|
||||
|
||||
Example:
|
||||
>>> acknowledge = "## Interfaces\\n..."
|
||||
>>> external_interfaces = await extract_external_interfaces(acknowledge=acknowledge)
|
||||
>>> print(external_interfaces)
|
||||
```json\n[\n{\n"id": 1,\n"inputs": {...
|
||||
"""
|
||||
compress_acknowledge = CompressExternalInterfaces()
|
||||
return await compress_acknowledge.run(acknowledge=acknowledge)
|
||||
|
||||
|
||||
@register_tool(tags=["system design", "write trd", "Write a TRD"])
|
||||
async def write_trd(
|
||||
use_case_actors: str,
|
||||
user_requirements: str,
|
||||
external_interfaces: str,
|
||||
investment: float = 10,
|
||||
context: Optional[Context] = None,
|
||||
) -> (str, str):
|
||||
"""
|
||||
Handles the writing of a Technical Requirements Document (TRD) based on user requirements.
|
||||
|
||||
Args:
|
||||
user_requirements (str): The new/incremental user requirements.
|
||||
use_case_actors (str): Description of the actors involved in the use case.
|
||||
external_interfaces (str): List of available external interfaces.
|
||||
investment (float): Budget. Automatically stops optimizing TRD when the budget is overdrawn.
|
||||
context (Context, optional): The context configuration. Default is None.
|
||||
Returns:
|
||||
str: The newly created TRD.
|
||||
|
||||
Example:
|
||||
>>> # Given a new user requirements, write out a new TRD.
|
||||
>>> user_requirements = "Write a 'snake game' TRD."
|
||||
>>> use_case_actors = "- Actor: game player;\\n- System: snake game; \\n- External System: game center;"
|
||||
>>> external_interfaces = "The available external interfaces returned by `CompressExternalInterfaces.run` are ..."
|
||||
>>> investment = 10.0
|
||||
>>> trd = await write_trd(
|
||||
>>> user_requirements=user_requirements,
|
||||
>>> use_case_actors=use_case_actors,
|
||||
>>> external_interfaces=external_interfaces,
|
||||
>>> investment=investment,
|
||||
>>> )
|
||||
>>> print(trd)
|
||||
## Technical Requirements Document\n ...
|
||||
"""
|
||||
context = context or Context(cost_manager=CostManager(max_budget=investment))
|
||||
detect_interaction = DetectInteraction(context=context)
|
||||
w_trd = WriteTRD(context=context)
|
||||
evaluate_trd = EvaluateTRD(context=context)
|
||||
is_pass = False
|
||||
evaluation_conclusion = ""
|
||||
interaction_events = ""
|
||||
trd = ""
|
||||
while not is_pass and (context.cost_manager.total_cost < context.cost_manager.max_budget):
|
||||
interaction_events = await detect_interaction.run(
|
||||
user_requirements=user_requirements,
|
||||
use_case_actors=use_case_actors,
|
||||
legacy_interaction_events=interaction_events,
|
||||
evaluation_conclusion=evaluation_conclusion,
|
||||
)
|
||||
trd = await w_trd.run(
|
||||
user_requirements=user_requirements,
|
||||
use_case_actors=use_case_actors,
|
||||
available_external_interfaces=external_interfaces,
|
||||
evaluation_conclusion=evaluation_conclusion,
|
||||
interaction_events=interaction_events,
|
||||
previous_version_trd=trd,
|
||||
)
|
||||
evaluation = await evaluate_trd.run(
|
||||
user_requirements=user_requirements,
|
||||
use_case_actors=use_case_actors,
|
||||
trd=trd,
|
||||
interaction_events=interaction_events,
|
||||
)
|
||||
is_pass = evaluation.is_pass
|
||||
evaluation_conclusion = evaluation.conclusion
|
||||
|
||||
return trd
|
||||
|
||||
|
||||
@register_tool(tags=["system design", "write software framework", "Write a software framework based on a TRD"])
|
||||
async def write_framework(
|
||||
use_case_actors: str,
|
||||
trd: str,
|
||||
acknowledge: str,
|
||||
additional_technical_requirements: str,
|
||||
output_dir: Optional[str] = "",
|
||||
investment: float = 10,
|
||||
context: Optional[Context] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Run the action to generate a software framework based on the provided TRD and related information.
|
||||
|
||||
Args:
|
||||
use_case_actors (str): Description of the use case actors involved.
|
||||
trd (str): Technical Requirements Document detailing the requirements.
|
||||
acknowledge (str): External acknowledgements or acknowledgements required.
|
||||
additional_technical_requirements (str): Any additional technical requirements.
|
||||
output_dir (str, optional): Path to save the software framework files. Default is en empty string.
|
||||
investment (float): Budget. Automatically stops optimizing TRD when the budget is overdrawn.
|
||||
context (Context, optional): The context configuration. Default is None.
|
||||
|
||||
Returns:
|
||||
str: The generated software framework as a string of pathnames.
|
||||
|
||||
Example:
|
||||
>>> use_case_actors = "- Actor: game player;\\n- System: snake game; \\n- External System: game center;"
|
||||
>>> trd = "## TRD\\n..."
|
||||
>>> acknowledge = "## Interfaces\\n..."
|
||||
>>> additional_technical_requirements = "Using Java language, ..."
|
||||
>>> investment = 10.0
|
||||
>>> framework = await write_framework(
|
||||
>>> use_case_actors=use_case_actors,
|
||||
>>> trd=trd,
|
||||
>>> acknowledge=acknowledge,
|
||||
>>> additional_technical_requirements=constraint,
|
||||
>>> investment=investment,
|
||||
>>> )
|
||||
>>> print(framework)
|
||||
[{"path":"balabala", "filename":"...", ...
|
||||
"""
|
||||
context = context or Context(cost_manager=CostManager(max_budget=investment))
|
||||
write_framework = WriteFramework(context=context)
|
||||
evaluate_framework = EvaluateFramework(context=context)
|
||||
is_pass = False
|
||||
framework = ""
|
||||
evaluation_conclusion = ""
|
||||
while not is_pass and (context.cost_manager.total_cost < context.cost_manager.max_budget):
|
||||
try:
|
||||
framework = await write_framework.run(
|
||||
use_case_actors=use_case_actors,
|
||||
trd=trd,
|
||||
acknowledge=acknowledge,
|
||||
legacy_output=framework,
|
||||
evaluation_conclusion=evaluation_conclusion,
|
||||
additional_technical_requirements=additional_technical_requirements,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.info(f"{e}")
|
||||
break
|
||||
evaluation = await evaluate_framework.run(
|
||||
use_case_actors=use_case_actors,
|
||||
trd=trd,
|
||||
acknowledge=acknowledge,
|
||||
legacy_output=framework,
|
||||
additional_technical_requirements=additional_technical_requirements,
|
||||
)
|
||||
is_pass = evaluation.is_pass
|
||||
evaluation_conclusion = evaluation.conclusion
|
||||
|
||||
file_list = await save_framework(dir_data=framework, output_dir=output_dir)
|
||||
logger.info(f"Output:\n{file_list}")
|
||||
return "\n".join(file_list)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue