From f4a3ff2261ca942cf57c29adbb44441ccd9a31c4 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Sat, 10 Aug 2024 17:08:41 +0800 Subject: [PATCH 01/47] ignore OSError when inspect.getsource call --- metagpt/tools/tool_registry.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/metagpt/tools/tool_registry.py b/metagpt/tools/tool_registry.py index 65d2fb662..49820b458 100644 --- a/metagpt/tools/tool_registry.py +++ b/metagpt/tools/tool_registry.py @@ -7,6 +7,7 @@ """ from __future__ import annotations +import contextlib import inspect import os from collections import defaultdict @@ -99,7 +100,9 @@ def register_tool(tags: list[str] = None, schema_path: str = "", **kwargs): if "metagpt" in file_path: # split to handle ../metagpt/metagpt/tools/... where only metapgt/tools/... is needed file_path = "metagpt" + file_path.split("metagpt")[-1] - source_code = inspect.getsource(cls) + source_code = "" + with contextlib.suppress(OSError): + source_code = inspect.getsource(cls) TOOL_REGISTRY.register_tool( tool_name=cls.__name__, From ee4a536d554f8fe43660087b5a1dbbdee7e79b01 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Sun, 11 Aug 2024 01:43:05 +0800 Subject: [PATCH 02/47] fix circular dependency for role/env --- metagpt/base/__init__.py | 0 metagpt/base/base_env.py | 38 ++++++++++++++ .../{environment => base}/base_env_space.py | 0 metagpt/base/base_role.py | 33 ++++++++++++ .../environment/android/android_ext_env.py | 2 +- metagpt/environment/base_env.py | 31 ++++------- .../minecraft/minecraft_ext_env.py | 2 +- .../environment/stanford_town/env_space.py | 2 +- .../environment/werewolf/werewolf_ext_env.py | 2 +- metagpt/ext/stanford_town/roles/st_role.py | 21 ++------ metagpt/roles/role.py | 21 ++------ setup.py | 51 ++++++++++--------- tests/metagpt/environment/test_base_env.py | 2 +- tests/metagpt/memory/test_longterm_memory.py | 4 -- 14 files changed, 120 insertions(+), 89 deletions(-) create mode 100644 metagpt/base/__init__.py create mode 100644 metagpt/base/base_env.py rename metagpt/{environment => base}/base_env_space.py (100%) create mode 100644 metagpt/base/base_role.py diff --git a/metagpt/base/__init__.py b/metagpt/base/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/metagpt/base/base_env.py b/metagpt/base/base_env.py new file mode 100644 index 000000000..7da9fd581 --- /dev/null +++ b/metagpt/base/base_env.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : base environment + +from abc import abstractmethod +from typing import Any, Optional + +from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams +from metagpt.schema import Message + + +class BaseEnvironment: + """Base environment""" + + @abstractmethod + def reset( + self, + *, + seed: Optional[int] = None, + options: Optional[dict[str, Any]] = None, + ) -> tuple[dict[str, Any], dict[str, Any]]: + """Implement this to get init observation""" + + @abstractmethod + def observe(self, obs_params: Optional[BaseEnvObsParams] = None) -> Any: + """Implement this if you want to get partial observation from the env""" + + @abstractmethod + def step(self, action: BaseEnvAction) -> tuple[dict[str, Any], float, bool, bool, dict[str, Any]]: + """Implement this to feed a action and then get new observation from the env""" + + @abstractmethod + def publish_message(self, message: Message, peekable: bool = True) -> bool: + """Distribute the message to the recipients.""" + + @abstractmethod + async def run(self, k=1): + """Process all task at once""" diff --git a/metagpt/environment/base_env_space.py b/metagpt/base/base_env_space.py similarity index 100% rename from metagpt/environment/base_env_space.py rename to metagpt/base/base_env_space.py diff --git a/metagpt/base/base_role.py b/metagpt/base/base_role.py new file mode 100644 index 000000000..49dbcd617 --- /dev/null +++ b/metagpt/base/base_role.py @@ -0,0 +1,33 @@ +from abc import abstractmethod +from typing import Optional, Union + +from metagpt.schema import Message + + +class BaseRole: + """Abstract base class for all roles.""" + + name: str + is_idle: bool + + @abstractmethod + def think(self): + """Consider what to do and decide on the next course of action.""" + raise NotImplementedError + + @abstractmethod + def act(self): + """Perform the current action.""" + raise NotImplementedError + + @abstractmethod + async def react(self) -> Message: + """Entry to one of three strategies by which Role reacts to the observed Message.""" + + @abstractmethod + async def run(self, with_message: Optional[Union[str, Message, list[str]]] = None) -> Optional[Message]: + """Observe, and think and act based on the results of the observation.""" + + @abstractmethod + def get_memories(self, k: int = 0) -> list[Message]: + """Return the most recent k memories of this role.""" diff --git a/metagpt/environment/android/android_ext_env.py b/metagpt/environment/android/android_ext_env.py index d2344fa1f..75e2e79ef 100644 --- a/metagpt/environment/android/android_ext_env.py +++ b/metagpt/environment/android/android_ext_env.py @@ -8,9 +8,9 @@ from typing import Any, Optional from pydantic import Field +from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.environment.android.const import ADB_EXEC_FAIL from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable -from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams class AndroidExtEnv(ExtEnv): diff --git a/metagpt/environment/base_env.py b/metagpt/environment/base_env.py index 0776ae9ff..03a4760c9 100644 --- a/metagpt/environment/base_env.py +++ b/metagpt/environment/base_env.py @@ -5,28 +5,26 @@ import asyncio from abc import abstractmethod from enum import Enum -from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Set, Union +from typing import Any, Dict, Iterable, Optional, Set, Union from gymnasium import spaces from gymnasium.core import ActType, ObsType from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator +from metagpt.base import BaseEnvironment, BaseRole +from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.context import Context from metagpt.environment.api.env_api import ( EnvAPIAbstract, ReadAPIRegistry, WriteAPIRegistry, ) -from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.logs import logger from metagpt.memory import Memory from metagpt.schema import Message from metagpt.utils.common import get_function_schema, is_coroutine_func, is_send_to from metagpt.utils.git_repository import GitRepository -if TYPE_CHECKING: - from metagpt.roles.role import Role # noqa: F401 - class EnvType(Enum): ANDROID = "Android" @@ -52,7 +50,7 @@ def mark_as_writeable(func): return func -class ExtEnv(BaseModel): +class ExtEnv(BaseEnvironment, BaseModel): """External Env to integrate actual game environment""" model_config = ConfigDict(arbitrary_types_allowed=True) @@ -131,8 +129,8 @@ class Environment(ExtEnv): model_config = ConfigDict(arbitrary_types_allowed=True) desc: str = Field(default="") # 环境描述 - roles: dict[str, SerializeAsAny["Role"]] = Field(default_factory=dict, validate_default=True) - member_addrs: Dict["Role", Set] = Field(default_factory=dict, exclude=True) + roles: dict[str, SerializeAsAny[BaseRole]] = Field(default_factory=dict, validate_default=True) + member_addrs: Dict[BaseRole, Set] = Field(default_factory=dict, exclude=True) history: Memory = Field(default_factory=Memory) # For debug context: Context = Field(default_factory=Context, exclude=True) @@ -155,7 +153,7 @@ class Environment(ExtEnv): self.add_roles(self.roles.values()) return self - def add_role(self, role: "Role"): + def add_role(self, role: BaseRole): """增加一个在当前环境的角色 Add a role in the current environment """ @@ -163,7 +161,7 @@ class Environment(ExtEnv): role.set_env(self) role.context = self.context - def add_roles(self, roles: Iterable["Role"]): + def add_roles(self, roles: Iterable[BaseRole]): """增加一批在当前环境的角色 Add a batch of characters in the current environment """ @@ -212,13 +210,13 @@ class Environment(ExtEnv): await asyncio.gather(*futures) logger.debug(f"is idle: {self.is_idle}") - def get_roles(self) -> dict[str, "Role"]: + def get_roles(self) -> dict[str, BaseRole]: """获得环境内的所有角色 Process all Role runs at once """ return self.roles - def get_role(self, name: str) -> "Role": + def get_role(self, name: str) -> BaseRole: """获得环境内的指定角色 get all the environment roles """ @@ -247,12 +245,3 @@ class Environment(ExtEnv): if auto_archive and self.context.kwargs.get("project_path"): git_repo = GitRepository(self.context.kwargs.project_path) git_repo.archive() - - @classmethod - def model_rebuild(cls, **kwargs): - from metagpt.roles.role import Role # noqa: F401 - - super().model_rebuild(**kwargs) - - -Environment.model_rebuild() diff --git a/metagpt/environment/minecraft/minecraft_ext_env.py b/metagpt/environment/minecraft/minecraft_ext_env.py index 0436bc3aa..fb43e97c9 100644 --- a/metagpt/environment/minecraft/minecraft_ext_env.py +++ b/metagpt/environment/minecraft/minecraft_ext_env.py @@ -10,8 +10,8 @@ from typing import Any, Optional import requests from pydantic import ConfigDict, Field, model_validator +from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.environment.base_env import ExtEnv, mark_as_writeable -from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.environment.minecraft.const import ( MC_CKPT_DIR, MC_CORE_INVENTORY_ITEMS, diff --git a/metagpt/environment/stanford_town/env_space.py b/metagpt/environment/stanford_town/env_space.py index e100a2952..1741cccfe 100644 --- a/metagpt/environment/stanford_town/env_space.py +++ b/metagpt/environment/stanford_town/env_space.py @@ -9,7 +9,7 @@ import numpy.typing as npt from gymnasium import spaces from pydantic import ConfigDict, Field, field_validator -from metagpt.environment.base_env_space import ( +from metagpt.base.base_env_space import ( BaseEnvAction, BaseEnvActionType, BaseEnvObsParams, diff --git a/metagpt/environment/werewolf/werewolf_ext_env.py b/metagpt/environment/werewolf/werewolf_ext_env.py index 3f2508b06..d9644eb9b 100644 --- a/metagpt/environment/werewolf/werewolf_ext_env.py +++ b/metagpt/environment/werewolf/werewolf_ext_env.py @@ -9,8 +9,8 @@ from typing import Any, Callable, Optional from pydantic import ConfigDict, Field +from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable -from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.logs import logger diff --git a/metagpt/ext/stanford_town/roles/st_role.py b/metagpt/ext/stanford_town/roles/st_role.py index 4856548f0..592b78a8f 100644 --- a/metagpt/ext/stanford_town/roles/st_role.py +++ b/metagpt/ext/stanford_town/roles/st_role.py @@ -16,7 +16,7 @@ import time from datetime import datetime, timedelta from operator import itemgetter from pathlib import Path -from typing import TYPE_CHECKING, Optional +from typing import Optional from pydantic import ConfigDict, Field, field_validator, model_validator @@ -27,6 +27,7 @@ from metagpt.environment.stanford_town.env_space import ( EnvObsParams, EnvObsType, ) +from metagpt.environment.stanford_town.stanford_town_env import StanfordTownEnv from metagpt.ext.stanford_town.actions.dummy_action import DummyAction, DummyMessage from metagpt.ext.stanford_town.actions.inner_voice_action import ( AgentWhisperThoughtAction, @@ -49,28 +50,15 @@ from metagpt.roles.role import Role, RoleContext from metagpt.schema import Message from metagpt.utils.common import any_to_str -if TYPE_CHECKING: - from metagpt.environment.stanford_town.stanford_town_env import ( # noqa: F401 - StanfordTownEnv, - ) - class STRoleContext(RoleContext): model_config = ConfigDict(arbitrary_types_allowed=True) - env: "StanfordTownEnv" = Field(default=None, exclude=True) + env: StanfordTownEnv = Field(default=None, exclude=True) memory: AgentMemory = Field(default_factory=AgentMemory) scratch: Scratch = Field(default_factory=Scratch) spatial_memory: MemoryTree = Field(default_factory=MemoryTree) - @classmethod - def model_rebuild(cls, **kwargs): - from metagpt.environment.stanford_town.stanford_town_env import ( # noqa: F401 - StanfordTownEnv, - ) - - super(RoleContext, cls).model_rebuild(**kwargs) - class STRole(Role): # add a role's property structure to store role's age and so on like GA's Scratch. @@ -635,6 +623,3 @@ class STRole(Role): time.sleep(0.5) return DummyMessage() - - -STRoleContext.model_rebuild() diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 344e1df5e..5d1050464 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -23,13 +23,14 @@ from __future__ import annotations from enum import Enum -from typing import TYPE_CHECKING, Iterable, Optional, Set, Type, Union +from typing import Iterable, Optional, Set, Type, Union from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator from metagpt.actions import Action, ActionOutput from metagpt.actions.action_node import ActionNode from metagpt.actions.add_requirement import UserRequirement +from metagpt.base import BaseEnvironment, BaseRole from metagpt.const import MESSAGE_ROUTE_TO_SELF from metagpt.context_mixin import ContextMixin from metagpt.logs import logger @@ -47,9 +48,6 @@ from metagpt.strategy.planner import Planner from metagpt.utils.common import any_to_name, any_to_str, role_raise_decorator from metagpt.utils.repair_llm_raw_output import extract_state_value_from_output -if TYPE_CHECKING: - from metagpt.environment import Environment # noqa: F401 - PREFIX_TEMPLATE = """You are a {profile}, named {name}, your goal is {goal}. """ CONSTRAINT_TEMPLATE = "the constraint is {constraints}. " @@ -97,7 +95,7 @@ class RoleContext(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) # # env exclude=True to avoid `RecursionError: maximum recursion depth exceeded in comparison` - env: "Environment" = Field(default=None, exclude=True) # # avoid circular import + env: BaseEnvironment = Field(default=None, exclude=True) # # avoid circular import # TODO judge if ser&deser msg_buffer: MessageQueue = Field( default_factory=MessageQueue, exclude=True @@ -123,14 +121,8 @@ class RoleContext(BaseModel): def history(self) -> list[Message]: return self.memory.get() - @classmethod - def model_rebuild(cls, **kwargs): - from metagpt.environment.base_env import Environment # noqa: F401 - super().model_rebuild(**kwargs) - - -class Role(SerializationMixin, ContextMixin, BaseModel): +class Role(BaseRole, SerializationMixin, ContextMixin, BaseModel): """Role/Agent""" model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") @@ -310,7 +302,7 @@ class Role(SerializationMixin, ContextMixin, BaseModel): logger.debug(f"actions={self.actions}, state={state}") self.set_todo(self.actions[self.rc.state] if state >= 0 else None) - def set_env(self, env: "Environment"): + def set_env(self, env: BaseEnvironment): """Set the environment in which the role works. The role can talk to the environment and can also receive messages by observing.""" self.rc.env = env @@ -590,6 +582,3 @@ class Role(SerializationMixin, ContextMixin, BaseModel): if self.actions: return any_to_name(self.actions[0]) return "" - - -RoleContext.model_rebuild() diff --git a/setup.py b/setup.py index 79b65ad47..c8e705bfb 100644 --- a/setup.py +++ b/setup.py @@ -68,28 +68,29 @@ extras_require["pyppeteer"] = [ extras_require["dev"] = (["pylint~=3.0.3", "black~=23.3.0", "isort~=5.12.0", "pre-commit~=3.6.0"],) -setup( - name="metagpt", - version="0.8.0", - description="The Multi-Agent Framework", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/geekan/MetaGPT", - author="Alexander Wu", - author_email="alexanderwu@deepwisdom.ai", - license="MIT", - keywords="metagpt multi-agent multi-role programming gpt llm metaprogramming", - packages=find_packages(exclude=["contrib", "docs", "examples", "tests*"]), - python_requires=">=3.9", - install_requires=requirements, - extras_require=extras_require, - cmdclass={ - "install_mermaid": InstallMermaidCLI, - }, - entry_points={ - "console_scripts": [ - "metagpt=metagpt.software_company:app", - ], - }, - include_package_data=True, -) +if __name__ == "__main__": + setup( + name="metagpt", + version="0.8.0", + description="The Multi-Agent Framework", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/geekan/MetaGPT", + author="Alexander Wu", + author_email="alexanderwu@deepwisdom.ai", + license="MIT", + keywords="metagpt multi-agent multi-role programming gpt llm metaprogramming", + packages=find_packages(exclude=["contrib", "docs", "examples", "tests*"]), + python_requires=">=3.9", + install_requires=requirements, + extras_require=extras_require, + cmdclass={ + "install_mermaid": InstallMermaidCLI, + }, + entry_points={ + "console_scripts": [ + "metagpt=metagpt.software_company:app", + ], + }, + include_package_data=True, + ) diff --git a/tests/metagpt/environment/test_base_env.py b/tests/metagpt/environment/test_base_env.py index 404f1c206..ecdc4e132 100644 --- a/tests/metagpt/environment/test_base_env.py +++ b/tests/metagpt/environment/test_base_env.py @@ -6,6 +6,7 @@ from typing import Any, Optional import pytest +from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams from metagpt.environment.api.env_api import EnvAPIAbstract from metagpt.environment.base_env import ( Environment, @@ -14,7 +15,6 @@ from metagpt.environment.base_env import ( mark_as_readable, mark_as_writeable, ) -from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams class ForTestEnv(Environment): diff --git a/tests/metagpt/memory/test_longterm_memory.py b/tests/metagpt/memory/test_longterm_memory.py index 990017fee..cbd161dfa 100644 --- a/tests/metagpt/memory/test_longterm_memory.py +++ b/tests/metagpt/memory/test_longterm_memory.py @@ -28,10 +28,6 @@ async def test_ltm_search(mocker): ) role_id = "UTUserLtm(Product Manager)" - from metagpt.environment import Environment - - Environment - RoleContext.model_rebuild() rc = RoleContext(watch={"metagpt.actions.add_requirement.UserRequirement"}) ltm = LongTermMemory() ltm.recover_memory(role_id, rc) From c5d1f0d7dc31109870181f70af2acc136daf9ecc Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Mon, 12 Aug 2024 14:08:34 +0800 Subject: [PATCH 03/47] fix base env import error --- metagpt/base/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/metagpt/base/__init__.py b/metagpt/base/__init__.py index e69de29bb..a2fbe8eaf 100644 --- a/metagpt/base/__init__.py +++ b/metagpt/base/__init__.py @@ -0,0 +1,8 @@ +from metagpt.base.base_env import BaseEnvironment +from metagpt.base.base_role import BaseRole + + +__all__ = [ + "BaseEnvironment", + "BaseRole", +] From febc592af73db77c458726b755d85f6d1a9b5196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 16 Aug 2024 19:47:40 +0800 Subject: [PATCH 04/47] Force reply to human when finishing the current task --- metagpt/prompts/di/role_zero.py | 19 ++++++++++++ metagpt/roles/di/role_zero.py | 54 ++++++++++++++++++++++++++++----- metagpt/roles/di/swe_agent.py | 35 ++++++++++----------- 3 files changed, 83 insertions(+), 25 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 3c47ebf5f..f85128b38 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -218,3 +218,22 @@ QUICK_RESPONSE_SYSTEM_PROMPT = """ {role_info} However, you MUST respond to the user message by yourself directly, DON'T ask your team members. """ + +REPORT_TO_HUMAN_PROMPT = """ +# Current Plan +{plan_status} + +Your have just finish a task, Use "RoleZero.reply_to_human" to report what you have done. +The output format is : +```json +[ + {{ + "command_name": "RoleZero.reply_to_human", + "args": {{ + "content": "" + }} + }} +] +``` + +""" diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index c6cbfdfa8..727360ae9 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -26,6 +26,7 @@ from metagpt.prompts.di.role_zero import ( QUICK_THINK_PROMPT, QUICK_THINK_SYSTEM_PROMPT, REGENERATE_PROMPT, + REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, SYSTEM_PROMPT, THOUGHT_GUIDANCE, @@ -86,6 +87,8 @@ class RoleZero(Role): use_fixed_sop: bool = False requirements_constraints: str = "" # the constraints in user requirements + command_history: list[str] = [] + @model_validator(mode="after") def set_plan_and_tool(self) -> "RoleZero": # We force using this parameter for DataAnalyst @@ -234,21 +237,57 @@ class RoleZero(Role): if self.use_fixed_sop: return await super()._act() - commands, ok = await self._parse_commands() + commands, ok = await self._parse_commands(self.command_rsp) if not ok: error_msg = commands + self.rc.memory.add(UserMessage(content=error_msg)) return error_msg logger.info(f"Commands: \n{commands}") outputs = await self._run_commands(commands) logger.info(f"Commands outputs: \n{outputs}") self.rc.memory.add(UserMessage(content=outputs)) + # Report what is done when finishing the task. + current_command_list = [command["command_name"] for command in commands] + self.command_history.extend(current_command_list) + if self.check_whether_report_to_human(): + memory = self.rc.memory.get(self.memory_k) + memory = await self.parse_browser_actions(memory) + memory = self.parse_images(memory) + plan_status, _ = self._get_plan_status() + prompt = REPORT_TO_HUMAN_PROMPT.format(plan_status=plan_status) + req = self.llm.format_msg(memory + [UserMessage(content=prompt)]) + respond_command = await self.llm.aask(msg=req) + commands, ok = await self._parse_commands(respond_command) + if ok and len(commands) == 1 and commands[0]["command_name"] == "RoleZero.reply_to_human": + cmd = commands[0] + report_result = await self.reply_to_human(cmd["args"]) + self.rc.memory.add(AIMessage(content=respond_command)) + self.rc.memory.add(UserMessage(content=report_result)) + self.command_history.append("RoleZero.reply_to_human") + logger.info(f"Commands outputs: \n{report_result}") + outputs += report_result + return AIMessage( content=f"I have finished the task, please mark my task as finished. Outputs: {outputs}", sent_from=self.name, cause_by=RunCommand, ) + def check_whether_report_to_human(self): + """ "Check whether add reply to human command when finish current task""" + interaction_with_human = ["RoleZero.ask_human", "RoleZero.reply_to_human"] + plan_end_action = ["Plan.finish_current_task", "end"] + flag = 0 + for command_name in self.command_history[::-1]: + if command_name in plan_end_action: + flag |= 1 + elif command_name in interaction_with_human: + flag |= 2 + else: + break + return flag == 1 + async def _react(self) -> Message: # NOTE: Diff 1: Each time landing here means news is observed, set todo to allow news processing in _think self._set_state(0) @@ -332,7 +371,7 @@ class RoleZero(Role): command_rsp = await self.llm.aask(regenerate_req) return command_rsp - async def _parse_commands(self) -> Tuple[List[Dict], bool]: + async def _parse_commands(self, command_rsp) -> Tuple[List[Dict], bool]: """Retrieves commands from the Large Language Model (LLM). This function attempts to retrieve a list of commands from the LLM by @@ -344,20 +383,20 @@ class RoleZero(Role): - A boolean flag indicating success (True) or failure (False). """ try: - commands = CodeParser.parse_code(block=None, lang="json", text=self.command_rsp) + commands = CodeParser.parse_code(block=None, lang="json", text=command_rsp) if commands.endswith("]") and not commands.startswith("["): commands = "[" + commands commands = json.loads(repair_llm_raw_output(output=commands, req_keys=[None], repair_type=RepairType.JSON)) except json.JSONDecodeError as e: - logger.warning(f"Failed to parse JSON for: {self.command_rsp}. Trying to repair...") + logger.warning(f"Failed to parse JSON for: {command_rsp}. Trying to repair...") commands = await self.llm.aask( - msg=JSON_REPAIR_PROMPT.format(json_data=self.command_rsp, json_decode_error=str(e)) + msg=JSON_REPAIR_PROMPT.format(json_data=command_rsp, json_decode_error=str(e)) ) try: commands = json.loads(CodeParser.parse_code(block=None, lang="json", text=commands)) except json.JSONDecodeError: # repair escape error of code and math - commands = CodeParser.parse_code(block=None, lang="json", text=self.command_rsp) + commands = CodeParser.parse_code(block=None, lang="json", text=command_rsp) new_command = repair_escape_error(commands) commands = json.loads( repair_llm_raw_output(output=new_command, req_keys=[None], repair_type=RepairType.JSON) @@ -365,8 +404,7 @@ class RoleZero(Role): except Exception as e: tb = traceback.format_exc() print(tb) - error_msg = UserMessage(content=str(e)) - self.rc.memory.add(error_msg) + error_msg = str(e) return error_msg, False # 为了对LLM不按格式生成进行容错 diff --git a/metagpt/roles/di/swe_agent.py b/metagpt/roles/di/swe_agent.py index e1d2c9613..e90fc3045 100644 --- a/metagpt/roles/di/swe_agent.py +++ b/metagpt/roles/di/swe_agent.py @@ -9,6 +9,7 @@ from metagpt.prompts.di.swe_agent import ( NEXT_STEP_TEMPLATE, ) from metagpt.roles.di.role_zero import RoleZero +from metagpt.schema import Message from metagpt.tools.libs.git import git_create_pull from metagpt.tools.libs.terminal import Bash @@ -32,8 +33,6 @@ class SWEAgent(RoleZero): async def _think(self) -> bool: await self._format_instruction() res = await super()._think() - if self.run_eval: - await self._parse_commands_for_eval() return res def _update_tool_execution(self): @@ -55,6 +54,12 @@ class SWEAgent(RoleZero): bash_state = json.loads(state_output) self.cmd_prompt_current_state = CURRENT_BASH_STATE.format(**bash_state).strip() + async def _act(self) -> Message: + message = await super()._act() + if self.run_eval: + self._parse_commands_for_eval() + return message + async def _parse_commands_for_eval(self): """ Handles actions based on parsed commands. @@ -65,23 +70,19 @@ class SWEAgent(RoleZero): This function is specifically added for SWE bench evaluation. """ # only import when evaluation is needed - from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch + if not self.rc.todo: + from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch - commands, ok = await self._parse_commands() - if not ok: - return - for cmd in commands: - if "end" != cmd.get("command_name", ""): - return - try: - diff_output = await self.terminal.run("git diff --cached") - clear_diff = extract_patch(diff_output) - logger.info(f"Diff output: \n{clear_diff}") - if clear_diff: - self.output_diff = clear_diff + # swe agent have been stop. it means 'end' or other command which can stop the swe agent have been executed + try: + diff_output = await self.terminal.run("git diff --cached") + clear_diff = extract_patch(diff_output) + logger.info(f"Diff output: \n{clear_diff}") + if clear_diff: + self.output_diff = clear_diff - except Exception as e: - logger.error(f"Error during submission: {e}") + except Exception as e: + logger.error(f"Error during submission: {e}") def _retrieve_experience(self) -> str: return MINIMAL_EXAMPLE From 6b4c00f28638c07ba7ec70e8118977a4c59c9fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Mon, 19 Aug 2024 19:15:33 +0800 Subject: [PATCH 05/47] Remove the 'User Message' tag in the quick answer process. --- metagpt/roles/di/role_zero.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index c6cbfdfa8..674960be4 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -295,6 +295,9 @@ class RoleZero(Role): if "QUICK" in intent_result or "AMBIGUOUS" in intent_result: # llm call with the original context async with ThoughtReporter(enable_llm_stream=True) as reporter: await reporter.async_report({"type": "quick"}) + # remove "[Message] from A to B :" tag. + pattern = r"\[Message\] from .* to .*:\s*" + memory[-1].content = re.sub(pattern, "", memory[-1].content, count=1) answer = await self.llm.aask( self.llm.format_msg(memory), system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())], From f3d9b8e767917038092395ee325d6dfec92ec8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 11:58:10 +0800 Subject: [PATCH 06/47] remove "[Message] from A to B:'"in quick answer --- metagpt/roles/di/role_zero.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 674960be4..fb191bfd9 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -295,13 +295,13 @@ class RoleZero(Role): if "QUICK" in intent_result or "AMBIGUOUS" in intent_result: # llm call with the original context async with ThoughtReporter(enable_llm_stream=True) as reporter: await reporter.async_report({"type": "quick"}) - # remove "[Message] from A to B :" tag. - pattern = r"\[Message\] from .* to .*:\s*" - memory[-1].content = re.sub(pattern, "", memory[-1].content, count=1) answer = await self.llm.aask( self.llm.format_msg(memory), system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())], ) + # If the answer contains the substring '[Message] from A to B:', remove it. + pattern = r"\[Message\] from .* to .*:\s*" + answer = re.sub(pattern, "", answer, count=1) elif "SEARCH" in intent_result: query = "\n".join(str(msg) for msg in memory) answer = await SearchEnhancedQA().run(query) From 0246b659b7f7448ef0545697742e89066b44b1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 17:13:15 +0800 Subject: [PATCH 07/47] Add a summary after the agent stops --- metagpt/prompts/di/role_zero.py | 27 ++++++++-------- metagpt/roles/di/role_zero.py | 57 ++++++++++++--------------------- metagpt/roles/di/swe_agent.py | 3 +- metagpt/roles/di/team_leader.py | 2 ++ 4 files changed, 37 insertions(+), 52 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index f85128b38..2294fa731 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -220,20 +220,19 @@ However, you MUST respond to the user message by yourself directly, DON'T ask yo """ REPORT_TO_HUMAN_PROMPT = """ -# Current Plan -{plan_status} +# Restrictions +{requirements_constraints} -Your have just finish a task, Use "RoleZero.reply_to_human" to report what you have done. -The output format is : -```json -[ - {{ - "command_name": "RoleZero.reply_to_human", - "args": {{ - "content": "" - }} - }} -] -``` +Your have just finish all tasks, Use "RoleZero.reply_to_human" to answer the user requirements. +Report to human what you have done. Do Not ouput any other format. +Your reply is: """ +SUMMARY_PROMPY = """ +# Restrictions +{requirements_constraints} + +You have just completed some tasks. +Summarize the tasks you have accomplished without including detailed information. +If there are any deliverables, list their descriptions and provide their file paths. +""" diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 727360ae9..48967d7ef 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -28,6 +28,7 @@ from metagpt.prompts.di.role_zero import ( REGENERATE_PROMPT, REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, + SUMMARY_PROMPY, SYSTEM_PROMPT, THOUGHT_GUIDANCE, ) @@ -68,6 +69,8 @@ class RoleZero(Role): react_mode: Literal["react"] = "react" max_react_loop: int = 20 # used for react mode + # Summary Mode + use_summary: bool = True # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None @@ -87,8 +90,6 @@ class RoleZero(Role): use_fixed_sop: bool = False requirements_constraints: str = "" # the constraints in user requirements - command_history: list[str] = [] - @model_validator(mode="after") def set_plan_and_tool(self) -> "RoleZero": # We force using this parameter for DataAnalyst @@ -247,26 +248,24 @@ class RoleZero(Role): logger.info(f"Commands outputs: \n{outputs}") self.rc.memory.add(UserMessage(content=outputs)) - # Report what is done when finishing the task. - current_command_list = [command["command_name"] for command in commands] - self.command_history.extend(current_command_list) - if self.check_whether_report_to_human(): - memory = self.rc.memory.get(self.memory_k) - memory = await self.parse_browser_actions(memory) - memory = self.parse_images(memory) - plan_status, _ = self._get_plan_status() - prompt = REPORT_TO_HUMAN_PROMPT.format(plan_status=plan_status) - req = self.llm.format_msg(memory + [UserMessage(content=prompt)]) - respond_command = await self.llm.aask(msg=req) - commands, ok = await self._parse_commands(respond_command) - if ok and len(commands) == 1 and commands[0]["command_name"] == "RoleZero.reply_to_human": - cmd = commands[0] - report_result = await self.reply_to_human(cmd["args"]) - self.rc.memory.add(AIMessage(content=respond_command)) - self.rc.memory.add(UserMessage(content=report_result)) - self.command_history.append("RoleZero.reply_to_human") - logger.info(f"Commands outputs: \n{report_result}") - outputs += report_result + if any(["end" == command["command_name"] for command in commands]): + # Ensure reply to the human before the "end" command is executed. + if all(["reply_to_human" not in memory.content for memory in self.get_memories(k=5)]): + memory = self.rc.memory.get(self.memory_k) + reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( + requirements_constraints=self.requirements_constraints, + ) + reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) + await self.reply_to_human(content=reply_content) + self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) + + # Summary of the Completed Task and Deliverables + if self.use_summary: + memory = self.rc.memory.get(self.memory_k) + summary_prompt = SUMMARY_PROMPY.format( + requirements_constraints=self.requirements_constraints, + ) + outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) return AIMessage( content=f"I have finished the task, please mark my task as finished. Outputs: {outputs}", @@ -274,20 +273,6 @@ class RoleZero(Role): cause_by=RunCommand, ) - def check_whether_report_to_human(self): - """ "Check whether add reply to human command when finish current task""" - interaction_with_human = ["RoleZero.ask_human", "RoleZero.reply_to_human"] - plan_end_action = ["Plan.finish_current_task", "end"] - flag = 0 - for command_name in self.command_history[::-1]: - if command_name in plan_end_action: - flag |= 1 - elif command_name in interaction_with_human: - flag |= 2 - else: - break - return flag == 1 - async def _react(self) -> Message: # NOTE: Diff 1: Each time landing here means news is observed, set todo to allow news processing in _think self._set_state(0) diff --git a/metagpt/roles/di/swe_agent.py b/metagpt/roles/di/swe_agent.py index e90fc3045..d54c9dd44 100644 --- a/metagpt/roles/di/swe_agent.py +++ b/metagpt/roles/di/swe_agent.py @@ -69,11 +69,10 @@ class SWEAgent(RoleZero): This function is specifically added for SWE bench evaluation. """ - # only import when evaluation is needed + # If todo switches to None, it indicates that this is the final round of reactions, and the Swe-Agent will stop. Use git diff to store any changes made. if not self.rc.todo: from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch - # swe agent have been stop. it means 'end' or other command which can stop the swe agent have been executed try: diff_output = await self.terminal.run("git diff --cached") clear_diff = extract_patch(diff_output) diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 97100f295..112ca5a84 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -30,6 +30,8 @@ class TeamLeader(RoleZero): experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever() + use_summary: bool = False + def _update_tool_execution(self): self.tool_execution_map.update( { From 91358f7dca688857a513e9660fc749c72a433696 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 20 Aug 2024 17:13:28 +0800 Subject: [PATCH 08/47] add use_llm_ranker config --- config/config2.example.yaml | 1 + metagpt/configs/exp_pool_config.py | 1 + metagpt/exp_pool/manager.py | 25 +++++++++++++++++-------- tests/metagpt/exp_pool/test_manager.py | 15 ++++++++++++++- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/config/config2.example.yaml b/config/config2.example.yaml index ba2b5527c..a24892c2a 100644 --- a/config/config2.example.yaml +++ b/config/config2.example.yaml @@ -80,6 +80,7 @@ exp_pool: enable_write: false persist_path: .chroma_exp_data # The directory. retrieval_type: bm25 # Default is `bm25`, can be set to `chroma` for vector storage, which requires setting up embedding. + use_llm_ranker: false # If `use_llm_ranker` is true, then it will use LLM Reranker to get better result, but it is not always guaranteed that the output will be parseable for reranking. azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY" azure_tts_region: "eastus" diff --git a/metagpt/configs/exp_pool_config.py b/metagpt/configs/exp_pool_config.py index ad918b481..7611dda27 100644 --- a/metagpt/configs/exp_pool_config.py +++ b/metagpt/configs/exp_pool_config.py @@ -21,3 +21,4 @@ class ExperiencePoolConfig(YamlModel): retrieval_type: ExperiencePoolRetrievalType = Field( default=ExperiencePoolRetrievalType.BM25, description="The retrieval type for experience pool." ) + use_llm_ranker: bool = Field(default=False, description="Use LLM Reranker to get better result.") diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index 56cc43970..afa2459d9 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -117,18 +117,14 @@ class ExperienceManager(BaseModel): try: from metagpt.rag.engines import SimpleEngine - from metagpt.rag.schema import ( - BM25IndexConfig, - BM25RetrieverConfig, - LLMRankerConfig, - ) + from metagpt.rag.schema import BM25IndexConfig, BM25RetrieverConfig except ImportError: raise ImportError("To use the experience pool, you need to install the rag module.") persist_path = Path(self.config.exp_pool.persist_path) docstore_path = persist_path / "docstore.json" - ranker_configs = [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)] + ranker_configs = self._get_ranker_configs() if not docstore_path.exists(): logger.debug(f"Path `{docstore_path}` not exists, try to create a new bm25 storage.") @@ -163,7 +159,7 @@ class ExperienceManager(BaseModel): try: from metagpt.rag.engines import SimpleEngine - from metagpt.rag.schema import ChromaRetrieverConfig, LLMRankerConfig + from metagpt.rag.schema import ChromaRetrieverConfig except ImportError: raise ImportError("To use the experience pool, you need to install the rag module.") @@ -174,12 +170,25 @@ class ExperienceManager(BaseModel): similarity_top_k=DEFAULT_SIMILARITY_TOP_K, ) ] - ranker_configs = [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)] + ranker_configs = self._get_ranker_configs() storage = SimpleEngine.from_objs(retriever_configs=retriever_configs, ranker_configs=ranker_configs) return storage + def _get_ranker_configs(self): + """Returns ranker configurations based on the configuration. + + If `use_llm_ranker` is True, returns a list with one `LLMRankerConfig` + instance. Otherwise, returns an empty list. + + Returns: + list: A list of `LLMRankerConfig` instances or an empty list. + """ + from metagpt.rag.schema import LLMRankerConfig + + return [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)] if self.config.exp_pool.use_llm_ranker else [] + _exp_manager = None diff --git a/tests/metagpt/exp_pool/test_manager.py b/tests/metagpt/exp_pool/test_manager.py index 933232031..b0e4e8537 100644 --- a/tests/metagpt/exp_pool/test_manager.py +++ b/tests/metagpt/exp_pool/test_manager.py @@ -7,7 +7,7 @@ from metagpt.configs.exp_pool_config import ( ) from metagpt.configs.llm_config import LLMConfig from metagpt.exp_pool.manager import Experience, ExperienceManager -from metagpt.exp_pool.schema import QueryType +from metagpt.exp_pool.schema import DEFAULT_SIMILARITY_TOP_K, QueryType class TestExperienceManager: @@ -129,3 +129,16 @@ class TestExperienceManager: manager = ExperienceManager(config=mock_config) storage = manager._create_chroma_storage() assert storage is not None + + def test_get_ranker_configs_use_llm_ranker_true(self, mock_config): + mock_config.exp_pool.use_llm_ranker = True + manager = ExperienceManager(config=mock_config) + ranker_configs = manager._get_ranker_configs() + assert len(ranker_configs) == 1 + assert ranker_configs[0].top_n == DEFAULT_SIMILARITY_TOP_K + + def test_get_ranker_configs_use_llm_ranker_false(self, mock_config): + mock_config.exp_pool.use_llm_ranker = False + manager = ExperienceManager(config=mock_config) + ranker_configs = manager._get_ranker_configs() + assert len(ranker_configs) == 0 From cd764737c81a1eeb08b5b83aa478dc23d874a0a5 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 20 Aug 2024 17:17:13 +0800 Subject: [PATCH 09/47] add use_llm_ranker config --- metagpt/exp_pool/manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index afa2459d9..e38906d90 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -185,6 +185,7 @@ class ExperienceManager(BaseModel): Returns: list: A list of `LLMRankerConfig` instances or an empty list. """ + from metagpt.rag.schema import LLMRankerConfig return [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)] if self.config.exp_pool.use_llm_ranker else [] From a0441c897ed92b560da03c7e8025755924f85eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 17:29:22 +0800 Subject: [PATCH 10/47] change re format --- metagpt/roles/di/role_zero.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index fb191bfd9..12d46783c 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -300,7 +300,7 @@ class RoleZero(Role): system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())], ) # If the answer contains the substring '[Message] from A to B:', remove it. - pattern = r"\[Message\] from .* to .*:\s*" + pattern = r"\[Message\] from .+? to .+?:\s*" answer = re.sub(pattern, "", answer, count=1) elif "SEARCH" in intent_result: query = "\n".join(str(msg) for msg in memory) From 502a0e2ad6e784af6668d4d2a119b6152a695128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 19:21:17 +0800 Subject: [PATCH 11/47] combine summary and last reply to human --- metagpt/prompts/di/role_zero.py | 6 +++-- metagpt/roles/di/role_zero.py | 48 ++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 2294fa731..80a505d17 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -233,6 +233,8 @@ SUMMARY_PROMPY = """ {requirements_constraints} You have just completed some tasks. -Summarize the tasks you have accomplished without including detailed information. -If there are any deliverables, list their descriptions and provide their file paths. +You must reply to human what you have done and what is the result. +If there are any deliverables, provide descriptions and file paths. +Do Not ouput any other format. limited in 300 words. +Your reply is: """ diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 48967d7ef..4ab70409e 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -26,7 +26,6 @@ from metagpt.prompts.di.role_zero import ( QUICK_THINK_PROMPT, QUICK_THINK_SYSTEM_PROMPT, REGENERATE_PROMPT, - REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, SUMMARY_PROMPY, SYSTEM_PROMPT, @@ -248,25 +247,6 @@ class RoleZero(Role): logger.info(f"Commands outputs: \n{outputs}") self.rc.memory.add(UserMessage(content=outputs)) - if any(["end" == command["command_name"] for command in commands]): - # Ensure reply to the human before the "end" command is executed. - if all(["reply_to_human" not in memory.content for memory in self.get_memories(k=5)]): - memory = self.rc.memory.get(self.memory_k) - reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( - requirements_constraints=self.requirements_constraints, - ) - reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) - await self.reply_to_human(content=reply_content) - self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) - - # Summary of the Completed Task and Deliverables - if self.use_summary: - memory = self.rc.memory.get(self.memory_k) - summary_prompt = SUMMARY_PROMPY.format( - requirements_constraints=self.requirements_constraints, - ) - outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) - return AIMessage( content=f"I have finished the task, please mark my task as finished. Outputs: {outputs}", sent_from=self.name, @@ -442,8 +422,7 @@ class RoleZero(Role): command_output = "Current task is finished. If all tasks are finished, use 'end' to stop." elif cmd["command_name"] == "end": - self._set_state(-1) - command_output = "" + command_output = await self._end() # output from bash.run may be empty, add decorations to the output to ensure visibility. elif cmd["command_name"] == "Bash.run": @@ -502,3 +481,28 @@ class RoleZero(Role): if not isinstance(self.rc.env, MGXEnv): return "Not in MGXEnv, command will not be executed." return await self.rc.env.reply_to_human(content, sent_from=self) + + async def _end(self): + # summary + memory = self.get_memories(k=self.memory_k) + summary_prompt = SUMMARY_PROMPY.format( + requirements_constraints=self.requirements_constraints, + ) + reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) + # Ensure reply to the human before the "end" command is executed. + need_reply = True + for memory in self.get_memories(k=5)[::-1]: + if "reply_to_human" in memory.content: + need_reply = False + break + if "[Message]" in memory.content: + # Receive new message from other + need_reply = True + break + if need_reply: + await self.reply_to_human(content=reply_content) + self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) + + self._set_state(-1) + + return f"{reply_content} \n Plan.end executed Task is finished" From 1730897730962dcaeb438a62ca14da2decaf7d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 19:45:13 +0800 Subject: [PATCH 12/47] fix issues --- metagpt/roles/di/role_zero.py | 8 +++----- metagpt/roles/di/team_leader.py | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 4ab70409e..59714b726 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -68,8 +68,6 @@ class RoleZero(Role): react_mode: Literal["react"] = "react" max_react_loop: int = 20 # used for react mode - # Summary Mode - use_summary: bool = True # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None @@ -483,6 +481,8 @@ class RoleZero(Role): return await self.rc.env.reply_to_human(content, sent_from=self) async def _end(self): + self._set_state(-1) + # summary memory = self.get_memories(k=self.memory_k) summary_prompt = SUMMARY_PROMPY.format( @@ -503,6 +503,4 @@ class RoleZero(Role): await self.reply_to_human(content=reply_content) self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) - self._set_state(-1) - - return f"{reply_content} \n Plan.end executed Task is finished" + return reply_content diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 112ca5a84..97100f295 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -30,8 +30,6 @@ class TeamLeader(RoleZero): experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever() - use_summary: bool = False - def _update_tool_execution(self): self.tool_execution_map.update( { From 85f976d4f36129c3447b9f6e454a48e5e6891423 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 20 Aug 2024 20:16:28 +0800 Subject: [PATCH 13/47] update llm reranker prompt --- config/config2.example.yaml | 2 +- metagpt/configs/exp_pool_config.py | 2 +- metagpt/rag/factories/ranker.py | 1 + metagpt/rag/prompts/__init__.py | 0 metagpt/rag/prompts/default_prompts.py | 34 ++++++++++++++++++++++++++ metagpt/rag/schema.py | 5 ++++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 metagpt/rag/prompts/__init__.py create mode 100644 metagpt/rag/prompts/default_prompts.py diff --git a/config/config2.example.yaml b/config/config2.example.yaml index a24892c2a..776ea6f54 100644 --- a/config/config2.example.yaml +++ b/config/config2.example.yaml @@ -80,7 +80,7 @@ exp_pool: enable_write: false persist_path: .chroma_exp_data # The directory. retrieval_type: bm25 # Default is `bm25`, can be set to `chroma` for vector storage, which requires setting up embedding. - use_llm_ranker: false # If `use_llm_ranker` is true, then it will use LLM Reranker to get better result, but it is not always guaranteed that the output will be parseable for reranking. + use_llm_ranker: true # If `use_llm_ranker` is true, then it will use LLM Reranker to get better result, but it is not always guaranteed that the output will be parseable for reranking. azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY" azure_tts_region: "eastus" diff --git a/metagpt/configs/exp_pool_config.py b/metagpt/configs/exp_pool_config.py index 7611dda27..8d33b25aa 100644 --- a/metagpt/configs/exp_pool_config.py +++ b/metagpt/configs/exp_pool_config.py @@ -21,4 +21,4 @@ class ExperiencePoolConfig(YamlModel): retrieval_type: ExperiencePoolRetrievalType = Field( default=ExperiencePoolRetrievalType.BM25, description="The retrieval type for experience pool." ) - use_llm_ranker: bool = Field(default=False, description="Use LLM Reranker to get better result.") + use_llm_ranker: bool = Field(default=True, description="Use LLM Reranker to get better result.") diff --git a/metagpt/rag/factories/ranker.py b/metagpt/rag/factories/ranker.py index 7abda162a..c825c228c 100644 --- a/metagpt/rag/factories/ranker.py +++ b/metagpt/rag/factories/ranker.py @@ -38,6 +38,7 @@ class RankerFactory(ConfigBasedFactory): def _create_llm_ranker(self, config: LLMRankerConfig, **kwargs) -> LLMRerank: config.llm = self._extract_llm(config, **kwargs) + return LLMRerank(**config.model_dump()) def _create_colbert_ranker(self, config: ColbertRerankConfig, **kwargs) -> LLMRerank: diff --git a/metagpt/rag/prompts/__init__.py b/metagpt/rag/prompts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/metagpt/rag/prompts/default_prompts.py b/metagpt/rag/prompts/default_prompts.py new file mode 100644 index 000000000..12a5e2f06 --- /dev/null +++ b/metagpt/rag/prompts/default_prompts.py @@ -0,0 +1,34 @@ +"""Set of default prompts.""" + +from llama_index.core.prompts.base import PromptTemplate +from llama_index.core.prompts.prompt_type import PromptType + +DEFAULT_CHOICE_SELECT_PROMPT_TMPL = """ +You are a highly efficient assistant, tasked with evaluating a list of documents to a given question. + +I will provide you with a question with a list of documents. Your task is to respond with the numbers of the documents you should consult to answer the question, in order of relevance, as well as the relevance score. + + +## Question +{query_str} + +## Documents +{context_str} + +## Format Example +Doc: 9, Relevance: 7 + +## Instructions +- Understand the question. +- Evaluate the relevance between the question and the documents. +- The relevance score is a number from 1-10 based on how relevant you think the document is to the question. +- Do not include any documents that are not relevant to the question. + +## Constraint +Format: Just print the result in format like **Format Example**. + +## Action +Follow instructions, generate output and make sure it follows the **Constraint**. +""" + +DEFAULT_CHOICE_SELECT_PROMPT = PromptTemplate(DEFAULT_CHOICE_SELECT_PROMPT_TMPL, prompt_type=PromptType.CHOICE_SELECT) diff --git a/metagpt/rag/schema.py b/metagpt/rag/schema.py index 5be2b050b..4180536a3 100644 --- a/metagpt/rag/schema.py +++ b/metagpt/rag/schema.py @@ -6,6 +6,7 @@ from typing import Any, ClassVar, List, Literal, Optional, Union from chromadb.api.types import CollectionMetadata from llama_index.core.embeddings import BaseEmbedding from llama_index.core.indices.base import BaseIndex +from llama_index.core.prompts import BasePromptTemplate from llama_index.core.schema import TextNode from llama_index.core.vector_stores.types import VectorStoreQueryMode from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, model_validator @@ -14,6 +15,7 @@ from metagpt.config2 import Config from metagpt.configs.embedding_config import EmbeddingType from metagpt.logs import logger from metagpt.rag.interface import RAGObject +from metagpt.rag.prompts.default_prompts import DEFAULT_CHOICE_SELECT_PROMPT class BaseRetrieverConfig(BaseModel): @@ -124,6 +126,9 @@ class LLMRankerConfig(BaseRankerConfig): default=None, description="The LLM to rerank with. using Any instead of LLM, as llama_index.core.llms.LLM is pydantic.v1.", ) + choice_select_prompt: Optional[BasePromptTemplate] = Field( + default=DEFAULT_CHOICE_SELECT_PROMPT, description="Choice select prompt." + ) class ColbertRerankConfig(BaseRankerConfig): From 30def415f026b87630c2f0317647998a338c6a4f Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 20 Aug 2024 20:18:58 +0800 Subject: [PATCH 14/47] update llm reranker prompt --- config/config2.example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config2.example.yaml b/config/config2.example.yaml index 776ea6f54..1c5b23292 100644 --- a/config/config2.example.yaml +++ b/config/config2.example.yaml @@ -80,7 +80,7 @@ exp_pool: enable_write: false persist_path: .chroma_exp_data # The directory. retrieval_type: bm25 # Default is `bm25`, can be set to `chroma` for vector storage, which requires setting up embedding. - use_llm_ranker: true # If `use_llm_ranker` is true, then it will use LLM Reranker to get better result, but it is not always guaranteed that the output will be parseable for reranking. + use_llm_ranker: true # Default is true, it will use LLM Reranker to get better result. azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY" azure_tts_region: "eastus" From 8546939b43908a6dd7283990b7cb61c6d0830dab Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 20 Aug 2024 20:19:42 +0800 Subject: [PATCH 15/47] update llm reranker prompt --- config/config2.example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config2.example.yaml b/config/config2.example.yaml index 1c5b23292..a7214f662 100644 --- a/config/config2.example.yaml +++ b/config/config2.example.yaml @@ -80,7 +80,7 @@ exp_pool: enable_write: false persist_path: .chroma_exp_data # The directory. retrieval_type: bm25 # Default is `bm25`, can be set to `chroma` for vector storage, which requires setting up embedding. - use_llm_ranker: true # Default is true, it will use LLM Reranker to get better result. + use_llm_ranker: true # Default is `true`, it will use LLM Reranker to get better result. azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY" azure_tts_region: "eastus" From 13f0e6b3dd4a65273b8cdd5e9e4476b04ac6db28 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Tue, 20 Aug 2024 20:22:12 +0800 Subject: [PATCH 16/47] add support for local file cr --- metagpt/ext/cr/actions/code_review.py | 29 +++++++++++++++-- metagpt/tools/libs/cr.py | 46 +++++++++++++++------------ 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/metagpt/ext/cr/actions/code_review.py b/metagpt/ext/cr/actions/code_review.py index e3e6e69f2..5f861c3e3 100644 --- a/metagpt/ext/cr/actions/code_review.py +++ b/metagpt/ext/cr/actions/code_review.py @@ -4,7 +4,9 @@ import json import re +from pathlib import Path +import aiofiles from unidiff import PatchSet from metagpt.actions.action import Action @@ -16,6 +18,7 @@ from metagpt.ext.cr.utils.cleaner import ( from metagpt.ext.cr.utils.schema import Point from metagpt.logs import logger from metagpt.utils.common import parse_json_code_block +from metagpt.utils.report import EditorReporter CODE_REVIEW_PROMPT_TEMPLATE = """ NOTICE @@ -193,16 +196,26 @@ class CodeReview(Action): patched_file_path = patched_file.path for c in comments_batch: c["commented_file"] = patched_file_path - comments += comments_batch + comments.extend(comments_batch) return comments - async def run(self, patch: PatchSet, points: list[Point]): + async def run(self, patch: PatchSet, points: list[Point], output_file: str): patch: PatchSet = rm_patch_useless_part(patch) patch: PatchSet = add_line_num_on_patch(patch) result = [] - comments = await self.cr_by_points(patch=patch, points=points) + async with EditorReporter(enable_llm_stream=True) as reporter: + log_cr_output_path = Path(output_file).with_suffix(".log") + await reporter.async_report( + {"src_path": str(log_cr_output_path), "filename": log_cr_output_path.name}, "meta" + ) + comments = await self.cr_by_points(patch=patch, points=points) + log_cr_output_path.parent.mkdir(exist_ok=True, parents=True) + async with aiofiles.open(log_cr_output_path, "w", encoding="utf-8") as f: + await f.write(json.dumps(comments, ensure_ascii=False, indent=2)) + await reporter.async_report(log_cr_output_path) + if len(comments) != 0: comments = self.format_comments(comments, points, patch) comments = await self.confirm_comments(patch=patch, comments=comments, points=points) @@ -210,4 +223,14 @@ class CodeReview(Action): if comment["code"]: if not (comment["code"].isspace()): result.append(comment) + + async with EditorReporter() as reporter: + src_path = output_file + cr_output_path = Path(output_file) + await reporter.async_report( + {"type": "CodeReview", "src_path": src_path, "filename": cr_output_path.name}, "meta" + ) + async with aiofiles.open(cr_output_path, "w", encoding="utf-8") as f: + await f.write(json.dumps(comments, ensure_ascii=False, indent=2)) + await reporter.async_report(cr_output_path) return result diff --git a/metagpt/tools/libs/cr.py b/metagpt/tools/libs/cr.py index 5fca23a66..cdc3f81ce 100644 --- a/metagpt/tools/libs/cr.py +++ b/metagpt/tools/libs/cr.py @@ -1,3 +1,4 @@ +import difflib import json from pathlib import Path from typing import Optional @@ -22,35 +23,34 @@ class CodeReview: async def review( self, patch_path: str, - cr_output_file: str, - cr_point_file: Optional[str] = None, + output_file: str, + point_file: Optional[str] = None, ) -> str: """Review a PR and save code review comments. + Notes: + If the user does not specify an output path, saved it using a relative path in the current working directory. + Args: - patch_path: The local path of the patch file or the url of the pull request. Example: "/data/xxx-pr-1.patch", "https://github.com/xx/XX/pull/1362" - cr_output_file: Output file path where code review comments will be saved. Example: "cr/xxx-pr-1.json" - cr_point_file: File path for specifying code review points. If not specified, this parameter is not passed.. + patch_path: The local path of the patch file or the URL of the pull request. + output_file: Output file path where code review comments will be saved. + point_file: File path for specifying code review points. If not specified, this parameter does not need to be passed. + + Examples: + + >>> cr = CodeReview() + >>> await cr.review(patch_path="https://github.com/geekan/MetaGPT/pull/136", output_file="cr/MetaGPT_136.json") + >>> await cr.review(patch_path="/data/uploads/dev-master.diff", output_file="cr/dev-master.json") + >>> await cr.review(patch_path="/data/uploads/main.py", output_file="cr/main.json") """ patch = await self._get_patch_content(patch_path) - cr_point_file = cr_point_file if cr_point_file else Path(metagpt.ext.cr.__file__).parent / "points.json" - async with aiofiles.open(cr_point_file, "rb") as f: + point_file = point_file if point_file else Path(metagpt.ext.cr.__file__).parent / "points.json" + async with aiofiles.open(point_file, "rb") as f: cr_point_content = await f.read() cr_points = [Point(**i) for i in json.loads(cr_point_content)] - async with EditorReporter(enable_llm_stream=True) as reporter: - src_path = cr_output_file - cr_output_path = Path(cr_output_file) - await reporter.async_report( - {"type": "CodeReview", "src_path": src_path, "filename": cr_output_path.name}, "meta" - ) - comments = await CodeReview_().run(patch, cr_points) - cr_output_path.parent.mkdir(exist_ok=True, parents=True) - async with aiofiles.open(cr_output_path, "w", encoding="utf-8") as f: - await f.write(json.dumps(comments, ensure_ascii=False)) - await reporter.async_report(cr_output_path) - - return f"The number of defects: {len(comments)} and the comments are stored in {cr_output_file}" + comments = await CodeReview_().run(patch, cr_points, output_file) + return f"The number of defects: {len(comments)} and the comments are stored in {output_file}" async def fix( self, @@ -88,6 +88,12 @@ class CodeReview: async with aiofiles.open(patch_path, encoding="utf-8") as f: patch_file_content = await f.read() await EditorReporter().async_report(patch_path) + if not patch_path.endswith((".diff", "patch")): + name = Path(patch_path).name + patch_file_content = "".join( + difflib.unified_diff([], patch_file_content.splitlines(keepends=True), "/dev/null", f"b/{name}"), + ) + patch_file_content = f"diff --git a/{name} b/{name}\n{patch_file_content}" patch: PatchSet = PatchSet(patch_file_content) return patch From b09617b9df56881ba9afab3e5e30ba578e621e59 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Tue, 20 Aug 2024 20:25:45 +0800 Subject: [PATCH 17/47] fix patch suffix error --- metagpt/tools/libs/cr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/tools/libs/cr.py b/metagpt/tools/libs/cr.py index cdc3f81ce..e3509591b 100644 --- a/metagpt/tools/libs/cr.py +++ b/metagpt/tools/libs/cr.py @@ -88,7 +88,7 @@ class CodeReview: async with aiofiles.open(patch_path, encoding="utf-8") as f: patch_file_content = await f.read() await EditorReporter().async_report(patch_path) - if not patch_path.endswith((".diff", "patch")): + if not patch_path.endswith((".diff", ".patch")): name = Path(patch_path).name patch_file_content = "".join( difflib.unified_diff([], patch_file_content.splitlines(keepends=True), "/dev/null", f"b/{name}"), From beb66b05b42ca8731f341edc652f84fa6d0df352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 20:41:47 +0800 Subject: [PATCH 18/47] decompose reply to human and summary --- metagpt/prompts/di/role_zero.py | 11 +++++----- metagpt/roles/di/role_zero.py | 37 ++++++++++++++++----------------- metagpt/roles/di/team_leader.py | 2 ++ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 80a505d17..caba52e57 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -223,8 +223,9 @@ REPORT_TO_HUMAN_PROMPT = """ # Restrictions {requirements_constraints} -Your have just finish all tasks, Use "RoleZero.reply_to_human" to answer the user requirements. -Report to human what you have done. Do Not ouput any other format. +You have just finished all tasks. +Reply to the human requirements. +Do not output any other format. Your reply is: """ @@ -233,8 +234,6 @@ SUMMARY_PROMPY = """ {requirements_constraints} You have just completed some tasks. -You must reply to human what you have done and what is the result. -If there are any deliverables, provide descriptions and file paths. -Do Not ouput any other format. limited in 300 words. -Your reply is: +Summarize the tasks you have accomplished without including detailed information. +If there are any deliverables, list their descriptions and provide their file paths. """ diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 59714b726..8dfd888d2 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -26,6 +26,7 @@ from metagpt.prompts.di.role_zero import ( QUICK_THINK_PROMPT, QUICK_THINK_SYSTEM_PROMPT, REGENERATE_PROMPT, + REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, SUMMARY_PROMPY, SYSTEM_PROMPT, @@ -68,6 +69,8 @@ class RoleZero(Role): react_mode: Literal["react"] = "react" max_react_loop: int = 20 # used for react mode + # Summary Mode + use_summary: bool = True # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None @@ -482,25 +485,21 @@ class RoleZero(Role): async def _end(self): self._set_state(-1) - - # summary - memory = self.get_memories(k=self.memory_k) - summary_prompt = SUMMARY_PROMPY.format( - requirements_constraints=self.requirements_constraints, - ) - reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) # Ensure reply to the human before the "end" command is executed. - need_reply = True - for memory in self.get_memories(k=5)[::-1]: - if "reply_to_human" in memory.content: - need_reply = False - break - if "[Message]" in memory.content: - # Receive new message from other - need_reply = True - break - if need_reply: + if not any(["reply_to_human" in memory.content for memory in self.get_memories(k=5)]): + memory = self.rc.memory.get(self.memory_k) + reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( + requirements_constraints=self.requirements_constraints, + ) + reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) await self.reply_to_human(content=reply_content) self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) - - return reply_content + outputs = "" + # Summary of the Completed Task and Deliverables + if self.use_summary: + memory = self.rc.memory.get(self.memory_k) + summary_prompt = SUMMARY_PROMPY.format( + requirements_constraints=self.requirements_constraints, + ) + outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) + return outputs diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 97100f295..112ca5a84 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -30,6 +30,8 @@ class TeamLeader(RoleZero): experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever() + use_summary: bool = False + def _update_tool_execution(self): self.tool_execution_map.update( { From ca66af381a8fedb1faadc3fe022dcd8a5819a877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 20:52:08 +0800 Subject: [PATCH 19/47] fix format issues --- metagpt/prompts/di/role_zero.py | 2 +- metagpt/roles/di/role_zero.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index caba52e57..ce8c61dd5 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -229,7 +229,7 @@ Do not output any other format. Your reply is: """ -SUMMARY_PROMPY = """ +SUMMARY_PROMPT = """ # Restrictions {requirements_constraints} diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 8dfd888d2..4e61ceab3 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -28,7 +28,7 @@ from metagpt.prompts.di.role_zero import ( REGENERATE_PROMPT, REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, - SUMMARY_PROMPY, + SUMMARY_PROMPT, SYSTEM_PROMPT, THOUGHT_GUIDANCE, ) @@ -485,9 +485,9 @@ class RoleZero(Role): async def _end(self): self._set_state(-1) + memory = self.rc.memory.get(self.memory_k) # Ensure reply to the human before the "end" command is executed. if not any(["reply_to_human" in memory.content for memory in self.get_memories(k=5)]): - memory = self.rc.memory.get(self.memory_k) reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( requirements_constraints=self.requirements_constraints, ) @@ -497,8 +497,7 @@ class RoleZero(Role): outputs = "" # Summary of the Completed Task and Deliverables if self.use_summary: - memory = self.rc.memory.get(self.memory_k) - summary_prompt = SUMMARY_PROMPY.format( + summary_prompt = SUMMARY_PROMPT.format( requirements_constraints=self.requirements_constraints, ) outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) From 14642407d15eee8e1522f5496856124038cf0f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 20 Aug 2024 20:55:33 +0800 Subject: [PATCH 20/47] fix format issues --- metagpt/roles/di/role_zero.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 4e61ceab3..1153cb22d 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -69,8 +69,6 @@ class RoleZero(Role): react_mode: Literal["react"] = "react" max_react_loop: int = 20 # used for react mode - # Summary Mode - use_summary: bool = True # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None @@ -89,6 +87,7 @@ class RoleZero(Role): memory_k: int = 20 # number of memories (messages) to use as historical context use_fixed_sop: bool = False requirements_constraints: str = "" # the constraints in user requirements + use_summary: bool = True # whether to summarize at the end @model_validator(mode="after") def set_plan_and_tool(self) -> "RoleZero": From 45ce4ad77d2a9b43831806db05a06c1d63d0c85f Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Tue, 20 Aug 2024 21:06:58 +0800 Subject: [PATCH 21/47] rm redundant --- metagpt/actions/di/detect_intent.py | 141 -------------- metagpt/roles/di/mgx.py | 68 ------- .../metagpt/actions/di/test_detect_intent.py | 56 ------ tests/metagpt/roles/di/test_mgx.py | 179 ------------------ 4 files changed, 444 deletions(-) delete mode 100644 metagpt/actions/di/detect_intent.py delete mode 100644 metagpt/roles/di/mgx.py delete mode 100644 tests/metagpt/actions/di/test_detect_intent.py delete mode 100644 tests/metagpt/roles/di/test_mgx.py diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py deleted file mode 100644 index 7d0c27b4a..000000000 --- a/metagpt/actions/di/detect_intent.py +++ /dev/null @@ -1,141 +0,0 @@ -from __future__ import annotations - -import asyncio -import re -from enum import Enum -from typing import Tuple - -from pydantic import BaseModel - -from metagpt.actions import Action -from metagpt.schema import Message - - -class SOPItemDef(BaseModel): - """ - Represents an item in a Standard Operating Procedure (SOP). - - Attributes: - name (str): name of the SOP item. - description (str): The description or title of the SOP. - sop (List[str]): The steps or details of the SOP. - """ - - name: str - description: str - sop: list[str] = [] - - -class SOPItem(Enum): - SOFTWARE_DEVELOPMENT = SOPItemDef( - name="software development", - description="Software development intention including developing or building software, games, app, websites, etc. EXCLUDING bug fixes, report any issues, environment setup, terminal operations, and pip install.", - sop=[ - "Writes a PRD based on software requirements.", - "Writes a system design to the project repository, based on the PRD of the project. Write high-level system design instead of the actual code.", - "Writes a project plan to the project repository, based on the design of the project.", - "Writes code to implement designed features according to the project plan and adds them to the project repository.", - "Stage and commit changes for the project repository using Git.", - ], - ) - # FIX_BUGS = SOPItemDef( - # name="fix bugs", - # description="Fix bugs in a given project.", - # sop=[ - # "Fix bugs in the project repository.", - # "Stage and commit changes for the project repository using Git.", - # ], - # ) - # FORMAT_REPO = SOPItemDef( - # name="format repo", - # description="download repository from git and format the project to MetaGPT project", - # sop=[ - # "Imports a project from a Git website and formats it to MetaGPT project format to enable incremental appending requirements.", - # "Stage and commit changes for the project repository using Git.", - # ], - # ) - WEB_OPERATION = SOPItemDef( - name="web operation", - description="web browsing, scraping, imitation and other interaction with the web", - ) - OTHER = SOPItemDef( - name="other", - description="Other intentions that do not fall into the above categories, including data science, data analysis, machine learning, deep learning and text-to-image etc.", - ) - - @property - def type_name(self): - return self.value.name - - @classmethod - def get_type(cls, type_name): - for member in cls: - if member.type_name == type_name: - return member.value - return None - - -DETECT_PROMPT = """ -# User Requirement -{user_requirement} -# Intentions -{intentions} -# Task -Classify user requirement into one type of the above intentions, output the index of the intention directly. -Intention index: -""" - -REQ_WITH_SOP = """ -{user_requirement} -## Knowledge -To meet user requirements, the following standard operating procedure(SOP) must be used: - -{sop} -""" - - -class DetectIntent(Action): - async def run(self, with_message: Message, **kwargs) -> Tuple[str, str]: - user_requirement = with_message.content - mappings = {i + 1: si for i, si in enumerate(SOPItem)} - intentions = "\n".join([f"{i + 1}. {si.type_name}: {si.value.description}" for i, si in enumerate(SOPItem)]) - prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions) - - rsp = await self._aask(prompt) - match = re.search(r"\d+", rsp) - index = len(SOPItem) + 1 # 1-based - if match: - index = int(match.group()) # 1-based - sop = mappings[index].value.sop if index in mappings else None - sop_type = mappings[index].type_name if index in mappings else SOPItem.OTHER.type_name - - req_with_sop = ( - REQ_WITH_SOP.format( - user_requirement=user_requirement, sop="\n".join([f"{i + 1}. {v}" for i, v in enumerate(sop)]) - ) - if sop - else user_requirement - ) - - return req_with_sop, sop_type - - -async def main(): - # Example usage of the DetectIntent action - user_requirements = [ - "Develop a 2048 game.", - "Run data analysis on sklearn wine dataset", - "帮我把pip的源设置成:https://pypi.tuna.tsinghua.edu.cn/simple", - "This is a website url does not require login: https://demosc.chinaz.net/Files/DownLoad//moban/202404/moban7767 please write a similar web page,developed in vue language, The package.json dependency must be generated", - "I would like to imitate the website available at https://demosc.chinaz.net/Files/DownLoad//moban/202404/moban7767. Could you please browse through it?", - ] - detect_intent = DetectIntent() - - for user_requirement in user_requirements: - req_with_sop, sop_type = await detect_intent.run(Message(role="user", content=user_requirement)) - print(req_with_sop) - print(f"Detected SOP Type: {sop_type}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py deleted file mode 100644 index 8e9a0dfae..000000000 --- a/metagpt/roles/di/mgx.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -# @Author : stellahong (stellahong@fuzhi.ai) -# @Desc : -import asyncio -from typing import Dict - -from metagpt.actions.di.detect_intent import DetectIntent, SOPItem -from metagpt.logs import logger -from metagpt.roles.di.data_interpreter import DataInterpreter -from metagpt.schema import Message -from metagpt.tools.tool_recommend import BM25ToolRecommender - - -class MGX(DataInterpreter): - use_intent: bool = True - intents: Dict = {} - - async def _detect_intent(self, user_msg: Message) -> str: - todo = DetectIntent(context=self.context) - request_with_sop, sop_type = await todo.run(user_msg) - logger.info(f"{sop_type} {request_with_sop}") - if sop_type == SOPItem.SOFTWARE_DEVELOPMENT.type_name: - self.tool_recommender = BM25ToolRecommender(tools=["software development"]) - else: - self.tool_recommender = BM25ToolRecommender(tools=[""]) - return request_with_sop - - 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 # retrieve latest user requirement - if self.use_intent: # add mode - user_message = Message(content=goal, role="user") - goal = await self._detect_intent(user_message) - - logger.info(f"Goal is {goal}") - - 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 - - -if __name__ == "__main__": - test_requirements = [ - "design a game using Gym (an open source Python library), including a graphical interface and interactive gameplay" - # "Run data analysis on sklearn Wine recognition dataset, include a plot, and train a model to predict wine class (20% as validation), and show validation accuracy" - # "获取https://www.stats.gov.cn/sj/sjjd/202307/t20230718_1941322.html的内容,并返回上半年cpi的增长或下降幅度" - ] - - for requirement in test_requirements: - mgx = MGX() - rsp = asyncio.run(mgx.run(requirement)) diff --git a/tests/metagpt/actions/di/test_detect_intent.py b/tests/metagpt/actions/di/test_detect_intent.py deleted file mode 100644 index e555680ef..000000000 --- a/tests/metagpt/actions/di/test_detect_intent.py +++ /dev/null @@ -1,56 +0,0 @@ -import pytest - -from metagpt.actions.di.detect_intent import DetectIntent -from metagpt.schema import Message - -SOFTWARE_DEV_REQ1 = """ -I'd like to create a personalized website that features the 'Game of Life' simulation. -""" - -SOFTWARE_DEV_REQ2 = """ -Create a website widget for TODO list management. -""" - -SOFTWARE_DEV_REQ3 = """ -Create an official website with a top bar, banner, About Us section, and footer. -""" - -DI_REQ1 = """ -can you finetune a 78 Llama model using https://github.com/huggingface/peft should be instructions in the Readme. -""" - -DI_REQ2 = """ -I came across a blog post on the website Mafengwo (https://www.mafengwo.cn/i/17171539.html) that discusses the possibility of generating images with hidden text. The post refers to a script that can be used for this purpose. Could you help me set up this script and use it to generate some images? I would like the images to have the hidden text 'MAX' and also some with 'MetaGPT' as the hidden text. -""" - -DI_REQ3 = """ -Extract all of the blog posts from `https://stripe.com/blog/page/1` and return a CSV with the columns `date`, `article_text`, `author` and `summary`. Generate a summary for each article yourself. -""" - -FIX_BUG_REQ = """ -Fix this error from the 2048 game repo: TypeError: __init__() takes 1 positional argument but 2 were given" -""" - -FORMAT_REPO_REQ = """ -git clone 'https://github.com/spec-first/connexion' and format to MetaGPT project -""" - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - "requirement, expected_intent_type", - [ - (SOFTWARE_DEV_REQ1, "software development"), - (SOFTWARE_DEV_REQ2, "software development"), - (SOFTWARE_DEV_REQ3, "software development"), - (DI_REQ1, "other"), - (DI_REQ2, "other"), - (DI_REQ3, "other"), - (FIX_BUG_REQ, "fix bugs"), - (FORMAT_REPO_REQ, "format repo"), - ], -) -async def test_detect_intent(requirement, expected_intent_type): - di = DetectIntent() - _, intent_type = await di.run(Message(role="user", content=requirement)) - assert intent_type == expected_intent_type diff --git a/tests/metagpt/roles/di/test_mgx.py b/tests/metagpt/roles/di/test_mgx.py deleted file mode 100644 index 534e32499..000000000 --- a/tests/metagpt/roles/di/test_mgx.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from typing import List - -import pytest - -from metagpt.roles.di.mgx import MGX -from metagpt.schema import Message - -DEMO_CONTENT = [ - { - "role": "user", - "content": "Can you build TextToSummarize which is a SMS number that I can text and it will scrape a website " - "and summarize it with ChatGPT", - }, - { - "role": "assistant", - "content": "Absolutely, I can build a service like TextToSummarize for you. The process will involve setting up" - " an SMS service that can receive your texts, scraping the website content you send, and then using" - " an AI to summarize the content.\nTo get started, I'll need to set up a number for receiving SMS" - ", then I'll work on the script to scrape the website content from the URLs you text, and finally." - " I'll integrate it with an AI service to Generalte the summaries.\n I'll keep you updated on my" - " progress!", - }, - {"role": "user", "content": "What exactly do we need the web app for?"}, - { - "role": "assistant", - "content": "The web app will service as the interface between the SMS service and the AI summarization service" - ". When you send a text with a URL to the SMS number, teh web app will process that, scrape the" - " content from the URL, and then use the AI to summarize it. The summary will then be sent back to" - " you. It's the central hub that ties all the components of the service togather.", - }, -] - -DEMO1_CONTENT = [ - { - "role": "user", - "content": "Extract all of the blog posts from `https://stripe.com/blog/page/1` and return a CSV with the" - " columns `date`, `article_text`, `author` and `summary`. Generate a summary for each article" - " yourself.", - } -] - -_DEMO2_CONTENT_ASSISTANT = """ -user: -## User Requirement -### User Requirement Detail -```text -2048 game -```` -### Knowledge -To meet user requirements, the following standard operating procedure(SOP) must be used. SOP descriptions cannot be modified; user requirements can only be appended to the end of corresponding steps. -- Writes a PRD based on software requirements. -- Writes a design to the project repository, based on the PRD of the project. -- Writes a project plan to the project repository, based on the design of the project. -- Writes code to implement designed features according to the project plan and adds them to the project repository. -- Stage and commit changes for the project repository using Git. -## Context - -## Current Plan -[ - { - "task_id": "1", - "dependent_task_ids": [], - "instruction": "Write a PRD based on the software requirements.", - "task_type": "other", - "code": "from metagpt.tools.libs.software_development import write_prd\n\nasync def create_2048_game_prd():\n idea = \"Create a 2048 game\"\n prd_path = await write_prd(idea)\n return prd_path\n\nawait create_2048_game_prd()\n", - "result": ",,,,,[,CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a, 2048 game\",\n \"Project Name\": \"game_2048\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n , \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \",As a player, I want to be able to choose difficulty levels\",\n \"As a player,, I want to see my score after each game\",\n \"As a player, I want, to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n , ],\n \"Competitive Analysis\": [\n \"2048 Game, A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my, best score shown, but many ads\"\n ],\n \"Competitive Quadrant, Chart\": \"quadrantChart\\n title, \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\",High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant,-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be, improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.,23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n , \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign, F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6,]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n, \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n, ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR,\": \"\"\n}\n[/CONTENT]\n,,,,,,,,,,,,{'output': '[{\"type_\": \"str\", \"name\": \"PRD File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/prd/20240401194850.json\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.mmd\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.pdf\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.png\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.svg\"}]', 'tool_name': 'write_prd'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/prd')", - "is_success": true, - "is_finished": true - }, - { - "task_id": "2", - "dependent_task_ids": [ - "1" - ], - "instruction": "Write a design to the project repository, based on the PRD of the project.", - "task_type": "other", - "code": "from metagpt.tools.libs.software_development import write_design\n\nasync def create_2048_game_design(prd_path):\n system_design_path = await write_design(prd_path)\n return system_design_path\n\nawait create_2048_game_design('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/prd/20240401194850.json')\n", - "result": ",[CONTENT]\n{\n \"Implementation approach\":, \"We will use the Py,game library to create the game interface and handle user input. The game logic will be implemented using Python classes and data structures to manage the game, board and tiles.\",\n \"File list\": [\n \"main.py,\",\n \"game.py\"\n, ],\n \"Data structures and interfaces\": \"\\nclassDiagram\\n class Game2048 {\\n -int[][] board,\\n -int score\\n -bool game_over\\n +__init__()\\n +reset()\\n +,move(direction: str)\\n +get_board() int[][]\\n +get_score() int\\n +is_game_over() bool\\n }\\n , class UI {\\n +display_board(board: int[][], score: int)\\n, +show_game_over()\\n }\\n Game,2048 --> UI\\n\",\n \"Program call flow\": \"\\nsequenceDiagram,\\n participant M as Main\\n participant G as Game2048,\\n participant UI as UI\\n M->>G: reset,()\\n G-->>UI: display_board(board, score)\\,n M->>G: move(direction)\\n G-->>,G: update board and score\\n G-->>UI:, display_board(board, score)\\n G->>G: is_game_over,()\\n G-->>UI: show_game_over()\\n\",\n,,,,, \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n,,,,,,,,,,,,,,,{'output': '[{\"type_\": \"str\", \"name\": \"Intermedia Design File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/system_design/20240401194850.json\"}, {\"type_\": \"str\", \"name\": \"Design File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/system_design/20240401194850.md\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.mmd\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.pdf\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.png\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.svg\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.mmd\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.pdf\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.png\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.svg\"}]', 'tool_name': 'write_design'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/system_design')", - "is_success": true, - "is_finished": true - }, - { - "task_id": "3", - "dependent_task_ids": [ - "2" - ], - "instruction": "Write a project plan to the project repository, based on the design of the project.", - "task_type": "other", - "code": "from metagpt.tools.libs.software_development import write_project_plan\n\nasync def create_2048_game_project_plan(system_design_path):\n task_path = await write_project_plan(system_design_path)\n return task_path\n\nawait create_2048_game_project_plan('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/system_design/20240401194850.json')\n", - "result": ",\n[CONTENT,]\n{\n \"Required Python packages\": [\n \"pygame==2.0.1\"\n ],\n \"Required, Other language third-party packages\": [\n \"No third-party dependencies required,\"\n ],\n \"Logic Analysis\": [\n , [\n \"game.py\",\n \"Contains Game2048, class and functions to manage the game board and tiles\"\n, ],\n [\n \"main.py\",\n \",Contains the main function, and imports Game2048, from game.py\"\n ]\n ],\n \"Task list,\": [\n \"game.py\",\n \"main.py\"\n, ],\n \"Full API spec\":, \"\",\n \"Shared Knowledge\": \"The Pygame library will be used, to create the game interface and handle user input. The game, logic will be implemented using Python classes and data structures to manage the game, board and tiles.\",\n \"Anything UNCLEAR\": \"No unclear aspects,,,,, mentioned.\"\n}\n[/CONTENT]\n{'output': '[{\"type_\": \"str\", \"name\": \"Project Plan\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/task/20240401194850.json\"}]', 'tool_name': 'write_project_plan'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/task')", - "is_success": true, - "is_finished": true - }, - { - "task_id": "4", - "dependent_task_ids": [ - "3" - ], - "instruction": "Write code to implement designed features according to the project plan and add them to the project repository.", - "task_type": "other", - "code": "from metagpt.tools.libs.software_development import write_codes\n\nasync def implement_2048_game_features(task_path):\n src_path = await write_codes(task_path)\n return src_path\n\nawait implement_2048_game_features('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/task/20240401194850.json')\n", - "result": ",```python\n,## game.py\n\nclass Game2048,:\n def __init__(self):\n, self.board = [[0 for _, in range(4)] for _ in, range(4)]\n self.score =, 0\n self.game_over =, False\n\n def reset(self):\n , self.board = [[0 for _ in, range(4)] for _, in range(4)]\n self.score = 0\n self,.game_over = False\n\n , def move(self, direction: str):\n, # Implement the logic to move the, tiles in the specified direction\n, pass\n\n def get_board(self) ->, list:\n return self.board\n\n , def get_score(self) -> int:\n, return self.score\n\n def is,_game_over(self) -> bool:\n ,,,,, return self.game_over\n```\n,```,python\n## main.py\n\nimport pygame\nfrom game import Game2048\n\ndef main():\n # Initialize the game\n game = Game2048()\n game.reset()\n\n # Initialize Pygame\n, pygame.init()\n screen = pygame.display.set_mode((400, 400))\n pygame.display.set_caption('2048 Game,')\n\n running = True\n while running:\n for event in pygame.event.get():\n if, event.type == pygame.QUIT:\n running = False\n elif event.type == pygame.KEYDOWN:\n if event.key ==, pygame.K_UP:\n game.move('up')\n elif event.key == pygame.K_DOWN:\n , game.move('down')\n elif event.key == pygame.K_LEFT:\n game.move('left')\n elif, event.key == pygame.K_RIGHT,:\n game.move('right')\n\n # Display the game board and score\n board = game.get_board()\n score = game.get_score()\n display_board(screen, board, score)\n\n # Check, if the game is over\n if game.is_game_over():\n show_game_over(screen)\n\n pygame.display.update()\n\n pygame.quit()\n\n,def display_board(screen, board, score):\n # Write,,,, code to display the game board and score on the Pygame screen\n pass\n\ndef show_game_over(screen):\n # Write code to display the game over message on the Pygame screen\n pass\n\nif __name__ == \"__main__\":\n main()\n```\n,,{'output': '[{\"type_\": \"str\", \"name\": \"Source File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048/__init__.py\"}, {\"type_\": \"str\", \"name\": \"Source File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048/game.py\"}, {\"type_\": \"str\", \"name\": \"Source File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048/main.py\"}]', 'tool_name': 'write_codes'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048')", - "is_success": true, - "is_finished": true - }, - { - "task_id": "5", - "dependent_task_ids": [ - "4" - ], - "instruction": "Stage and commit changes for the project repository using Git.", - "task_type": "other", - "code": "from metagpt.tools.libs.software_development import git_archive\n\nproject_path = '/Users/iorishinier/github/MetaGPT/workspace/game_2048'\ngit_log = await git_archive(project_path)\nprint(git_log)\n", - "result": ",{'output': '[{\"type_\": \"str\", \"name\": \"Git Commit\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048\"}]', 'tool_name': 'git_archive'}commit 41c731341d0da8b630709bca50d4841dcea98d43\nAuthor: 莘权 马 \nDate: Mon Apr 1 19:50:53 2024 +0800\n\n Archive\n\ncommit a61daa93f24e74bf934ea5a3ba217038f167ab1f\nAuthor: 莘权 马 \nDate: Mon Apr 1 19:48:40 2024 +0800\n\n Add .gitignore\n", - "is_success": true, - "is_finished": true - } -] -## Current Task -{} - -""" - -DEMO2_CONTENT = [ - {"role": "user", "content": 'Create a "2048 game"'}, - {"role": "assistant", "content": _DEMO2_CONTENT_ASSISTANT}, - {"role": "user", "content": "TypeError: __init__() takes 1 positional argument but 2 were given"}, -] - -DEMO3_CONTENT = [ - {"role": "user", "content": "git clone 'https://github.com/spec-first/connexion' and format to MetaGPT project"} -] - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - "user_messages", - [ - [Message.model_validate(i) for i in DEMO2_CONTENT if i["role"] == "user"], - [Message.model_validate(i) for i in DEMO_CONTENT if i["role"] == "user"], - [Message.model_validate(i) for i in DEMO1_CONTENT if i["role"] == "user"], - ], -) -@pytest.mark.skip -async def test_mgx(user_messages: List[Message], context): - mgx = MGX(context=context, tools=[""]) - - for i, msg in enumerate(user_messages): - await mgx.run(msg) - data = mgx.model_dump_json() - await context.repo.test_outputs.save(filename=f"{i}.json", content=data) - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - ("user_message", "history_messages"), - [(Message.model_validate(DEMO2_CONTENT[2]), [Message.model_validate(i) for i in DEMO2_CONTENT[0:2]])], -) -@pytest.mark.skip -async def test_mgx_fixbug(user_message: Message, history_messages: List[Message], context): - mgx = MGX(context=context, tools=[""]) - mgx.rc.memory.add_batch(history_messages) - await mgx.run(user_message) - data = mgx.model_dump_json() - await context.repo.test_outputs.save(filename="test_mgx_fixbug.json", content=data) - - -@pytest.mark.asyncio -@pytest.mark.parametrize("user_message", [Message.model_validate(i) for i in DEMO3_CONTENT if i["role"] == "user"]) -@pytest.mark.skip -async def test_git_import(user_message, context): - mgx = MGX(context=context, tools=[""]) - await mgx.run(user_message) - - -if __name__ == "__main__": - pytest.main([__file__, "-s"]) From 96c6a27e4459aa0a03a0630a04abdb94bf79c987 Mon Sep 17 00:00:00 2001 From: zhanglei Date: Tue, 20 Aug 2024 21:54:14 +0800 Subject: [PATCH 22/47] =?UTF-8?q?update:=20CR=E5=B7=A5=E5=85=B7=E5=90=8D?= =?UTF-8?q?=E6=94=B9=E4=BA=86=EF=BC=8C=E8=BF=99=E9=87=8C=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=AF=8D=E5=AF=B9=E5=BA=94=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metagpt/prompts/di/engineer2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 57387ec98..099e46177 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -10,7 +10,7 @@ EXTRA_INSTRUCTION = """ 10. When provided system 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. 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code, ensuring that no line numbers are included in the final write. -13. When a system design or project schedule is provided, at the end of the plan, add a CodeRview Task for each file; for example, if there are three files, add three CodeRview Tasks. For each CodeRview Task, just call ValidateAndRewriteCode.run. +13. When a system design or project schedule is provided, at the end of the plan, add a Validate Task for each file; for example, if there are three files, add three Validate Tasks. For each Validate Task, just call ValidateAndRewriteCode.run. 14. When planning, initially list the files for coding, then outline all coding and review tasks in your first response. 15. Note 'Task for {file_name} completed.' — signifies the {file_name} coding task is done. 16. Avoid re-reviewing or re-coding the same code. When you decide to take a write or review action, include the command 'finish current task' in the same response. From fe4fa59e0f9abd43a9a3b30e28a685427e28b562 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 21 Aug 2024 11:01:55 +0800 Subject: [PATCH 23/47] add report for reply to human when end --- metagpt/roles/di/role_zero.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 260c937a5..947e46dbf 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -190,13 +190,13 @@ class RoleZero(Role): memory = self.parse_images(memory) req = self.llm.format_msg(memory + [UserMessage(content=prompt)]) + state_data = dict( + plan_status=plan_status, + current_task=current_task, + instruction=instruction, + ) async with ThoughtReporter(enable_llm_stream=True) as reporter: await reporter.async_report({"type": "react"}) - state_data = dict( - plan_status=plan_status, - current_task=current_task, - instruction=instruction, - ) self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=[system_prompt], state_data=state_data) self.command_rsp = await self._check_duplicates(req, self.command_rsp) @@ -303,13 +303,13 @@ class RoleZero(Role): self.llm.format_msg(memory), system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())], ) - # If the answer contains the substring '[Message] from A to B:', remove it. - pattern = r"\[Message\] from .+? to .+?:\s*" - answer = re.sub(pattern, "", answer, count=1) - if "command_name" in answer: - # an actual TASK intent misclassified as QUICK, correct it here, FIXME: a better way is to classify it correctly in the first place - answer = "" - intent_result = "TASK" + # If the answer contains the substring '[Message] from A to B:', remove it. + pattern = r"\[Message\] from .+? to .+?:\s*" + answer = re.sub(pattern, "", answer, count=1) + if "command_name" in answer: + # an actual TASK intent misclassified as QUICK, correct it here, FIXME: a better way is to classify it correctly in the first place + answer = "" + intent_result = "TASK" elif "SEARCH" in intent_result: query = "\n".join(str(msg) for msg in memory) answer = await SearchEnhancedQA().run(query) @@ -492,17 +492,21 @@ class RoleZero(Role): async def _end(self): self._set_state(-1) memory = self.rc.memory.get(self.memory_k) - # Ensure reply to the human before the "end" command is executed. + # Ensure reply to the human before the "end" command is executed. Hard code k=5 for checking. if not any(["reply_to_human" in memory.content for memory in self.get_memories(k=5)]): + logger.info("manually reply to human") reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( requirements_constraints=self.requirements_constraints, ) - reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) + async with ThoughtReporter(enable_llm_stream=True) as reporter: + await reporter.async_report({"type": "quick"}) + reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) await self.reply_to_human(content=reply_content) self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) outputs = "" # Summary of the Completed Task and Deliverables if self.use_summary: + logger.info("end current run and summarize") summary_prompt = SUMMARY_PROMPT.format( requirements_constraints=self.requirements_constraints, ) From 9bd1c6fa79093bd3be4e497b9982f55e879f5a3f Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 21 Aug 2024 11:21:31 +0800 Subject: [PATCH 24/47] minor update, summary prompt change --- metagpt/prompts/di/role_zero.py | 9 ++------- metagpt/prompts/di/team_leader.py | 2 +- metagpt/roles/di/data_analyst.py | 2 +- metagpt/roles/di/role_zero.py | 5 +---- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index ce8c61dd5..54f325be1 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -227,13 +227,8 @@ You have just finished all tasks. Reply to the human requirements. Do not output any other format. Your reply is: - """ SUMMARY_PROMPT = """ -# Restrictions -{requirements_constraints} - -You have just completed some tasks. -Summarize the tasks you have accomplished without including detailed information. -If there are any deliverables, list their descriptions and provide their file paths. +Summarize what you have accomplished lately. Be concise. +If you produce any deliverables, include their short descriptions and file paths. If there are any metrics or quantitative results, include them, too. """ diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 8d4f55ae5..3ba9a8b0d 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -16,7 +16,7 @@ Note: 1. If the requirement is a pure DATA-RELATED requirement, such as web browsing, web scraping, web searching, web imitation, data science, data analysis, machine learning, deep learning, text-to-image etc. DON'T decompose it, assign a single task with the original user requirement as instruction directly to Data Analyst. 2. If the requirement is developing a software, game, app, or website, excluding the above data-related tasks, you should decompose the requirement into multiple tasks and assign them to different team members based on their expertise. The software default development process has four steps: creating a Product Requirement Document (PRD) by the Product Manager -> writing a System Design by the Architect -> creating tasks by the Project Manager -> and coding by the Engineer. You may choose to execute any of these steps. When publishing message to Product Manager, you should directly copy the full original user requirement. 2.1. If the requirement contains both DATA-RELATED part mentioned in 1 and software development part mentioned in 2, you should decompose the software development part and assign them to different team members based on their expertise, and assign the DATA-RELATED part to Data Analyst David directly. -3.1 If the task involves code review or code checking, you should assign it to Engineer. +3.1 If the task involves code review (CR) or code checking, you should assign it to Engineer. 3.2. If the requirement is to fix a bug or issue, you should assign it to Issue Solver. However, if the code is written by Engineer, Engineer must maintain the code. 4. If the requirement is a common-sense, logical, or math problem, you should respond directly without assigning any task to team members. 5. If you think the requirement is not clear or ambiguous, you should ask the user for clarification immediately. Assign tasks only after all info is clear. diff --git a/metagpt/roles/di/data_analyst.py b/metagpt/roles/di/data_analyst.py index d7a42a88f..329b3c45d 100644 --- a/metagpt/roles/di/data_analyst.py +++ b/metagpt/roles/di/data_analyst.py @@ -26,7 +26,7 @@ from metagpt.tools.tool_registry import register_tool class DataAnalyst(RoleZero): name: str = "David" profile: str = "DataAnalyst" - goal: str = "Take on any data-related tasks, such as data analysis, machine learning, deep learning, web browsing, web scraping, web searching, web deployment, terminal operation, document QA & analysis, etc." + goal: str = "Take on any data-related tasks, such as data analysis, machine learning, deep learning, web browsing, web scraping, web searching, terminal operation, document QA & analysis, etc." instruction: str = ROLE_INSTRUCTION + EXTRA_INSTRUCTION task_type_desc: str = TASK_TYPE_DESC diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 947e46dbf..4edb2e37d 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -507,8 +507,5 @@ class RoleZero(Role): # Summary of the Completed Task and Deliverables if self.use_summary: logger.info("end current run and summarize") - summary_prompt = SUMMARY_PROMPT.format( - requirements_constraints=self.requirements_constraints, - ) - outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) + outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(SUMMARY_PROMPT)])) return outputs From 9cc70f665127b04ac9f442b327d3cbd0e3888faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 21 Aug 2024 15:15:27 +0800 Subject: [PATCH 25/47] update rolezero reply to human prompt --- metagpt/prompts/di/role_zero.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index ce8c61dd5..8946de29a 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -220,20 +220,24 @@ However, you MUST respond to the user message by yourself directly, DON'T ask yo """ REPORT_TO_HUMAN_PROMPT = """ -# Restrictions -{requirements_constraints} +## Examlpe +example 1: +User requirement:开发贪吃蛇游戏 +reply: 贪吃蛇游戏的开发已经完成。所有文件(index.html、style.css和script.js)已经创建并经过审查。 -You have just finished all tasks. -Reply to the human requirements. -Do not output any other format. -Your reply is: +example 2: +User requirement: Crawl and extract all the herb names from the website, Tell me the number of herbs. +reply : The herb names have been successfully extracted and saved to the file /home/mgx/mgx/MetaGPT/src/herb_name.txt. A total of 8 herb names were extracted. +------------ + +Carefully review the history and respond to the user in the expected language to meet their requirements. +If you have any deliverables that are helpful in explaining the results (such as files, metrics, quantitative results, etc.), provide brief descriptions of them. +Your reply must be concise. +Directly output your reply content. Do not add any output format. """ SUMMARY_PROMPT = """ -# Restrictions -{requirements_constraints} - -You have just completed some tasks. -Summarize the tasks you have accomplished without including detailed information. -If there are any deliverables, list their descriptions and provide their file paths. +Summarize what you have accomplished lately. Be concise. +If you produce any deliverables, include their short descriptions and file paths. If there are any metrics or quantitative results, include them, too. +If the deliverable is code, only output the file path. """ From e3c56715ed94ea8af690405e950320cbca06a269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 21 Aug 2024 15:17:23 +0800 Subject: [PATCH 26/47] update example --- metagpt/prompts/di/role_zero.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 8946de29a..b4d8e4fcc 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -227,7 +227,7 @@ reply: 贪吃蛇游戏的开发已经完成。所有文件(index.html、style. example 2: User requirement: Crawl and extract all the herb names from the website, Tell me the number of herbs. -reply : The herb names have been successfully extracted and saved to the file /home/mgx/mgx/MetaGPT/src/herb_name.txt. A total of 8 herb names were extracted. +reply : The herb names have been successfully extracted. A total of 8 herb names were extracted. ------------ From d9ab1ae85cecab65801c91617ebecea6d6893fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Wed, 21 Aug 2024 19:32:20 +0800 Subject: [PATCH 27/47] update prompt --- metagpt/prompts/di/role_zero.py | 5 +++-- metagpt/roles/di/role_zero.py | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index b4d8e4fcc..3356ab1c0 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -222,8 +222,8 @@ However, you MUST respond to the user message by yourself directly, DON'T ask yo REPORT_TO_HUMAN_PROMPT = """ ## Examlpe example 1: -User requirement:开发贪吃蛇游戏 -reply: 贪吃蛇游戏的开发已经完成。所有文件(index.html、style.css和script.js)已经创建并经过审查。 +User requirement: create a 2048 game +reply: The development of the 2048 game has been completed. All files (index.html, style.css, and script.js) have been created and reviewed. example 2: User requirement: Crawl and extract all the herb names from the website, Tell me the number of herbs. @@ -234,6 +234,7 @@ reply : The herb names have been successfully extracted. A total of 8 herb names Carefully review the history and respond to the user in the expected language to meet their requirements. If you have any deliverables that are helpful in explaining the results (such as files, metrics, quantitative results, etc.), provide brief descriptions of them. Your reply must be concise. +{lanaguge_restruction} Directly output your reply content. Do not add any output format. """ SUMMARY_PROMPT = """ diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 260c937a5..37392673a 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -494,17 +494,15 @@ class RoleZero(Role): memory = self.rc.memory.get(self.memory_k) # Ensure reply to the human before the "end" command is executed. if not any(["reply_to_human" in memory.content for memory in self.get_memories(k=5)]): - reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( - requirements_constraints=self.requirements_constraints, - ) + pattern = r"\[Language Restrictions\](.*?)\n" + match = re.search(pattern, self.requirements_constraints, re.DOTALL) + reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format(lanaguge_restruction=match.group(0) if match else "") reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) await self.reply_to_human(content=reply_content) self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) outputs = "" # Summary of the Completed Task and Deliverables if self.use_summary: - summary_prompt = SUMMARY_PROMPT.format( - requirements_constraints=self.requirements_constraints, - ) + summary_prompt = SUMMARY_PROMPT.format() outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) return outputs From b2e8be2d785d5b7b8c9146a7d5888c92bb4e3cd9 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Thu, 22 Aug 2024 12:05:12 +0800 Subject: [PATCH 28/47] fix base_llm.py obfuscate error --- metagpt/provider/base_llm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metagpt/provider/base_llm.py b/metagpt/provider/base_llm.py index 813e77d95..75d8bfe00 100644 --- a/metagpt/provider/base_llm.py +++ b/metagpt/provider/base_llm.py @@ -300,7 +300,6 @@ class BaseLLM(ABC): if compress_type == CompressType.NO_COMPRESS: return messages - current_token_count = 0 max_token = TOKEN_MAX.get(self.config.model, max_token) keep_token = int(max_token * threshold) compressed = [] @@ -318,7 +317,7 @@ class BaseLLM(ABC): # system_msgs = [msg for msg in messages if msg["role"] == system_msg_val] # user_assistant_msgs = [msg for msg in messages if msg["role"] != system_msg_val] compressed.extend(system_msgs) - current_token_count += self.count_tokens(system_msgs) + current_token_count = self.count_tokens(system_msgs) if compress_type in [CompressType.POST_CUT_BY_TOKEN, CompressType.POST_CUT_BY_MSG]: # Under keep_token constraint, keep as many latest messages as possible From fef8bfd9bc48a4cab789ad8cb16a87c9d34d176b Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Thu, 22 Aug 2024 20:51:29 +0800 Subject: [PATCH 29/47] fix env serialized error --- metagpt/base/base_role.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/metagpt/base/base_role.py b/metagpt/base/base_role.py index 49dbcd617..2f6c9f963 100644 --- a/metagpt/base/base_role.py +++ b/metagpt/base/base_role.py @@ -8,7 +8,10 @@ class BaseRole: """Abstract base class for all roles.""" name: str - is_idle: bool + + @property + def is_idle(self) -> bool: + raise NotImplementedError @abstractmethod def think(self): From 9d327081baca2203f70c92fea9c3cef1742573ed Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Fri, 23 Aug 2024 10:14:12 +0800 Subject: [PATCH 30/47] add collection_name in exp_pool config --- config/config2.example.yaml | 2 ++ metagpt/configs/exp_pool_config.py | 1 + metagpt/exp_pool/manager.py | 9 ++------- metagpt/exp_pool/schema.py | 1 - metagpt/schema.py | 4 ++-- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/config/config2.example.yaml b/config/config2.example.yaml index a7214f662..2a0ebcc47 100644 --- a/config/config2.example.yaml +++ b/config/config2.example.yaml @@ -81,6 +81,8 @@ exp_pool: persist_path: .chroma_exp_data # The directory. retrieval_type: bm25 # Default is `bm25`, can be set to `chroma` for vector storage, which requires setting up embedding. use_llm_ranker: true # Default is `true`, it will use LLM Reranker to get better result. + collection_name: experience_pool # When `retrieval_type` is `chroma`, `collection_name` is the collection name in chromadb. + azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY" azure_tts_region: "eastus" diff --git a/metagpt/configs/exp_pool_config.py b/metagpt/configs/exp_pool_config.py index 8d33b25aa..a4a2d5d41 100644 --- a/metagpt/configs/exp_pool_config.py +++ b/metagpt/configs/exp_pool_config.py @@ -22,3 +22,4 @@ class ExperiencePoolConfig(YamlModel): default=ExperiencePoolRetrievalType.BM25, description="The retrieval type for experience pool." ) use_llm_ranker: bool = Field(default=True, description="Use LLM Reranker to get better result.") + collection_name: str = Field(default="experience_pool", description="The collection name in chromadb") diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index e38906d90..2d50b052f 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -7,12 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field from metagpt.config2 import Config from metagpt.configs.exp_pool_config import ExperiencePoolRetrievalType -from metagpt.exp_pool.schema import ( - DEFAULT_COLLECTION_NAME, - DEFAULT_SIMILARITY_TOP_K, - Experience, - QueryType, -) +from metagpt.exp_pool.schema import DEFAULT_SIMILARITY_TOP_K, Experience, QueryType from metagpt.logs import logger from metagpt.utils.exceptions import handle_exception @@ -166,7 +161,7 @@ class ExperienceManager(BaseModel): retriever_configs = [ ChromaRetrieverConfig( persist_path=self.config.exp_pool.persist_path, - collection_name=DEFAULT_COLLECTION_NAME, + collection_name=self.config.exp_pool.collection_name, similarity_top_k=DEFAULT_SIMILARITY_TOP_K, ) ] diff --git a/metagpt/exp_pool/schema.py b/metagpt/exp_pool/schema.py index b119e5850..a45910f0d 100644 --- a/metagpt/exp_pool/schema.py +++ b/metagpt/exp_pool/schema.py @@ -7,7 +7,6 @@ from pydantic import BaseModel, Field MAX_SCORE = 10 -DEFAULT_COLLECTION_NAME = "experience_pool" DEFAULT_SIMILARITY_TOP_K = 2 diff --git a/metagpt/schema.py b/metagpt/schema.py index 8ef7dd0bb..5f9a5667f 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -148,7 +148,7 @@ class SerializationMixin(BaseModel, extra="forbid"): serialized_data = self.model_dump() write_json_file(file_path, serialized_data) - logger.info(f"{self.__class__.__qualname__} serialization successful. File saved at: {file_path}") + logger.debug(f"{self.__class__.__qualname__} serialization successful. File saved at: {file_path}") return file_path @@ -171,7 +171,7 @@ class SerializationMixin(BaseModel, extra="forbid"): data: dict = read_json_file(file_path) model = cls(**data) - logger.info(f"{cls.__qualname__} deserialization successful. Instance created from file: {file_path}") + logger.debug(f"{cls.__qualname__} deserialization successful. Instance created from file: {file_path}") return model From b68c139125213b4fa22f5e119f1c6bd1c8476eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 26 Aug 2024 14:07:02 +0800 Subject: [PATCH 31/47] feat: +omniparse timeout --- metagpt/configs/omniparse_config.py | 1 + metagpt/tools/libs/editor.py | 20 +++++++++++--------- metagpt/utils/omniparse_client.py | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/metagpt/configs/omniparse_config.py b/metagpt/configs/omniparse_config.py index bf1516fc8..ecae78697 100644 --- a/metagpt/configs/omniparse_config.py +++ b/metagpt/configs/omniparse_config.py @@ -3,3 +3,4 @@ from metagpt.utils.yaml_model import YamlModel class OmniParseConfig(YamlModel): url: str = "" + timeout: int = 600 diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 240c28767..76f009fa6 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -3,7 +3,7 @@ import os import shutil import subprocess from pathlib import Path -from typing import List, Optional, Union +from typing import List, Optional, Tuple, Union from pydantic import BaseModel, ConfigDict @@ -251,15 +251,17 @@ class Editor(BaseModel): from metagpt.tools.libs import get_env_default from metagpt.utils.omniparse_client import OmniParseClient - base_url = await get_env_default(key="base_url", app_name="OmniParse", default_value="") - if not base_url: - base_url = await Editor._read_omniparse_config() + base_url1 = await get_env_default(key="base_url", app_name="OmniParse", default_value="") + timeout1 = await get_env_default(key="timeout", app_name="OmniParse", default_value="") + base_url2, timeout2 = await Editor._read_omniparse_config() + + base_url = base_url1 or base_url2 if not base_url: return None api_key = await get_env_default(key="api_key", app_name="OmniParse", default_value="") - v = await get_env_default(key="timeout", app_name="OmniParse", default_value="120") + timeout = timeout1 or timeout2 or 120 try: - timeout = int(v) or 120 + timeout = int(timeout) except ValueError: timeout = 120 @@ -287,8 +289,8 @@ class Editor(BaseModel): return result @staticmethod - async def _read_omniparse_config() -> str: + async def _read_omniparse_config() -> Tuple[str, int]: config = Config.default() if config.omniparse and config.omniparse.url: - return config.omniparse.url - return "" + return config.omniparse.url, config.omniparse.timeout + return "", 0 diff --git a/metagpt/utils/omniparse_client.py b/metagpt/utils/omniparse_client.py index cb2341944..361e84fd1 100644 --- a/metagpt/utils/omniparse_client.py +++ b/metagpt/utils/omniparse_client.py @@ -189,7 +189,7 @@ class OmniParseClient: # Do not verify if only byte data is provided return - file_ext = Path(verify_file_path).suffix + file_ext = Path(verify_file_path).suffix.lower() if file_ext not in allowed_file_extensions: raise ValueError(f"Not allowed {file_ext} File extension must be one of {allowed_file_extensions}") From 62140a6d560008f3e350785e239c08f0d6bd8a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 26 Aug 2024 16:42:26 +0800 Subject: [PATCH 32/47] feat: png -> svg --- metagpt/utils/mermaid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index 64996717e..008bb849d 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -38,7 +38,7 @@ async def mermaid_to_file( Returns: int: 0 if the conversion is successful, -1 if the conversion fails. """ - suffixes = suffixes or ["png"] + suffixes = suffixes or ["svg"] # Write the Mermaid code to a temporary file config = config if config else Config.default() dir_name = os.path.dirname(output_file_without_suffix) From 128552b9fbd6423919ea5594018ab1f25339e8dd Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Mon, 26 Aug 2024 17:41:56 +0800 Subject: [PATCH 33/47] svg reporter --- metagpt/actions/design_api.py | 2 +- metagpt/actions/write_prd.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index 86fa699bb..68a66d5a4 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -231,7 +231,7 @@ class WriteDesign(Action): async def _save_mermaid_file(self, data: str, pathname: Path): pathname.parent.mkdir(parents=True, exist_ok=True) await mermaid_to_file(self.config.mermaid.engine, data, pathname) - image_path = pathname.parent / f"{pathname.name}.png" + image_path = pathname.parent / f"{pathname.name}.svg" if image_path.exists(): await GalleryReporter().async_report(image_path, "path") diff --git a/metagpt/actions/write_prd.py b/metagpt/actions/write_prd.py index 4b2015145..7a04520d6 100644 --- a/metagpt/actions/write_prd.py +++ b/metagpt/actions/write_prd.py @@ -278,7 +278,7 @@ class WritePRD(Action): pathname = output_filename or self.repo.workdir / COMPETITIVE_ANALYSIS_FILE_REPO / Path(prd_doc.filename).stem pathname.parent.mkdir(parents=True, exist_ok=True) await mermaid_to_file(self.config.mermaid.engine, quadrant_chart, pathname) - image_path = pathname.parent / f"{pathname.name}.png" + image_path = pathname.parent / f"{pathname.name}.svg" if image_path.exists(): await GalleryReporter().async_report(image_path, "path") From d3199604a258bbdd888a272905ee005b82a41c05 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Mon, 26 Aug 2024 19:35:00 +0800 Subject: [PATCH 34/47] exp_pool add delete_all_exps --- metagpt/exp_pool/manager.py | 19 +++++++++++++++++-- metagpt/rag/engines/simple.py | 12 +++++++++++- metagpt/rag/retrievers/base.py | 16 +++++++++++++++- metagpt/rag/retrievers/bm25_retriever.py | 16 ++++++++++++++++ metagpt/rag/retrievers/chroma_retriever.py | 13 +++++++++++-- 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index 2d50b052f..f6b38f9e7 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -47,7 +47,7 @@ class ExperienceManager(BaseModel): exp (Experience): The experience to add. """ - if not self.config.exp_pool.enabled or not self.config.exp_pool.enable_write: + if not self._is_writable(): return self.storage.add_objs([exp]) @@ -66,7 +66,7 @@ class ExperienceManager(BaseModel): list[Experience]: A list of experiences that match the args. """ - if not self.config.exp_pool.enabled or not self.config.exp_pool.enable_read: + if not self._is_readable(): return [] nodes = await self.storage.aretrieve(req) @@ -86,6 +86,21 @@ class ExperienceManager(BaseModel): return self.storage.count() + @handle_exception + def delete_all_exps(self): + """Delete the all experiences.""" + + if not self._is_writable(): + return + + self.storage.clear(persist_dir=self.config.exp_pool.persist_path) + + def _is_readable(self) -> bool: + return self.config.exp_pool.enabled and self.config.exp_pool.enable_read + + def _is_writable(self) -> bool: + return self.config.exp_pool.enabled and self.config.exp_pool.enable_write + def _resolve_storage(self) -> "SimpleEngine": """Selects the appropriate storage creation method based on the configured retrieval type.""" diff --git a/metagpt/rag/engines/simple.py b/metagpt/rag/engines/simple.py index be4c3daf5..e48decdab 100644 --- a/metagpt/rag/engines/simple.py +++ b/metagpt/rag/engines/simple.py @@ -38,6 +38,7 @@ from metagpt.rag.factories import ( ) from metagpt.rag.interface import NoEmbedding, RAGObject from metagpt.rag.retrievers.base import ( + DeletableRAGRetriever, ModifiableRAGRetriever, PersistableRAGRetriever, QueryableRAGRetriever, @@ -218,7 +219,13 @@ class SimpleEngine(RetrieverQueryEngine): """Count.""" self._ensure_retriever_queryable() - return self._retriever.query_total_count() + return self.retriever.query_total_count() + + def clear(self, **kwargs): + """Clear.""" + self._ensure_retriever_deletable() + + return self.retriever.clear(**kwargs) @staticmethod def get_obj_nodes(objs: Optional[list[RAGObject]] = None) -> list[ObjectNode]: @@ -277,6 +284,9 @@ class SimpleEngine(RetrieverQueryEngine): def _ensure_retriever_queryable(self): self._ensure_retriever_of_type(QueryableRAGRetriever) + def _ensure_retriever_deletable(self): + self._ensure_retriever_of_type(DeletableRAGRetriever) + def _ensure_retriever_of_type(self, required_type: BaseRetriever): """Ensure that self.retriever is required_type, or at least one of its components, if it's a SimpleHybridRetriever. diff --git a/metagpt/rag/retrievers/base.py b/metagpt/rag/retrievers/base.py index 5bd04adca..69475d6ea 100644 --- a/metagpt/rag/retrievers/base.py +++ b/metagpt/rag/retrievers/base.py @@ -58,4 +58,18 @@ class QueryableRAGRetriever(RAGRetriever): @abstractmethod def query_total_count(self) -> int: - """To support querying total count, must implement this func""" + """To support querying total count, must implement this func.""" + + +class DeletableRAGRetriever(RAGRetriever): + """Support deleting all nodes.""" + + @classmethod + def __subclasshook__(cls, C): + if cls is DeletableRAGRetriever: + return check_methods(C, "clear") + return NotImplemented + + @abstractmethod + def clear(self, **kwargs) -> int: + """To support deleting all nodes, must implement this func.""" diff --git a/metagpt/rag/retrievers/bm25_retriever.py b/metagpt/rag/retrievers/bm25_retriever.py index ace1bb86c..74cba5124 100644 --- a/metagpt/rag/retrievers/bm25_retriever.py +++ b/metagpt/rag/retrievers/bm25_retriever.py @@ -1,4 +1,5 @@ """BM25 retriever.""" +from pathlib import Path from typing import Callable, Optional from llama_index.core import VectorStoreIndex @@ -52,3 +53,18 @@ class DynamicBM25Retriever(BM25Retriever): """Support query total count.""" return len(self._nodes) + + def clear(self, **kwargs) -> None: + """Support deleting all nodes.""" + self._delete_json_files(kwargs.get("persist_dir")) + self._nodes = [] + + @staticmethod + def _delete_json_files(directory: str): + """Delete all JSON files in the specified directory.""" + + if not directory: + return + + for file in Path(directory).glob("*.json"): + file.unlink() diff --git a/metagpt/rag/retrievers/chroma_retriever.py b/metagpt/rag/retrievers/chroma_retriever.py index 6c466e49f..4d3d4469e 100644 --- a/metagpt/rag/retrievers/chroma_retriever.py +++ b/metagpt/rag/retrievers/chroma_retriever.py @@ -8,6 +8,10 @@ from llama_index.vector_stores.chroma import ChromaVectorStore class ChromaRetriever(VectorIndexRetriever): """Chroma retriever.""" + @property + def vector_store(self) -> ChromaVectorStore: + return self._vector_store + def add_nodes(self, nodes: list[BaseNode], **kwargs) -> None: """Support add nodes.""" self._index.insert_nodes(nodes, **kwargs) @@ -20,6 +24,11 @@ class ChromaRetriever(VectorIndexRetriever): def query_total_count(self) -> int: """Support query total count.""" - vector_store: ChromaVectorStore = self._vector_store + return self.vector_store._collection.count() - return vector_store._collection.count() + def clear(self, **kwargs) -> None: + """Support deleting all nodes.""" + + ids = self.vector_store._collection.get()["ids"] + if ids: + self.vector_store._collection.delete(ids=ids) From 4846f60d6011206c49dc460524b21d6abbdfb174 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 27 Aug 2024 10:37:34 +0800 Subject: [PATCH 35/47] exp_pool storage --- metagpt/exp_pool/manager.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index f6b38f9e7..5fbac4013 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -39,6 +39,18 @@ class ExperienceManager(BaseModel): return self._storage + @storage.setter + def storage(self, value): + self._storage = value + + @property + def _is_readable(self) -> bool: + return self.config.exp_pool.enabled and self.config.exp_pool.enable_read + + @property + def _is_writable(self) -> bool: + return self.config.exp_pool.enabled and self.config.exp_pool.enable_write + @handle_exception def create_exp(self, exp: Experience): """Adds an experience to the storage if writing is enabled. @@ -47,7 +59,7 @@ class ExperienceManager(BaseModel): exp (Experience): The experience to add. """ - if not self._is_writable(): + if not self._is_writable: return self.storage.add_objs([exp]) @@ -66,7 +78,7 @@ class ExperienceManager(BaseModel): list[Experience]: A list of experiences that match the args. """ - if not self._is_readable(): + if not self._is_readable: return [] nodes = await self.storage.aretrieve(req) @@ -81,25 +93,19 @@ class ExperienceManager(BaseModel): return exps - def get_exps_count(self) -> int: - """Get the total number of experiences.""" - - return self.storage.count() - @handle_exception def delete_all_exps(self): """Delete the all experiences.""" - if not self._is_writable(): + if not self._is_writable: return self.storage.clear(persist_dir=self.config.exp_pool.persist_path) - def _is_readable(self) -> bool: - return self.config.exp_pool.enabled and self.config.exp_pool.enable_read + def get_exps_count(self) -> int: + """Get the total number of experiences.""" - def _is_writable(self) -> bool: - return self.config.exp_pool.enabled and self.config.exp_pool.enable_write + return self.storage.count() def _resolve_storage(self) -> "SimpleEngine": """Selects the appropriate storage creation method based on the configured retrieval type.""" From 16e76179595f6f03f3ce9613c191f609692930b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Tue, 27 Aug 2024 14:30:56 +0800 Subject: [PATCH 36/47] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metagpt/tools/libs/editor.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 76f009fa6..e6f1525a4 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -251,15 +251,15 @@ class Editor(BaseModel): from metagpt.tools.libs import get_env_default from metagpt.utils.omniparse_client import OmniParseClient - base_url1 = await get_env_default(key="base_url", app_name="OmniParse", default_value="") - timeout1 = await get_env_default(key="timeout", app_name="OmniParse", default_value="") - base_url2, timeout2 = await Editor._read_omniparse_config() + env_base_url = await get_env_default(key="base_url", app_name="OmniParse", default_value="") + env_timeout = await get_env_default(key="timeout", app_name="OmniParse", default_value="") + conf_base_url, conf_timeout = await Editor._read_omniparse_config() - base_url = base_url1 or base_url2 + base_url = env_base_url or conf_base_url if not base_url: return None api_key = await get_env_default(key="api_key", app_name="OmniParse", default_value="") - timeout = timeout1 or timeout2 or 120 + timeout = env_timeout or conf_timeout or 120 try: timeout = int(timeout) except ValueError: From aae2bfe1fafc0df635ab648faa0d8949f93c6ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Tue, 27 Aug 2024 14:59:28 +0800 Subject: [PATCH 37/47] feat: default timeout 120 -> 600 --- metagpt/tools/libs/editor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index e6f1525a4..478903d9c 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -259,11 +259,11 @@ class Editor(BaseModel): if not base_url: return None api_key = await get_env_default(key="api_key", app_name="OmniParse", default_value="") - timeout = env_timeout or conf_timeout or 120 + timeout = env_timeout or conf_timeout or 600 try: timeout = int(timeout) except ValueError: - timeout = 120 + timeout = 600 try: if not await check_http_endpoint(url=base_url): From 5ff3e0de7d1b652c7a6d0a3572ab7ac68fa639f1 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 27 Aug 2024 15:57:11 +0800 Subject: [PATCH 38/47] update comment --- metagpt/schema.py | 2 +- metagpt/utils/common.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/metagpt/schema.py b/metagpt/schema.py index 5f9a5667f..201ff4357 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -147,7 +147,7 @@ class SerializationMixin(BaseModel, extra="forbid"): serialized_data = self.model_dump() - write_json_file(file_path, serialized_data) + write_json_file(file_path, serialized_data, use_fallback=True) logger.debug(f"{self.__class__.__qualname__} serialization successful. File saved at: {file_path}") return file_path diff --git a/metagpt/utils/common.py b/metagpt/utils/common.py index 2b2a209be..42a872c76 100644 --- a/metagpt/utils/common.py +++ b/metagpt/utils/common.py @@ -581,20 +581,20 @@ def read_json_file(json_file: str, encoding: str = "utf-8") -> list[Any]: def handle_unknown_serialization(x: Any) -> str: - """For `to_jsonable_python` debug, unknown values will be logged instead of raising an exception.""" + """For `to_jsonable_python` debug, get more detail about the x.""" if inspect.ismethod(x): - logger.error(f"Method: {x.__self__.__class__.__name__}.{x.__func__.__name__}") + tip = f"Cannot serialize method '{x.__func__.__name__}' of class '{x.__self__.__class__.__name__}'" elif inspect.isfunction(x): - logger.error(f"Function: {x.__name__}") + tip = f"Cannot serialize function '{x.__name__}'" elif hasattr(x, "__class__"): - logger.error(f"Instance of: {x.__class__.__name__}") + tip = f"Cannot serialize instance of '{x.__class__.__name__}'" elif hasattr(x, "__name__"): - logger.error(f"Class or module: {x.__name__}") + tip = f"Cannot serialize class or module '{x.__name__}'" else: - logger.error(f"Unknown type: {type(x)}") + tip = f"Cannot serialize object of type '{type(x).__name__}'" - return f"" + raise TypeError(tip) def write_json_file(json_file: str, data: Any, encoding: str = "utf-8", indent: int = 4, use_fallback: bool = False): From 49505d37eb01cdd7e7935fbaec886f03c51b8c28 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Tue, 27 Aug 2024 20:02:48 +0800 Subject: [PATCH 39/47] log and load exp --- examples/exp_pool/init_exp_pool.py | 4 +- examples/exp_pool/load_exps_from_log.py | 85 +++++++++++++++++++++++++ metagpt/exp_pool/decorator.py | 14 +++- metagpt/exp_pool/manager.py | 33 +++++++--- metagpt/exp_pool/schema.py | 4 ++ 5 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 examples/exp_pool/load_exps_from_log.py diff --git a/examples/exp_pool/init_exp_pool.py b/examples/exp_pool/init_exp_pool.py index 62747b8d8..c7412af22 100644 --- a/examples/exp_pool/init_exp_pool.py +++ b/examples/exp_pool/init_exp_pool.py @@ -46,8 +46,8 @@ async def add_exp(req: str, resp: str, tag: str, metric: Metric = None): metric=metric or Metric(score=Score(val=10, reason="Manual")), ) exp_manager = get_exp_manager() - exp_manager.config.exp_pool.enabled = True - exp_manager.config.exp_pool.enable_write = True + exp_manager.is_writable = True + exp_manager.create_exp(exp) logger.info(f"New experience created for the request `{req[:10]}`.") diff --git a/examples/exp_pool/load_exps_from_log.py b/examples/exp_pool/load_exps_from_log.py new file mode 100644 index 000000000..77eeff6dd --- /dev/null +++ b/examples/exp_pool/load_exps_from_log.py @@ -0,0 +1,85 @@ +"""Load and save experiences from the log file.""" + +import json +from pathlib import Path + +from metagpt.exp_pool import get_exp_manager +from metagpt.exp_pool.schema import LOG_NEW_EXPERIENCE_PREFIX, Experience +from metagpt.logs import logger + + +def load_exps(log_file_path: str) -> list[Experience]: + """Loads experiences from a log file. + + Args: + log_file_path (str): The path to the log file. + + Returns: + list[Experience]: A list of Experience objects loaded from the log file. + """ + + if not Path(log_file_path).exists(): + logger.warning(f"`load_exps` called with a non-existent log file path: {log_file_path}") + return + + exps = [] + with open(log_file_path, "r") as log_file: + for line in log_file: + if LOG_NEW_EXPERIENCE_PREFIX in line: + json_str = line.split(LOG_NEW_EXPERIENCE_PREFIX, 1)[1].strip() + exp_data = json.loads(json_str) + + exp = Experience(**exp_data) + exps.append(exp) + + logger.info(f"Loaded {len(exps)} experiences from log file: {log_file_path}") + + return exps + + +def save_exps(exps: list[Experience]): + """Saves a list of experiences to the experience pool. + + Args: + exps (list[Experience]): The list of experiences to save. + """ + + if not exps: + logger.warning("`save_exps` called with an empty list of experiences.") + return + + manager = get_exp_manager() + manager.is_writable = True + + manager.create_exps(exps) + logger.info(f"Saved {len(exps)} experiences.") + + +def get_log_file_path() -> str: + """Retrieves the path to the log file. + + Returns: + str: The path to the log file. + + Raises: + ValueError: If the log file path cannot be found. + """ + + handlers = logger._core.handlers + + for handler in handlers.values(): + if "log" in handler._name: + return handler._name[1:-1] + + raise ValueError("Log file not found") + + +def main(): + log_file_path = get_log_file_path() + + exps = load_exps(log_file_path) + save_exps(exps) + + +if __name__ == "__main__": + main() diff --git a/metagpt/exp_pool/decorator.py b/metagpt/exp_pool/decorator.py index 9b2cf3474..d49c13e95 100644 --- a/metagpt/exp_pool/decorator.py +++ b/metagpt/exp_pool/decorator.py @@ -10,7 +10,13 @@ from metagpt.config2 import Config from metagpt.exp_pool.context_builders import BaseContextBuilder, SimpleContextBuilder from metagpt.exp_pool.manager import ExperienceManager, get_exp_manager from metagpt.exp_pool.perfect_judges import BasePerfectJudge, SimplePerfectJudge -from metagpt.exp_pool.schema import Experience, Metric, QueryType, Score +from metagpt.exp_pool.schema import ( + LOG_NEW_EXPERIENCE_PREFIX, + Experience, + Metric, + QueryType, + Score, +) from metagpt.exp_pool.scorers import BaseScorer, SimpleScorer from metagpt.exp_pool.serializers import BaseSerializer, SimpleSerializer from metagpt.logs import logger @@ -173,6 +179,7 @@ class ExpCacheHandler(BaseModel): exp = Experience(req=self._req, resp=self._resp, tag=self.tag, metric=Metric(score=self._score)) self.exp_manager.create_exp(exp) + self._log_exp(exp) @staticmethod def choose_wrapper(func, wrapped_func): @@ -215,3 +222,8 @@ class ExpCacheHandler(BaseModel): return await self.func(*self.args, **self.kwargs) return self.func(*self.args, **self.kwargs) + + def _log_exp(self, exp: Experience): + log_entry = exp.model_dump_json(include={"uuid", "req", "resp", "tag"}) + + logger.debug(f"{LOG_NEW_EXPERIENCE_PREFIX}{log_entry}") diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index 5fbac4013..38772239b 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -31,7 +31,7 @@ class ExperienceManager(BaseModel): _storage: Any = None @property - def storage(self): + def storage(self) -> "SimpleEngine": if self._storage is None: logger.info(f"exp_pool config: {self.config.exp_pool}") @@ -44,13 +44,21 @@ class ExperienceManager(BaseModel): self._storage = value @property - def _is_readable(self) -> bool: + def is_readable(self) -> bool: return self.config.exp_pool.enabled and self.config.exp_pool.enable_read + @is_readable.setter + def is_readable(self, value: bool): + self.config.exp_pool.enabled = self.config.exp_pool.enable_read = value + @property - def _is_writable(self) -> bool: + def is_writable(self) -> bool: return self.config.exp_pool.enabled and self.config.exp_pool.enable_write + @is_writable.setter + def is_writable(self, value: bool): + self.config.exp_pool.enabled = self.config.exp_pool.enable_write = value + @handle_exception def create_exp(self, exp: Experience): """Adds an experience to the storage if writing is enabled. @@ -59,10 +67,19 @@ class ExperienceManager(BaseModel): exp (Experience): The experience to add. """ - if not self._is_writable: + self.create_exps([exp]) + + @handle_exception + def create_exps(self, exps: list[Experience]): + """Adds multiple experiences to the storage if writing is enabled. + + Args: + exps (list[Experience]): A list of experiences to add. + """ + if not self.is_writable: return - self.storage.add_objs([exp]) + self.storage.add_objs(exps) self.storage.persist(self.config.exp_pool.persist_path) @handle_exception(default_return=[]) @@ -78,7 +95,7 @@ class ExperienceManager(BaseModel): list[Experience]: A list of experiences that match the args. """ - if not self._is_readable: + if not self.is_readable: return [] nodes = await self.storage.aretrieve(req) @@ -97,7 +114,7 @@ class ExperienceManager(BaseModel): def delete_all_exps(self): """Delete the all experiences.""" - if not self._is_writable: + if not self.is_writable: return self.storage.clear(persist_dir=self.config.exp_pool.persist_path) @@ -210,7 +227,7 @@ class ExperienceManager(BaseModel): _exp_manager = None -def get_exp_manager(): +def get_exp_manager() -> ExperienceManager: global _exp_manager if _exp_manager is None: _exp_manager = ExperienceManager() diff --git a/metagpt/exp_pool/schema.py b/metagpt/exp_pool/schema.py index a45910f0d..fea48a7f7 100644 --- a/metagpt/exp_pool/schema.py +++ b/metagpt/exp_pool/schema.py @@ -2,6 +2,7 @@ import time from enum import Enum from typing import Optional +from uuid import UUID, uuid4 from pydantic import BaseModel, Field @@ -9,6 +10,8 @@ MAX_SCORE = 10 DEFAULT_SIMILARITY_TOP_K = 2 +LOG_NEW_EXPERIENCE_PREFIX = "New experience: " + class QueryType(str, Enum): """Type of query experiences.""" @@ -67,6 +70,7 @@ class Experience(BaseModel): tag: str = Field(default="", description="Tagging experience.") traj: Optional[Trajectory] = Field(default=None, description="Trajectory.") timestamp: Optional[float] = Field(default_factory=time.time) + uuid: Optional[UUID] = Field(default_factory=uuid4) def rag_key(self): return self.req From 6597141bee79165d526b8cc0bfd2a95187854773 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Tue, 27 Aug 2024 23:32:54 +0800 Subject: [PATCH 40/47] Increase the priority of directly passing configurations through Config.default. --- metagpt/config2.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metagpt/config2.py b/metagpt/config2.py index 525fe3daf..7b6ddf8c6 100644 --- a/metagpt/config2.py +++ b/metagpt/config2.py @@ -110,8 +110,7 @@ class Config(CLIParams, YamlModel): CONFIG_ROOT / "config2.yaml", ) if reload or default_config_paths not in _CONFIG_CACHE: - dicts = [kwargs, dict(os.environ)] - dicts += [Config.read_yaml(path) for path in default_config_paths] + dicts = [dict(os.environ), *(Config.read_yaml(path) for path in default_config_paths), kwargs] final = merge_dict(dicts) _CONFIG_CACHE[default_config_paths] = Config(**final) return _CONFIG_CACHE[default_config_paths] From 37c4b5c1587419e4178cec80ae83e50b8df48693 Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Wed, 28 Aug 2024 10:30:51 +0800 Subject: [PATCH 41/47] update comment --- metagpt/rag/retrievers/bm25_retriever.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metagpt/rag/retrievers/bm25_retriever.py b/metagpt/rag/retrievers/bm25_retriever.py index 74cba5124..4891fad50 100644 --- a/metagpt/rag/retrievers/bm25_retriever.py +++ b/metagpt/rag/retrievers/bm25_retriever.py @@ -37,6 +37,7 @@ class DynamicBM25Retriever(BM25Retriever): def add_nodes(self, nodes: list[BaseNode], **kwargs) -> None: """Support add nodes.""" + self._nodes.extend(nodes) self._corpus = [self._tokenizer(node.get_content()) for node in self._nodes] self.bm25 = BM25Okapi(self._corpus) @@ -46,6 +47,7 @@ class DynamicBM25Retriever(BM25Retriever): def persist(self, persist_dir: str, **kwargs) -> None: """Support persist.""" + if self._index: self._index.storage_context.persist(persist_dir) @@ -56,6 +58,7 @@ class DynamicBM25Retriever(BM25Retriever): def clear(self, **kwargs) -> None: """Support deleting all nodes.""" + self._delete_json_files(kwargs.get("persist_dir")) self._nodes = [] From dbf41120b0999aafdfec32cd4617bbdd3290874a Mon Sep 17 00:00:00 2001 From: seehi <6580@pm.me> Date: Wed, 28 Aug 2024 11:46:05 +0800 Subject: [PATCH 42/47] update setter of is_writable and is_readable --- metagpt/exp_pool/manager.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/metagpt/exp_pool/manager.py b/metagpt/exp_pool/manager.py index 38772239b..35de17079 100644 --- a/metagpt/exp_pool/manager.py +++ b/metagpt/exp_pool/manager.py @@ -49,7 +49,11 @@ class ExperienceManager(BaseModel): @is_readable.setter def is_readable(self, value: bool): - self.config.exp_pool.enabled = self.config.exp_pool.enable_read = value + self.config.exp_pool.enable_read = value + + # If set to True, ensure that enabled is also True. + if value: + self.config.exp_pool.enabled = True @property def is_writable(self) -> bool: @@ -57,7 +61,11 @@ class ExperienceManager(BaseModel): @is_writable.setter def is_writable(self, value: bool): - self.config.exp_pool.enabled = self.config.exp_pool.enable_write = value + self.config.exp_pool.enable_write = value + + # If set to True, ensure that enabled is also True. + if value: + self.config.exp_pool.enabled = True @handle_exception def create_exp(self, exp: Experience): From ac9eee347a4f9a4b0b743efedc0834c3d8d3bc90 Mon Sep 17 00:00:00 2001 From: zhanglei Date: Wed, 28 Aug 2024 15:03:46 +0800 Subject: [PATCH 43/47] update: CR points.json use English --- metagpt/ext/cr/points.json | 1310 ++++++++++++++++----------------- metagpt/ext/cr/points_cn.json | 656 +++++++++++++++++ 2 files changed, 1311 insertions(+), 655 deletions(-) create mode 100644 metagpt/ext/cr/points_cn.json diff --git a/metagpt/ext/cr/points.json b/metagpt/ext/cr/points.json index 10fc951c0..f0920cacc 100644 --- a/metagpt/ext/cr/points.json +++ b/metagpt/ext/cr/points.json @@ -1,656 +1,656 @@ [ - { - "id": 1, - "text": "避免未使用的临时变量", - "language": "Java", - "detail": "缺陷类型:避免未使用的临时变量;对应Fixer:UnusedLocalVariableFixer;修复方案:删除未使用的临时变量", - "yes_example": "### 被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic String initCreationForm(Map model) {\n\t\tOwner owner = new Owner();\n\t\tmodel.put(\"owner\", owner);\n\t\tint unusedVar = 10;\n\t\treturn VIEWS_OWNER_CREATE_OR_UPDATE_FORM;\n\t}\n上述代码中unusedVar变量未被使用,所以这个被判定为\"避免未使用的临时变量\"\n\n<例子2>\nint unusedVariable = 10;\nSystem.out.println(\"Hello, World!\");\n这段代码的变量\"unusedVariable\"未被使用或者引用,所以这个不能判定为\"避免未使用的临时变量\"\n", - "no_example": "### 不能被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map transientVariables) {\nthrow new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}\n这段代码的\"transientVariables\"是函数参数而不是临时变量,虽然transientVariables没有被使用或者引用,但是这个也不能判定为\"避免未使用的临时变量\"\n\n\n<例子2>\npublic class TriggerCmd extends NeedsActiveExecutionCmd {\n protected Map transientVariables;\n public TriggerCmd(Map transientVariables) {\n this.transientVariables = transientVariables;\n }\n}\n上述代码中transientVariables不属于临时变量,它是类属性,且它在构造函数中被使用,所以这个不能被判定为\"避免未使用的临时变量\"\n" - }, - { - "id": 2, - "text": "不要使用 System.out.println 去打印", - "language": "Java", - "detail": "缺陷类型:不要使用 System.out.println 去打印;对应Fixer:SystemPrintlnFixer;修复方案:注释System.out.println代码", - "yes_example": "### 被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nSystem.out.println(\"Initializing new owner form.\");\n上述代码使用了\"System.out.println\"进行打印,所以这个被判定为\"不要使用 System.out.println 去打印\"\n", - "no_example": "### 不能被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nthrow new IllegalStateException(\"There is no authenticated user, we need a user authenticated to find tasks\");\n上述代码是抛出异常的代码,没有使用\"System.out.print\",所以这个不能被判定为\"不要使用 System.out.println 去打印\"\n" - }, - { - "id": 3, - "text": "避免函数中未使用的形参", - "language": "Java", - "detail": "缺陷类型:避免函数中未使用的形参;修复方案:忽略", - "yes_example": "### 被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map transientVariables) {\n throw new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}这段代码中的形参\"transientVariables\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n\n\n<例子2>\nprotected void modifyFetchPersistencePackageRequest(PersistencePackageRequest ppr, Map pathVars) {}\n这段代码中的形参\"ppr\"和\"pathVars\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n", - "no_example": "### 不能被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic String processFindForm(@RequestParam(value = \"pageNo\", defaultValue = \"1\") int pageNo) {\n\tlastName = owner.getLastName();\n\treturn addPaginationModel(pageNo, paginationModel, lastName, ownersResults);\n}这段代码中的形参\"pageNo\"在当前函数'processFindForm'内被'return addPaginationModel(pageNo, paginationModel, lastName, ownersResults);'这一句被使用,虽然pageNo没有被用于逻辑计算,但作为了函数调用其他函数的参数使用了,所以这个不能被判定为\"避免函数中未使用的形参\"\n\n<例子2>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的形参date在System.out.println(\"Formatted date: \" + sdf.format(date))这一句中被引用到,所以这个不能被判定为\"避免函数中未使用的形参\"\n" - }, - { - "id": 4, - "text": "if语句块不能为空", - "language": "Java", - "detail": "缺陷类型:if 语句块不能为空;对应Fixer:EmptyIfStmtFixer;修复方案:删除if语句块 或 适当的逻辑处理 或 注释说明为何为空", - "yes_example": "### 被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void emptyIfStatement() {\n\tif (getSpecialties().isEmpty()) {\n\t}\n}这段代码中的if语句块内容是空的,所以这个被判定为\"if语句块不能为空\"\n\n\n<例子2>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\t// judge persion if not null\n\t}\n}\n这段代码中的if语句块虽然有内容,但是\"// judge persion if not null\"只是代码注释,if语句块内并没有实际的逻辑代码,所以这个被判定为\"if语句块不能为空\"\n", - "no_example": "### 不能被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\treturn 0;\n\t}\n}这段代码中的if语句块里有内容,且里面有非注释代码的逻辑代码\"return 0;\",所以这个不能被判定为\"if语句块不能为空\"\n" - }, - { - "id": 5, - "text": "循环体不能为空", - "language": "Java", - "detail": "缺陷类型:循环体不能为空;对应Fixer:EmptyStatementNotInLoopFixer;修复方案:删除对应while、for、foreach 循环体 或 添加适当的逻辑处理或者注释说明为何为空", - "yes_example": "### 被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t}\n}这段代码中的for循环体的内容是空的,所以这个被判定为\"循环体不能为空\"\n\n\n<例子2>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t// this is a code example\n\t}\n}这段代码中的while循环体的内容虽然不是空的,但内容只是代码注释,无逻辑内容,所以这个被判定为\"循环体不能为空\"\n\n\n<例子3>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t\n\t}\n}这段代码中的while循环体内容是空的,所以这个被判定为\"循环体不能为空\"\n", - "no_example": "### 不能被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t\ta = 1;\n\t\tif (a == 1) {\n\t\t\tretrun a;\n\t\t}\n\t}\n}上述代码的for循环体的内容不为空,且内容不全是代码注释,所以这个不能被判定为\"循环体不能为空\"\n" - }, - { - "id": 6, - "text": "避免使用 printStackTrace(),应该使用日志的方式去记录", - "language": "Java", - "detail": "缺陷类型:避免使用 printStackTrace(),应该使 用日志的方式去记录;修复方案:用日志的方式去记录", - "yes_example": "### 被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\te.printStackTrace();\n\t}\n}这段代码中的catch语句中使用了printStackTrace(),所以这个被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n", - "no_example": "### 不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\tlogging.info(\"info\");\n\t}\n}这段代码的catch语句中使用的是日志记录的方式,所以这个不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n" - }, - { - "id": 7, - "text": "catch 语句块不能为空", - "language": "Java", - "detail": "缺陷类型:catch 语句块不能为空;对应Fixer:EmptyCatchBlockFixer;修复方案:在catch里面添加注释", - "yes_example": "### 被判定为\"catch语句块不能为空\"的例子\n<例子1>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n \n}\n这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n\n<例子2>\ntry {\n String str = null;\n str.length();\n} catch (NullPointerException e) {\n \n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n\n<例子3>\npublic class EmptyCatchExample {\n public static void main(String[] args) {\n try {\n // 尝试除以零引发异常\n int result = 10 / 0;\n } catch (ArithmeticException e) {\n \n }\n }\n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n<例子4>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n \n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n<例子5>\ntry {\n Object obj = \"string\";\n Integer num = (Integer) obj;\n} catch (ClassCastException e) {\n\t\n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n", - "no_example": "### 不能被判定为\"catch语句块不能为空\"的例子\n<例子1>\npersionNum = 1\ntry {\n\treturn True;\n} catch (Exception e) {\n\t// 如果人数为1则返回false\n\tif (persionNum == 1){\n\t\treturn False;\n\t}\n}这段代码的catch语句中不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n\n\n<例子2>\ntry {\n\tthrow new Exception(\"Fake exception\");\n} catch (Exception e) {\n\te.printStackTrace();\n}这段代码的catch语句中虽然只有\"e.printStackTrace();\"但确实不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n" - }, - { - "id": 8, - "text": "避免不必要的永真/永假判断", - "language": "Java", - "detail": "缺陷类型:避免不必要的永真/永假判断;对应Fixer:UnconditionalIfStatement Fixer;修复方案:删除永真/永假判断逻辑", - "yes_example": "### 被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\npublic void someMethod() {\n\twhile (true) {\n\t}\n}这段代码中的\"while (true)\"是一个使用true做判断条件,但是没有循环结束标记,所以这个被判定为\"避免不必要的永真/永假判断\"\n\n\n<例子2>\nif (true) {\n\tSystem.out.println(\"This is always true\");\n}这段代码中的\"if (true)\"是一个使用true条件做条件,但是没有循环结束标记,所以这个被判定为\"避免不必要的永真/永假判断\"\n\n\n<例子3>\na = 1;\nwhile(a > 0){\n\ta = a + 1\n}这段代码初始化a=1,是大于0的,while循环体的逻辑是每次加1,那么判断条件a > 0会永远是真的,不会退出循环,所以这个被判定为\"避免不必要的永真/永假判断\"\n<例子3>", - "no_example": "### 不能被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\na = 0;\nwhile (a < 5) {\n\ta = a + 1;\n}这段代码中的a<5是一个判断,当执行了5次while语句中的逻辑a=a+1之后,a会满足a < 5,就会退出循环,所以这个能被判定为\"避免不必要的永真/永假判断\"\n" - }, - { - "id": 9, - "text": "switch 中 default 必须放在最后", - "language": "Java", - "detail": "缺陷类型:switch 中 default 必须放在最后;对应Fixer:DefaultLabelNotLastInSwitchStmtFixer;修复方案:switch 中 default 放在最后", - "yes_example": "### 被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tcase 2:\n\t\tSystem.out.println(\"Number two\");\n\t\tbreak;\n}这段代码是一个switch语句,但是里面的default没有放在最后,所以这个被判定为\"switch 中 default 必须放在最后\"\n", - "no_example": "### 不能被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\ncase 3:\n\tSystem.out.println(\"Number one\");\n\tbreak;\ncase 4:\n\tSystem.out.println(\"Number two\");\n\tbreak;\ndefault:\n\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\tbreak;\n}这段代码是一个switch语句且里面的default放在了最后,所以这个不能被判定为\"switch 中 default 必须放在最后\"\n" - }, - { - "id": 10, - "text": "未使用equals()函数对 String 作比较", - "language": "Java", - "detail": "缺陷类型:未使用equals()函数对 String 作比较;对应Fixer:UnSynStaticDateFormatter Fixer;修复方案:使用equals()函数对 String 作比较", - "yes_example": "### 被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (existingPet != null && existingPet.getName() == petName) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中所涉及的existingPet.getName()和petName均是字符串,但是在if语句里做比较的时候使用了==而没有使用equals()对string做比较,所以这个被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\" == isOk) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串,但在if判断中与\"ok\"比较的时候使用的是==,未使用equals()对string做比较,应该使用\"ok\".equals(isOk),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子3>\nString str1 = \"Hello\";\nString str2 = \"Hello\";\nif (str1 == str2) {\n\tSystem.out.println(\"str1 和 str2 引用相同\");\n} else {\n\tSystem.out.println(\"str1 和 str2 引用不同\");\n}\n这段代码中的if (str1 == str2) 使用了==进行str1和str2的比较,未使用equals()对string做比较,应该使用str1.equals(str2),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子4>\nString str = \"This is string\";\nif (str == \"This is not str\") {\n\treturn str;\n}这段代码中的if (str == \"This is not str\")使用了==进行字符串比较,未使用equals()对string做比较,\"This is not str\".equals(str),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n", - "no_example": "### 不能被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (PROPERTY_VALUE_YES.equalsIgnoreCase(readWriteReqNode))\n formProperty.setRequired(true);\n这段代码中的PROPERTY_VALUE_YES和readWriteReqNode均是字符串,在if语句里比较PROPERTY_VALUE_YES和readWriteReqNode的使用的是equalsIgnoreCase(字符串比较忽略大小写),所以equalsIgnoreCase也是符合使用equals()函数对 String 作比较的,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\".equals(isOk)) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串,在if判断中与\"ok\"比较的时候使用的是equals()对string做比较,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n" - }, - { - "id": 11, - "text": "禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象", - "language": "Java", - "detail": "缺陷类型:禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象 输出异常;对应Fixer:ConcatExceptionFixer;修复方案:使用占位符传递异常对象", - "yes_example": "### 被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\ntry {\n listenersNode = objectMapper.readTree(listenersNode.asText());\n} catch (Exception e) {\n LOGGER.info(\"Listeners node can not be read\", e);\n}这段代码中日志输出内容内容是直接使用字符串\"Listeners node can not be read\"拼接,日志输出异常时,应使用占位符输出异常信息,而不是直接使用字符串拼接,所以这个被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n", - "no_example": "### 不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\nPersion persion = persionService.getPersion(1);\nif (persion == null){\n\tLOGGER.error(PERSION_NOT_EXIT);\n}这段代码中的PERSION_NOT_EXIT是一个用户自定义的异常常量,代表persion不存在,没有直接使用字符串\"persion not exit\"拼接,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n<例子1>\n\n<例子2>\ntry {\n a = a + 1;\n} catch (Exception e) {\n Persion persion = persionService.getPersion(1);\n LOGGER.info(persion);\n}这段代码中输出日志没有直接使用字符串拼接,而是使用的Persion对象输出,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n" - }, - { - "id": 12, - "text": "finally 语句块不能为空", - "language": "Java", - "detail": "缺陷类型:finally 语句块不能为空;对应Fixer:EmptyFinallyBlockFixer;修复方案:删除空 finally 语句块", - "yes_example": "### 被判定为\"finally 语句块不能为空\"的例子\n<例子1>\ntry {\n\tPersion persion = persionService.getPersion(1);\n\treturn persion;\n} finally {\n\t\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子2>\ntry {\n\tSystem.out.println(\"Inside try block\");\n} finally {\n\t// 空的finally块,没有任何语句,这是一个缺陷\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子3>\ntry {\n int result = 10 / 0;\n} catch (ArithmeticException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子4>\ntry {\n String str = null;\n System.out.println(str.length());\n} catch (NullPointerException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子5>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n e.printStackTrace();\n} finally {\n // 只有注释的 finally 语句块\n // 这是一个空的 finally 块\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子6>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n e.printStackTrace();\n} finally {\n // 只有空行的 finally 语句块\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n", - "no_example": "### 不能被判定为\"finally 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"finally 语句块不能为空\"\n" - }, - { - "id": 13, - "text": "try 语句块不能为空", - "language": "Java", - "detail": "缺陷类型:try 语句块不能为空;对应Fixer:EmptyTryBlockFixer;修复方案:删除整个 try 语句", - "yes_example": "### 被判定为\"try 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\n\t}\n\treturn null;\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n\n\n<例子2>\npublic void demoFinallyBlock() {\n\ttry {\n\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n\n\n<例子3>\ntry {\n \n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n\n\n<例子4>\ntry {\n // 只有注释的 try 语句块\n\t\n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内只有注释和空行,也可以认定为这种情况是try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n", - "no_example": "### 不能被判定为\"try 语句块不能为空\"的例子\n<例子1>\ntry {\n\ta = a + 1;\n} catch (Exception e) {\n\te.printStackTrace();\n}\n这段代码中的try语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"try 语句块不能为空\"\n" - }, - { - "id": 14, - "text": "避免对象进行不必要的 NULL或者null 检查", - "language": "Java", - "detail": "缺陷类型:避免对象进行不必要的 NULL或者null 检查;对应Fixer:LogicalOpNpeFixer;修复方案:删除对对象不必要的 NULL 检查的逻辑", - "yes_example": "### 被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\na = \"dog\";\nif (a != null){\n\treturn a;\n}这段代码中的对象a已经是确定的值\"dog\",所以if条件句的判断\"a != null\"是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子2>\nif (authenticatedUserId != null && !authenticatedUserId.isEmpty() && userGroupManager!=null){\n\treturn authenticatedUserId;\n}这段代码中的\"authenticatedUserId != null\"和\"!authenticatedUserId.isEmpty()\"都是对\"authenticatedUserId\"的空判断,重复了,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子3>\nList list = new ArrayList<>();\nif (list != null) {\n list.add(1);\n}这段代码中的list已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子4>\nif (this.type != null && this.type.getName() != null) {\n\tSystem.out.println(\"Type name is not null\");\n}这段代码中的对象type已经检查过非null,再次检查getName()是否为null是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n\n<例子5>\nif (\"dog\".equals(null)){\n\treturn a;\n}这段代码中的\"dog\"是个确定的字符串,不需要进行null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子6>\nInteger num = 10;\nif (num != null) {\n System.out.println(num);\n}这段代码中的num 已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n", - "no_example": "### 不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\nCat cat = catService.get(1);\nif (cat != null){\n\tretrun cat;\n}这段代码中的对象\"cat\"是通过service获取到的,不确定是否为空,所以if条件句的判断的\"cat != null\"是必要的,所以这个不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n" - }, - { - "id": 15, - "text": "避免 finally 块中出现 return", - "language": "Java", - "detail": "缺陷类型:避免 finally 块中出现 return;修复方案:无需修复", - "yes_example": "### 被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块内容包含\"return\",所以这个被判定为\"避免 finally 块中出现 return\"\n", - "no_example": "### 不能被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\tLOGGER.info(PERSION_NOT_EXIT);\n\t}\n}这段代码中的finally语句块中内容不包含\"return\",所以这个不能被判定为\"避免 finally 块中出现 return\"\n" - }, - { - "id": 16, - "text": "避免空的 static 初始化", - "language": "Java", - "detail": "缺陷类型:避免空的 static 初始化;对应Fixer:EmptyInitializerFixer;修复方案:删除整个空初始化块", - "yes_example": "### 被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class PetValidator implements Validator {\n\tstatic {\n\n\t}\n}这段代码中的static语句块没有内容,是空的,所以这个被判定为\"避免空的 static 初始化\"\n\n\n<例子2>\npublic class Persion {\n\tstatic {\n\t\t// 初始化的静态块\n\t}\n}这段代码中的static语句块是有内容的,不是空的,但是static初始化语句块中只有注释代码,没有实际的逻辑,所以这个被判定为\"避免空的 static 初始化\"\n", - "no_example": "### 不能被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class Cat {\n\tstatic {\n\t\t// 初始化的静态块\n\t\tcat = null;\n\t}\n}这段代码中的static语句块是有内容的,不是空的,且static初始化语句块中有非注释代码,有实际的逻辑,所以这个不能被判定为\"避免空的 static 初始化\"\n" - }, - { - "id": 17, - "text": "避免日历类用法不当风险", - "language": "Java", - "detail": "缺陷类型:避免日历类用法不当风险;修复方案:使用Java 8 及以上版本中的 java.time 包的LocalDate", - "yes_example": "### 被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final Calendar calendar = new GregorianCalendar(2020, Calendar.JANUARY, 1);\n这段代码中的Calendar和GregorianCalendar是线程不安全的,所以这个被判定为\"避免日历类用法不当风险\"\n", - "no_example": "### 不能被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final LocalDate calendar = LocalDate.of(2020, 1, 1);\n这段代码中的LocalDate使用的是Java 8 及以上版本中的 java.time 包,LocalDate 是不可变的并且是线程安全的,不会有线程安全和性能方面的问题,所以这个不能被判定为\"避免日历类用法不当风险\"\n" - }, - { - "id": 18, - "text": "使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()", - "language": "Java", - "detail": "缺陷类型:使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size();对应Fixer:ClassCastExpWithToArrayF ixer;修复方案:使用集合的toArray(T[]array),且传入的是类型完全一样的数组", - "yes_example": "### 被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"的例子\n<例子1>\nList stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nObject[] objectArray = stringList.toArray(new Object[5]);\n这段代码使用集合转数组的方法的时候使用了toArray(new Object[5]),但是传入的数组类型不一致,所以这个被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"\n", - "no_example": "### 不能被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"的例子\n<例子1>\nList stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nString[] stringArray = stringList.toArray(new String[stringList.size()]);\n这段代码使用集合转数组的方法的时候使用了toArray(new String[stringList.size()]),传入的是类型完全一样的数组,所以这个不能被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"\n" - }, - { - "id": 19, - "text": "禁止在 equals()中使用 NULL或者null 做比较", - "language": "Java", - "detail": "缺陷类型:禁止在 equals()中使用 NULL或者null 做比较;对应Fixer:EqualsNullFixer;修复方案:使用Object的判空函数 做比较", - "yes_example": "### 被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\nif (\"test\".equals(null)) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码\"test\".equals(null)使用equals()函数与null进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子2>\nif (!rangeValues[1].equals(\"null\")) {\n\tmaxValue = new BigDecimal(rangeValues[1]);\n}这段代码中if条件中的代码!rangeValues[1].equals(\"null\")使用equals()函数与Nnull进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子3>\nString str1 = \"example\";\nif (str1.equals(\"null\")) {\n System.out.println(\"str1 is null\");\n}这段代码中if条件中的代码str1.equals(null)使用equals()函数与null进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子4>\nString str3 = \"example\";\nif (str3 != null && str3.equals(\"null\")) {\n System.out.println(\"str3 is null\");\n}这段代码中if条件中的代码str3.equals(\"null\")使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子5>\nInteger num1 = 10;\nif (num1.equals(null)) {\n System.out.println(\"num1 is null\");\n}这段代码中if条件中的代码num1.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子6>\nObject obj = new Object();\nif (obj.equals(null)) {\n System.out.println(\"obj is null\");\n}这段代码中if条件中的代码obj.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n", - "no_example": "### 不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\na = \"test\";\nif (a.equals(\"test\")) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码a.equals(\"test\")使用equals()函数与\"test\"进行了比较,所以这个不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n" - }, - { - "id": 20, - "text": "switch 语句块不能为空", - "language": "Java", - "detail": "缺陷类型:switch 语句块不能为空;对应Fixer:EmptySwitchStatementsFix;修复方案:删除整个空 switch 语句块", - "yes_example": "### 被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\t\n}这段代码是一个switch语句块,但是里面没有内容,所以这个被判定为\"switch 语句块不能为空\"\n\n\n<例子2>\nswitch (number) {\n\t// 这是一个switch语句块\n}这段代码是一个switch语句块,里面虽然有内容,但是内容仅仅是注释内容,没有实际的逻辑,所以这个被判定为\"switch 语句块不能为空\"\n", - "no_example": "### 不能被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n}这段代码是一个switch语句块,里面有内容,而且内容里有非注释的代码,有实际的逻辑,所以这个不能被判定为\"switch 语句块不能为空\"\n" - }, - { - "id": 21, - "text": "在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开", - "detail": "缺陷类型:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开;修复方案:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开。", - "language": "Java", - "yes_example": "### 被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int) 3.0;\n\n<例子2>\nint b = (int) 4.0;\n\n<例子3>\nlong a = (long) 5;\n\n<例子4>\nstring a = (string) 3.5;\n\n<例子5>\nPersion a = (Persion) \"zhangsan\";\n", - "no_example": "### 不能被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int)3.0;\n" - }, - { - "id": 22, - "text": "方法参数在定义和传入时,多个参数逗号后面必须加空格", - "detail": "缺陷类型:方法参数在定义和传入时,多个参数逗号后面必须加空格;修复方案:方法参数在定义和传入时,多个参数逗号后面必须加空格。", - "language": "Java", - "yes_example": "### 被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a,int b,int c) {}\n", - "no_example": "### 不能被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a, int b, int c) {}\n" - }, - { - "id": 23, - "text": "禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象", - "detail": "缺陷类型:禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象;修复方案:推荐使用 BigDecimal 的 valueOf 方法。", - "language": "Java", - "yes_example": "### 被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = new BigDecimal(0.1);\n", - "no_example": "### 不能被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = BigDecimal.valueOf(0.1);\n" - }, - { - "id": 24, - "text": "不能有多余的分号", - "detail": "缺陷类型:多余的分号;修复方案:删除多余的分号", - "yes_example": "### 被判定为\"不能有多余的分号\"的例子\n<例子1>\npublic void trigger(String executionId, Map processVariables) {\n commandExecutor.execute(new TriggerCmd(executionId, processVariables));\n}\n;\na = 1;\nb = 2;\nsum = a + b;\n这段代码中包含一个多余的分号\";\",所以这个被判定为\"不能有多余的分号\"\n", - "no_example": "### 不能被判定为\"不能有多余的分号\"的例子\n<例子1>\nwhile (True) {\n\ta = a + 1;\n\tbreak;\n}这段代码每个分号都是必须要的,所以这个能被判定为\"不能有多余的分号\"\n" - }, - { - "id": 25, - "text": "非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized", - "detail": "缺陷类型:非线程安全的 SimpleDateFormat 使用;修复方案:在函数或代码块级别加上synchronized修饰 或 使用其他线程安全的方式", - "yes_example": "### 被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的函数formatDate在未使用synchronized同步修饰的情况下使用了SimpleDateFormat,这是线程不安全的,所以这个被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"\n", - "no_example": "### 不能被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic synchronized void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码是在synchronized同步块对函数'formatDate'进行保护,保证了线程安全,所以这个不能被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"\n" - }, - { - "id": 26, - "text": "未按驼峰命名规范进行命名,类名使用驼峰式UpperCamelCase风格, 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格", - "detail": "缺陷类型:未按驼峰命名规范进行命名;修复方案:类名使用UpperCamelCase风格,方法名、参数名、成员变量、局部变量使用lowerCamelCase风格。", - "language": "Java", - "yes_example": "### 被判定为\"未按驼峰命名规范进行命名\"的例子\n<例子1>\npublic class myClass {\n private int MyVariable;\n public void MyMethod() {}\n}\n这段代码中的类名、成员变量和方法名没有遵循驼峰命名法,所以被判定为命名规范问题。\n", - "no_example": "### 不能被判定为\"未按驼峰命名规范进行命名\"的例子\n<例子1>\npublic class MyClass {\n private int myVariable;\n public void myMethod() {}\n}\n这段代码中的类名、成员变量和方法名都遵循了驼峰命名法,所以不能被判定为命名规范问题。\n" - }, - { - "id": 27, - "text": "抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾", - "detail": "缺陷类型:命名规范;修复方案:抽象类命名使用 Abstract 或 Base 开头,异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾。", - "language": "Java", - "yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class MyAbstractClass {}\npublic class MyExceptionClass {}\npublic class TestMyClass {}\n这段代码中的抽象类、异常类和测试类的命名不符合规范,所以被判定为命名规范问题。\n", - "no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic abstract class AbstractMyClass {}\npublic class MyCustomException extends Exception {}\npublic class MyClassTest {}\n这段代码中的抽象类、异常类和测试类的命名都符合规范,所以不能被判定为命名规范问题。\n" - }, - { - "id": 28, - "text": "POJO 类中的任何布尔类型的变量,避免加\"is\" 前缀", - "detail": "缺陷类型:命名规范;修复方案:POJO 类中的布尔类型变量不要加 is 前缀。", - "language": "Java", - "yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class User {\n private boolean isActive;\n}\n这段代码中的布尔类型变量加了 is 前缀,所以被判定为命名规范问题。\n", - "no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic class User {\n private boolean active;\n}\n这段代码中的布尔类型变量没有加 is 前缀,所以不能被判定为命名规范问题。\n" - }, - { - "id": 29, - "text": "杜绝完全不规范的英文缩写,避免望文不知义。", - "detail": "缺陷类型:命名规范;修复方案:避免使用不规范的英文缩写,确保代码可读性。", - "language": "Java", - "yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class CfgMgr {\n private int cnt;\n}\n这段代码中的类名和变量名使用了不规范的英文缩写,所以被判定为命名规范问题。\n", - "no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic class ConfigManager {\n private int count;\n}\n这段代码中的类名和变量名没有使用不规范的英文缩写,所以不能被判定为命名规范问题。\n" - }, - { - "id": 30, - "text": "避免出现魔法字符和数字,应声明为常量", - "detail": "缺陷类型:避免出现魔法字符和数字,应声明为常量;修复方案:将魔法值定义为常量。", - "language": "Java", - "yes_example": "### 被判定为\"避免出现魔法字符和数字,应声明为常量\"的例子\n<例子1>\npublic class MagicNumberExample {\n public void calculate() {\n int result = 42 * 2;\n }\n}\n这段代码中直接使用了魔法值 42,所以被判定为代码规范问题。\n\n<例子2>\npublic class MagicNumberExample {\n public void calculate() {\n String result = \"This is a result\";\n }\n}\n这段代码中直接使用了魔法值 \"This is a result\",所以被判定为代码规范问题。\n", - "no_example": "### 不能被判定为\"避免出现魔法字符和数字,应声明为常量\"的例子\n<例子1>\npublic class MagicNumberExample {\n private static final int MULTIPLIER = 42;\n public void calculate() {\n int result = MULTIPLIER * 2;\n }\n}\n这段代码中将魔法值定义为了常量,所以不能被判定为代码规范问题。\n" - }, - { - "id": 31, - "text": "long 或 Long 赋值时,数值后使用大写 L,不能是小写 l,浮点数类型的数值后缀统一为大写的 D 或 F", - "detail": "缺陷类型:代码规范;修复方案:long 或 Long 赋值时使用大写 L,浮点数类型的数值后缀使用大写的 D 或 F。", - "language": "Java", - "yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000l;\n private double pi = 3.14d;\n}\n这段代码中使用了小写的 l 和 d,所以被判定为代码规范问题。\n", - "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000L;\n private double pi = 3.14D;\n}\n这段代码中使用了大写的 L 和 D,所以不能被判定为代码规范问题。\n" - }, - { - "id": 32, - "text": "如果大括号内为空,简洁地写成{}即可,大括号中间无需换行和空格;如果是非空代码块,则:1)左大括号前不换行。2)左大括号后换行。3)右大括号前换行。4)右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。", - "detail": "缺陷类型:代码格式;修复方案:遵循大括号的使用规范。", - "language": "Java", - "yes_example": "### 被判定为\"代码格式\"的例子\n<例子1>\npublic class BracketExample{public void method(){\n if (true) {\n }}\n}\n这段代码中的大括号使用不符合规范,所以被判定为代码格式问题。\n", - "no_example": "### 不能被判定为\"代码格式\"的例子\n<例子1>\npublic class BracketExample {\n public void method() {\n if (true) {\n // do something\n }\n }\n}\n这段代码中的大括号使用符合规范,所以不能被判定为代码格式问题。\n" - }, - { - "id": 33, - "text": "左小括号和右边相邻字符之间不需要空格;右小括号和左边相邻字符之间也不需要空格;而左大括号前需要加空格。", - "detail": "缺陷类型:代码格式;修复方案:遵循括号和空格的使用规范。", - "language": "Java", - "yes_example": "### 被判定为\"代码格式\"的例子\n<例子1>\npublic class SpaceExample {\n public void method (){\n }\n}\n这段代码中的括号和空格使用不符合规范,所以被判定为代码格式问题。\n", - "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class SpaceExample {\n public void method() {}\n}\n这段代码中的括号和空格使用符合规范,所以不能被判定为代码格式问题。\n" - }, - { - "id": 34, - "text": "if / for / while / switch / do 等保留字与左右括号之间都必须加空格。", - "detail": "缺陷类型:代码格式;修复方案:保留字与左右括号之间加空格。", - "language": "Java", - "yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if(true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间没有空格,所以被判定为代码格式问题。\n", - "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if (true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间有空格,所以不能被判定为代码格式问题。\n" - }, - { - "id": 35, - "text": "所有整型包装类对象之间值的比较,全部使用 equals 方法比较", - "detail": "缺陷类型:代码规范;修复方案:整型包装类对象之间的值比较使用 equals 方法。", - "language": "Java", - "yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a == b) {\n }\n }\n}\n这段代码中使用了 == 比较整型包装类对象,所以被判定为代码规范问题。\n", - "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a.equals(b)) {\n }\n }\n}\n这段代码中使用了 equals 方法比较整型包装类对象,所以不能被判定为代码规范问题。\n" - }, - { - "id": 36, - "text": "BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法。", - "detail": "缺陷类型:BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法;修复方案:使用 compareTo() 方法进行比较。", - "language": "Java", - "yes_example": "### 被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.equals(b)) {\n // 这段代码会返回 false,因为 equals() 方法会比较精度\n}\n", - "no_example": "### 不能被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.compareTo(b) == 0) {\n // 这段代码会返回 true,因为 compareTo() 方法只比较数值\n}\n" - }, - { - "id": 37, - "text": "禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法。", - "detail": "缺陷类型:POJO 类中存在重复的 getter 方法;修复方案:确保只存在一个 getter 方法。", - "language": "Java", - "yes_example": "### 被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private boolean active;\n public boolean isActive() {\n return active;\n }\n public boolean getActive() {\n return active;\n }\n}\n", - "no_example": "### 不能被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private int age;\n public int getAge() {\n return age;\n }\n}\n" - }, - { - "id": 38, - "text": "日期格式化时,传入 pattern 中表示年份统一使用小写的 y。", - "detail": "缺陷类型:日期格式化错误;修复方案:使用小写的 y 表示年份。", - "language": "Java", - "yes_example": "### 被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"YYYY-MM-dd\");\n", - "no_example": "### 不能被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n" - }, - { - "id": 39, - "text": "禁止在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp。", - "detail": "缺陷类型:使用了 java.sql 包中的日期类;修复方案:使用 java.time 包中的日期类。", - "language": "Java", - "yes_example": "### 被判定为\"禁止在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp\"的例子\n<例子1>\njava.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());\n", - "no_example": "### 不能被判定为\"禁止在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp\"的例子\n<例子1>\njava.time.LocalDate localDate = java.time.LocalDate.now();\n" - }, - { - "id": 40, - "text": "判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式。", - "detail": "缺陷类型:集合判空方式错误;修复方案:使用 isEmpty() 方法。", - "language": "Java", - "yes_example": "### 被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList list = new ArrayList<>();\nif (list.size() == 0) {\n // 判空逻辑\n}\n", - "no_example": "### 不能被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList list = new ArrayList<>();\nif (list.isEmpty()) {\n // 判空逻辑\n}\n" - }, - { - "id": 41, - "text": "只要重写 equals,就必须重写 hashCode。", - "detail": "缺陷类型:未重写 hashCode 方法;修复方案:同时重写 equals 和 hashCode 方法。", - "language": "Java", - "yes_example": "### 被判定为\"只要重写 equals,就必须重写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n}\n", - "no_example": "### 不能被判定为\"只要重写 equals,就必须重写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n @Override\n public int hashCode() {\n return Objects.hash(name);\n }\n}\n" - }, - { - "id": 42, - "text": "使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。", - "detail": "缺陷类型:对 Map 的 keySet() / values() / entrySet() 返回的集合进行添加操作;修复方案:避免对这些集合进行添加操作。", - "language": "Java", - "yes_example": "### 被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet keys = map.keySet();\nkeys.add(\"key2\");\n", - "no_example": "### 不能被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet keys = map.keySet();\n// 不进行添加操作\n" - }, - { - "id": 43, - "text": "不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator", - "detail": "缺陷类型:在 foreach 循环中进行元素的 remove / add 操作;修复方案:使用 iterator 进行元素的 remove 操作。", - "language": "Java", - "yes_example": "### 被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nfor (String s : list) {\n if (s.equals(\"a\")) {\n list.remove(s);\n }\n}\n", - "no_example": "### 不能被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nIterator iterator = list.iterator();\nwhile (iterator.hasNext()) {\n String s = iterator.next();\n if (s.equals(\"a\")) {\n iterator.remove();\n }\n}\n" - }, - { - "id": 44, - "text": "类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式。", - "detail": "缺陷类型:注释不符合 Javadoc 规范;修复方案:使用 Javadoc 规范的注释格式。", - "language": "Java", - "yes_example": "### 被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\npublic class Example {\n // 这是一个类注释\n private String name;\n // 这是一个属性注释\n public String getName() {\n return name;\n }\n // 这是一个方法注释\n}\n", - "no_example": "### 不能被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\n/**\n * 这是一个类注释\n */\npublic class Example {\n /**\n * 这是一个属性注释\n */\n private String name;\n /**\n * 这是一个方法注释\n */\n public String getName() {\n return name;\n }\n}\n" - }, - { - "id": 45, - "text": "所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释", - "detail": "缺陷类型:所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释;修复方案:为所有的抽象方法(包括接口中的方法)添加 Javadoc 注释,除了返回值、参数异常说明外,还必须指出该方法做什么事情,实现什么功能。", - "language": "Java", - "yes_example": "### 被判定为\"所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释\"的例子\n<例子1>\npublic interface MyInterface {\n void doSomething();\n}\n这段代码中的接口方法 doSomething() 没有 Javadoc 注释,所以被判定为缺少 Javadoc 注释。\n", - "no_example": "### 不能被判定为\"所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释\"的例子\n<例子1>\n/**\n * 执行某个操作\n * @param param 参数说明\n * @return 返回值说明\n * @throws Exception 异常说明\n */\npublic interface MyInterface {\n void doSomething(String param) throws Exception;\n}\n这段代码中的接口方法 doSomething() 有完整的 Javadoc 注释,所以不能被判定为缺少 Javadoc 注释。\n" - }, - { - "id": 46, - "text": "方法内部单行注释和多行注释的使用规范", - "detail": "缺陷类型:注释使用不规范;修复方案:方法内部单行注释,在被注释语句上方另起一行,使用 // 注释。方法内部多行注释使用 /* */注释,注意与代码对齐。", - "language": "Java", - "yes_example": "### 被判定为\"注释使用不规范\"的例子\n<例子1>\npublic void exampleMethod() {\n int a = 1; // 初始化变量a\n int b = 2; /* 初始化变量b */\n}\n这段代码中的单行注释和多行注释没有按照规范使用,所以被判定为注释使用不规范。\n", - "no_example": "### 不能被判定为\"注释使用不规范\"的例子\n<例子1>\npublic void exampleMethod() {\n // 初始化变量a\n int a = 1;\n /*\n * 初始化变量b\n */\n int b = 2;\n}\n这段代码中的单行注释和多行注释按照规范使用,所以不能被判定为注释使用不规范。\n" - }, - { - "id": 47, - "text": "所有的枚举类型字段必须要有注释", - "detail": "缺陷类型:枚举类型字段缺少注释;修复方案:为所有的枚举类型字段添加注释,说明每个数据项的用途。", - "language": "Java", - "yes_example": "### 被判定为\"枚举类型字段缺少注释\"的例子\n<例子1>\npublic enum Status {\n ACTIVE,\n INACTIVE\n}\n这段代码中的枚举类型字段没有注释,所以被判定为枚举类型字段缺少注释。\n", - "no_example": "### 不能被判定为\"枚举类型字段缺少注释\"的例子\n<例子1>\npublic enum Status {\n /**\n * 活跃状态\n */\n ACTIVE,\n /**\n * 非活跃状态\n */\n INACTIVE\n}\n这段代码中的枚举类型字段有注释,所以不能被判定为枚举类型字段缺少注释。\n" - }, - { - "id": 48, - "text": "finally 块必须对资源对象、流对象进行关闭", - "detail": "缺陷类型:资源对象、流对象未在 finally 块中关闭;修复方案:在 finally 块中对资源对象、流对象进行关闭,有异常也要做 try-catch。", - "language": "Java", - "yes_example": "### 被判定为\"资源对象、流对象未在 finally 块中关闭\"的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n }\n}\n这段代码中的 FileInputStream 对象没有在 finally 块中关闭,所以被判定为资源对象、流对象未在 finally 块中关闭。\n", - "no_example": "### 不能被判定为\"资源对象、流对象未在 finally 块中关闭\"的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n if (fis != null) {\n try {\n fis.close();\n } catch (IOException e) {\n e.printStackTrace();\n }\n }\n }\n}\n这段代码中的 FileInputStream 对象在 finally 块中关闭,所以不能被判定为资源对象、流对象未在 finally 块中关闭。\n" - }, - { - "id": 49, - "text": "常量命名应该全部大写,单词间用下划线隔开", - "detail": "缺陷类型:常量命名不规范;修复方案:常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。", - "language": "Java", - "yes_example": "### 被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int maxCount = 100;\n", - "no_example": "### 不能被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int MAX_COUNT = 100;\n" - }, - { - "id": 50, - "text": "任何二目、三目运算符的左右两边都需要加一个空格", - "detail": "缺陷类型:运算符两边缺少空格;修复方案:任何二目、三目运算符的左右两边都需要加一个空格。", - "language": "Java", - "yes_example": "### 被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a=b+c;\n", - "no_example": "### 不能被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a = b + c;\n" - }, - { - "id": 51, - "text": "避免使用from import *", - "detail": "缺陷类型:避免使用from import *,导入所有内容会造成命名冲突;修复方案:每个使用到的子依赖需分别导入。", - "language": "Python", - "yes_example": "### 被判定为\"避免使用from import *\"的例子\n<例子1>from math import * \n", - "no_example": "### 不能被判定为\"避免使用from import *\"的例子\n<例子1>from math import sqrt, pi \n" - }, - { - "id": 52, - "text": "避免使用__import__()函数动态导入模块", - "detail": "缺陷类型:避免使用__import__()函数动态导入模块;修复方案:使用标准的import语句。", - "language": "Python", - "yes_example": "### 被判定为\"使用__import__()函数动态导入模块\"的例子\n<例子1>module = __import__('math') \n", - "no_example": "### 不能被判定为\"使用__import__()函数动态导入模块\"的例子\n<例子1>import math \n" - }, - { - "id": 53, - "text": "导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组", - "detail": "缺陷类型:导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组;修复方案:按顺序分组导入语句。", - "language": "Python", - "yes_example": "### 被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>\nimport numpy as np\nimport os\nimport sys\nfrom my_local_module import my_function\n在这个样例中,先导入了第三方库,然后导入了标准库。\n\n<例子2>\nfrom my_project import my_local_function\nimport datetime\nimport requests\n在这个样例中,先导入了本地模块,然后导入了标准库。\n\n<例子3>\nimport os\nfrom my_project.local_module import some_function\nimport pandas as pd\nimport sys\nfrom another_local_module import another_function\nimport math\n在这个样例中,导入语句完全混乱,没有遵循任何顺序。\n\n<例子4>\nimport os\nimport requests\nimport sys\nimport numpy as np\nfrom local_package import local_module\n在这个样例中,导入标准库和第三方库交替进行。\n", - "no_example": "### 不能被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>import os \n\n import requests \n\n import mymodule \n" - }, - { - "id": 54, - "text": "避免未使用的函数形参", - "detail": "缺陷类型:避免未使用的函数形参;修复方案:移除未使用的函数形参。", - "language": "Python", - "yes_example": "### 被判定为'避免未使用的函数形参'的例子\n<例子1>def func(a, b): \n return a\n<例子2>def start_game(unused_param): \npuzzle = Puzzle() \npuzzle.solve()\n<例子3>def make_move(self, board):\npass \n\n<例子4>def move(self, direction):\npass \n", - "no_example": "### 不能被判定为'避免未使用的函数形参'的例子\n<例子1>def func(a): \n return a" - }, - { - "id": 55, - "text": "使用is not None来检查一个变量是否不是None", - "detail": "缺陷类型:未使用is not None来检查一个变量是否不是None;修复方案:使用is not None来检查。", - "language": "Python", - "yes_example": "### 被判定为'未使用is not None来检查一个变量是否不是None'的例子\n<例子1>if variable != None:\n pass", - "no_example": "### 不能被判定为'未使用is not None来检查一个变量是否不是None'的例子\n<例子1>if variable is not None:\n pass" - }, - { - "id": 56, - "text": "避免使用==或!=来比较对象实例的等价性", - "detail": "缺陷类型:使用==或!=来比较对象实例的等价性;修复方案:应使用equals比较。", - "language": "Python", - "yes_example": "### 被判定为'使用==或!=来比较对象实例的等价性'的例子\n<例子1>obj1 = MyClass() \n obj2 = MyClass() if obj1 == obj2: \n pass\n", - "no_example": "### 不能被判定为'使用==或!=来比较对象实例的等价性'的例子\n<例子1>obj1 = MyClass() \n obj2 = MyClass() if obj1.equals(obj2): \n pass\n\n<例子2>obj1 = 21 \n obj2 = 22 \n if obj1.equals(obj2):\n pass" - }, - { - "id": 57, - "text": "避免使用单字母变量名,使用描述性变量名", - "detail": "缺陷类型:避免使用单字母变量名,使用描述性变量名;修复方案:使用描述性变量名。", - "language": "Python", - "yes_example": "### 被判定为'避免使用单字母变量名,使用描述性变量名'的例子\n<例子1>x = 10 \n\n<例子2>y = 10 \n", - "no_example": "### 不能被判定为'避免使用单字母变量名,使用描述性变量名'的例子\n<例子1>count = 10 \n" - }, - { - "id": 58, - "text": "常量命名使用全大写字母,并用下划线分隔", - "detail": "缺陷类型:常量命名未使用全大写字母或未用下划线分隔;修复方案:常量命名使用全大写字母,并用下划线分隔。", - "language": "Python", - "yes_example": "### 被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>pi = 3.14159", - "no_example": "### 不能被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>PI = 3.14159\n<例子2>max_size = 1 \n max_size += 1" - }, - { - "id": 59, - "text": "类名应使用驼峰式命名(CamelCase)", - "detail": "缺陷类型:类名未使用驼峰式命名;修复方案:类名使用驼峰式命名。", - "language": "Python", - "yes_example": "### 被判定为'类名未使用驼峰式命名(CamelCase)'的例子\n<例子1>class my_class: \n pass\n<例子2>class my_class: \n def solve(self):\n pass", - "no_example": "### 不能被判定为'类名未使用驼峰式命名(CamelCase)'的例子\n<例子1>class MyClass: \n pass" - }, - { - "id": 60, - "text": "尽量使用with语句来管理资源", - "detail": "缺陷类型:未使用with语句来管理资源;修复方案:使用with语句来管理资源。", - "language": "Python", - "yes_example": "### 被判定为'未使用with语句来管理资源'的例子\n<例子1>file = open('file.txt', 'r') \n content = file.read() \n file.close()", - "no_example": "### 不能被判定为'未使用with语句来管理资源'的例子\n<例子1>with open('file.txt', 'r') as file: \n content = file.read()" - }, - { - "id": 61, - "text": "避免使用except 或 通用的Exception来捕获所有异常,应该指定异常类型", - "detail": "缺陷类型:捕获所有异常;修复方案:指定具体的异常类型。", - "language": "Python", - "yes_example": "### 被判定为'使用except:来捕获所有异常'的例子\n<例子1>try: \n # some code \n except: \n handle_error()\n### 被判定为'抛出通用的Exception异常'的例子\n<例子2>\n try:\n process_data(data) \n except: \n raise Exception('An error occurred') \n ", - "no_example": "### 不能被判定为'使用except:来捕获所有异常'的例子\n<例子1>try: \n # some code \n except ValueError: \n handle_value_error()" - }, - { - "id": 62, - "text": "尽量避免手动拼接字符串", - "detail": "缺陷类型:手动拼接字符串;修复方案:使用格式化字符串或join方法。", - "language": "Python", - "yes_example": "### 被判定为'手动拼接字符串'的例子\n<例子1>\n name = 'John' \n greeting = 'Hello, ' + name + '!' \n \n <例子2>greeting = '2048' + 'game' \n \n <例子3>pygame.display.set_caption('贪吃蛇' + '游戏')", - "no_example": "### 不能被判定为'手动拼接字符串'的例子\n<例子1>\n name = 'John' \n greeting = f'Hello, {name}!' \n" - }, - { - "id": 63, - "text": "避免出现魔法字符和数字,应声明为常量", - "detail": "缺陷类型:使用魔法字符和数字;修复方案:将其声明为常量。", - "language": "Python", - "yes_example": "### 被判定为'出现魔法字符和数字'的例子\n<例子1>\n if status == 1: \n print('Active')' \n\n<例子2>\n self.board = [[0] * 4 for _ in range(4)] \n self.score = 0\n<例子3>\ndef __init__(self, width=10, height=10, mines=15):\n\n<例子4>\nx, y = event.x // 20, event.y // 20\n\n<例子5>\nraise ValueError(\"余额不足\")\n\n<例子6>\ntransfer(bank, \"123\", \"456\", 200)\n\n<例子7>\nbank.add_account(Account(\"123\", 1000))\n", - "no_example": "### 不能被判定为'出现魔法字符和数字'的例子\n<例子1>\n ACTIVE_STATUS = 1 \n if status == ACTIVE_STATUS:\n print(ACTIVE_STATUS)' \n" - }, - { - "id": 64, - "text": "boolean变量判断无需显式比较", - "detail": "缺陷类型:显式比较boolean变量;修复方案:直接使用boolean变量进行判断。", - "language": "Python", - "yes_example": "### 被判定为'显式比较boolean变量'的例子\n<例子1>flag = True \n if flag == True: \n print('Flag is true')\n<例子2>if self.game.is_game_over() == True: \n return<例子3>if self.canvas.drawings ==True:", - "no_example": "### 不能被判定为'显式比较boolean变量'的例子\n<例子1>flag = True \n if flag: \n print('Flag is true') \n" - }, - { - "id": 65, - "text": "避免使用type()检查对象类型", - "detail": "缺陷类型:避免使用type()检查对象类型;修复方案:使用isinstance()函数。", - "language": "Python", - "yes_example": "### 被判定为'避免使用type()检查对象类型'的例子\n<例子1>\n if type(obj) == list: \n print('obj is a list')", - "no_example": "### 不能被判定为'避免使用type()检查对象类型'的例子\n<例子1>\n if isinstance(obj, list): \n print('obj is a list') \n" - }, - { - "id": 66, - "text": "避免使用os.system()来调用外部命令", - "detail": "缺陷类型:使用os.system()调用外部命令;修复方案:使用subprocess模块。", - "language": "Python", - "yes_example": "### 被判定为'使用os.system()来调用外部命令'的例子\n<例子1>os.system('ls -l')\n<例子2>os.system('ls -l')", - "no_example": "### 不能被判定为'使用os.system()来调用外部命令'的例子\n<例子1>import subprocess \n subprocess.run(['ls', '-l'])" - }, - { - "id": 67, - "text": "只使用@property装饰器创建只读属性,而非修改属性", - "detail": "缺陷类型:使用@property装饰器创建可修改属性;修复方案:只使用@property装饰器创建只读属性。", - "language": "Python", - "yes_example": "### 被判定为'使用@property装饰器来创建可修改属性'的例子\n<例子1>@property \n def value(self, new_value): \n self._value = new_value\n<例子2>@property \n def game_over(self): \n return self._is_game_over() \n def _is_game_over(self): \n pass", - "no_example": "### 不能被判定为'使用@property装饰器来创建可修改属性'的例子\n<例子1>@property \n def value(self): \n return self._value\n<例子2>@property \n def __str__(self): \n return 'Maze Game State'" - }, - { - "id": 68, - "text": "在使用索引或切片时,不要在方括号或冒号内加空格", - "detail": "缺陷类型:在索引或切片的方括号或冒号内加空格;修复方案:去掉方括号或冒号内的空格。", - "language": "Python", - "yes_example": "### 被判定为'在使用索引或切片时,在方括号或冒号内加空格'的例子\n<例子1>list = [1, 2, 3, 4] \n sublist = list[ 1 : 3 ]\n<例子2>start_point = self.canvas.drawings[ -1] \n<例子3>if head[ 0] < 0 or head[ 0] >= GRID_WIDTH or head[ 1] < 0 or head[ 1] >= GRID_HEIGHT:\n<例子4>for segment in self.snake[ 1:]:", - "no_example": "### 不能被判定为'在使用索引或切片时,在方括号或冒号内加空格'的例子\n<例子1>list = [1, 2, 3, 4] \n sublist = list[1:3]" - }, - { - "id": 69, - "text": "在逗号、分号或冒号前不要加空格,但在它们之后要加空格", - "detail": "缺陷类型:在逗号、分号或冒号前加空格或在它们之后不加空格;修复方案:在逗号、分号或冒号前不要加空格,但在它们之后要加空格。", - "language": "Python", - "yes_example": "### 被判定为'在逗号、分号或冒号前加空格,或没在它们之后加空格'的例子\n<例子1>if x == 4 : \n print(x , y)\n<例子2>if event.keysym == 'Up' or event.keysym == 'Down' or event.keysym == 'Left' or event.keysym == 'Right' :\n<例子3>x ,y = 1 ,2\n<例子4>def on_key_press(self , event) :\n<例子5>elif event.keysym == 'Down' ; \n<例子6>def update_status(self ,message: str) : \n pass ", - "no_example": "### 不能被判定为'在逗号、分号或冒号前加空格,或没在它们之后加空格'的例子\n<例子1>if x == 4: \n print(x, y)" - }, - { - "id": 70, - "text": "对于二元操作符,两边都应有空格", - "detail": "缺陷类型:二元操作符两边没有空格;修复方案:在二元操作符两边加空格", - "language": "Python", - "yes_example": "### 被判定为'二元操作符两边没有空格'的例子\n<例子1>a=b+1", - "no_example": "### 不能被判定为'二元操作符两边没有空格'的例子\n<例子1>a = b + 1\n<例子2>label = tk.Label(self.root, text=str(cell), bg='white')\n<例子3>label.grid(row=i, column=j)" - }, - { - "id": 71, - "text": "避免使用Python关键字作为变量名或函数名", - "detail": "缺陷类型:使用Python关键字作为变量名或函数名;修复方案:使用非关键字的名称。", - "language": "Python", - "yes_example": "### 被判定为'使用Python关键字作为变量名或函数名'的例子\n<例子1>def class(): \n pass\n<例子2>for = 5\n<例子3>def if(self): ", - "no_example": "### 不能被判定为'使用Python关键字作为变量名或函数名'的例子\n<例子1>def my_function(): \n pass\n<例子2>number = 5" - }, - { - "id": 72, - "text": "避免使用特殊字符作为变量名/方法名/类名,例如$或@", - "detail": "缺陷类型:使用特殊字符作为变量名/方法名/类名;修复方案:使用合法的变量名。", - "language": "Python", - "yes_example": "### 被判定为'使用特殊字符作为变量名/方法名/类名,例如$或@'的例子\n<例子1>my$var = 10\n<例子2>@var = 20\n<例子3>def add_score@(self, points): \n self.score += points\n<例子4>class @MyClass: \n pass\n<例子5>def mine@(self):", - "no_example": "### 不能被判定为'使用特殊字符作为变量名/方法名/类名,例如$或@'的例子\n<例子1>my_var = 10\n<例子2>var_20 = 20" - }, - { - "id": 73, - "text": "避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪", - "detail": "缺陷类型:使用raise重新抛出当前异常;修复方案:使用raise ... from ...语法。", - "language": "Python", - "yes_example": "### 被判定为'避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError: \n raise SomeException('新的异常信息')\n\n<例子2>\ntry:\n db.get_data()\nexcept ValueError as e:\n raise ValueError(\"Something went wrong!\")\n\n<例子3>\ntry:\n\traise Exception(\"形状添加失败\")\nexcept Exception as e:\n\tpass\n", - "no_example": "### 不能被判定为'避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n raise RuntimeError('Error occurred') from e \n\n<例子2>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n\tlogger.error(e)\n raise \n" - }, - { - "id": 74, - "text": "避免在except块中使用pass,这会捕获并忽略异常", - "detail": "缺陷类型:在except块中使用pass;修复方案:处理异常或记录日志。", - "language": "Python", - "yes_example": "### 被判定为'在except块中使用pass'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError: \n pass \n \n<例子2>\n try: \n 1 / 0 \n except ZeroDivisionError: \n pass \n", - "no_example": "### 不能被判定为'在except块中使用pass'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n logging.error('Error occurred: %s', e) \n" - }, - { - "id": 75, - "text": "避免使用assert语句来执行重要的运行时检查", - "detail": "缺陷类型:使用assert语句执行重要的运行时检查;修复方案:使用显式的条件检查和异常处理。", - "language": "Python", - "yes_example": "### 被判定为'使用assert语句来执行重要的运行时检查'的例子\n<例子1>\n def divide(a, b): \n assert b != 0 \n return a / b \n", - "no_example": "### 不能被判定为'使用assert语句来执行重要的运行时检查'的例子\n<例子1>\n def divide(a, b): \n if b == 0: \n raise ValueError('b cannot be zero') \n return a / b \n" - }, - { - "id": 76, - "text": "避免使用eval()和exec(),这些函数可能会带来安全风险", - "detail": "缺陷类型:使用eval()和exec()函数;修复方案:使用安全的替代方案。", - "language": "Python", - "yes_example": "### 被判定为'使用eval()和exec()'的例子\n<例子1>\n eval('print(1)') \n\n<例子2> \n exec('a = 1') \n", - "no_example": "### 不能被判定为'使用eval()和exec()'的例子\n<例子1>\n compiled_code = compile('print(1)', '', 'exec') \n exec(compiled_code) \n" - }, - { - "id": 77, - "text": "避免使用sys.exit(),应使用异常来控制程序的退出", - "detail": "缺陷类型:避免使用sys.exit(),应使用异常来控制程序的退出;修复方案:使用异常来控制程序的退出。", - "language": "Python", - "yes_example": "### 被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\n import sys\nsys.exit(1)\n\n<例子2>\n import sys \n sys.exit()\n\n<例子3>\nif event.type == pygame.QUIT:\n\tpygame.quit()\n\texit()\n\n<例子4>\n import sys \n sys.exit('退出程序'))\n", - "no_example": "### 不能被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\n raise SystemExit(1)\n" - }, - { - "id": 78, - "text": "避免使用time.sleep()进行线程同步,应使用同步原语,如锁或事件", - "detail": "缺陷类型:使用time.sleep()进行线程同步;修复方案:使用同步原语。", - "language": "Python", - "yes_example": "### 被判定为'使用time.sleep()进行线程同步'的例子\n<例子1>\n import time \n\n def worker(): \n time.sleep(1) \n\n<例子2>\n import time \n\n time.sleep(1) \n", - "no_example": "### 不能被判定为'使用time.sleep()进行线程同步'的例子\n<例子1>\n import threading \n\n event = threading.Event() \n\n def worker(): \n event.wait()\n" - }, - { - "id": 79, - "text": "每行代码避免超过79个字符", - "detail": "缺陷类型:每行代码避免超过79个字符;修复方案:将长行代码格式化为多行。", - "language": "Python", - "yes_example": "### 被判定为'每行代码避免超过79个字符'的例子\n<例子1>\n print('This is a very long line of code that exceeds the 79 characters limit........') \n", - "no_example": "### 不能被判定为'每行代码避免超过79个字符'的例子\n<例子1>\n print('This is a very long line of code that exceeds the 79 characters limit' + \n ' but it is split into two lines')\n" - }, - { - "id": 80, - "text": "模块级别的函数和类定义之间用两个空行分隔,类内部的方法定义之间用一个空行分隔", - "detail": "缺陷类型:模块级别的函数和类定义之间没有用两个空行分隔,类内部的方法定义之间没有用一个空行分隔;修复方案:按照规范添加空行。", - "language": "Python", - "yes_example": "### 被判定为'模块级别的函数和类定义之间没用两个空行分隔,类内部的方法定义之间没用一个空行分隔'的例子\n<例子1>\n def func1(): \n pass \n def func2(): \n pass \n\n<例子2>\n class MyClass: \n def method1(self): \n pass \n def method2(self): \n pass \n", - "no_example": "### 不能被判定为'模块级别的函数和类定义之间没用两个空行分隔,类内部的方法定义之间没用一个空行分隔'的例子\n<例子1>\n def func1(): \n pass \n\n\n def func2(): \n pass \n\n<例子2>\n class MyClass: \n def method1(self): \n pass \n\n def method2(self): \n pass \n" - }, - { - "id": 81, - "text": "使用小写字母和下划线分隔的方式命名变量和函数名", - "detail": "缺陷类型:变量和函数命名不符合小写字母和下划线分隔的方式;修复方案:使用小写字母和下划线分隔的方式命名。", - "language": "Python", - "yes_example": "### 被判定为'未使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\n def myFunction(): \n pass \n\n<例子2>\n myVariable = 10 \n\n<例子3>\n def Calculatesquareroot(self, x): \n return 1 \n", - "no_example": "### 不能被判定为'未使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\n def my_function(): \n pass \n\n<例子2>\n my_variable = 10 \n" - }, - { - "id": 82, - "text": "不允许使用print()函数来记录日志,使用logging模块等来记录日志", - "detail": "缺陷类型:使用print()函数记录日志;修复方案:使用logging模块记录日志。", - "language": "Python", - "yes_example": "### 被判定为'使用print()函数来记录日志'的例子\n<例子1>\n print('Error occurred') \n\n<例子2>\n print('打印的日志字符串内容') \n\n<例子3>\n task = 'xxx' \n print(task) \n\n<例子4>\n print(1)\n", - "no_example": "### 不能被判定为'使用print()函数来记录日志'的例子\n<例子1>\n import logging \n logging.error('Error occurred') \n" - } -] + { + "id": 1, + "text": "Avoid unused temporary variables", + "language": "Java", + "detail": "Defect type: Avoid unused temporary variables; Corresponding Fixer: UnusedLocalVariableFixer; Fix solution: Delete unused temporary variables", + "yes_example": "Examples of being judged as 'avoid unused temporary variables'", + "no_example": "Examples that cannot be judged as 'avoiding unused temporary variables'\n\npublic void setTransientVariablesLocal(Map transientVariables) {\n throw new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}\nThis code's 'transientVariables' is a function parameter rather than a temporary variable. Although 'transientVariables' is not used or referenced, this cannot be judged as 'avoiding unused temporary variables'\n\n\n\npublic class TriggerCmd extends NeedsActiveExecutionCmd {\n protected Map transientVariables;\n public TriggerCmd(Map transientVariables) {\n this.transientVariables = transientVariables;\n }\n}\nIn the above code, 'transientVariables' is not a temporary variable; it is a class attribute and is used in the constructor, so this cannot be judged as 'avoiding unused temporary variables'\n" + }, + { + "id": 2, + "text": "Do not use System.out.println to print", + "language": "Java", + "detail": "Defect type: Do not use System.out.println to print; Corresponding Fixer: SystemPrintlnFixer; Fixing solution: Comment out the System.out.println code", + "yes_example": "Example of being judged as 'Do not use System.out.println for printing'", + "no_example": "Examples that cannot be judged as 'Do not use System.out.println to print'\n\nthrow new IllegalStateException(\"There is no authenticated user, we need a user authenticated to find tasks\");\nThe above code is throwing an exception, not using 'System.out.print', so this cannot be judged as 'Do not use System.out.println to print'\n" + }, + { + "id": 3, + "text": "Avoid unused formal parameters in functions", + "language": "Java", + "detail": "Defect type: Avoid unused formal parameters in functions; Fix solution: Ignore", + "yes_example": "Examples of being judged as 'avoiding unused formal parameters' in functions\n\n\npublic void setTransientVariablesLocal(Map transientVariables) {\n throw new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}In this code, the formal parameter \"transientVariables\" does not appear in the function body, so this is judged as 'avoiding unused formal parameters'\n\n\n\nprotected void modifyFetchPersistencePackageRequest(PersistencePackageRequest ppr, Map pathVars) {}\nIn this code, the formal parameters \"ppr\" and \"pathVars\" do not appear in the function body, so this is judged as 'avoiding unused formal parameters'\n", + "no_example": "Examples that cannot be judged as 'avoiding unused parameters in functions'\n\npublic String processFindForm(@RequestParam(value = \"pageNo\", defaultValue = \"1\") int pageNo) {\n\tlastName = owner.getLastName();\n\treturn addPaginationModel(pageNo, paginationModel, lastName, ownersResults);\n}In this code, the parameter 'pageNo' is used within the current function 'processFindForm' in the statement 'return addPaginationModel(pageNo, paginationModel, lastName, ownersResults);', although pageNo is not used for logical calculations, it is used as a parameter in a function call to another function, so this cannot be judged as 'avoiding unused parameters in functions'\n\n\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}In this code, the parameter 'date' is referenced in the statement 'System.out.println(\"Formatted date: \" + sdf.format(date))', so this cannot be judged as 'avoiding unused parameters in functions'\n" + }, + { + "id": 4, + "text": "if statement block cannot be empty", + "language": "Java", + "detail": "Defect type: if statement block cannot be empty; Corresponding Fixer: EmptyIfStmtFixer; Fixing solution: delete the if statement block or handle the logic appropriately or comment to explain why it is empty", + "yes_example": "Examples of being judged as 'if statement block cannot be empty'\n\npublic void emptyIfStatement() {\n\tif (getSpecialties().isEmpty()) {\n\t}\n}\nThis code's if statement block is empty, so it is judged as 'if statement block cannot be empty'\n\n\n\npublic void judgePersion() {\n\tif (persion != null) {\n\t\t// judge persion if not null\n\t}\n}\nAlthough this code's if statement block has content, the '// judge persion if not null' is just a code comment, and there is no actual logic code inside the if statement block, so it is judged as 'if statement block cannot be empty'\n", + "no_example": "Example that cannot be judged as 'if statement block cannot be empty'" + }, + { + "id": 5, + "text": "Loop body cannot be empty", + "language": "Java", + "detail": "Defect type: loop body cannot be empty; Corresponding Fixer: EmptyStatementNotInLoopFixer; Repair solution: delete the corresponding while, for, foreach loop body or add appropriate logical processing or comment explaining why it is empty", + "yes_example": "Examples of being judged as 'Loop body cannot be empty'\n\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t}\n}\nThis code's for loop body is empty, so it is judged as 'Loop body cannot be empty'\n\n\n\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t// this is a code example\n\t}\n}\nThe while loop body in this code is not empty, but the content is just a code comment with no logical content, so it is judged as 'Loop body cannot be empty'\n\n\n\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t\n\t}\n}\nThe while loop body in this code is empty, so it is judged as 'Loop body cannot be empty'\n", + "no_example": "Example that cannot be judged as 'loop body cannot be empty'\n\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t\ta = 1;\n\t\tif (a == 1) {\n\t\t\tretrun a;\n\t\t}\n\t}\n}\nThe content of the for loop in the above code is not empty, and the content is not entirely code comments, so this cannot be judged as 'loop body cannot be empty'\n" + }, + { + "id": 6, + "text": "Avoid using printStackTrace(), and instead use logging to record.", + "language": "Java", + "detail": "Defect type: Avoid using printStackTrace(), should use logging to record; Repair solution: Use logging to record", + "yes_example": "Example of being judged as 'Avoid using printStackTrace(), should use logging to record'", + "no_example": "### Example that cannot be judged as 'avoid using printStackTrace(), should use logging to record'\n\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\tlogging.info(\"info\");\n\t}\n}\nThis code uses logging in the catch statement, so it cannot be judged as 'avoid using printStackTrace(), should use logging to record'\n" + }, + { + "id": 7, + "text": "The catch block cannot be empty", + "language": "Java", + "detail": "Defect type: catch block cannot be empty; Corresponding Fixer: EmptyCatchBlockFixer; Fix solution: Add a comment inside the catch block", + "yes_example": "Examples of being judged as 'catch block cannot be empty'\n\n\n\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n \n}\nThis code has an empty catch block, so it is judged as 'catch block cannot be empty'\n\n\n\n\ntry {\n String str = null;\n str.length();\n} catch (NullPointerException e) {\n \n}\nThis code has an empty catch block, so it is judged as 'catch block cannot be empty'\n\n\n\npublic class EmptyCatchExample {\n public static void main(String[] args) {\n try {\n // Attempt to divide by zero to trigger an exception\n int result = 10 / 0;\n } catch (ArithmeticException e) {\n \n }\n }\n}\nThis code has an empty catch block, so it is judged as 'catch block cannot be empty'\n\n\n\n\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n \n}\nThis code has an empty catch block, so it is judged as 'catch block cannot be empty'\n\n\n\n\ntry {\n Object obj = \"string\";\n Integer num = (Integer) obj;\n} catch (ClassCastException e) {\n\t\n}\nThis code has an empty catch block, so it is judged as 'catch block cannot be empty'\n", + "no_example": "Examples that cannot be judged as 'catch block cannot be empty'\n\npersionNum = 1\ntry {\n\treturn True;\n} catch (Exception e) {\n\t// If the number of people is 1, return false\n\tif (persionNum == 1){\n\t\treturn False;\n\t}\n}This catch statement is not empty, so it cannot be judged as 'catch block cannot be empty'\n\n\n\ntry {\n\tthrow new Exception(\"Fake exception\");\n} catch (Exception e) {\n\te.printStackTrace();\n}Although this catch statement only has 'e.printStackTrace();', it is indeed not empty, so it cannot be judged as 'catch block cannot be empty'\n" + }, + { + "id": 8, + "text": "Avoid unnecessary tautologies/contradictions", + "language": "Java", + "detail": "Defect type: Avoid unnecessary true/false judgments; Corresponding Fixer: UnconditionalIfStatement Fixer; Fixing solution: Delete true/false judgment logic", + "yes_example": "Examples of being judged as 'avoiding unnecessary always true/always false judgments'", + "no_example": "Examples that cannot be judged as 'avoiding unnecessary always true/always false judgments'" + }, + { + "id": 9, + "text": "In a switch statement, default must be placed at the end", + "language": "Java", + "detail": "Defect type: The default in switch must be placed at the end; Corresponding Fixer: DefaultLabelNotLastInSwitchStmtFixer; Fixing solution: Place default at the end in switch", + "yes_example": "Example of being judged as 'default in switch must be placed at the end'", + "no_example": "Example that cannot be judged as 'the default in switch must be placed at the end'" + }, + { + "id": 10, + "text": "Comparison of String without using equals() function", + "language": "Java", + "detail": "Defect type: Not using the equals() function to compare Strings; Corresponding Fixer: UnSynStaticDateFormatter Fixer; Fix solution: Use the equals() function to compare Strings", + "yes_example": "Examples of being judged as 'not using the equals() function to compare Strings'\n\n\nif (existingPet != null && existingPet.getName() == petName) {\n result.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}\nIn this code, both existingPet.getName() and petName are strings, but the comparison in the if statement uses == instead of equals() to compare the strings, so this is judged as 'not using the equals() function to compare Strings'.\n\n\n\nString isOk = \"ok\";\nif (\"ok\" == isOk) {\n result.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}\nIn this code, isOk is a string, but in the if statement, it is compared with \"ok\" using ==, not using equals() to compare the strings, it should use \"ok\".equals(isOk), so this is judged as 'not using the equals() function to compare Strings'.\n\n\n\nString str1 = \"Hello\";\nString str2 = \"Hello\";\nif (str1 == str2) {\n System.out.println(\"str1 and str2 reference the same object\");\n} else {\n System.out.println(\"str1 and str2 reference different objects\");\n}\nIn this code, if (str1 == str2) uses == to compare str1 and str2, not using equals() to compare the strings, it should use str1.equals(str2), so this is judged as 'not using the equals() function to compare Strings'.\n\n\n\nString str = \"This is string\";\nif (str == \"This is not str\") {\n return str;\n}\nIn this code, if (str == \"This is not str\") uses == to compare the strings, not using equals() to compare the strings, it should use \"This is not str\".equals(str), so this is judged as 'not using the equals() function to compare Strings'.\n", + "no_example": "Examples that cannot be judged as 'not using the equals() function to compare Strings'\n\n\nif (PROPERTY_VALUE_YES.equalsIgnoreCase(readWriteReqNode))\n formProperty.setRequired(true);\nIn this code, both PROPERTY_VALUE_YES and readWriteReqNode are strings. The comparison between PROPERTY_VALUE_YES and readWriteReqNode in the if statement uses equalsIgnoreCase (case-insensitive string comparison), which is also in line with using the equals() function to compare Strings. Therefore, this cannot be judged as 'not using the equals() function to compare Strings'\n\n\n\nString isOk = \"ok\";\nif (\"ok\".equals(isOk)) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}In this code, isOk is a string. In the if statement, the comparison with \"ok\" uses the equals() function to compare Strings, so this cannot be judged as 'not using the equals() function to compare Strings'\n" + }, + { + "id": 11, + "text": "Prohibit the direct use of string output for exceptions in logs, please use placeholders to pass the exception object", + "language": "Java", + "detail": "Defect type: Do not directly output exceptions as strings in logs, use placeholders to pass the exception object; Corresponding Fixer: ConcatExceptionFixer; Fix solution: Use placeholders to pass the exception object", + "yes_example": "Example of being judged as 'Prohibited to directly output exceptions using string in logs, please use placeholders to pass exception objects'\n\ntry {\n listenersNode = objectMapper.readTree(listenersNode.asText());\n} catch (Exception e) {\n LOGGER.info(\"Listeners node can not be read\", e);\n}In this code, the log output content is directly concatenated using the string \"Listeners node can not be read\". When outputting exceptions in logs, placeholders should be used to output exception information, rather than directly concatenating strings. Therefore, this is judged as 'Prohibited to directly output exceptions using string in logs, please use placeholders to pass exception objects'.\n", + "no_example": "Examples that cannot be judged as 'Prohibited to directly output exceptions using string in logs, please use placeholders to pass exception objects':\n\n\nPerson person = personService.getPerson(1);\nif (person == null) {\n LOGGER.error(PERSION_NOT_EXIT);\n}\nIn this code, PERSION_NOT_EXIT is a user-defined exception constant representing that the person does not exist, and it does not directly use the string 'person not exit' for concatenation, so this cannot be judged as 'Prohibited to directly output exceptions using string in logs, please use placeholders to pass exception objects'.\n\n\n\ntry {\n a = a + 1;\n} catch (Exception e) {\n Person person = personService.getPerson(1);\n LOGGER.info(person);\n}\nIn this code, the log output does not directly use string concatenation, but rather uses the Person object for output, so this cannot be judged as 'Prohibited to directly output exceptions using string in logs, please use placeholders to pass exception objects'.\n" + }, + { + "id": 12, + "text": "The finally block cannot be empty", + "language": "Java", + "detail": "Defect type: finally block cannot be empty; Corresponding Fixer: EmptyFinallyBlockFixer; Fix solution: Delete the empty finally block", + "yes_example": "Examples of being judged as 'finally block cannot be empty'\n\n\n\ntry {\n Persion persion = persionService.getPersion(1);\n return persion;\n} finally {\n \n}\nThis code has an empty finally block, so it is judged as 'finally block cannot be empty'\n\n\n\n\n\ntry {\n System.out.println(\"Inside try block\");\n} finally {\n // Empty finally block with no statements, this is a defect\n}\nThis code has an empty finally block, so it is judged as 'finally block cannot be empty'\n\n\n\n\n\ntry {\n int result = 10 / 0;\n} catch (ArithmeticException e) {\n e.printStackTrace();\n} finally {\n \n}\nThis code has an empty finally block, so it is judged as 'finally block cannot be empty'\n\n\n\n\n\ntry {\n String str = null;\n System.out.println(str.length());\n} catch (NullPointerException e) {\n e.printStackTrace();\n} finally {\n \n}\nThis code has an empty finally block, so it is judged as 'finally block cannot be empty'\n\n\n\n\n\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n e.printStackTrace();\n} finally {\n // Finally block with only comments\n // This is an empty finally block\n}\nThis code has an empty finally block, so it is judged as 'finally block cannot be empty'\n\n\n\n\n\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n e.printStackTrace();\n} finally {\n // Finally block with only empty lines\n \n}\nThis code has an empty finally block, so it is judged as 'finally block cannot be empty'\n\n", + "no_example": "Example that cannot be judged as 'finally block cannot be empty'\n\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){\n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}\nThis code's finally block contains non-comment content 'return null;', so this cannot be judged as 'finally block cannot be empty'\n" + }, + { + "id": 13, + "text": "try block cannot be empty", + "language": "Java", + "detail": "Defect type: try block cannot be empty; Corresponding Fixer: EmptyTryBlockFixer; Fix solution: Delete the entire try statement", + "yes_example": "Examples of being judged as 'try block cannot be empty'\n\npublic void getPersion() {\n\ttry {\n\n\t}\n\treturn null;\n}This code's try block is empty, so it is judged as 'try block cannot be empty'\n\n\n\npublic void demoFinallyBlock() {\n\ttry {\n\n\t} finally {\n\t\treturn null;\n\t}\n}This code's try block is empty, so it is judged as 'try block cannot be empty'\n\n\n\ntry {\n \n} catch (Exception e) {\n e.printStackTrace();\n}This code's try block is empty, so it is judged as 'try block cannot be empty'\n\n\n\ntry {\n // try block with only comments\n\t\n} catch (Exception e) {\n e.printStackTrace();\n}This code's try block contains only comments and blank lines, which can also be considered as having no content in the try block, so it is judged as 'try block cannot be empty'\n", + "no_example": "### Example that cannot be judged as 'try block cannot be empty'\n\ntry {\n\ta = a + 1;\n} catch (Exception e) {\n\te.printStackTrace();\n}\nThis code snippet contains non-comment content 'return null;' in the try block, so it cannot be judged as 'try block cannot be empty'\n" + }, + { + "id": 14, + "text": "Avoid unnecessary NULL or null checks on objects", + "language": "Java", + "detail": "Defect type: Avoid unnecessary NULL or null checks on objects; Corresponding Fixer: LogicalOpNpeFixer; Fix solution: Remove the logic of unnecessary NULL checks on objects", + "yes_example": "Examples of being judged as 'avoiding unnecessary NULL or null checks':", + "no_example": "Example that cannot be judged as 'avoiding unnecessary NULL or null checks'\n\nCat cat = catService.get(1);\nif (cat != null){\n\tretrun cat;\n}In this code, the object 'cat' is obtained through the service and it is uncertain whether it is null or not, so the condition 'cat != null' in the if statement is necessary, therefore this cannot be judged as 'avoiding unnecessary NULL or null checks'\n" + }, + { + "id": 15, + "text": "Avoid return in finally block", + "language": "Java", + "detail": "Defect type: Avoid return in finally block; Repair solution: No need for repair", + "yes_example": "Example judged as 'avoid return in finally block'", + "no_example": "Example that cannot be judged as 'avoiding return in finally block'\n\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\tLOGGER.info(PERSION_NOT_EXIT);\n\t}\n}\nThis code's finally block does not contain 'return', so it cannot be judged as 'avoiding return in finally block'\n" + }, + { + "id": 16, + "text": "Avoid empty static initialization", + "language": "Java", + "detail": "Defect type: Avoid empty static initialization; Corresponding Fixer: EmptyInitializerFixer; Fix solution: Delete the entire empty initialization block", + "yes_example": "Examples of being judged as 'Avoid empty static initialization'", + "no_example": "Example that cannot be judged as 'avoiding empty static initialization'\n\npublic class Cat {\n\tstatic {\n\t\t// Static initialization block\n\t\tcat = null;\n\t}\n}\nThis code has a static block with content, not empty, and the static initialization block contains non-commented code with actual logic, so this cannot be judged as 'avoiding empty static initialization'\n" + }, + { + "id": 17, + "text": "Avoid risks of improper use of calendar", + "language": "Java", + "detail": "Defect type: Avoid improper usage risks of calendar classes; Fix solution: Use LocalDate from the java.time package in Java 8 and above", + "yes_example": "Examples of being judged as 'avoiding improper use of calendar class risks'\n\nprivate static final Calendar calendar = new GregorianCalendar(2020, Calendar.JANUARY, 1);\nThe Calendar and GregorianCalendar in this code are not thread-safe, so this is judged as 'avoiding improper use of calendar class risks'\n", + "no_example": "Examples that cannot be judged as 'avoiding improper use of calendar class risks'" + }, + { + "id": 18, + "text": "To convert a collection to an array, you must use the toArray(T[] array) method of the collection, passing in an array of the exact same type, with a size equal to list.size()", + "language": "Java", + "detail": "Defect type: When converting a collection to an array, you must use the toArray(T[] array) method of the collection, passing an array of the exact same type, with a size equal to list.size(); Corresponding Fixer: ClassCastExpWithToArrayFixer; Repair solution: Use the toArray(T[] array) method of the collection, and pass an array of the exact same type", + "yes_example": "Example judged as 'When converting a collection to an array, you must use the collection's toArray(T[] array) method, passing an array of exactly the same type, with the size being list.size()'", + "no_example": "Example that cannot be judged as 'using the method of converting a collection to an array, you must use the toArray(T[] array) of the collection, passing in an array of exactly the same type, and the size is list.size()':" + }, + { + "id": 19, + "text": "Prohibit the use of NULL or null for comparison in equals()", + "language": "Java", + "detail": "Defect type: Prohibit using NULL or null for comparison in equals(); Corresponding Fixer: EqualsNullFixer; Fixing solution: Use Object's null check function for comparison", + "yes_example": "Examples of being judged as 'Prohibited to use NULL or null for comparison in equals()'", + "no_example": "Examples that cannot be judged as 'prohibiting the use of NULL or null for comparison in equals()'" + }, + { + "id": 20, + "text": "switch statement block cannot be empty", + "language": "Java", + "detail": "Defect type: switch statement block cannot be empty; Corresponding Fixer: EmptySwitchStatementsFix; Fix solution: Delete the entire empty switch statement block", + "yes_example": "Examples of being judged as 'switch statement block cannot be empty'\n\nswitch (number) {\n \n}This code is a switch statement block, but it contains no content, so it is judged as 'switch statement block cannot be empty'\n\n\n\nswitch (number) {\n // This is a switch statement block\n}This code is a switch statement block, which contains content, but the content is only comments without actual logic, so it is judged as 'switch statement block cannot be empty'\n", + "no_example": "Example that cannot be judged as 'switch statement block cannot be empty'\n\nswitch (number) {\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n}\nThis code is a switch statement block that contains content, and the content includes non-commented code with actual logic, so it cannot be judged as 'switch statement block cannot be empty'.\n" + }, + { + "id": 21, + "text": "When performing type coercion, no spaces are needed between the right parenthesis and the coercion value.", + "detail": "Defect type: When performing type coercion, no space is required between the right parenthesis and the coercion value; Fix solution: When performing type coercion, no space is required between the right parenthesis and the coercion value.", + "language": "Java", + "yes_example": "Examples judged as 'When performing type casting, no space is needed between the closing parenthesis and the cast value'", + "no_example": "Examples that cannot be judged as 'When performing type coercion, no spaces are required between the right parenthesis and the coercion value'" + }, + { + "id": 22, + "text": "Method parameters must have a space after the comma when defined and passed", + "detail": "Defect type: In the definition and passing of method parameters, a space must be added after the comma for multiple parameters; Repair solution: In the definition and passing of method parameters, a space must be added after the comma for multiple parameters.", + "language": "Java", + "yes_example": "Example of being judged as 'Method parameters must have a space after the comma when defined and passed'", + "no_example": "Examples that cannot be judged as 'Method parameters must have a space after the comma both in definition and when passed'" + }, + { + "id": 23, + "text": "Prohibit the use of the BigDecimal(double) constructor to convert a double value to a BigDecimal object", + "detail": "Defect type: Do not use the constructor BigDecimal(double) to convert a double value to a BigDecimal object; Repair solution: It is recommended to use the valueOf method of BigDecimal.", + "language": "Java", + "yes_example": "Example of being judged as 'Prohibited to use the constructor BigDecimal(double) to convert a double value to a BigDecimal object'", + "no_example": "Examples that cannot be considered as 'prohibiting the use of the BigDecimal(double) constructor to convert a double value to a BigDecimal object'" + }, + { + "id": 24, + "text": "No extra semicolons allowed", + "detail": "Defect type: extra semicolon; Fix solution: remove extra semicolon", + "yes_example": "Example of being judged as 'cannot have extra semicolons'", + "no_example": "Examples that cannot be judged as 'cannot have extra semicolons'\n\nwhile (True) {\n\ta = a + 1;\n\tbreak;\n}This code requires every semicolon, so it can be judged as 'cannot have extra semicolons'\n" + }, + { + "id": 25, + "text": "Non-thread-safe SimpleDateFormat usage must be synchronized at the function or code block level", + "detail": "Defect type: Non-thread-safe SimpleDateFormat usage; Fix solution: Add synchronized modifier at the function or code block level or use other thread-safe methods", + "yes_example": "Example of 'Non-thread-safe SimpleDateFormat usage, must be used with synchronized at the function or block level'", + "no_example": "Example that cannot be judged as 'Unsafe use of SimpleDateFormat, which must be used at the function or code block level with synchronized':\n\npublic synchronized void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}\nThis code is protected by a synchronized block on the function 'formatDate', ensuring thread safety, so it cannot be judged as 'Unsafe use of SimpleDateFormat, which must be used at the function or code block level with synchronized'.\n" + }, + { + "id": 26, + "text": "Naming does not follow the camel case specification. Class names should use UpperCamelCase style, while method names, parameter names, member variables, and local variables should all use lowerCamelCase style.", + "detail": "Defect type: Not following camel case naming convention; Fix solution: Class names should use UpperCamelCase style, method names, parameter names, member variables, and local variables should use lowerCamelCase style.", + "language": "Java", + "yes_example": "Examples of being judged as 'not following the camel case naming convention'\n\npublic class myClass {\n private int MyVariable;\n public void MyMethod() {}\n}\nThis code does not follow the camel case naming convention for class names, member variables, and method names, so it is judged as a naming convention issue.\n", + "no_example": "Examples that cannot be judged as 'not following the camel case naming convention'\n\npublic class MyClass {\n private int myVariable;\n public void myMethod() {}\n}\nThe class name, member variable, and method name in this code all follow the camel case naming convention, so it cannot be judged as a naming convention issue.\n" + }, + { + "id": 27, + "text": "Abstract class names start with Abstract or Base; exception class names end with Exception; test class names begin with the name of the class they are testing and end with Test", + "detail": "Defect type: Naming convention; Solution: Abstract class names should start with Abstract or Base, exception class names should end with Exception, and test class names should start with the name of the class they are testing and end with Test.", + "language": "Java", + "yes_example": "Examples of being judged as 'naming conventions'\n\npublic class MyAbstractClass {}\npublic class MyExceptionClass {}\npublic class TestMyClass {}\nThe naming of the abstract class, exception class, and test class in this code does not conform to the conventions, so it is judged as a naming convention issue.\n", + "no_example": "Examples that cannot be judged as 'naming conventions'" + }, + { + "id": 28, + "text": "Avoid adding the 'is' prefix to any boolean type variables in POJO classes", + "detail": "Defect type: Naming convention; Fix solution: Do not prefix boolean variables in POJO classes with 'is'.", + "language": "Java", + "yes_example": "Examples of being judged as 'naming convention' issues\n\npublic class User {\n private boolean isActive;\n}\nIn this code, the boolean type variable has the 'is' prefix, so it is judged as a naming convention issue.\n", + "no_example": "Examples that cannot be judged as 'naming conventions'" + }, + { + "id": 29, + "text": "Eliminate completely non-standard English abbreviations to avoid confusion when interpreting them.", + "detail": "Defect type: Naming conventions; Solution: Avoid using non-standard English abbreviations to ensure code readability.", + "language": "Java", + "yes_example": "Examples of being judged as 'naming conventions'\n\npublic class CfgMgr {\n private int cnt;\n}\nIn this code, the class name and variable name use non-standard English abbreviations, so they are judged as naming convention issues.\n", + "no_example": "Examples that cannot be judged as 'naming conventions'" + }, + { + "id": 30, + "text": "Avoid using magic characters and numbers, they should be declared as constants", + "detail": "Defect type: Avoid using magic characters and numbers, they should be declared as constants; Fix solution: Define magic values as constants.", + "language": "Java", + "yes_example": "Examples of being judged as 'avoiding magic characters and numbers, should be declared as constants'", + "no_example": "Examples that cannot be judged as 'avoiding magic characters and numbers, should be declared as constants'" + }, + { + "id": 31, + "text": "When assigning values to long or Long, use uppercase L after the number, not lowercase l. The suffix for floating-point numbers should be uppercase D or F.", + "detail": "Defect type: Code specification; Repair solution: Use uppercase L when assigning values to long or Long, and use uppercase D or F as suffixes for floating-point type values.", + "language": "Java", + "yes_example": "Examples of being judged as 'code specification'", + "no_example": "Examples that cannot be judged as 'code specification'" + }, + { + "id": 32, + "text": "If the curly braces are empty, simply write {} without line breaks or spaces inside the braces; if it is a non-empty code block, then: 1) Do not line break before the left curly brace. 2) Line break after the left curly brace. 3) Line break before the right curly brace. 4) Do not line break after the right curly brace if there is code like 'else' following it; the right curly brace indicating termination must be followed by a line break.", + "detail": "Defect type: code formatting; Fix solution: follow the curly brace usage standard.", + "language": "Java", + "yes_example": "Example of being judged as 'code format'", + "no_example": "Examples that cannot be judged as 'code format' issues\n\npublic class BracketExample {\n public void method() {\n if (true) {\n // do something\n }\n }\n}\nThe use of curly braces in this code is in accordance with the standards, so it cannot be judged as a code format issue.\n" + }, + { + "id": 33, + "text": "No space is needed between the left parenthesis and the adjacent character; no space is needed between the right parenthesis and the adjacent character; and a space is required before the left brace.", + "detail": "Defect type: code formatting; Fix solution: follow the usage rules for brackets and spaces.", + "language": "Java", + "yes_example": "Example of being judged as 'code format'\n\npublic class SpaceExample {\n public void method (){\n }\n}\nThe use of brackets and spaces in this code does not conform to the standard, so it is judged as a code format issue.\n", + "no_example": "Examples that cannot be judged as 'code specification'\n\npublic class SpaceExample {\n public void method() {}\n}\nThis code uses brackets and spaces in accordance with the specification, so it cannot be judged as a code format issue.\n" + }, + { + "id": 34, + "text": "Reserved words such as if / for / while / switch / do must be separated from the parentheses on both sides by spaces.", + "detail": "Defect type: code format; Fix solution: add spaces between reserved words and parentheses.", + "language": "Java", + "yes_example": "Example of being judged as 'code specification'\n\npublic class KeywordExample {\n public void method() {\n if(true) {\n }\n }\n}\nIn this code, there is no space between the if keyword and the parentheses, so it is judged as a code formatting issue.\n", + "no_example": "Examples that cannot be judged as 'code specification'" + }, + { + "id": 35, + "text": "All value comparisons between integer wrapper class objects should be done using the equals method", + "detail": "Defect type: Code specification; Repair solution: Use the equals method for value comparison between integer wrapper class objects.", + "language": "Java", + "yes_example": "Examples of being judged as 'code specification'", + "no_example": "### Example that cannot be judged as 'code specification'\n\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a.equals(b)) {\n }\n }\n}\nIn this code, the equals method is used to compare integer wrapper class objects, so it cannot be judged as a code specification issue.\n" + }, + { + "id": 36, + "text": "For comparing BigDecimal values, the compareTo() method should be used instead of the equals() method.", + "detail": "Defect type: The equality comparison of BigDecimal should use the compareTo() method instead of the equals() method; Fix solution: Use the compareTo() method for comparison.", + "language": "Java", + "yes_example": "Example of being judged as 'For BigDecimal equality comparison, the compareTo() method should be used instead of the equals() method'\n\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.equals(b)) {\n // This code will return false because the equals() method compares precision\n}\n", + "no_example": "Examples that cannot be judged as 'For BigDecimal equality comparison, the compareTo() method should be used instead of the equals() method'" + }, + { + "id": 37, + "text": "Prohibit having both isXxx() and getXxx() methods for the same attribute xxx in a POJO class.", + "detail": "Defect type: Duplicate getter methods in POJO class; Fix solution: Ensure only one getter method exists.", + "language": "Java", + "yes_example": "Example of being judged as 'Prohibited to have both isXxx() and getXxx() methods for the corresponding attribute xxx in a POJO class'", + "no_example": "Examples that cannot be judged as 'Prohibiting the existence of both isXxx() and getXxx() methods for the corresponding attribute xxx in a POJO class'" + }, + { + "id": 38, + "text": "When formatting dates, use the lowercase 'y' uniformly to represent the year in the pattern.", + "detail": "Defect type: date formatting error; Fix solution: use lowercase y to represent the year.", + "language": "Java", + "yes_example": "Example judged as 'When formatting dates, use lowercase y for the year in the pattern'", + "no_example": "Examples that cannot be judged as 'When formatting dates, use lowercase y for the year in the pattern'" + }, + { + "id": 39, + "text": "Prohibited from using in any part of the program: 1) java.sql.Date 2) java.sql.Time 3) java.sql.Timestamp.", + "detail": "Defect type: used date classes from the java.sql package; Fix solution: use date classes from the java.time package.", + "language": "Java", + "yes_example": "Examples of being judged as \"Prohibited from using in any part of the program: 1) java.sql.Date 2) java.sql.Time 3) java.sql.Timestamp\"", + "no_example": "Examples that cannot be judged as 'Prohibited to use in any part of the program: 1) java.sql.Date 2) java.sql.Time 3) java.sql.Timestamp'" + }, + { + "id": 40, + "text": "Determine if all elements within a collection are empty using the isEmpty() method, rather than using the size() == 0 approach.", + "detail": "Defect type: Incorrect method for checking empty collection; Fix solution: Use isEmpty() method.", + "language": "Java", + "yes_example": "Example of being judged as 'To determine if all elements within a collection are empty, use the isEmpty() method instead of the size() == 0 approach'\n\nList list = new ArrayList<>();\nif (list.size() == 0) {\n // Empty logic\n}\n", + "no_example": "Examples that cannot be considered as 'judging whether all elements within a set are empty using the isEmpty() method instead of the size() == 0 approach'" + }, + { + "id": 41, + "text": "Whenever you override equals, you must also override hashCode.", + "detail": "Defect type: hashCode method not overridden; Fix solution: Override both equals and hashCode methods.", + "language": "Java", + "yes_example": "An example where it is judged that 'if you override equals, you must also override hashCode'", + "no_example": "An example where it cannot be judged as 'Whenever you override equals, you must also override hashCode'" + }, + { + "id": 42, + "text": "When using the Map methods keySet() / values() / entrySet() to return a collection object, you cannot perform element addition operations on it, otherwise a UnsupportedOperationException will be thrown.", + "detail": "Defect type: Adding operations to the collections returned by keySet() / values() / entrySet() of a Map; Repair solution: Avoid adding operations to these collections.", + "language": "Java", + "yes_example": "Example of being judged as 'When using the Map methods keySet() / values() / entrySet() to return a collection object, you cannot perform element addition operations on it, otherwise a UnsupportedOperationException exception will be thrown'", + "no_example": "Example that cannot be judged as 'When using the methods keySet() / values() / entrySet() of Map to return a collection object, it is not allowed to perform element addition operations on it, otherwise a UnsupportedOperationException will be thrown'" + }, + { + "id": 43, + "text": "Do not perform element removal / addition operations within a foreach loop. Use the iterator method for removing elements. If concurrent operations are required, the iterator must be synchronized.", + "detail": "Defect type: performing remove / add operations on elements within a foreach loop; Repair solution: use iterator to perform remove operations on elements.", + "language": "Java", + "yes_example": "Example of being judged as 'Do not perform element remove / add operations within a foreach loop. Use the iterator method for removing elements; if concurrent operations are required, the iterator must be synchronized.'", + "no_example": "Example that cannot be judged as 'Do not perform element remove / add operations inside a foreach loop. Use the iterator method for removing elements. If concurrent operations are required, the iterator should be synchronized.'\n\nList list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nIterator iterator = list.iterator();\nwhile (iterator.hasNext()) {\n String s = iterator.next();\n if (s.equals(\"a\")) {\n iterator.remove();\n }\n}\n" + }, + { + "id": 44, + "text": "Class, class attributes, and class methods must use Javadoc specifications for comments, using the format /** content */, and must not use the // xxx format.", + "detail": "Defect type: Comments do not conform to Javadoc standards; Solution: Use Javadoc-compliant comment format.", + "language": "Java", + "yes_example": "Examples of being judged as 'class, class attribute, class method annotations must use Javadoc specification, using the format /** content */, not using the // xxx method'", + "no_example": "Examples that cannot be judged as 'Class, class attribute, and class method comments must follow the Javadoc specification, using the /** content */ format, not the // xxx format'" + }, + { + "id": 45, + "text": "All abstract methods (including methods in interfaces) must be annotated with Javadoc comments", + "detail": "Defect type: All abstract methods (including methods in interfaces) must be annotated with Javadoc; Repair solution: Add Javadoc comments to all abstract methods (including methods in interfaces), in addition to the return value, parameter exception description, it must also indicate what the method does and what function it implements.", + "language": "Java", + "yes_example": "Example of being judged as 'All abstract methods (including methods in interfaces) must be annotated with Javadoc'", + "no_example": "Example that cannot be judged as 'all abstract methods (including methods in interfaces) must be annotated with Javadoc comments'" + }, + { + "id": 46, + "text": "Usage guidelines for single-line and multi-line comments within methods", + "detail": "Defect type: Improper use of comments; Repair solution: Single-line comments inside the method, start a new line above the commented statement, use // for comments. Multi-line comments inside the method use /* */ comments, and pay attention to aligning with the code.", + "language": "Java", + "yes_example": "### Examples of being judged as 'Improper Use of Comments'\n\npublic void exampleMethod() {\n int a = 1; // Initialize variable a\n int b = 2; /* Initialize variable b */\n}\nThe single-line and multi-line comments in this code are not used according to the standard, so they are judged as improper use of comments.\n", + "no_example": "Examples that cannot be judged as 'improper use of comments'\n\npublic void exampleMethod() {\n // Initialize variable a\n int a = 1;\n /*\n * Initialize variable b\n */\n int b = 2;\n}\nThis code uses single-line and multi-line comments according to the standard, so it cannot be judged as improper use of comments.\n" + }, + { + "id": 47, + "text": "All enumeration type fields must have comments", + "detail": "Defect type: Enumeration type field lacks comments; Fix plan: Add comments to all enumeration type fields to explain the purpose of each data item.", + "language": "Java", + "yes_example": "Example of being judged as 'Enumeration type field lacks comments'\n\npublic enum Status {\n ACTIVE,\n INACTIVE\n}\nThe enumeration type fields in this code are not commented, so they are judged as lacking comments for enumeration type fields.\n", + "no_example": "Examples that cannot be judged as 'missing comments for enum fields'\n\npublic enum Status {\n /**\n * Active status\n */\n ACTIVE,\n /**\n * Inactive status\n */\n INACTIVE\n}\nThis code has comments for the enum fields, so it cannot be judged as missing comments for enum fields.\n" + }, + { + "id": 48, + "text": "The finally block must close resource objects and stream objects.", + "detail": "Defect type: resource objects and stream objects are not closed in the finally block; Fix solution: Close resource objects and stream objects in the finally block, and use try-catch for exceptions.", + "language": "Java", + "yes_example": "Example of being judged as 'resource object, stream object not closed in finally block'", + "no_example": "Examples that cannot be judged as 'resource objects, stream objects not closed in the finally block'" + }, + { + "id": 49, + "text": "Constant names should be in all uppercase, with words separated by underscores.", + "detail": "Defect type: Constant naming is not standardized; Fix solution: Constant names should be all uppercase, words separated by underscores, and strive for complete and clear semantic expression, do not be afraid of long names.", + "language": "Java", + "yes_example": "Examples of being judged as 'Constant names should be in all uppercase, with words separated by underscores'", + "no_example": "Examples that cannot be judged as 'constant names should be all uppercase, with words separated by underscores'" + }, + { + "id": 50, + "text": "Spaces are required on both sides of any binary or ternary operator.", + "detail": "Defect type: Lack of space around operators; Fix solution: Any binary or ternary operator should have a space on both sides.", + "language": "Java", + "yes_example": "Examples of being judged as 'Any binary or ternary operator must have spaces on both sides'", + "no_example": "Examples that cannot be judged as 'any binary, ternary operator needs a space on both sides'" + }, + { + "id": 51, + "text": "Avoid using from import *", + "detail": "Defect type: Avoid using 'from import *', importing everything can cause naming conflicts; Solution: Each sub-dependency used should be imported separately.", + "language": "Python", + "yes_example": "Example of being judged as 'avoid using from import *'", + "no_example": "Examples that cannot be judged as 'avoid using from import *'" + }, + { + "id": 52, + "text": "Avoid using the __import__() function to dynamically import modules", + "detail": "Defect type: Avoid using __import__() function to dynamically import modules; Repair solution: Use standard import statements.", + "language": "Python", + "yes_example": "Example of being judged as 'dynamically importing modules using the __import__() function'", + "no_example": "Examples that cannot be judged as 'dynamically importing modules using the __import__() function'" + }, + { + "id": 53, + "text": "Import statements are not grouped in the order of standard library imports, related third-party imports, and local application/library specific imports.", + "detail": "Defect type: Import statements are not grouped in the order of standard library imports, related third-party imports, and local application/library specific imports; Solution: Group import statements in order.", + "language": "Python", + "yes_example": "Examples of being judged as 'import statements not grouped in the order of standard library imports, related third-party imports, and local application/library specific imports'", + "no_example": "Example that cannot be judged as 'import statements not grouped in the order of standard library imports, related third-party imports, local application/library specific imports'" + }, + { + "id": 54, + "text": "Avoid unused function parameters", + "detail": "Defect type: Avoid unused function parameters; Fix solution: Remove unused function parameters.", + "language": "Python", + "yes_example": "Examples of being judged as 'avoid unused function parameters'", + "no_example": "Examples that cannot be judged as 'avoiding unused function parameters'" + }, + { + "id": 55, + "text": "Use is not None to check if a variable is not None", + "detail": "Defect type: Not using 'is not None' to check if a variable is not None; Fix solution: Use 'is not None' to check.", + "language": "Python", + "yes_example": "Example of being judged as 'not using is not None to check if a variable is not None'", + "no_example": "Examples that cannot be judged as 'not using is not None to check if a variable is not None'" + }, + { + "id": 56, + "text": "Avoid using == or != to compare the equivalence of object instances", + "detail": "Defect type: Using == or != to compare object instances for equivalence; Fix solution: Should use equals for comparison.", + "language": "Python", + "yes_example": "Example of being judged as 'using == or != to compare the equivalence of object instances'", + "no_example": "Examples that cannot be judged as 'using == or != to compare the equivalence of object instances'" + }, + { + "id": 57, + "text": "Avoid using single-letter variable names, use descriptive variable names", + "detail": "Defect type: Avoid using single-letter variable names, use descriptive variable names; Fix solution: Use descriptive variable names.", + "language": "Python", + "yes_example": "Examples of being judged as 'avoid using single-letter variable names, use descriptive variable names'", + "no_example": "Examples that cannot be judged as 'avoid using single-letter variable names, use descriptive variable names'" + }, + { + "id": 58, + "text": "Constant names use all uppercase letters and separate words with underscores", + "detail": "Defect type: Constant naming does not use all uppercase letters or does not use underscores to separate; Repair solution: Use all uppercase letters for constant naming and separate with underscores.", + "language": "Python", + "yes_example": "Example of being judged as 'Constant naming not using all uppercase letters and separated by underscores'", + "no_example": "Examples that cannot be judged as 'constant naming not using all uppercase letters and separated by underscores'" + }, + { + "id": 59, + "text": "Class names should use camel case (CamelCase)", + "detail": "Defect type: Class name not using camel case; Repair solution: Use camel case for class names.", + "language": "Python", + "yes_example": "Examples of being judged as 'class name not using CamelCase'", + "no_example": "Examples that cannot be judged as 'class name not using CamelCase'" + }, + { + "id": 60, + "text": "Try to use the with statement to manage resources as much as possible", + "detail": "Defect type: Not using the with statement to manage resources; Fix solution: Use the with statement to manage resources.", + "language": "Python", + "yes_example": "Example of being judged as 'not using the with statement to manage resources'", + "no_example": "Examples that cannot be judged as 'not using the with statement to manage resources'" + }, + { + "id": 61, + "text": "Avoid using except or generic Exception to catch all exceptions, specify the exception type instead.", + "detail": "Defect type: catch all exceptions; Fix solution: specify specific exception types.", + "language": "Python", + "yes_example": "Examples judged as 'catching all exceptions using except:' and 'throwing a generic Exception exception'", + "no_example": "Example that cannot be judged as 'using except: to catch all exceptions'" + }, + { + "id": 62, + "text": "Avoid manual string concatenation whenever possible", + "detail": "Defect type: manual string concatenation; Fix solution: use formatted strings or join method.", + "language": "Python", + "yes_example": "Examples of being judged as 'manual string concatenation'", + "no_example": "Examples that cannot be judged as 'manual string concatenation'" + }, + { + "id": 63, + "text": "Avoid using magic characters and numbers, should be declared as constants", + "detail": "Defect type: Using magic characters and numbers; Fix solution: Declare them as constants.", + "language": "Python", + "yes_example": "Examples of being judged as 'having magic characters and numbers'", + "no_example": "Examples that cannot be judged as 'containing magic characters and numbers'" + }, + { + "id": 64, + "text": "Boolean variable judgment does not require explicit comparison", + "detail": "Defect type: explicit comparison of boolean variables; fix solution: directly use boolean variables for judgment.", + "language": "Python", + "yes_example": "Examples of being judged as 'explicit comparison of boolean variables'", + "no_example": "Examples that cannot be judged as 'explicit comparison of boolean variables'" + }, + { + "id": 65, + "text": "Avoid using type() to check object types", + "detail": "Defect type: Avoid using type() to check object type; Fix solution: Use isinstance() function.", + "language": "Python", + "yes_example": "Example of being judged as 'avoid using type() to check object type'", + "no_example": "Examples that cannot be judged as 'avoid using type() to check object type'" + }, + { + "id": 66, + "text": "Avoid using os.system() to call external commands", + "detail": "Defect type: Using os.system() to call external commands; Fix solution: Use the subprocess module.", + "language": "Python", + "yes_example": "Examples of being judged as 'using os.system() to call external commands'\nos.system('ls -l')\nos.system('ls -l')", + "no_example": "Examples that cannot be judged as 'using os.system() to call external commands'" + }, + { + "id": 67, + "text": "Create read-only properties using the @property decorator instead of modifying properties", + "detail": "Defect type: Creating modifiable properties using the @property decorator; Fix solution: Only use the @property decorator to create read-only properties.", + "language": "Python", + "yes_example": "Examples of being judged as 'using the @property decorator to create modifiable attributes'", + "no_example": "Examples that cannot be judged as 'using the @property decorator to create a modifiable attribute'" + }, + { + "id": 68, + "text": "When using indexing or slicing, do not add spaces inside the brackets or colons.", + "detail": "Defect type: adding spaces inside brackets or colons for indexing or slicing; Repair solution: remove spaces inside brackets or colons.", + "language": "Python", + "yes_example": "Examples judged as 'using spaces inside brackets or colons when using indexing or slicing'", + "no_example": "Examples that cannot be judged as 'adding spaces inside brackets or colons when using indexes or slices'" + }, + { + "id": 69, + "text": "Do not add a space before a comma, semicolon, or colon, but add a space after them", + "detail": "Defect type: adding a space before a comma, semicolon, or colon, or not adding a space after them; Fix solution: do not add a space before a comma, semicolon, or colon, but add a space after them.", + "language": "Python", + "yes_example": "Examples judged as 'adding a space before a comma, semicolon, or colon, or not adding a space after them'", + "no_example": "Examples that cannot be judged as 'adding a space before a comma, semicolon, or colon, or not adding a space after them'" + }, + { + "id": 70, + "text": "For binary operators, there should be spaces on both sides", + "detail": "Defect type: no spaces around binary operators; Fix solution: add spaces around binary operators", + "language": "Python", + "yes_example": "Example of being judged as 'no space around binary operator'", + "no_example": "Examples that cannot be judged as 'no space on both sides of the binary operator'" + }, + { + "id": 71, + "text": "Avoid using Python keywords as variable or function names", + "detail": "Defect type: Using Python keywords as variable names or function names; Repair solution: Use non-keyword names.", + "language": "Python", + "yes_example": "Examples of being judged as 'using Python keywords as variable names or function names'", + "no_example": "Examples that cannot be judged as 'using Python keywords as variable names or function names'\ndef my_function():\n pass\nnumber = 5" + }, + { + "id": 72, + "text": "Avoid using special characters as variable names/method names/class names, such as $ or @", + "detail": "Defect type: Using special characters as variable names/method names/class names; Repair solution: Use legal variable names.", + "language": "Python", + "yes_example": "Examples of being judged as 'using special characters as variable names/method names/class names, such as $ or @'", + "no_example": "Examples that cannot be judged as 'using special characters as variable names/method names/class names, such as $ or @'" + }, + { + "id": 73, + "text": "Avoid using raise to rethrow the current exception, as it will lose the original stack trace.", + "detail": "Defect type: Re-raise the current exception using raise; Fix solution: Use the raise ... from ... syntax.", + "language": "Python", + "yes_example": "Examples of being judged as 'avoid using raise to rethrow the current exception, as it will lose the original stack trace'", + "no_example": "Examples that cannot be judged as 'avoid using raise to rethrow the current exception, as it will lose the original stack trace'" + }, + { + "id": 74, + "text": "Avoid using pass in except block, as it will catch and ignore the exception", + "detail": "Defect type: using pass in except block; Fix solution: handle the exception or log the error.", + "language": "Python", + "yes_example": "Examples of being judged as 'using pass in except block'", + "no_example": "Examples that cannot be judged as 'using pass in an except block'" + }, + { + "id": 75, + "text": "Avoid using assert statements to perform important runtime checks", + "detail": "Defect type: Using assert statements for important runtime checks; Fix solution: Use explicit condition checks and exception handling.", + "language": "Python", + "yes_example": "Example of being judged as 'using assert statements to perform important runtime checks'", + "no_example": "Examples that cannot be judged as 'using assert statements to perform important runtime checks'" + }, + { + "id": 76, + "text": "Avoid using eval() and exec(), these functions may bring security risks", + "detail": "Defect type: Use of eval() and exec() functions; Repair solution: Use secure alternatives.", + "language": "Python", + "yes_example": "Examples of being judged as 'using eval() and exec()'\n\n eval('print(1)') \n\n \n exec('a = 1') \n", + "no_example": "Examples that cannot be judged as 'using eval() and exec()'\n\ncompiled_code = compile('print(1)', '', 'exec')\nexec(compiled_code)\n" + }, + { + "id": 77, + "text": "Avoid using sys.exit(), use exceptions to control program exit instead.", + "detail": "Defect type: Avoid using sys.exit(), should use exceptions to control program exit; Repair solution: Use exceptions to control program exit.", + "language": "Python", + "yes_example": "Examples of being judged as 'avoid using sys.exit(), should use exceptions to control program exit'", + "no_example": "Examples that cannot be judged as 'avoid using sys.exit(), should use exceptions to control program exit'" + }, + { + "id": 78, + "text": "Avoid using time.sleep() for thread synchronization, and instead use synchronization primitives such as locks or events.", + "detail": "Defect type: Using time.sleep() for thread synchronization; Fix solution: Use synchronization primitives.", + "language": "Python", + "yes_example": "Examples of being judged as 'using time.sleep() for thread synchronization'", + "no_example": "Examples that cannot be judged as 'using time.sleep() for thread synchronization'" + }, + { + "id": 79, + "text": "Avoid exceeding 79 characters per line of code", + "detail": "Defect type: Avoid exceeding 79 characters per line of code; Fix solution: Format long lines of code into multiple lines.", + "language": "Python", + "yes_example": "Example of being judged as 'avoiding more than 79 characters per line of code'", + "no_example": "Examples that cannot be judged as 'each line of code should not exceed 79 characters'" + }, + { + "id": 80, + "text": "Functions and class definitions at the module level are separated by two blank lines, and method definitions within a class are separated by one blank line", + "detail": "Defect type: There is no separation of two blank lines between function and class definitions at the module level, and no separation of one blank line between method definitions within the class; Solution: Add blank lines according to the specification.", + "language": "Python", + "yes_example": "Example of being judged as 'Functions at the module level are not separated by two blank lines, and method definitions within a class are not separated by one blank line'", + "no_example": "Examples that cannot be judged as 'There is no two blank lines between module-level function and class definitions, and no one blank line between method definitions inside a class'" + }, + { + "id": 81, + "text": "Use lowercase letters and underscores to separate variable and function names", + "detail": "Defect type: Variable and function naming do not conform to the lowercase letters and underscore separation method; Repair solution: Use lowercase letters and underscore separation method for naming.", + "language": "Python", + "yes_example": "Examples of being judged as 'not using lowercase letters and underscores to separate variable and function names'", + "no_example": "Examples that cannot be judged as 'naming variables and functions without using lowercase letters and underscores to separate them'" + }, + { + "id": 82, + "text": "It is not allowed to use the print() function to record logs, use the logging module, etc. to record logs", + "detail": "Defect type: Using the print() function to log; Fix solution: Use the logging module to log.", + "language": "Python", + "yes_example": "Examples of being judged as 'using the print() function to log'", + "no_example": "Examples that cannot be considered as 'using the print() function to log'" + } +] \ No newline at end of file diff --git a/metagpt/ext/cr/points_cn.json b/metagpt/ext/cr/points_cn.json new file mode 100644 index 000000000..10fc951c0 --- /dev/null +++ b/metagpt/ext/cr/points_cn.json @@ -0,0 +1,656 @@ +[ + { + "id": 1, + "text": "避免未使用的临时变量", + "language": "Java", + "detail": "缺陷类型:避免未使用的临时变量;对应Fixer:UnusedLocalVariableFixer;修复方案:删除未使用的临时变量", + "yes_example": "### 被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic String initCreationForm(Map model) {\n\t\tOwner owner = new Owner();\n\t\tmodel.put(\"owner\", owner);\n\t\tint unusedVar = 10;\n\t\treturn VIEWS_OWNER_CREATE_OR_UPDATE_FORM;\n\t}\n上述代码中unusedVar变量未被使用,所以这个被判定为\"避免未使用的临时变量\"\n\n<例子2>\nint unusedVariable = 10;\nSystem.out.println(\"Hello, World!\");\n这段代码的变量\"unusedVariable\"未被使用或者引用,所以这个不能判定为\"避免未使用的临时变量\"\n", + "no_example": "### 不能被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map transientVariables) {\nthrow new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}\n这段代码的\"transientVariables\"是函数参数而不是临时变量,虽然transientVariables没有被使用或者引用,但是这个也不能判定为\"避免未使用的临时变量\"\n\n\n<例子2>\npublic class TriggerCmd extends NeedsActiveExecutionCmd {\n protected Map transientVariables;\n public TriggerCmd(Map transientVariables) {\n this.transientVariables = transientVariables;\n }\n}\n上述代码中transientVariables不属于临时变量,它是类属性,且它在构造函数中被使用,所以这个不能被判定为\"避免未使用的临时变量\"\n" + }, + { + "id": 2, + "text": "不要使用 System.out.println 去打印", + "language": "Java", + "detail": "缺陷类型:不要使用 System.out.println 去打印;对应Fixer:SystemPrintlnFixer;修复方案:注释System.out.println代码", + "yes_example": "### 被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nSystem.out.println(\"Initializing new owner form.\");\n上述代码使用了\"System.out.println\"进行打印,所以这个被判定为\"不要使用 System.out.println 去打印\"\n", + "no_example": "### 不能被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nthrow new IllegalStateException(\"There is no authenticated user, we need a user authenticated to find tasks\");\n上述代码是抛出异常的代码,没有使用\"System.out.print\",所以这个不能被判定为\"不要使用 System.out.println 去打印\"\n" + }, + { + "id": 3, + "text": "避免函数中未使用的形参", + "language": "Java", + "detail": "缺陷类型:避免函数中未使用的形参;修复方案:忽略", + "yes_example": "### 被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map transientVariables) {\n throw new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}这段代码中的形参\"transientVariables\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n\n\n<例子2>\nprotected void modifyFetchPersistencePackageRequest(PersistencePackageRequest ppr, Map pathVars) {}\n这段代码中的形参\"ppr\"和\"pathVars\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n", + "no_example": "### 不能被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic String processFindForm(@RequestParam(value = \"pageNo\", defaultValue = \"1\") int pageNo) {\n\tlastName = owner.getLastName();\n\treturn addPaginationModel(pageNo, paginationModel, lastName, ownersResults);\n}这段代码中的形参\"pageNo\"在当前函数'processFindForm'内被'return addPaginationModel(pageNo, paginationModel, lastName, ownersResults);'这一句被使用,虽然pageNo没有被用于逻辑计算,但作为了函数调用其他函数的参数使用了,所以这个不能被判定为\"避免函数中未使用的形参\"\n\n<例子2>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的形参date在System.out.println(\"Formatted date: \" + sdf.format(date))这一句中被引用到,所以这个不能被判定为\"避免函数中未使用的形参\"\n" + }, + { + "id": 4, + "text": "if语句块不能为空", + "language": "Java", + "detail": "缺陷类型:if 语句块不能为空;对应Fixer:EmptyIfStmtFixer;修复方案:删除if语句块 或 适当的逻辑处理 或 注释说明为何为空", + "yes_example": "### 被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void emptyIfStatement() {\n\tif (getSpecialties().isEmpty()) {\n\t}\n}这段代码中的if语句块内容是空的,所以这个被判定为\"if语句块不能为空\"\n\n\n<例子2>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\t// judge persion if not null\n\t}\n}\n这段代码中的if语句块虽然有内容,但是\"// judge persion if not null\"只是代码注释,if语句块内并没有实际的逻辑代码,所以这个被判定为\"if语句块不能为空\"\n", + "no_example": "### 不能被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\treturn 0;\n\t}\n}这段代码中的if语句块里有内容,且里面有非注释代码的逻辑代码\"return 0;\",所以这个不能被判定为\"if语句块不能为空\"\n" + }, + { + "id": 5, + "text": "循环体不能为空", + "language": "Java", + "detail": "缺陷类型:循环体不能为空;对应Fixer:EmptyStatementNotInLoopFixer;修复方案:删除对应while、for、foreach 循环体 或 添加适当的逻辑处理或者注释说明为何为空", + "yes_example": "### 被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t}\n}这段代码中的for循环体的内容是空的,所以这个被判定为\"循环体不能为空\"\n\n\n<例子2>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t// this is a code example\n\t}\n}这段代码中的while循环体的内容虽然不是空的,但内容只是代码注释,无逻辑内容,所以这个被判定为\"循环体不能为空\"\n\n\n<例子3>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t\n\t}\n}这段代码中的while循环体内容是空的,所以这个被判定为\"循环体不能为空\"\n", + "no_example": "### 不能被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t\ta = 1;\n\t\tif (a == 1) {\n\t\t\tretrun a;\n\t\t}\n\t}\n}上述代码的for循环体的内容不为空,且内容不全是代码注释,所以这个不能被判定为\"循环体不能为空\"\n" + }, + { + "id": 6, + "text": "避免使用 printStackTrace(),应该使用日志的方式去记录", + "language": "Java", + "detail": "缺陷类型:避免使用 printStackTrace(),应该使 用日志的方式去记录;修复方案:用日志的方式去记录", + "yes_example": "### 被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\te.printStackTrace();\n\t}\n}这段代码中的catch语句中使用了printStackTrace(),所以这个被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n", + "no_example": "### 不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\tlogging.info(\"info\");\n\t}\n}这段代码的catch语句中使用的是日志记录的方式,所以这个不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n" + }, + { + "id": 7, + "text": "catch 语句块不能为空", + "language": "Java", + "detail": "缺陷类型:catch 语句块不能为空;对应Fixer:EmptyCatchBlockFixer;修复方案:在catch里面添加注释", + "yes_example": "### 被判定为\"catch语句块不能为空\"的例子\n<例子1>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n \n}\n这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n\n<例子2>\ntry {\n String str = null;\n str.length();\n} catch (NullPointerException e) {\n \n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n\n<例子3>\npublic class EmptyCatchExample {\n public static void main(String[] args) {\n try {\n // 尝试除以零引发异常\n int result = 10 / 0;\n } catch (ArithmeticException e) {\n \n }\n }\n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n<例子4>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n \n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n\n<例子5>\ntry {\n Object obj = \"string\";\n Integer num = (Integer) obj;\n} catch (ClassCastException e) {\n\t\n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n", + "no_example": "### 不能被判定为\"catch语句块不能为空\"的例子\n<例子1>\npersionNum = 1\ntry {\n\treturn True;\n} catch (Exception e) {\n\t// 如果人数为1则返回false\n\tif (persionNum == 1){\n\t\treturn False;\n\t}\n}这段代码的catch语句中不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n\n\n<例子2>\ntry {\n\tthrow new Exception(\"Fake exception\");\n} catch (Exception e) {\n\te.printStackTrace();\n}这段代码的catch语句中虽然只有\"e.printStackTrace();\"但确实不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n" + }, + { + "id": 8, + "text": "避免不必要的永真/永假判断", + "language": "Java", + "detail": "缺陷类型:避免不必要的永真/永假判断;对应Fixer:UnconditionalIfStatement Fixer;修复方案:删除永真/永假判断逻辑", + "yes_example": "### 被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\npublic void someMethod() {\n\twhile (true) {\n\t}\n}这段代码中的\"while (true)\"是一个使用true做判断条件,但是没有循环结束标记,所以这个被判定为\"避免不必要的永真/永假判断\"\n\n\n<例子2>\nif (true) {\n\tSystem.out.println(\"This is always true\");\n}这段代码中的\"if (true)\"是一个使用true条件做条件,但是没有循环结束标记,所以这个被判定为\"避免不必要的永真/永假判断\"\n\n\n<例子3>\na = 1;\nwhile(a > 0){\n\ta = a + 1\n}这段代码初始化a=1,是大于0的,while循环体的逻辑是每次加1,那么判断条件a > 0会永远是真的,不会退出循环,所以这个被判定为\"避免不必要的永真/永假判断\"\n<例子3>", + "no_example": "### 不能被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\na = 0;\nwhile (a < 5) {\n\ta = a + 1;\n}这段代码中的a<5是一个判断,当执行了5次while语句中的逻辑a=a+1之后,a会满足a < 5,就会退出循环,所以这个能被判定为\"避免不必要的永真/永假判断\"\n" + }, + { + "id": 9, + "text": "switch 中 default 必须放在最后", + "language": "Java", + "detail": "缺陷类型:switch 中 default 必须放在最后;对应Fixer:DefaultLabelNotLastInSwitchStmtFixer;修复方案:switch 中 default 放在最后", + "yes_example": "### 被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tcase 2:\n\t\tSystem.out.println(\"Number two\");\n\t\tbreak;\n}这段代码是一个switch语句,但是里面的default没有放在最后,所以这个被判定为\"switch 中 default 必须放在最后\"\n", + "no_example": "### 不能被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\ncase 3:\n\tSystem.out.println(\"Number one\");\n\tbreak;\ncase 4:\n\tSystem.out.println(\"Number two\");\n\tbreak;\ndefault:\n\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\tbreak;\n}这段代码是一个switch语句且里面的default放在了最后,所以这个不能被判定为\"switch 中 default 必须放在最后\"\n" + }, + { + "id": 10, + "text": "未使用equals()函数对 String 作比较", + "language": "Java", + "detail": "缺陷类型:未使用equals()函数对 String 作比较;对应Fixer:UnSynStaticDateFormatter Fixer;修复方案:使用equals()函数对 String 作比较", + "yes_example": "### 被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (existingPet != null && existingPet.getName() == petName) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中所涉及的existingPet.getName()和petName均是字符串,但是在if语句里做比较的时候使用了==而没有使用equals()对string做比较,所以这个被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\" == isOk) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串,但在if判断中与\"ok\"比较的时候使用的是==,未使用equals()对string做比较,应该使用\"ok\".equals(isOk),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子3>\nString str1 = \"Hello\";\nString str2 = \"Hello\";\nif (str1 == str2) {\n\tSystem.out.println(\"str1 和 str2 引用相同\");\n} else {\n\tSystem.out.println(\"str1 和 str2 引用不同\");\n}\n这段代码中的if (str1 == str2) 使用了==进行str1和str2的比较,未使用equals()对string做比较,应该使用str1.equals(str2),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子4>\nString str = \"This is string\";\nif (str == \"This is not str\") {\n\treturn str;\n}这段代码中的if (str == \"This is not str\")使用了==进行字符串比较,未使用equals()对string做比较,\"This is not str\".equals(str),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n", + "no_example": "### 不能被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (PROPERTY_VALUE_YES.equalsIgnoreCase(readWriteReqNode))\n formProperty.setRequired(true);\n这段代码中的PROPERTY_VALUE_YES和readWriteReqNode均是字符串,在if语句里比较PROPERTY_VALUE_YES和readWriteReqNode的使用的是equalsIgnoreCase(字符串比较忽略大小写),所以equalsIgnoreCase也是符合使用equals()函数对 String 作比较的,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\".equals(isOk)) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串,在if判断中与\"ok\"比较的时候使用的是equals()对string做比较,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n" + }, + { + "id": 11, + "text": "禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象", + "language": "Java", + "detail": "缺陷类型:禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象 输出异常;对应Fixer:ConcatExceptionFixer;修复方案:使用占位符传递异常对象", + "yes_example": "### 被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\ntry {\n listenersNode = objectMapper.readTree(listenersNode.asText());\n} catch (Exception e) {\n LOGGER.info(\"Listeners node can not be read\", e);\n}这段代码中日志输出内容内容是直接使用字符串\"Listeners node can not be read\"拼接,日志输出异常时,应使用占位符输出异常信息,而不是直接使用字符串拼接,所以这个被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n", + "no_example": "### 不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\nPersion persion = persionService.getPersion(1);\nif (persion == null){\n\tLOGGER.error(PERSION_NOT_EXIT);\n}这段代码中的PERSION_NOT_EXIT是一个用户自定义的异常常量,代表persion不存在,没有直接使用字符串\"persion not exit\"拼接,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n<例子1>\n\n<例子2>\ntry {\n a = a + 1;\n} catch (Exception e) {\n Persion persion = persionService.getPersion(1);\n LOGGER.info(persion);\n}这段代码中输出日志没有直接使用字符串拼接,而是使用的Persion对象输出,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n" + }, + { + "id": 12, + "text": "finally 语句块不能为空", + "language": "Java", + "detail": "缺陷类型:finally 语句块不能为空;对应Fixer:EmptyFinallyBlockFixer;修复方案:删除空 finally 语句块", + "yes_example": "### 被判定为\"finally 语句块不能为空\"的例子\n<例子1>\ntry {\n\tPersion persion = persionService.getPersion(1);\n\treturn persion;\n} finally {\n\t\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子2>\ntry {\n\tSystem.out.println(\"Inside try block\");\n} finally {\n\t// 空的finally块,没有任何语句,这是一个缺陷\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子3>\ntry {\n int result = 10 / 0;\n} catch (ArithmeticException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子4>\ntry {\n String str = null;\n System.out.println(str.length());\n} catch (NullPointerException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子5>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n e.printStackTrace();\n} finally {\n // 只有注释的 finally 语句块\n // 这是一个空的 finally 块\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n\n\n<例子6>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n e.printStackTrace();\n} finally {\n // 只有空行的 finally 语句块\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n", + "no_example": "### 不能被判定为\"finally 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"finally 语句块不能为空\"\n" + }, + { + "id": 13, + "text": "try 语句块不能为空", + "language": "Java", + "detail": "缺陷类型:try 语句块不能为空;对应Fixer:EmptyTryBlockFixer;修复方案:删除整个 try 语句", + "yes_example": "### 被判定为\"try 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\n\t}\n\treturn null;\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n\n\n<例子2>\npublic void demoFinallyBlock() {\n\ttry {\n\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n\n\n<例子3>\ntry {\n \n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n\n\n<例子4>\ntry {\n // 只有注释的 try 语句块\n\t\n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内只有注释和空行,也可以认定为这种情况是try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n", + "no_example": "### 不能被判定为\"try 语句块不能为空\"的例子\n<例子1>\ntry {\n\ta = a + 1;\n} catch (Exception e) {\n\te.printStackTrace();\n}\n这段代码中的try语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"try 语句块不能为空\"\n" + }, + { + "id": 14, + "text": "避免对象进行不必要的 NULL或者null 检查", + "language": "Java", + "detail": "缺陷类型:避免对象进行不必要的 NULL或者null 检查;对应Fixer:LogicalOpNpeFixer;修复方案:删除对对象不必要的 NULL 检查的逻辑", + "yes_example": "### 被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\na = \"dog\";\nif (a != null){\n\treturn a;\n}这段代码中的对象a已经是确定的值\"dog\",所以if条件句的判断\"a != null\"是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子2>\nif (authenticatedUserId != null && !authenticatedUserId.isEmpty() && userGroupManager!=null){\n\treturn authenticatedUserId;\n}这段代码中的\"authenticatedUserId != null\"和\"!authenticatedUserId.isEmpty()\"都是对\"authenticatedUserId\"的空判断,重复了,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子3>\nList list = new ArrayList<>();\nif (list != null) {\n list.add(1);\n}这段代码中的list已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子4>\nif (this.type != null && this.type.getName() != null) {\n\tSystem.out.println(\"Type name is not null\");\n}这段代码中的对象type已经检查过非null,再次检查getName()是否为null是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n\n<例子5>\nif (\"dog\".equals(null)){\n\treturn a;\n}这段代码中的\"dog\"是个确定的字符串,不需要进行null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n\n<例子6>\nInteger num = 10;\nif (num != null) {\n System.out.println(num);\n}这段代码中的num 已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n", + "no_example": "### 不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\nCat cat = catService.get(1);\nif (cat != null){\n\tretrun cat;\n}这段代码中的对象\"cat\"是通过service获取到的,不确定是否为空,所以if条件句的判断的\"cat != null\"是必要的,所以这个不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n" + }, + { + "id": 15, + "text": "避免 finally 块中出现 return", + "language": "Java", + "detail": "缺陷类型:避免 finally 块中出现 return;修复方案:无需修复", + "yes_example": "### 被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块内容包含\"return\",所以这个被判定为\"避免 finally 块中出现 return\"\n", + "no_example": "### 不能被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\tLOGGER.info(PERSION_NOT_EXIT);\n\t}\n}这段代码中的finally语句块中内容不包含\"return\",所以这个不能被判定为\"避免 finally 块中出现 return\"\n" + }, + { + "id": 16, + "text": "避免空的 static 初始化", + "language": "Java", + "detail": "缺陷类型:避免空的 static 初始化;对应Fixer:EmptyInitializerFixer;修复方案:删除整个空初始化块", + "yes_example": "### 被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class PetValidator implements Validator {\n\tstatic {\n\n\t}\n}这段代码中的static语句块没有内容,是空的,所以这个被判定为\"避免空的 static 初始化\"\n\n\n<例子2>\npublic class Persion {\n\tstatic {\n\t\t// 初始化的静态块\n\t}\n}这段代码中的static语句块是有内容的,不是空的,但是static初始化语句块中只有注释代码,没有实际的逻辑,所以这个被判定为\"避免空的 static 初始化\"\n", + "no_example": "### 不能被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class Cat {\n\tstatic {\n\t\t// 初始化的静态块\n\t\tcat = null;\n\t}\n}这段代码中的static语句块是有内容的,不是空的,且static初始化语句块中有非注释代码,有实际的逻辑,所以这个不能被判定为\"避免空的 static 初始化\"\n" + }, + { + "id": 17, + "text": "避免日历类用法不当风险", + "language": "Java", + "detail": "缺陷类型:避免日历类用法不当风险;修复方案:使用Java 8 及以上版本中的 java.time 包的LocalDate", + "yes_example": "### 被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final Calendar calendar = new GregorianCalendar(2020, Calendar.JANUARY, 1);\n这段代码中的Calendar和GregorianCalendar是线程不安全的,所以这个被判定为\"避免日历类用法不当风险\"\n", + "no_example": "### 不能被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final LocalDate calendar = LocalDate.of(2020, 1, 1);\n这段代码中的LocalDate使用的是Java 8 及以上版本中的 java.time 包,LocalDate 是不可变的并且是线程安全的,不会有线程安全和性能方面的问题,所以这个不能被判定为\"避免日历类用法不当风险\"\n" + }, + { + "id": 18, + "text": "使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()", + "language": "Java", + "detail": "缺陷类型:使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size();对应Fixer:ClassCastExpWithToArrayF ixer;修复方案:使用集合的toArray(T[]array),且传入的是类型完全一样的数组", + "yes_example": "### 被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"的例子\n<例子1>\nList stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nObject[] objectArray = stringList.toArray(new Object[5]);\n这段代码使用集合转数组的方法的时候使用了toArray(new Object[5]),但是传入的数组类型不一致,所以这个被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"\n", + "no_example": "### 不能被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"的例子\n<例子1>\nList stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nString[] stringArray = stringList.toArray(new String[stringList.size()]);\n这段代码使用集合转数组的方法的时候使用了toArray(new String[stringList.size()]),传入的是类型完全一样的数组,所以这个不能被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"\n" + }, + { + "id": 19, + "text": "禁止在 equals()中使用 NULL或者null 做比较", + "language": "Java", + "detail": "缺陷类型:禁止在 equals()中使用 NULL或者null 做比较;对应Fixer:EqualsNullFixer;修复方案:使用Object的判空函数 做比较", + "yes_example": "### 被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\nif (\"test\".equals(null)) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码\"test\".equals(null)使用equals()函数与null进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子2>\nif (!rangeValues[1].equals(\"null\")) {\n\tmaxValue = new BigDecimal(rangeValues[1]);\n}这段代码中if条件中的代码!rangeValues[1].equals(\"null\")使用equals()函数与Nnull进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子3>\nString str1 = \"example\";\nif (str1.equals(\"null\")) {\n System.out.println(\"str1 is null\");\n}这段代码中if条件中的代码str1.equals(null)使用equals()函数与null进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子4>\nString str3 = \"example\";\nif (str3 != null && str3.equals(\"null\")) {\n System.out.println(\"str3 is null\");\n}这段代码中if条件中的代码str3.equals(\"null\")使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子5>\nInteger num1 = 10;\nif (num1.equals(null)) {\n System.out.println(\"num1 is null\");\n}这段代码中if条件中的代码num1.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n\n\n<例子6>\nObject obj = new Object();\nif (obj.equals(null)) {\n System.out.println(\"obj is null\");\n}这段代码中if条件中的代码obj.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n", + "no_example": "### 不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\na = \"test\";\nif (a.equals(\"test\")) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码a.equals(\"test\")使用equals()函数与\"test\"进行了比较,所以这个不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n" + }, + { + "id": 20, + "text": "switch 语句块不能为空", + "language": "Java", + "detail": "缺陷类型:switch 语句块不能为空;对应Fixer:EmptySwitchStatementsFix;修复方案:删除整个空 switch 语句块", + "yes_example": "### 被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\t\n}这段代码是一个switch语句块,但是里面没有内容,所以这个被判定为\"switch 语句块不能为空\"\n\n\n<例子2>\nswitch (number) {\n\t// 这是一个switch语句块\n}这段代码是一个switch语句块,里面虽然有内容,但是内容仅仅是注释内容,没有实际的逻辑,所以这个被判定为\"switch 语句块不能为空\"\n", + "no_example": "### 不能被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n}这段代码是一个switch语句块,里面有内容,而且内容里有非注释的代码,有实际的逻辑,所以这个不能被判定为\"switch 语句块不能为空\"\n" + }, + { + "id": 21, + "text": "在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开", + "detail": "缺陷类型:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开;修复方案:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开。", + "language": "Java", + "yes_example": "### 被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int) 3.0;\n\n<例子2>\nint b = (int) 4.0;\n\n<例子3>\nlong a = (long) 5;\n\n<例子4>\nstring a = (string) 3.5;\n\n<例子5>\nPersion a = (Persion) \"zhangsan\";\n", + "no_example": "### 不能被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int)3.0;\n" + }, + { + "id": 22, + "text": "方法参数在定义和传入时,多个参数逗号后面必须加空格", + "detail": "缺陷类型:方法参数在定义和传入时,多个参数逗号后面必须加空格;修复方案:方法参数在定义和传入时,多个参数逗号后面必须加空格。", + "language": "Java", + "yes_example": "### 被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a,int b,int c) {}\n", + "no_example": "### 不能被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a, int b, int c) {}\n" + }, + { + "id": 23, + "text": "禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象", + "detail": "缺陷类型:禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象;修复方案:推荐使用 BigDecimal 的 valueOf 方法。", + "language": "Java", + "yes_example": "### 被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = new BigDecimal(0.1);\n", + "no_example": "### 不能被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = BigDecimal.valueOf(0.1);\n" + }, + { + "id": 24, + "text": "不能有多余的分号", + "detail": "缺陷类型:多余的分号;修复方案:删除多余的分号", + "yes_example": "### 被判定为\"不能有多余的分号\"的例子\n<例子1>\npublic void trigger(String executionId, Map processVariables) {\n commandExecutor.execute(new TriggerCmd(executionId, processVariables));\n}\n;\na = 1;\nb = 2;\nsum = a + b;\n这段代码中包含一个多余的分号\";\",所以这个被判定为\"不能有多余的分号\"\n", + "no_example": "### 不能被判定为\"不能有多余的分号\"的例子\n<例子1>\nwhile (True) {\n\ta = a + 1;\n\tbreak;\n}这段代码每个分号都是必须要的,所以这个能被判定为\"不能有多余的分号\"\n" + }, + { + "id": 25, + "text": "非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized", + "detail": "缺陷类型:非线程安全的 SimpleDateFormat 使用;修复方案:在函数或代码块级别加上synchronized修饰 或 使用其他线程安全的方式", + "yes_example": "### 被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的函数formatDate在未使用synchronized同步修饰的情况下使用了SimpleDateFormat,这是线程不安全的,所以这个被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"\n", + "no_example": "### 不能被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic synchronized void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码是在synchronized同步块对函数'formatDate'进行保护,保证了线程安全,所以这个不能被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"\n" + }, + { + "id": 26, + "text": "未按驼峰命名规范进行命名,类名使用驼峰式UpperCamelCase风格, 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格", + "detail": "缺陷类型:未按驼峰命名规范进行命名;修复方案:类名使用UpperCamelCase风格,方法名、参数名、成员变量、局部变量使用lowerCamelCase风格。", + "language": "Java", + "yes_example": "### 被判定为\"未按驼峰命名规范进行命名\"的例子\n<例子1>\npublic class myClass {\n private int MyVariable;\n public void MyMethod() {}\n}\n这段代码中的类名、成员变量和方法名没有遵循驼峰命名法,所以被判定为命名规范问题。\n", + "no_example": "### 不能被判定为\"未按驼峰命名规范进行命名\"的例子\n<例子1>\npublic class MyClass {\n private int myVariable;\n public void myMethod() {}\n}\n这段代码中的类名、成员变量和方法名都遵循了驼峰命名法,所以不能被判定为命名规范问题。\n" + }, + { + "id": 27, + "text": "抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾", + "detail": "缺陷类型:命名规范;修复方案:抽象类命名使用 Abstract 或 Base 开头,异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾。", + "language": "Java", + "yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class MyAbstractClass {}\npublic class MyExceptionClass {}\npublic class TestMyClass {}\n这段代码中的抽象类、异常类和测试类的命名不符合规范,所以被判定为命名规范问题。\n", + "no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic abstract class AbstractMyClass {}\npublic class MyCustomException extends Exception {}\npublic class MyClassTest {}\n这段代码中的抽象类、异常类和测试类的命名都符合规范,所以不能被判定为命名规范问题。\n" + }, + { + "id": 28, + "text": "POJO 类中的任何布尔类型的变量,避免加\"is\" 前缀", + "detail": "缺陷类型:命名规范;修复方案:POJO 类中的布尔类型变量不要加 is 前缀。", + "language": "Java", + "yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class User {\n private boolean isActive;\n}\n这段代码中的布尔类型变量加了 is 前缀,所以被判定为命名规范问题。\n", + "no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic class User {\n private boolean active;\n}\n这段代码中的布尔类型变量没有加 is 前缀,所以不能被判定为命名规范问题。\n" + }, + { + "id": 29, + "text": "杜绝完全不规范的英文缩写,避免望文不知义。", + "detail": "缺陷类型:命名规范;修复方案:避免使用不规范的英文缩写,确保代码可读性。", + "language": "Java", + "yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class CfgMgr {\n private int cnt;\n}\n这段代码中的类名和变量名使用了不规范的英文缩写,所以被判定为命名规范问题。\n", + "no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic class ConfigManager {\n private int count;\n}\n这段代码中的类名和变量名没有使用不规范的英文缩写,所以不能被判定为命名规范问题。\n" + }, + { + "id": 30, + "text": "避免出现魔法字符和数字,应声明为常量", + "detail": "缺陷类型:避免出现魔法字符和数字,应声明为常量;修复方案:将魔法值定义为常量。", + "language": "Java", + "yes_example": "### 被判定为\"避免出现魔法字符和数字,应声明为常量\"的例子\n<例子1>\npublic class MagicNumberExample {\n public void calculate() {\n int result = 42 * 2;\n }\n}\n这段代码中直接使用了魔法值 42,所以被判定为代码规范问题。\n\n<例子2>\npublic class MagicNumberExample {\n public void calculate() {\n String result = \"This is a result\";\n }\n}\n这段代码中直接使用了魔法值 \"This is a result\",所以被判定为代码规范问题。\n", + "no_example": "### 不能被判定为\"避免出现魔法字符和数字,应声明为常量\"的例子\n<例子1>\npublic class MagicNumberExample {\n private static final int MULTIPLIER = 42;\n public void calculate() {\n int result = MULTIPLIER * 2;\n }\n}\n这段代码中将魔法值定义为了常量,所以不能被判定为代码规范问题。\n" + }, + { + "id": 31, + "text": "long 或 Long 赋值时,数值后使用大写 L,不能是小写 l,浮点数类型的数值后缀统一为大写的 D 或 F", + "detail": "缺陷类型:代码规范;修复方案:long 或 Long 赋值时使用大写 L,浮点数类型的数值后缀使用大写的 D 或 F。", + "language": "Java", + "yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000l;\n private double pi = 3.14d;\n}\n这段代码中使用了小写的 l 和 d,所以被判定为代码规范问题。\n", + "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000L;\n private double pi = 3.14D;\n}\n这段代码中使用了大写的 L 和 D,所以不能被判定为代码规范问题。\n" + }, + { + "id": 32, + "text": "如果大括号内为空,简洁地写成{}即可,大括号中间无需换行和空格;如果是非空代码块,则:1)左大括号前不换行。2)左大括号后换行。3)右大括号前换行。4)右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。", + "detail": "缺陷类型:代码格式;修复方案:遵循大括号的使用规范。", + "language": "Java", + "yes_example": "### 被判定为\"代码格式\"的例子\n<例子1>\npublic class BracketExample{public void method(){\n if (true) {\n }}\n}\n这段代码中的大括号使用不符合规范,所以被判定为代码格式问题。\n", + "no_example": "### 不能被判定为\"代码格式\"的例子\n<例子1>\npublic class BracketExample {\n public void method() {\n if (true) {\n // do something\n }\n }\n}\n这段代码中的大括号使用符合规范,所以不能被判定为代码格式问题。\n" + }, + { + "id": 33, + "text": "左小括号和右边相邻字符之间不需要空格;右小括号和左边相邻字符之间也不需要空格;而左大括号前需要加空格。", + "detail": "缺陷类型:代码格式;修复方案:遵循括号和空格的使用规范。", + "language": "Java", + "yes_example": "### 被判定为\"代码格式\"的例子\n<例子1>\npublic class SpaceExample {\n public void method (){\n }\n}\n这段代码中的括号和空格使用不符合规范,所以被判定为代码格式问题。\n", + "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class SpaceExample {\n public void method() {}\n}\n这段代码中的括号和空格使用符合规范,所以不能被判定为代码格式问题。\n" + }, + { + "id": 34, + "text": "if / for / while / switch / do 等保留字与左右括号之间都必须加空格。", + "detail": "缺陷类型:代码格式;修复方案:保留字与左右括号之间加空格。", + "language": "Java", + "yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if(true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间没有空格,所以被判定为代码格式问题。\n", + "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if (true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间有空格,所以不能被判定为代码格式问题。\n" + }, + { + "id": 35, + "text": "所有整型包装类对象之间值的比较,全部使用 equals 方法比较", + "detail": "缺陷类型:代码规范;修复方案:整型包装类对象之间的值比较使用 equals 方法。", + "language": "Java", + "yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a == b) {\n }\n }\n}\n这段代码中使用了 == 比较整型包装类对象,所以被判定为代码规范问题。\n", + "no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a.equals(b)) {\n }\n }\n}\n这段代码中使用了 equals 方法比较整型包装类对象,所以不能被判定为代码规范问题。\n" + }, + { + "id": 36, + "text": "BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法。", + "detail": "缺陷类型:BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法;修复方案:使用 compareTo() 方法进行比较。", + "language": "Java", + "yes_example": "### 被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.equals(b)) {\n // 这段代码会返回 false,因为 equals() 方法会比较精度\n}\n", + "no_example": "### 不能被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.compareTo(b) == 0) {\n // 这段代码会返回 true,因为 compareTo() 方法只比较数值\n}\n" + }, + { + "id": 37, + "text": "禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法。", + "detail": "缺陷类型:POJO 类中存在重复的 getter 方法;修复方案:确保只存在一个 getter 方法。", + "language": "Java", + "yes_example": "### 被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private boolean active;\n public boolean isActive() {\n return active;\n }\n public boolean getActive() {\n return active;\n }\n}\n", + "no_example": "### 不能被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private int age;\n public int getAge() {\n return age;\n }\n}\n" + }, + { + "id": 38, + "text": "日期格式化时,传入 pattern 中表示年份统一使用小写的 y。", + "detail": "缺陷类型:日期格式化错误;修复方案:使用小写的 y 表示年份。", + "language": "Java", + "yes_example": "### 被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"YYYY-MM-dd\");\n", + "no_example": "### 不能被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n" + }, + { + "id": 39, + "text": "禁止在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp。", + "detail": "缺陷类型:使用了 java.sql 包中的日期类;修复方案:使用 java.time 包中的日期类。", + "language": "Java", + "yes_example": "### 被判定为\"禁止在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp\"的例子\n<例子1>\njava.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());\n", + "no_example": "### 不能被判定为\"禁止在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp\"的例子\n<例子1>\njava.time.LocalDate localDate = java.time.LocalDate.now();\n" + }, + { + "id": 40, + "text": "判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式。", + "detail": "缺陷类型:集合判空方式错误;修复方案:使用 isEmpty() 方法。", + "language": "Java", + "yes_example": "### 被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList list = new ArrayList<>();\nif (list.size() == 0) {\n // 判空逻辑\n}\n", + "no_example": "### 不能被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList list = new ArrayList<>();\nif (list.isEmpty()) {\n // 判空逻辑\n}\n" + }, + { + "id": 41, + "text": "只要重写 equals,就必须重写 hashCode。", + "detail": "缺陷类型:未重写 hashCode 方法;修复方案:同时重写 equals 和 hashCode 方法。", + "language": "Java", + "yes_example": "### 被判定为\"只要重写 equals,就必须重写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n}\n", + "no_example": "### 不能被判定为\"只要重写 equals,就必须重写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n @Override\n public int hashCode() {\n return Objects.hash(name);\n }\n}\n" + }, + { + "id": 42, + "text": "使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。", + "detail": "缺陷类型:对 Map 的 keySet() / values() / entrySet() 返回的集合进行添加操作;修复方案:避免对这些集合进行添加操作。", + "language": "Java", + "yes_example": "### 被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet keys = map.keySet();\nkeys.add(\"key2\");\n", + "no_example": "### 不能被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet keys = map.keySet();\n// 不进行添加操作\n" + }, + { + "id": 43, + "text": "不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator", + "detail": "缺陷类型:在 foreach 循环中进行元素的 remove / add 操作;修复方案:使用 iterator 进行元素的 remove 操作。", + "language": "Java", + "yes_example": "### 被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nfor (String s : list) {\n if (s.equals(\"a\")) {\n list.remove(s);\n }\n}\n", + "no_example": "### 不能被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nIterator iterator = list.iterator();\nwhile (iterator.hasNext()) {\n String s = iterator.next();\n if (s.equals(\"a\")) {\n iterator.remove();\n }\n}\n" + }, + { + "id": 44, + "text": "类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式。", + "detail": "缺陷类型:注释不符合 Javadoc 规范;修复方案:使用 Javadoc 规范的注释格式。", + "language": "Java", + "yes_example": "### 被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\npublic class Example {\n // 这是一个类注释\n private String name;\n // 这是一个属性注释\n public String getName() {\n return name;\n }\n // 这是一个方法注释\n}\n", + "no_example": "### 不能被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\n/**\n * 这是一个类注释\n */\npublic class Example {\n /**\n * 这是一个属性注释\n */\n private String name;\n /**\n * 这是一个方法注释\n */\n public String getName() {\n return name;\n }\n}\n" + }, + { + "id": 45, + "text": "所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释", + "detail": "缺陷类型:所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释;修复方案:为所有的抽象方法(包括接口中的方法)添加 Javadoc 注释,除了返回值、参数异常说明外,还必须指出该方法做什么事情,实现什么功能。", + "language": "Java", + "yes_example": "### 被判定为\"所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释\"的例子\n<例子1>\npublic interface MyInterface {\n void doSomething();\n}\n这段代码中的接口方法 doSomething() 没有 Javadoc 注释,所以被判定为缺少 Javadoc 注释。\n", + "no_example": "### 不能被判定为\"所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释\"的例子\n<例子1>\n/**\n * 执行某个操作\n * @param param 参数说明\n * @return 返回值说明\n * @throws Exception 异常说明\n */\npublic interface MyInterface {\n void doSomething(String param) throws Exception;\n}\n这段代码中的接口方法 doSomething() 有完整的 Javadoc 注释,所以不能被判定为缺少 Javadoc 注释。\n" + }, + { + "id": 46, + "text": "方法内部单行注释和多行注释的使用规范", + "detail": "缺陷类型:注释使用不规范;修复方案:方法内部单行注释,在被注释语句上方另起一行,使用 // 注释。方法内部多行注释使用 /* */注释,注意与代码对齐。", + "language": "Java", + "yes_example": "### 被判定为\"注释使用不规范\"的例子\n<例子1>\npublic void exampleMethod() {\n int a = 1; // 初始化变量a\n int b = 2; /* 初始化变量b */\n}\n这段代码中的单行注释和多行注释没有按照规范使用,所以被判定为注释使用不规范。\n", + "no_example": "### 不能被判定为\"注释使用不规范\"的例子\n<例子1>\npublic void exampleMethod() {\n // 初始化变量a\n int a = 1;\n /*\n * 初始化变量b\n */\n int b = 2;\n}\n这段代码中的单行注释和多行注释按照规范使用,所以不能被判定为注释使用不规范。\n" + }, + { + "id": 47, + "text": "所有的枚举类型字段必须要有注释", + "detail": "缺陷类型:枚举类型字段缺少注释;修复方案:为所有的枚举类型字段添加注释,说明每个数据项的用途。", + "language": "Java", + "yes_example": "### 被判定为\"枚举类型字段缺少注释\"的例子\n<例子1>\npublic enum Status {\n ACTIVE,\n INACTIVE\n}\n这段代码中的枚举类型字段没有注释,所以被判定为枚举类型字段缺少注释。\n", + "no_example": "### 不能被判定为\"枚举类型字段缺少注释\"的例子\n<例子1>\npublic enum Status {\n /**\n * 活跃状态\n */\n ACTIVE,\n /**\n * 非活跃状态\n */\n INACTIVE\n}\n这段代码中的枚举类型字段有注释,所以不能被判定为枚举类型字段缺少注释。\n" + }, + { + "id": 48, + "text": "finally 块必须对资源对象、流对象进行关闭", + "detail": "缺陷类型:资源对象、流对象未在 finally 块中关闭;修复方案:在 finally 块中对资源对象、流对象进行关闭,有异常也要做 try-catch。", + "language": "Java", + "yes_example": "### 被判定为\"资源对象、流对象未在 finally 块中关闭\"的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n }\n}\n这段代码中的 FileInputStream 对象没有在 finally 块中关闭,所以被判定为资源对象、流对象未在 finally 块中关闭。\n", + "no_example": "### 不能被判定为\"资源对象、流对象未在 finally 块中关闭\"的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n if (fis != null) {\n try {\n fis.close();\n } catch (IOException e) {\n e.printStackTrace();\n }\n }\n }\n}\n这段代码中的 FileInputStream 对象在 finally 块中关闭,所以不能被判定为资源对象、流对象未在 finally 块中关闭。\n" + }, + { + "id": 49, + "text": "常量命名应该全部大写,单词间用下划线隔开", + "detail": "缺陷类型:常量命名不规范;修复方案:常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。", + "language": "Java", + "yes_example": "### 被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int maxCount = 100;\n", + "no_example": "### 不能被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int MAX_COUNT = 100;\n" + }, + { + "id": 50, + "text": "任何二目、三目运算符的左右两边都需要加一个空格", + "detail": "缺陷类型:运算符两边缺少空格;修复方案:任何二目、三目运算符的左右两边都需要加一个空格。", + "language": "Java", + "yes_example": "### 被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a=b+c;\n", + "no_example": "### 不能被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a = b + c;\n" + }, + { + "id": 51, + "text": "避免使用from import *", + "detail": "缺陷类型:避免使用from import *,导入所有内容会造成命名冲突;修复方案:每个使用到的子依赖需分别导入。", + "language": "Python", + "yes_example": "### 被判定为\"避免使用from import *\"的例子\n<例子1>from math import * \n", + "no_example": "### 不能被判定为\"避免使用from import *\"的例子\n<例子1>from math import sqrt, pi \n" + }, + { + "id": 52, + "text": "避免使用__import__()函数动态导入模块", + "detail": "缺陷类型:避免使用__import__()函数动态导入模块;修复方案:使用标准的import语句。", + "language": "Python", + "yes_example": "### 被判定为\"使用__import__()函数动态导入模块\"的例子\n<例子1>module = __import__('math') \n", + "no_example": "### 不能被判定为\"使用__import__()函数动态导入模块\"的例子\n<例子1>import math \n" + }, + { + "id": 53, + "text": "导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组", + "detail": "缺陷类型:导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组;修复方案:按顺序分组导入语句。", + "language": "Python", + "yes_example": "### 被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>\nimport numpy as np\nimport os\nimport sys\nfrom my_local_module import my_function\n在这个样例中,先导入了第三方库,然后导入了标准库。\n\n<例子2>\nfrom my_project import my_local_function\nimport datetime\nimport requests\n在这个样例中,先导入了本地模块,然后导入了标准库。\n\n<例子3>\nimport os\nfrom my_project.local_module import some_function\nimport pandas as pd\nimport sys\nfrom another_local_module import another_function\nimport math\n在这个样例中,导入语句完全混乱,没有遵循任何顺序。\n\n<例子4>\nimport os\nimport requests\nimport sys\nimport numpy as np\nfrom local_package import local_module\n在这个样例中,导入标准库和第三方库交替进行。\n", + "no_example": "### 不能被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>import os \n\n import requests \n\n import mymodule \n" + }, + { + "id": 54, + "text": "避免未使用的函数形参", + "detail": "缺陷类型:避免未使用的函数形参;修复方案:移除未使用的函数形参。", + "language": "Python", + "yes_example": "### 被判定为'避免未使用的函数形参'的例子\n<例子1>def func(a, b): \n return a\n<例子2>def start_game(unused_param): \npuzzle = Puzzle() \npuzzle.solve()\n<例子3>def make_move(self, board):\npass \n\n<例子4>def move(self, direction):\npass \n", + "no_example": "### 不能被判定为'避免未使用的函数形参'的例子\n<例子1>def func(a): \n return a" + }, + { + "id": 55, + "text": "使用is not None来检查一个变量是否不是None", + "detail": "缺陷类型:未使用is not None来检查一个变量是否不是None;修复方案:使用is not None来检查。", + "language": "Python", + "yes_example": "### 被判定为'未使用is not None来检查一个变量是否不是None'的例子\n<例子1>if variable != None:\n pass", + "no_example": "### 不能被判定为'未使用is not None来检查一个变量是否不是None'的例子\n<例子1>if variable is not None:\n pass" + }, + { + "id": 56, + "text": "避免使用==或!=来比较对象实例的等价性", + "detail": "缺陷类型:使用==或!=来比较对象实例的等价性;修复方案:应使用equals比较。", + "language": "Python", + "yes_example": "### 被判定为'使用==或!=来比较对象实例的等价性'的例子\n<例子1>obj1 = MyClass() \n obj2 = MyClass() if obj1 == obj2: \n pass\n", + "no_example": "### 不能被判定为'使用==或!=来比较对象实例的等价性'的例子\n<例子1>obj1 = MyClass() \n obj2 = MyClass() if obj1.equals(obj2): \n pass\n\n<例子2>obj1 = 21 \n obj2 = 22 \n if obj1.equals(obj2):\n pass" + }, + { + "id": 57, + "text": "避免使用单字母变量名,使用描述性变量名", + "detail": "缺陷类型:避免使用单字母变量名,使用描述性变量名;修复方案:使用描述性变量名。", + "language": "Python", + "yes_example": "### 被判定为'避免使用单字母变量名,使用描述性变量名'的例子\n<例子1>x = 10 \n\n<例子2>y = 10 \n", + "no_example": "### 不能被判定为'避免使用单字母变量名,使用描述性变量名'的例子\n<例子1>count = 10 \n" + }, + { + "id": 58, + "text": "常量命名使用全大写字母,并用下划线分隔", + "detail": "缺陷类型:常量命名未使用全大写字母或未用下划线分隔;修复方案:常量命名使用全大写字母,并用下划线分隔。", + "language": "Python", + "yes_example": "### 被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>pi = 3.14159", + "no_example": "### 不能被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>PI = 3.14159\n<例子2>max_size = 1 \n max_size += 1" + }, + { + "id": 59, + "text": "类名应使用驼峰式命名(CamelCase)", + "detail": "缺陷类型:类名未使用驼峰式命名;修复方案:类名使用驼峰式命名。", + "language": "Python", + "yes_example": "### 被判定为'类名未使用驼峰式命名(CamelCase)'的例子\n<例子1>class my_class: \n pass\n<例子2>class my_class: \n def solve(self):\n pass", + "no_example": "### 不能被判定为'类名未使用驼峰式命名(CamelCase)'的例子\n<例子1>class MyClass: \n pass" + }, + { + "id": 60, + "text": "尽量使用with语句来管理资源", + "detail": "缺陷类型:未使用with语句来管理资源;修复方案:使用with语句来管理资源。", + "language": "Python", + "yes_example": "### 被判定为'未使用with语句来管理资源'的例子\n<例子1>file = open('file.txt', 'r') \n content = file.read() \n file.close()", + "no_example": "### 不能被判定为'未使用with语句来管理资源'的例子\n<例子1>with open('file.txt', 'r') as file: \n content = file.read()" + }, + { + "id": 61, + "text": "避免使用except 或 通用的Exception来捕获所有异常,应该指定异常类型", + "detail": "缺陷类型:捕获所有异常;修复方案:指定具体的异常类型。", + "language": "Python", + "yes_example": "### 被判定为'使用except:来捕获所有异常'的例子\n<例子1>try: \n # some code \n except: \n handle_error()\n### 被判定为'抛出通用的Exception异常'的例子\n<例子2>\n try:\n process_data(data) \n except: \n raise Exception('An error occurred') \n ", + "no_example": "### 不能被判定为'使用except:来捕获所有异常'的例子\n<例子1>try: \n # some code \n except ValueError: \n handle_value_error()" + }, + { + "id": 62, + "text": "尽量避免手动拼接字符串", + "detail": "缺陷类型:手动拼接字符串;修复方案:使用格式化字符串或join方法。", + "language": "Python", + "yes_example": "### 被判定为'手动拼接字符串'的例子\n<例子1>\n name = 'John' \n greeting = 'Hello, ' + name + '!' \n \n <例子2>greeting = '2048' + 'game' \n \n <例子3>pygame.display.set_caption('贪吃蛇' + '游戏')", + "no_example": "### 不能被判定为'手动拼接字符串'的例子\n<例子1>\n name = 'John' \n greeting = f'Hello, {name}!' \n" + }, + { + "id": 63, + "text": "避免出现魔法字符和数字,应声明为常量", + "detail": "缺陷类型:使用魔法字符和数字;修复方案:将其声明为常量。", + "language": "Python", + "yes_example": "### 被判定为'出现魔法字符和数字'的例子\n<例子1>\n if status == 1: \n print('Active')' \n\n<例子2>\n self.board = [[0] * 4 for _ in range(4)] \n self.score = 0\n<例子3>\ndef __init__(self, width=10, height=10, mines=15):\n\n<例子4>\nx, y = event.x // 20, event.y // 20\n\n<例子5>\nraise ValueError(\"余额不足\")\n\n<例子6>\ntransfer(bank, \"123\", \"456\", 200)\n\n<例子7>\nbank.add_account(Account(\"123\", 1000))\n", + "no_example": "### 不能被判定为'出现魔法字符和数字'的例子\n<例子1>\n ACTIVE_STATUS = 1 \n if status == ACTIVE_STATUS:\n print(ACTIVE_STATUS)' \n" + }, + { + "id": 64, + "text": "boolean变量判断无需显式比较", + "detail": "缺陷类型:显式比较boolean变量;修复方案:直接使用boolean变量进行判断。", + "language": "Python", + "yes_example": "### 被判定为'显式比较boolean变量'的例子\n<例子1>flag = True \n if flag == True: \n print('Flag is true')\n<例子2>if self.game.is_game_over() == True: \n return<例子3>if self.canvas.drawings ==True:", + "no_example": "### 不能被判定为'显式比较boolean变量'的例子\n<例子1>flag = True \n if flag: \n print('Flag is true') \n" + }, + { + "id": 65, + "text": "避免使用type()检查对象类型", + "detail": "缺陷类型:避免使用type()检查对象类型;修复方案:使用isinstance()函数。", + "language": "Python", + "yes_example": "### 被判定为'避免使用type()检查对象类型'的例子\n<例子1>\n if type(obj) == list: \n print('obj is a list')", + "no_example": "### 不能被判定为'避免使用type()检查对象类型'的例子\n<例子1>\n if isinstance(obj, list): \n print('obj is a list') \n" + }, + { + "id": 66, + "text": "避免使用os.system()来调用外部命令", + "detail": "缺陷类型:使用os.system()调用外部命令;修复方案:使用subprocess模块。", + "language": "Python", + "yes_example": "### 被判定为'使用os.system()来调用外部命令'的例子\n<例子1>os.system('ls -l')\n<例子2>os.system('ls -l')", + "no_example": "### 不能被判定为'使用os.system()来调用外部命令'的例子\n<例子1>import subprocess \n subprocess.run(['ls', '-l'])" + }, + { + "id": 67, + "text": "只使用@property装饰器创建只读属性,而非修改属性", + "detail": "缺陷类型:使用@property装饰器创建可修改属性;修复方案:只使用@property装饰器创建只读属性。", + "language": "Python", + "yes_example": "### 被判定为'使用@property装饰器来创建可修改属性'的例子\n<例子1>@property \n def value(self, new_value): \n self._value = new_value\n<例子2>@property \n def game_over(self): \n return self._is_game_over() \n def _is_game_over(self): \n pass", + "no_example": "### 不能被判定为'使用@property装饰器来创建可修改属性'的例子\n<例子1>@property \n def value(self): \n return self._value\n<例子2>@property \n def __str__(self): \n return 'Maze Game State'" + }, + { + "id": 68, + "text": "在使用索引或切片时,不要在方括号或冒号内加空格", + "detail": "缺陷类型:在索引或切片的方括号或冒号内加空格;修复方案:去掉方括号或冒号内的空格。", + "language": "Python", + "yes_example": "### 被判定为'在使用索引或切片时,在方括号或冒号内加空格'的例子\n<例子1>list = [1, 2, 3, 4] \n sublist = list[ 1 : 3 ]\n<例子2>start_point = self.canvas.drawings[ -1] \n<例子3>if head[ 0] < 0 or head[ 0] >= GRID_WIDTH or head[ 1] < 0 or head[ 1] >= GRID_HEIGHT:\n<例子4>for segment in self.snake[ 1:]:", + "no_example": "### 不能被判定为'在使用索引或切片时,在方括号或冒号内加空格'的例子\n<例子1>list = [1, 2, 3, 4] \n sublist = list[1:3]" + }, + { + "id": 69, + "text": "在逗号、分号或冒号前不要加空格,但在它们之后要加空格", + "detail": "缺陷类型:在逗号、分号或冒号前加空格或在它们之后不加空格;修复方案:在逗号、分号或冒号前不要加空格,但在它们之后要加空格。", + "language": "Python", + "yes_example": "### 被判定为'在逗号、分号或冒号前加空格,或没在它们之后加空格'的例子\n<例子1>if x == 4 : \n print(x , y)\n<例子2>if event.keysym == 'Up' or event.keysym == 'Down' or event.keysym == 'Left' or event.keysym == 'Right' :\n<例子3>x ,y = 1 ,2\n<例子4>def on_key_press(self , event) :\n<例子5>elif event.keysym == 'Down' ; \n<例子6>def update_status(self ,message: str) : \n pass ", + "no_example": "### 不能被判定为'在逗号、分号或冒号前加空格,或没在它们之后加空格'的例子\n<例子1>if x == 4: \n print(x, y)" + }, + { + "id": 70, + "text": "对于二元操作符,两边都应有空格", + "detail": "缺陷类型:二元操作符两边没有空格;修复方案:在二元操作符两边加空格", + "language": "Python", + "yes_example": "### 被判定为'二元操作符两边没有空格'的例子\n<例子1>a=b+1", + "no_example": "### 不能被判定为'二元操作符两边没有空格'的例子\n<例子1>a = b + 1\n<例子2>label = tk.Label(self.root, text=str(cell), bg='white')\n<例子3>label.grid(row=i, column=j)" + }, + { + "id": 71, + "text": "避免使用Python关键字作为变量名或函数名", + "detail": "缺陷类型:使用Python关键字作为变量名或函数名;修复方案:使用非关键字的名称。", + "language": "Python", + "yes_example": "### 被判定为'使用Python关键字作为变量名或函数名'的例子\n<例子1>def class(): \n pass\n<例子2>for = 5\n<例子3>def if(self): ", + "no_example": "### 不能被判定为'使用Python关键字作为变量名或函数名'的例子\n<例子1>def my_function(): \n pass\n<例子2>number = 5" + }, + { + "id": 72, + "text": "避免使用特殊字符作为变量名/方法名/类名,例如$或@", + "detail": "缺陷类型:使用特殊字符作为变量名/方法名/类名;修复方案:使用合法的变量名。", + "language": "Python", + "yes_example": "### 被判定为'使用特殊字符作为变量名/方法名/类名,例如$或@'的例子\n<例子1>my$var = 10\n<例子2>@var = 20\n<例子3>def add_score@(self, points): \n self.score += points\n<例子4>class @MyClass: \n pass\n<例子5>def mine@(self):", + "no_example": "### 不能被判定为'使用特殊字符作为变量名/方法名/类名,例如$或@'的例子\n<例子1>my_var = 10\n<例子2>var_20 = 20" + }, + { + "id": 73, + "text": "避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪", + "detail": "缺陷类型:使用raise重新抛出当前异常;修复方案:使用raise ... from ...语法。", + "language": "Python", + "yes_example": "### 被判定为'避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError: \n raise SomeException('新的异常信息')\n\n<例子2>\ntry:\n db.get_data()\nexcept ValueError as e:\n raise ValueError(\"Something went wrong!\")\n\n<例子3>\ntry:\n\traise Exception(\"形状添加失败\")\nexcept Exception as e:\n\tpass\n", + "no_example": "### 不能被判定为'避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n raise RuntimeError('Error occurred') from e \n\n<例子2>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n\tlogger.error(e)\n raise \n" + }, + { + "id": 74, + "text": "避免在except块中使用pass,这会捕获并忽略异常", + "detail": "缺陷类型:在except块中使用pass;修复方案:处理异常或记录日志。", + "language": "Python", + "yes_example": "### 被判定为'在except块中使用pass'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError: \n pass \n \n<例子2>\n try: \n 1 / 0 \n except ZeroDivisionError: \n pass \n", + "no_example": "### 不能被判定为'在except块中使用pass'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n logging.error('Error occurred: %s', e) \n" + }, + { + "id": 75, + "text": "避免使用assert语句来执行重要的运行时检查", + "detail": "缺陷类型:使用assert语句执行重要的运行时检查;修复方案:使用显式的条件检查和异常处理。", + "language": "Python", + "yes_example": "### 被判定为'使用assert语句来执行重要的运行时检查'的例子\n<例子1>\n def divide(a, b): \n assert b != 0 \n return a / b \n", + "no_example": "### 不能被判定为'使用assert语句来执行重要的运行时检查'的例子\n<例子1>\n def divide(a, b): \n if b == 0: \n raise ValueError('b cannot be zero') \n return a / b \n" + }, + { + "id": 76, + "text": "避免使用eval()和exec(),这些函数可能会带来安全风险", + "detail": "缺陷类型:使用eval()和exec()函数;修复方案:使用安全的替代方案。", + "language": "Python", + "yes_example": "### 被判定为'使用eval()和exec()'的例子\n<例子1>\n eval('print(1)') \n\n<例子2> \n exec('a = 1') \n", + "no_example": "### 不能被判定为'使用eval()和exec()'的例子\n<例子1>\n compiled_code = compile('print(1)', '', 'exec') \n exec(compiled_code) \n" + }, + { + "id": 77, + "text": "避免使用sys.exit(),应使用异常来控制程序的退出", + "detail": "缺陷类型:避免使用sys.exit(),应使用异常来控制程序的退出;修复方案:使用异常来控制程序的退出。", + "language": "Python", + "yes_example": "### 被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\n import sys\nsys.exit(1)\n\n<例子2>\n import sys \n sys.exit()\n\n<例子3>\nif event.type == pygame.QUIT:\n\tpygame.quit()\n\texit()\n\n<例子4>\n import sys \n sys.exit('退出程序'))\n", + "no_example": "### 不能被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\n raise SystemExit(1)\n" + }, + { + "id": 78, + "text": "避免使用time.sleep()进行线程同步,应使用同步原语,如锁或事件", + "detail": "缺陷类型:使用time.sleep()进行线程同步;修复方案:使用同步原语。", + "language": "Python", + "yes_example": "### 被判定为'使用time.sleep()进行线程同步'的例子\n<例子1>\n import time \n\n def worker(): \n time.sleep(1) \n\n<例子2>\n import time \n\n time.sleep(1) \n", + "no_example": "### 不能被判定为'使用time.sleep()进行线程同步'的例子\n<例子1>\n import threading \n\n event = threading.Event() \n\n def worker(): \n event.wait()\n" + }, + { + "id": 79, + "text": "每行代码避免超过79个字符", + "detail": "缺陷类型:每行代码避免超过79个字符;修复方案:将长行代码格式化为多行。", + "language": "Python", + "yes_example": "### 被判定为'每行代码避免超过79个字符'的例子\n<例子1>\n print('This is a very long line of code that exceeds the 79 characters limit........') \n", + "no_example": "### 不能被判定为'每行代码避免超过79个字符'的例子\n<例子1>\n print('This is a very long line of code that exceeds the 79 characters limit' + \n ' but it is split into two lines')\n" + }, + { + "id": 80, + "text": "模块级别的函数和类定义之间用两个空行分隔,类内部的方法定义之间用一个空行分隔", + "detail": "缺陷类型:模块级别的函数和类定义之间没有用两个空行分隔,类内部的方法定义之间没有用一个空行分隔;修复方案:按照规范添加空行。", + "language": "Python", + "yes_example": "### 被判定为'模块级别的函数和类定义之间没用两个空行分隔,类内部的方法定义之间没用一个空行分隔'的例子\n<例子1>\n def func1(): \n pass \n def func2(): \n pass \n\n<例子2>\n class MyClass: \n def method1(self): \n pass \n def method2(self): \n pass \n", + "no_example": "### 不能被判定为'模块级别的函数和类定义之间没用两个空行分隔,类内部的方法定义之间没用一个空行分隔'的例子\n<例子1>\n def func1(): \n pass \n\n\n def func2(): \n pass \n\n<例子2>\n class MyClass: \n def method1(self): \n pass \n\n def method2(self): \n pass \n" + }, + { + "id": 81, + "text": "使用小写字母和下划线分隔的方式命名变量和函数名", + "detail": "缺陷类型:变量和函数命名不符合小写字母和下划线分隔的方式;修复方案:使用小写字母和下划线分隔的方式命名。", + "language": "Python", + "yes_example": "### 被判定为'未使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\n def myFunction(): \n pass \n\n<例子2>\n myVariable = 10 \n\n<例子3>\n def Calculatesquareroot(self, x): \n return 1 \n", + "no_example": "### 不能被判定为'未使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\n def my_function(): \n pass \n\n<例子2>\n my_variable = 10 \n" + }, + { + "id": 82, + "text": "不允许使用print()函数来记录日志,使用logging模块等来记录日志", + "detail": "缺陷类型:使用print()函数记录日志;修复方案:使用logging模块记录日志。", + "language": "Python", + "yes_example": "### 被判定为'使用print()函数来记录日志'的例子\n<例子1>\n print('Error occurred') \n\n<例子2>\n print('打印的日志字符串内容') \n\n<例子3>\n task = 'xxx' \n print(task) \n\n<例子4>\n print(1)\n", + "no_example": "### 不能被判定为'使用print()函数来记录日志'的例子\n<例子1>\n import logging \n logging.error('Error occurred') \n" + } +] From 381fc0d4baecefc6df4bb6243b8630550ea63ad7 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 28 Aug 2024 21:00:01 +0800 Subject: [PATCH 44/47] add write_new_code for code writing. Remove Editor.write & ValidateAndRewriteCode from Engineer2 --- metagpt/prompts/di/engineer2.py | 36 +++++++++++++++- metagpt/roles/di/engineer2.py | 44 ++++++++++++++++--- metagpt/strategy/experience_retriever.py | 55 +----------------------- 3 files changed, 74 insertions(+), 61 deletions(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 099e46177..0501b4d33 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -1,6 +1,6 @@ from metagpt.prompts.di.role_zero import ROLE_INSTRUCTION -EXTRA_INSTRUCTION = """ +EXTRA_INSTRUCTION_DEPRECATED = """ 4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand. 5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response. 6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path. @@ -18,5 +18,39 @@ EXTRA_INSTRUCTION = """ 18. If you plan to read a file, do not include other plans in the same response. """ +EXTRA_INSTRUCTION = """ +6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path. +7. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file. +8. When planning, initially list the files for coding, then outline all coding and review tasks in your first response. +9. If you plan to read a file, do not include other plans in the same response. +10. Use Engineer2.write_new_code to create or modify a file. Write only one code file each time. +11. When the requirement is simple, you don't need to create a plan, just do it right away. +""" ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip() + +WRITE_CODE_SYSTEM_PROMPT = """ +You are a world-class engineer, your goal is to write google-style, elegant, modular, readable, maintainable, fully functional, and ready-for-production code. + +Pay attention to the conversation history and the following constraints: +1. When provided system 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. +2. When modifying a code, rewrite the full code instead of updating or inserting a snippet. +3. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. +""" + +WRITE_CODE_PROMPT = """ +# User Requirement +{user_requirement} + +# Plan Status +{plan_status} + +# Further Instruction +{instruction} + +# Output +While some concise thoughts are helpful, code is absolutely required. Always output one and only one code block in your response. Output code in the following format: +``` +your code +``` +""" diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 7af5280e1..82f928679 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -2,13 +2,21 @@ from __future__ import annotations from pydantic import Field -from metagpt.actions.write_code_review import ValidateAndRewriteCode -from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION +# from metagpt.actions.write_code_review import ValidateAndRewriteCode +from metagpt.prompts.di.engineer2 import ( + ENGINEER2_INSTRUCTION, + WRITE_CODE_PROMPT, + WRITE_CODE_SYSTEM_PROMPT, +) from metagpt.roles.di.role_zero import RoleZero +from metagpt.schema import UserMessage from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE from metagpt.tools.libs.terminal import Terminal +from metagpt.tools.tool_registry import register_tool +from metagpt.utils.common import CodeParser, awrite +@register_tool(include_functions=["write_new_code"]) class Engineer2(RoleZero): name: str = "Alex" profile: str = "Engineer" @@ -17,16 +25,16 @@ class Engineer2(RoleZero): terminal: Terminal = Field(default_factory=Terminal, exclude=True) - tools: list[str] = ["Plan", "Editor:write,read", "RoleZero", "Terminal:run_command", "ValidateAndRewriteCode"] + tools: list[str] = ["Plan", "Editor:read", "RoleZero", "Terminal:run_command", "Engineer2"] def _update_tool_execution(self): - validate = ValidateAndRewriteCode() - + # validate = ValidateAndRewriteCode() self.tool_execution_map.update( { "Terminal.run_command": self.terminal.run_command, - "ValidateAndRewriteCode.run": validate.run, - "ValidateAndRewriteCode": validate.run, + "Engineer2.write_new_code": self.write_new_code, + # "ValidateAndRewriteCode.run": validate.run, + # "ValidateAndRewriteCode": validate.run, } ) @@ -42,3 +50,25 @@ class Engineer2(RoleZero): command_output += "All tasks are finished.\n" command_output += await super()._run_special_command(cmd) return command_output + + async def write_new_code(self, path: str, instruction: str = "") -> str: + """Write a new code file. + + Args: + path (str): The absolute path of the file to be created. + instruction (optional, str): Further hints or notice other than the current task instruction, must be very concise and can be empty. Defaults to "". + """ + plan_status, _ = self._get_plan_status() + prompt = WRITE_CODE_PROMPT.format( + user_requirement=self.planner.plan.goal, + plan_status=plan_status, + instruction=instruction, + ) + context = self.llm.format_msg(self.rc.memory.get(self.memory_k) + [UserMessage(content=prompt)]) + rsp = await self.llm.aask(context, system_msgs=[WRITE_CODE_SYSTEM_PROMPT]) + code = CodeParser.parse_code(text=rsp) + + await awrite(path, code) + + # TODO: Consider adding line no to be ready for editing. + return f"The file {path} has been successfully created, with content:\n{code}" diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index 52884296d..4b1c825c9 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -849,7 +849,6 @@ Here's the plan: 1. **Task 1**: Create `index.html` - This file will contain the HTML structure necessary for the game's UI. 2. **Task 2**: Create `style.css` - This file will define the CSS styles to make the game visually appealing and responsive. 3. **Task 3**: Create `script.js` - This file will contain the JavaScript code for the game logic and UI interactions. -4. **Code Review Tasks**: Review each file to ensure they meet the project requirements and adhere to the system design. Let's start by appending the first task to the plan. @@ -881,33 +880,6 @@ Let's start by appending the first task to the plan. "instruction": "Create the script.js file containing the JavaScript logic for the 2048 game.", "assignee": "Alex" } - }, - { - "command_name": "Plan.append_task", - "args": { - "task_id": "4", - "dependent_task_ids": ["1"], - "instruction": "Use ValidateAndRewriteCode to review the code in index.html to ensure it meets the design specifications.", - "assignee": "Alex" - } - }, - { - "command_name": "Plan.append_task", - "args": { - "task_id": "5", - "dependent_task_ids": ["2"], - "instruction": "Use ValidateAndRewriteCode to review the code in style.css to ensure it meets the design specifications.", - "assignee": "Alex" - } - }, - { - "command_name": "Plan.append_task", - "args": { - "task_id": "6", - "dependent_task_ids": ["3"], - "instruction": "Use ValidateAndRewriteCode to review the code in script.js to ensure it meets the design specifications. ", - "assignee": "Alex" - } } ] ``` @@ -918,32 +890,9 @@ Explanation: Take on one task, such as writing a file. Upon completion, finish c ```json [ { - "command_name": "Editor.write", + "command_name": "Engineer2.write_new_code", "args": { - "path": "/Users/gary/Files/temp/workspace/snake_game/src/index.html", - "content": "the code ..." - } - }, - { - "command_name": "Plan.finish_current_task", - "args": {{}} - } -] -``` - -## example 4 -I will now review the code in `script.js`. -Explanation: to review the code, call ValidateAndRewriteCode.run. - -```json -[ - { - "command_name": "ValidateAndRewriteCode.run", - "args": { - "code_path": "/tmp/src/script.js", - "system_design_input": "/tmp/docs/system_design.json", - "project_schedule_input": "/tmp/docs/project_schedule.json", - "code_validate_k_times": 2 + "path": "src/index.html" } }, { From e63566813939444abb4c2dc292ae04f2e1bf10f8 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 28 Aug 2024 21:11:37 +0800 Subject: [PATCH 45/47] raise max_react_loop to 50, ask human when limit is reached --- metagpt/roles/di/role_zero.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 4edb2e37d..e24acfdc9 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -67,7 +67,7 @@ class RoleZero(Role): # React Mode react_mode: Literal["react"] = "react" - max_react_loop: int = 20 # used for react mode + max_react_loop: int = 50 # used for react mode # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools @@ -276,6 +276,14 @@ class RoleZero(Role): logger.debug(f"{self._setting}: {self.rc.state=}, will do {self.rc.todo}") rsp = await self._act() actions_taken += 1 + + # post-check + if self.rc.max_react_loop >= 10 and actions_taken >= self.rc.max_react_loop: + # If max_react_loop is a small value (e.g. < 10), it is intended to be reached and make the agent stop + logger.warning(f"reached max_react_loop: {actions_taken}") + rsp = await self.ask_human("I have reached my max action rounds, do you want me to continue? Yes or no") + if "yes" in rsp.lower(): + actions_taken = 0 return rsp # return output from the last action def format_quick_system_prompt(self) -> str: From 400a1671ef8a7fd1f0ab6a03e843a87015a5bb3f Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 28 Aug 2024 21:58:03 +0800 Subject: [PATCH 46/47] report write_new_code --- metagpt/roles/di/engineer2.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 82f928679..4600e3254 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -1,5 +1,7 @@ from __future__ import annotations +from pathlib import Path + from pydantic import Field # from metagpt.actions.write_code_review import ValidateAndRewriteCode @@ -14,6 +16,7 @@ from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE from metagpt.tools.libs.terminal import Terminal from metagpt.tools.tool_registry import register_tool from metagpt.utils.common import CodeParser, awrite +from metagpt.utils.report import EditorReporter @register_tool(include_functions=["write_new_code"]) @@ -65,10 +68,13 @@ class Engineer2(RoleZero): instruction=instruction, ) context = self.llm.format_msg(self.rc.memory.get(self.memory_k) + [UserMessage(content=prompt)]) - rsp = await self.llm.aask(context, system_msgs=[WRITE_CODE_SYSTEM_PROMPT]) - code = CodeParser.parse_code(text=rsp) - await awrite(path, code) + async with EditorReporter(enable_llm_stream=True) as reporter: + await reporter.async_report({"type": "code", "filename": Path(path).name, "src_path": path}, "meta") + rsp = await self.llm.aask(context, system_msgs=[WRITE_CODE_SYSTEM_PROMPT]) + code = CodeParser.parse_code(text=rsp) + await awrite(path, code) + await reporter.async_report(path, "path") # TODO: Consider adding line no to be ready for editing. return f"The file {path} has been successfully created, with content:\n{code}" From a63b7d2829c75d8b539e4409980d9d5adf1dccdd Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 28 Aug 2024 22:03:51 +0800 Subject: [PATCH 47/47] make the role aware of current time --- metagpt/roles/di/role_zero.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index e24acfdc9..ce87d13dc 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -4,6 +4,7 @@ import inspect import json import re import traceback +from datetime import datetime from typing import Annotated, Callable, Dict, List, Literal, Optional, Tuple from pydantic import Field, model_validator @@ -233,6 +234,10 @@ class RoleZero(Role): msg.add_metadata(IMAGES, images) return memory + def _get_prefix(self) -> str: + time_info = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + return super()._get_prefix() + f" The current time is {time_info}." + async def _act(self) -> Message: if self.use_fixed_sop: return await super()._act()