feat(tasks): add skill-templated task graph execution

This commit is contained in:
2026-06-23 10:22:58 +08:00
parent 6843d89b2c
commit 53b13e8eac
53 changed files with 4773 additions and 756 deletions

View File

@ -2,6 +2,8 @@
from __future__ import annotations
import json
import re
from dataclasses import dataclass, field
from typing import Any
@ -126,6 +128,37 @@ class EvidenceBuilder:
)
def evaluate_node_evidence(
evidence: RunEvidence,
required_evidence: list[str],
output_text: str,
) -> list[str]:
"""Evaluate v1 coarse-grained node evidence requirements."""
gaps: list[str] = []
successful_tools = [
item
for item in evidence.tool_results
if item.event_payload.get("success") is True
]
for raw_requirement in required_evidence:
requirement = str(raw_requirement).strip()
if not requirement:
continue
if requirement == "tool_result":
if not successful_tools:
_append_unique(gaps, "missing required evidence: tool_result")
elif requirement == "url":
if not any(_tool_evidence_contains_url(item) for item in successful_tools):
_append_unique(gaps, "missing required evidence: url")
elif requirement == "output":
if not output_text.strip():
_append_unique(gaps, "missing required evidence: output")
else:
_append_unique(gaps, f"unsupported evidence requirement: {requirement}")
return gaps
def render_task_evidence(packet: TaskEvidencePacket) -> str:
sections = [
f"Task evidence packet: task_id={packet.task_id} attempt={packet.attempt_index}",
@ -181,3 +214,20 @@ def _render_tool_evidence(item: ToolEvidence) -> str:
def _optional_str(value: Any) -> str | None:
return str(value) if value is not None else None
_URL_RE = re.compile(r"https?://[^\s<>'\"]+", re.IGNORECASE)
def _tool_evidence_contains_url(item: ToolEvidence) -> bool:
values = [
item.url or "",
item.content,
json.dumps(item.event_payload, ensure_ascii=False, default=str),
]
return any(_URL_RE.search(value) is not None for value in values)
def _append_unique(values: list[str], value: str) -> None:
if value not in values:
values.append(value)