init the project
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# IR 验证器:Schema 校验 + Principles 规则检查
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import jsonschema
|
||||
import yaml
|
||||
|
||||
from server.config import settings
|
||||
from server.services.ir_engine.generator import ir_generator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IRValidator:
|
||||
"""Validate IR against skill schema and soul principles."""
|
||||
|
||||
async def validate(self, ir_yaml: str, skill_name: str = "default") -> dict:
|
||||
"""Validate IR YAML content and return {valid, issues}."""
|
||||
issues = []
|
||||
|
||||
# 1. Parse YAML
|
||||
try:
|
||||
ir_data = yaml.safe_load(ir_yaml)
|
||||
except yaml.YAMLError as e:
|
||||
return {"valid": False, "issues": [{
|
||||
"severity": "error",
|
||||
"message": f"YAML 解析失败: {e}",
|
||||
}]}
|
||||
|
||||
if not isinstance(ir_data, dict):
|
||||
return {"valid": False, "issues": [{
|
||||
"severity": "error",
|
||||
"message": "IR 内容必须是 YAML 字典/对象",
|
||||
}]}
|
||||
|
||||
# 2. Schema validation
|
||||
skill = ir_generator.load_skill(skill_name)
|
||||
schema = skill.get("schema", {})
|
||||
if schema:
|
||||
try:
|
||||
jsonschema.validate(instance=ir_data, schema=schema)
|
||||
except jsonschema.ValidationError as e:
|
||||
issues.append({
|
||||
"severity": "error",
|
||||
"message": f"Schema 校验失败: {e.message}",
|
||||
"location": ".".join(str(p) for p in e.absolute_path),
|
||||
})
|
||||
|
||||
# 3. Principles validation
|
||||
principles = self._load_principles()
|
||||
for principle in principles:
|
||||
rule = principle.get("rule", "")
|
||||
pid = principle.get("id", "")
|
||||
|
||||
# Check completeness: each feature must have both positive and negative paths
|
||||
if pid == "completeness":
|
||||
features = ir_data.get("features", [])
|
||||
if not features:
|
||||
issues.append({
|
||||
"severity": "warning",
|
||||
"message": f"原则 '{pid}': 未检测到功能点,无法验证完整性",
|
||||
})
|
||||
|
||||
# Check traceability: features should have descriptions
|
||||
if pid == "traceability":
|
||||
features = ir_data.get("features", [])
|
||||
for i, feat in enumerate(features):
|
||||
if not feat.get("description"):
|
||||
issues.append({
|
||||
"severity": "warning",
|
||||
"message": f"原则 '{pid}': 功能点 {feat.get('feature_name', f'#{i}')} 缺少描述",
|
||||
})
|
||||
|
||||
# Check no-hallucination: warn about potentially vague features
|
||||
if pid == "no-hallucination":
|
||||
features = ir_data.get("features", [])
|
||||
for feat in features:
|
||||
desc = feat.get("description", "")
|
||||
if "应该" in desc or "可能" in desc or "猜测" in desc:
|
||||
issues.append({
|
||||
"severity": "warning",
|
||||
"message": f"原则 '{pid}': 功能点 '{feat.get('feature_name', '')}' 描述包含不确定措辞",
|
||||
})
|
||||
|
||||
valid = len([i for i in issues if i["severity"] == "error"]) == 0
|
||||
logger.info("IR validation: valid=%s, issues=%d", valid, len(issues))
|
||||
return {"valid": valid, "issues": issues}
|
||||
|
||||
def _load_principles(self) -> list[dict]:
|
||||
"""Load principles from .zeekerwatchmen/soul/principles.yaml."""
|
||||
principles_path = settings.SOUL_DIR / "principles.yaml"
|
||||
if not principles_path.exists():
|
||||
return []
|
||||
with open(principles_path, "r", encoding="utf-8") as f:
|
||||
data = yaml.safe_load(f)
|
||||
return data.get("principles", []) if data else []
|
||||
|
||||
|
||||
ir_validator = IRValidator()
|
||||
Reference in New Issue
Block a user