Files
aiagent/backend/app/services/requirement_estimator.py
renjianbo ab1589921a fix: 修复35个安全与功能缺陷,补全知识进化/数字孪生/行为采集模块
## 安全修复 (12项)
- Webhook接口添加全局Token认证,过滤敏感请求头
- 修复JWT Base64 padding公式,防止签名验证绕过
- 数据库密码/飞书Token从源码移除,改为环境变量
- 工作流引擎添加路径遍历防护 (_resolve_safe_path)
- eval()添加模板长度上限检查
- 审批API添加认证依赖
- 前端v-html增强XSS转义,console.log仅开发模式输出
- 500错误不再暴露内部异常详情

## Agent运行时修复 (7项)
- 删除_inject_knowledge_context中未定义db变量的finally块
- 工具执行添加try/except保护,异常不崩溃Agent
- LLM重试计入budget计数器
- self_review异常时passed=False
- max_iterations截断标记success=False
- 工具参数JSON解析失败时记录警告日志
- run()开始时重置_llm_invocations计数器

## 配置与基础设施
- DEBUG默认False,SQL_ECHO独立配置项
- init_db()补全13个缺失模型导入
- 新增WEBHOOK_AUTH_TOKEN/SQL_ECHO配置项
- 新增.env.example模板文件

## 前端修复 (12项)
- 登录改用URLSearchParams替代FormData
- 401拦截器通过Pinia store统一清理状态
- SSE流超时从60s延长至300s
- final/error事件时清除streamTimeout
- localStorage聊天记录添加24h TTL
- safeParseArgCount替代模板中裸JSON.parse
- fetchUser 401时同时清除user对象

## 新增模块
- 知识进化: knowledge_extractor/retriever/tasks
- 数字孪生: shadow_executor/comparison模型
- 行为采集: behavior_middleware/collector/fingerprint_engine
- 代码审查: code_review_agent/document_review_agent
- 反馈学习: feedback_learner
- 瓶颈检测/优化引擎/成本估算/需求估算
- 速率限制器 (rate_limiter)
- Alembic迁移 015-020

## 文档
- 商业化落地计划
- 8篇docs文档 (架构/API/部署/开发/贡献等)
- Docker Compose生产配置

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-10 19:50:20 +08:00

