完全采用交互式使用方式;

This commit is contained in:
sonto.lau 2026-03-06 23:41:59 +08:00
parent ef9a846b51
commit cb479138af
2 changed files with 957 additions and 915 deletions

View File

@ -1,6 +1,7 @@
# core/requirement_analyzer.py - 需求分解、模块分类、函数签名生成 # core/requirement_analyzer.py - 需求分解、模块分类、函数签名生成
import json import json
from typing import List, Optional, Callable from typing import List, Optional, Callable
import config import config
from core.llm_client import LLMClient from core.llm_client import LLMClient
from database.models import FunctionalRequirement, ChangeHistory from database.models import FunctionalRequirement, ChangeHistory
@ -25,6 +26,7 @@ class RequirementAnalyzer:
project_id: int, project_id: int,
raw_req_id: int, raw_req_id: int,
knowledge: str = "", knowledge: str = "",
start_index: int = 1,
) -> List[FunctionalRequirement]: ) -> List[FunctionalRequirement]:
""" """
将原始需求文本分解为功能需求列表含模块分类 将原始需求文本分解为功能需求列表含模块分类
@ -34,6 +36,7 @@ class RequirementAnalyzer:
project_id: 所属项目 ID project_id: 所属项目 ID
raw_req_id: 原始需求记录 ID raw_req_id: 原始需求记录 ID
knowledge: 知识库文本可选 knowledge: 知识库文本可选
start_index: 功能需求序号起始值新增需求时传入当前最大序号+1
Returns: Returns:
FunctionalRequirement 对象列表未持久化id=None FunctionalRequirement 对象列表未持久化id=None
@ -42,8 +45,8 @@ class RequirementAnalyzer:
f"【参考知识库】\n{knowledge}\n" if knowledge else "" f"【参考知识库】\n{knowledge}\n" if knowledge else ""
) )
prompt = config.DECOMPOSE_PROMPT_TEMPLATE.format( prompt = config.DECOMPOSE_PROMPT_TEMPLATE.format(
raw_requirement=raw_requirement, raw_requirement = raw_requirement,
knowledge_section=knowledge_section, knowledge_section = knowledge_section,
) )
try: try:
@ -54,22 +57,57 @@ class RequirementAnalyzer:
raise RuntimeError(f"需求分解失败: {e}") raise RuntimeError(f"需求分解失败: {e}")
reqs = [] reqs = []
for i, item in enumerate(items, 1): for i, item in enumerate(items, start_index):
req = FunctionalRequirement( req = FunctionalRequirement(
project_id=project_id, project_id = project_id,
raw_req_id=raw_req_id, raw_req_id = raw_req_id,
index_no=i, index_no = i,
title=item.get("title", f"功能{i}"), title = item.get("title", f"功能{i}"),
description=item.get("description", ""), description = item.get("description", ""),
function_name=item.get("function_name", f"function_{i}"), function_name = item.get("function_name", f"function_{i}"),
priority=item.get("priority", "medium"), priority = item.get("priority", "medium"),
module=item.get("module", config.DEFAULT_MODULE), module = item.get("module", config.DEFAULT_MODULE),
status="pending", status = "pending",
is_custom=False, is_custom = False,
) )
reqs.append(req) reqs.append(req)
return reqs return reqs
# ══════════════════════════════════════════════════
# 新增需求(追加到已有功能需求列表)
# ══════════════════════════════════════════════════
def add_new_requirements(
self,
new_requirement_text: str,
project_id: int,
raw_req_id: int,
existing_reqs: List[FunctionalRequirement],
knowledge: str = "",
) -> List[FunctionalRequirement]:
"""
将新需求文本分解为功能需求序号接续已有需求返回新增部分列表未持久化
Args:
new_requirement_text: 新增需求描述文本
project_id: 所属项目 ID
raw_req_id: 原始需求记录 ID
existing_reqs: 已有功能需求列表用于计算起始序号
knowledge: 知识库文本可选
Returns:
新增的 FunctionalRequirement 列表未持久化
"""
start_index = max((r.index_no for r in existing_reqs), default=0) + 1
new_reqs = self.decompose(
raw_requirement = new_requirement_text,
project_id = project_id,
raw_req_id = raw_req_id,
knowledge = knowledge,
start_index = start_index,
)
return new_reqs
# ══════════════════════════════════════════════════ # ══════════════════════════════════════════════════
# 模块分类(独立步骤,可对已有需求列表重新分类) # 模块分类(独立步骤,可对已有需求列表重新分类)
# ══════════════════════════════════════════════════ # ══════════════════════════════════════════════════
@ -100,8 +138,8 @@ class RequirementAnalyzer:
] ]
knowledge_section = f"【参考知识库】\n{knowledge}\n" if knowledge else "" knowledge_section = f"【参考知识库】\n{knowledge}\n" if knowledge else ""
prompt = config.MODULE_CLASSIFY_PROMPT_TEMPLATE.format( prompt = config.MODULE_CLASSIFY_PROMPT_TEMPLATE.format(
requirements_json=json.dumps(req_list, ensure_ascii=False, indent=2), requirements_json = json.dumps(req_list, ensure_ascii=False, indent=2),
knowledge_section=knowledge_section, knowledge_section = knowledge_section,
) )
try: try:
result = self.llm.chat_json(prompt) result = self.llm.chat_json(prompt)
@ -137,18 +175,17 @@ class RequirementAnalyzer:
""" """
knowledge_section = f"【参考知识库】\n{knowledge}\n" if knowledge else "" knowledge_section = f"【参考知识库】\n{knowledge}\n" if knowledge else ""
prompt = config.FUNC_SIGNATURE_PROMPT_TEMPLATE.format( prompt = config.FUNC_SIGNATURE_PROMPT_TEMPLATE.format(
requirement_id=requirement_id or f"REQ.{func_req.index_no:02d}", requirement_id = requirement_id or f"REQ.{func_req.index_no:02d}",
title=func_req.title, title = func_req.title,
description=func_req.description, description = func_req.description,
function_name=func_req.function_name, function_name = func_req.function_name,
module=func_req.module or config.DEFAULT_MODULE, module = func_req.module or config.DEFAULT_MODULE,
knowledge_section=knowledge_section, knowledge_section = knowledge_section,
) )
try: try:
sig = self.llm.chat_json(prompt) sig = self.llm.chat_json(prompt)
if not isinstance(sig, dict): if not isinstance(sig, dict):
raise ValueError("LLM 返回结果不是 dict") raise ValueError("LLM 返回结果不是 dict")
# 确保 module 字段存在
if "module" not in sig: if "module" not in sig:
sig["module"] = func_req.module or config.DEFAULT_MODULE sig["module"] = func_req.module or config.DEFAULT_MODULE
return sig return sig
@ -211,7 +248,7 @@ class RequirementAnalyzer:
} }
# ══════════════════════════════════════════════════ # ══════════════════════════════════════════════════
# 记录变更 # 变更记录与分析
# ══════════════════════════════════════════════════ # ══════════════════════════════════════════════════
def log_change(self, project_id: int, changes: str) -> None: def log_change(self, project_id: int, changes: str) -> None:
@ -223,22 +260,32 @@ class RequirementAnalyzer:
"""查询项目变更历史""" """查询项目变更历史"""
return db.list_change_history(project_id) return db.list_change_history(project_id)
def analyze_changes(self, old_reqs: List[FunctionalRequirement], new_reqs: List[FunctionalRequirement]) -> List[ def analyze_changes(
str]: self,
old_reqs: List[FunctionalRequirement],
new_reqs: List[FunctionalRequirement],
) -> List[str]:
""" """
分析需求变更返回需要变更的代码文件列表 分析需求变更返回受影响的函数名列表
Args: Args:
old_reqs: 旧的功能需求列表 old_reqs: 旧的功能需求列表
new_reqs: 新的功能需求列表 new_reqs: 变更/增后的功能需求列表
Returns: Returns:
需要变更的文件列表 受影响的 function_name 列表
""" """
changed_files = [] changed_funcs = []
old_func_names = {req.function_name: req for req in old_reqs} old_func_map = {req.function_name: req for req in old_reqs}
for new_req in new_reqs: for new_req in new_reqs:
old_req = old_func_names.get(new_req.function_name) old_req = old_func_map.get(new_req.function_name)
if not old_req or old_req.description != new_req.description or old_req.module != new_req.module: # 新增需求 或 描述/模块有变化
changed_files.append(new_req.function_name) if (
return changed_files not old_req
or old_req.description != new_req.description
or old_req.module != new_req.module
):
changed_funcs.append(new_req.function_name)
return changed_funcs

File diff suppressed because it is too large Load Diff