352 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
需求评估代理 — 数字分身从历史数据估算需求工时,提供分阶段方案和风险评估
"""
from __future__ import annotations
import logging
import re
from typing import Any, Dict, List, Optional
from sqlalchemy.orm import Session
from app.core.database import SessionLocal
from app.models.execution import Execution
from app.models.task import Task
from app.services.fingerprint_engine import fingerprint_engine
from app.services.behavior_collector import behavior_collector
logger = logging.getLogger(__name__)
class RequirementEstimator:
"""需求评估代理 — 基于历史数据估算工时与风险"""
def estimate(self, user_id: str, title: str, description: str = "",
modules: Optional[List[str]] = None) -> Dict[str, Any]:
"""评估需求的工作量和时间。
Args:
user_id: 用户ID
title: 需求标题
description: 需求描述
modules: 涉及的模块/组件列表
"""
modules = modules or self._extract_modules(description)
# 1. 历史同类需求分析
similar_tasks = self._find_similar_tasks(title, description)
# 2. 复杂度评估
complexity = self._assess_complexity(title, description, modules)
# 3. 时间估算
time_estimate = self._estimate_time(complexity, similar_tasks, modules)
# 4. 风险识别
risks = self._identify_risks(title, description, modules)
# 5. 分阶段建议
phases = self._suggest_phases(title, description, modules, time_estimate)
# 6. 方案建议
suggestions = self._suggest_approach(complexity, similar_tasks, risks)
result = {
"title": title,
"complexity": complexity,
"time_estimate": time_estimate,
"modules": modules,
"similar_tasks": similar_tasks,
"risks": risks,
"phases": phases,
"suggestions": suggestions,
}
# 记录行为
behavior_collector.log_fire_and_forget(
user_id=user_id,
category="decision",
action="estimate_requirement",
context={"title": title, "modules": modules},
result={"complexity": complexity["level"], "estimated_hours": time_estimate["best_case_hours"]},
)
return result
def _extract_modules(self, description: str) -> List[str]:
"""从描述中提取可能涉及的模块名。"""
modules = []
patterns = [
r'(?:模块|组件|服务)[:]\s*(\S+)',
r'(\w+(?:模块|组件|服务|API|数据库|前端|后端))',
r'(?:涉及|影响|修改)[:]?\s*([^,\n]+)',
]
for pattern in patterns:
matches = re.findall(pattern, description)
modules.extend(matches)
# 去重
seen = set()
result = []
for m in modules:
if m not in seen:
seen.add(m)
result.append(m)
return result[:10]
def _find_similar_tasks(self, title: str, description: str) -> List[Dict[str, Any]]:
"""查找历史同类需求(基于关键词匹配)。"""
db: Optional[Session] = None
try:
db = SessionLocal()
# 提取关键词
keywords = set(re.findall(r'[\w]{2,}', title + description))
# 过滤常用词
stopwords = {'', '', '', '', '', '', '', '', '', '', ''}
keywords = keywords - stopwords
if not keywords:
return []
tasks = db.query(Task).filter(
Task.title.isnot(None),
Task.actual_hours.isnot(None),
).order_by(Task.created_at.desc()).limit(200).all()
similar = []
for task in tasks:
task_text = (task.title or "") + (task.description or "")
score = sum(1 for kw in keywords if kw in task_text)
if score >= 2:
similar.append({
"task_id": str(task.id) if task.id else None,
"title": task.title[:100] if task.title else "",
"actual_hours": float(task.actual_hours) if task.actual_hours else None,
"similarity_score": score,
})
similar.sort(key=lambda x: x.get("similarity_score", 0), reverse=True)
return similar[:5]
except Exception as e:
logger.error("查找同类需求失败: %s", e)
return []
finally:
if db:
try:
db.close()
except Exception:
pass
def _assess_complexity(self, title: str, description: str,
modules: List[str]) -> Dict[str, Any]:
"""评估需求复杂度。"""
text = title + " " + description
factors = {
"module_count": min(len(modules) if modules else 1, 10),
"has_migration": 1 if re.search(r'迁移|migration|schema.*change', text, re.I) else 0,
"has_payment": 1 if re.search(r'支付|付款|资金|金额', text) else 0,
"has_permission": 1 if re.search(r'权限|permission|auth|role', text, re.I) else 0,
"has_external_api": 1 if re.search(r'第三方|外部.*API|webhook|回调', text) else 0,
"has_report": 1 if re.search(r'报表|统计|chart|dashboard|导出', text) else 0,
"has_real_time": 1 if re.search(r'实时|websocket|push|消息推送', text) else 0,
"has_batch": 1 if re.search(r'批量|import|export|导入|导出', text) else 0,
}
score = (
factors["module_count"] * 3
+ factors["has_migration"] * 10
+ factors["has_payment"] * 8
+ factors["has_permission"] * 5
+ factors["has_external_api"] * 5
+ factors["has_report"] * 3
+ factors["has_real_time"] * 4
+ factors["has_batch"] * 3
)
if score <= 15:
level = "simple"
label = "简单"
elif score <= 30:
level = "medium"
label = "中等"
elif score <= 50:
level = "complex"
label = "复杂"
else:
level = "very_complex"
label = "非常复杂"
return {
"level": level,
"label": label,
"score": score,
"factors": factors,
}
def _estimate_time(self, complexity: Dict[str, Any],
similar_tasks: List[Dict[str, Any]],
modules: List[str]) -> Dict[str, Any]:
"""估算开发时间。"""
level = complexity["level"]
module_count = len(modules) if modules else 1
# 基准时间(小时/模块)
base_hours = {
"simple": 8,
"medium": 16,
"complex": 32,
"very_complex": 56,
}.get(level, 16)
# 历史数据调整
if similar_tasks:
hist_hours = [t["actual_hours"] for t in similar_tasks if t.get("actual_hours")]
if hist_hours:
hist_avg = sum(hist_hours) / len(hist_hours)
base_hours = base_hours * 0.4 + hist_avg * 0.6
best_case = round(base_hours * module_count * 0.8, 1)
expected = round(base_hours * module_count, 1)
worst_case = round(base_hours * module_count * 1.8, 1)
# 转换为天8小时/天)
return {
"best_case_hours": best_case,
"expected_hours": expected,
"worst_case_hours": worst_case,
"best_case_days": round(best_case / 8, 1),
"expected_days": round(expected / 8, 1),
"worst_case_days": round(worst_case / 8, 1),
"confidence": "high" if similar_tasks and len(similar_tasks) >= 3 else "medium",
"based_on": f"{len(similar_tasks)} 个历史同类需求" if similar_tasks else "行业基准估算",
}
def _identify_risks(self, title: str, description: str,
modules: List[str]) -> List[Dict[str, Any]]:
"""识别需求风险。"""
text = title + " " + description
risks = []
risk_checks = [
("high", r'数据.*迁移|schema.*变更|数据库.*改', "数据库结构变更"),
("high", r'支付|交易|退款|结算', "涉及支付/资金流程"),
("high", r'删除|物理删除|DROP|TRUNCATE', "包含数据删除操作"),
("medium", r'第三方.*API|外部.*依赖', "依赖外部API可用性"),
("medium", r'权限.*改|角色.*调整|auth.*change', "权限体系变更"),
("medium", r'性能|并发|QPS|高并发', "性能/并发要求"),
("medium", r'多个.*系统|跨.*系统|集成', "多系统集成复杂度"),
("low", r'导出|报表|dashboard|可视化', "导出/报表格式兼容"),
("low", r'手机|移动端|H5|小程序|APP', "多端适配工作"),
("low", r'旧.*兼容|legacy|向后兼容', "历史兼容性约束"),
]
for severity, pattern, label in risk_checks:
if re.search(pattern, text):
risks.append({
"severity": severity,
"label": label,
"mitigation": self._suggest_mitigation(label),
})
return risks
def _suggest_mitigation(self, risk_label: str) -> str:
"""根据风险类型建议缓解措施。"""
mitigations = {
"数据库结构变更": "建议编写 migration 脚本并先在 staging 验证",
"涉及支付/资金流程": "建议增加财务团队 review 环节和完整测试用例",
"包含数据删除操作": "建议实现软删除,添加二次确认机制",
"依赖外部API可用性": "建议添加超时重试和降级方案",
"权限体系变更": "建议灰度发布,充分回归测试",
"性能/并发要求": "建议提前压测并设置监控告警",
"多系统集成复杂度": "建议明确接口契约,增加集成测试",
"导出/报表格式兼容": "建议确认所有目标格式和编码",
"多端适配工作": "建议使用响应式设计或明确优先级",
"历史兼容性约束": "建议增加兼容性测试覆盖",
}
return mitigations.get(risk_label, "建议细化方案后评估")
def _suggest_phases(self, title: str, description: str,
modules: List[str],
time_estimate: Dict[str, Any]) -> List[Dict[str, Any]]:
"""建议分阶段实施计划。"""
total_days = time_estimate["expected_days"]
if total_days <= 3:
return [{
"phase": 1,
"name": "一站式开发",
"tasks": [f"实现 {title}"],
"estimated_days": total_days,
"checkpoint": "功能验收",
}]
phases = []
if modules and len(modules) > 1:
mid = max(1, len(modules) // 2)
phases.append({
"phase": 1,
"name": "核心功能",
"tasks": [f"实现 {m} 模块" for m in modules[:mid]],
"estimated_days": round(total_days * 0.5, 1),
"checkpoint": "核心功能可 Demo",
})
phases.append({
"phase": 2,
"name": "扩展功能",
"tasks": [f"实现 {m} 模块" for m in modules[mid:]],
"estimated_days": round(total_days * 0.3, 1),
"checkpoint": "完整功能验收",
})
phases.append({
"phase": 3,
"name": "优化 & 测试",
"tasks": ["性能优化", "完善测试", "文档整理"],
"estimated_days": round(total_days * 0.2, 1),
"checkpoint": "上线发布",
})
else:
phases = [
{"phase": 1, "name": "设计与准备", "tasks": ["技术方案设计", "接口定义"], "estimated_days": round(total_days * 0.2, 1), "checkpoint": "方案评审通过"},
{"phase": 2, "name": "核心开发", "tasks": [f"实现 {title}"], "estimated_days": round(total_days * 0.5, 1), "checkpoint": "功能完成"},
{"phase": 3, "name": "测试与上线", "tasks": ["集成测试", "性能测试", "上线部署"], "estimated_days": round(total_days * 0.3, 1), "checkpoint": "上线发布"},
]
return phases
def _suggest_approach(self, complexity: Dict[str, Any],
similar_tasks: List[Dict[str, Any]],
risks: List[Dict[str, Any]]) -> List[str]:
"""生成开发策略建议。"""
suggestions = []
level = complexity["level"]
if level in ("complex", "very_complex"):
suggestions.append("建议先做技术方案评审,再启动开发")
suggestions.append("建议拆分为多个独立可交付的 milestone")
if len(risks) >= 3:
suggestions.append(f"识别到 {len(risks)} 项风险,建议逐项制定应对预案")
high_risks = [r for r in risks if r["severity"] == "high"]
if high_risks:
suggestions.append(f"存在 {len(high_risks)} 项高风险: "
f"{', '.join(r['label'] for r in high_risks)},强烈建议专项评审")
if similar_tasks:
suggestions.append(f"参考 {len(similar_tasks)} 个历史同类需求的实际工时数据")
if level == "simple":
suggestions.append("需求较简单,可快速迭代,不必过度设计")
# 模块数多时建议并行
factors = complexity.get("factors", {})
if factors.get("module_count", 0) > 4:
suggestions.append("涉及模块较多,建议多人并行开发")
return suggestions
requirement_estimator = RequirementEstimator()