feat(report): distinguish release evidence tiers
This commit is contained in:
@@ -1482,13 +1482,14 @@ func headlineItemFromModelEvent(event ModelEvent) HeadlineItem {
|
|||||||
|
|
||||||
switch event.EventType {
|
switch event.EventType {
|
||||||
case "official_release":
|
case "official_release":
|
||||||
item.Label = "官方发布"
|
item.Label = "一级官方发布"
|
||||||
item.Title = fmt.Sprintf("%s 官方发布", event.ModelName)
|
item.Title = fmt.Sprintf("%s 官方发布", event.ModelName)
|
||||||
if event.SourceKindLabel == "权威佐证发布" {
|
item.Tone = "official-primary"
|
||||||
item.Label = "权威佐证"
|
if event.SourceKindLabel == "二级权威佐证发布" {
|
||||||
|
item.Label = "二级权威佐证"
|
||||||
item.Title = fmt.Sprintf("%s 进入权威佐证发布时间线", event.ModelName)
|
item.Title = fmt.Sprintf("%s 进入权威佐证发布时间线", event.ModelName)
|
||||||
|
item.Tone = "secondary-evidence"
|
||||||
}
|
}
|
||||||
item.Tone = "info"
|
|
||||||
case "new_model":
|
case "new_model":
|
||||||
item.Label = "新模型"
|
item.Label = "新模型"
|
||||||
item.Title = fmt.Sprintf("%s 进入今日情报池", event.ModelName)
|
item.Title = fmt.Sprintf("%s 进入今日情报池", event.ModelName)
|
||||||
@@ -1547,15 +1548,15 @@ func buildPriceEvidenceDetail(changePct, oldPrice, newPrice float64, currency st
|
|||||||
func buildReleaseSourceKindLabel(dateSourceKind, dateConfidence string) string {
|
func buildReleaseSourceKindLabel(dateSourceKind, dateConfidence string) string {
|
||||||
switch {
|
switch {
|
||||||
case dateSourceKind == "secondary_authoritative_report" || dateConfidence == "secondary_authoritative":
|
case dateSourceKind == "secondary_authoritative_report" || dateConfidence == "secondary_authoritative":
|
||||||
return "权威佐证发布"
|
return "二级权威佐证发布"
|
||||||
case dateSourceKind == "official_announcement" && dateConfidence == "official_primary":
|
case dateSourceKind == "official_announcement" && dateConfidence == "official_primary":
|
||||||
return "官方发布"
|
return "一级官方发布"
|
||||||
case dateSourceKind == "official_product_page":
|
case dateSourceKind == "official_product_page":
|
||||||
return "官方产品页"
|
return "官方产品页"
|
||||||
case dateSourceKind == "catalog_backfill":
|
case dateSourceKind == "catalog_backfill":
|
||||||
return "目录回填"
|
return "目录回填"
|
||||||
default:
|
default:
|
||||||
return "官方发布"
|
return "一级官方发布"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2241,6 +2242,42 @@ body {
|
|||||||
color: var(--ink-soft);
|
color: var(--ink-soft);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
.headline-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
.badge-official-primary {
|
||||||
|
background: rgba(18,60,99,0.12);
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
.badge-secondary-evidence {
|
||||||
|
background: rgba(173,107,17,0.14);
|
||||||
|
color: var(--amber);
|
||||||
|
}
|
||||||
|
.badge-info {
|
||||||
|
background: rgba(18,60,99,0.10);
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
.badge-success {
|
||||||
|
background: rgba(31,122,76,0.12);
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
.badge-warning {
|
||||||
|
background: rgba(173,107,17,0.12);
|
||||||
|
color: var(--amber);
|
||||||
|
}
|
||||||
|
.badge-caution {
|
||||||
|
background: rgba(165,59,42,0.12);
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
.badge-neutral {
|
||||||
|
background: rgba(81,101,121,0.12);
|
||||||
|
color: var(--ink-soft);
|
||||||
|
}
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1.18rem;
|
font-size: 1.18rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@@ -2388,6 +2425,14 @@ th {
|
|||||||
.tone-info { border-color: rgba(18,60,99,0.18); }
|
.tone-info { border-color: rgba(18,60,99,0.18); }
|
||||||
.tone-caution { border-color: rgba(165,59,42,0.18); }
|
.tone-caution { border-color: rgba(165,59,42,0.18); }
|
||||||
.tone-neutral { border-color: rgba(81,101,121,0.16); }
|
.tone-neutral { border-color: rgba(81,101,121,0.16); }
|
||||||
|
.tone-official-primary {
|
||||||
|
border-color: rgba(18,60,99,0.26);
|
||||||
|
background: linear-gradient(180deg, rgba(18,60,99,0.05), rgba(255,255,255,0.94));
|
||||||
|
}
|
||||||
|
.tone-secondary-evidence {
|
||||||
|
border-color: rgba(173,107,17,0.24);
|
||||||
|
background: linear-gradient(180deg, rgba(173,107,17,0.06), rgba(255,255,255,0.94));
|
||||||
|
}
|
||||||
.footer {
|
.footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@@ -2455,7 +2500,7 @@ th {
|
|||||||
<div class="headline-grid">
|
<div class="headline-grid">
|
||||||
{{range .HeadlineItems}}
|
{{range .HeadlineItems}}
|
||||||
<article class="headline-card tone-{{.Tone}}">
|
<article class="headline-card tone-{{.Tone}}">
|
||||||
<div class="card-kicker">{{.Label}}</div>
|
<div class="card-kicker headline-badge badge-{{.Tone}}">{{.Label}}</div>
|
||||||
<div class="card-title">{{.Title}}</div>
|
<div class="card-title">{{.Title}}</div>
|
||||||
<div class="card-summary">{{.Summary}}</div>
|
<div class="card-summary">{{.Summary}}</div>
|
||||||
{{if .SourceKindLabel}}<div class="source-line">事件来源:{{.SourceKindLabel}}</div>{{end}}
|
{{if .SourceKindLabel}}<div class="source-line">事件来源:{{.SourceKindLabel}}</div>{{end}}
|
||||||
|
|||||||
@@ -183,6 +183,34 @@ func TestBuildFreeSourceBreakdown(t *testing.T) {
|
|||||||
func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
||||||
report := sampleReportForV1()
|
report := sampleReportForV1()
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
|
{
|
||||||
|
EventType: "official_release",
|
||||||
|
ModelName: "GLM-5",
|
||||||
|
ProviderName: "Zhipu",
|
||||||
|
OperatorName: "Zhipu",
|
||||||
|
TrustLabel: "官方来源 / 一级证据",
|
||||||
|
Baseline: "官方首次发布",
|
||||||
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
||||||
|
SourceKindLabel: "一级官方发布",
|
||||||
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
||||||
|
UpdatedAt: "2026-05-13 08:30",
|
||||||
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
||||||
|
Priority: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: "official_release",
|
||||||
|
ModelName: "Doubao Seed 1.8",
|
||||||
|
ProviderName: "ByteDance",
|
||||||
|
OperatorName: "ByteDance Volcano",
|
||||||
|
TrustLabel: "官方来源 / 二级佐证",
|
||||||
|
Baseline: "官方首次发布",
|
||||||
|
Summary: "次级权威报道已形成稳定发布日期信号,适合进入观察池但不应与一级公告混同。",
|
||||||
|
SourceKindLabel: "二级权威佐证发布",
|
||||||
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
||||||
|
UpdatedAt: "2025-12-18 00:00",
|
||||||
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
||||||
|
Priority: 110,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
@@ -219,8 +247,8 @@ func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
|||||||
if report.PageMode != "hot" {
|
if report.PageMode != "hot" {
|
||||||
t.Fatalf("expected hot page mode, got %q", report.PageMode)
|
t.Fatalf("expected hot page mode, got %q", report.PageMode)
|
||||||
}
|
}
|
||||||
if !strings.Contains(report.HeroSummary, "2 个新模型") {
|
if !strings.Contains(report.HeroSummary, "GLM-5 已出现官方发布信号") {
|
||||||
t.Fatalf("hero summary missing new model signal: %s", report.HeroSummary)
|
t.Fatalf("hero summary missing official release signal: %s", report.HeroSummary)
|
||||||
}
|
}
|
||||||
if len(report.ActionItems) != 3 {
|
if len(report.ActionItems) != 3 {
|
||||||
t.Fatalf("expected 3 action items, got %d", len(report.ActionItems))
|
t.Fatalf("expected 3 action items, got %d", len(report.ActionItems))
|
||||||
@@ -231,8 +259,8 @@ func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
|||||||
if report.ActionItems[0].Evidence == "" {
|
if report.ActionItems[0].Evidence == "" {
|
||||||
t.Fatalf("expected action item evidence to be populated")
|
t.Fatalf("expected action item evidence to be populated")
|
||||||
}
|
}
|
||||||
if !strings.Contains(report.HeadlineItems[0].Title, "DeepSeek-V4-Flash") {
|
if !strings.Contains(report.HeadlineItems[0].Title, "GLM-5") {
|
||||||
t.Fatalf("expected first headline to come from model events, got %+v", report.HeadlineItems[0])
|
t.Fatalf("expected first headline to prioritize official release, got %+v", report.HeadlineItems[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +314,34 @@ func TestGenerateMarkdownV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
|
{
|
||||||
|
EventType: "official_release",
|
||||||
|
ModelName: "GLM-5",
|
||||||
|
ProviderName: "Zhipu",
|
||||||
|
OperatorName: "Zhipu",
|
||||||
|
TrustLabel: "官方来源 / 一级证据",
|
||||||
|
Baseline: "官方首次发布",
|
||||||
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
||||||
|
SourceKindLabel: "一级官方发布",
|
||||||
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
||||||
|
UpdatedAt: "2026-05-13 08:30",
|
||||||
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
||||||
|
Priority: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: "official_release",
|
||||||
|
ModelName: "Doubao Seed 1.8",
|
||||||
|
ProviderName: "ByteDance",
|
||||||
|
OperatorName: "ByteDance Volcano",
|
||||||
|
TrustLabel: "官方来源 / 二级佐证",
|
||||||
|
Baseline: "官方首次发布",
|
||||||
|
Summary: "次级权威报道已形成稳定发布日期信号,适合进入观察池但不应与一级公告混同。",
|
||||||
|
SourceKindLabel: "二级权威佐证发布",
|
||||||
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
||||||
|
UpdatedAt: "2025-12-18 00:00",
|
||||||
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
||||||
|
Priority: 110,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
@@ -339,6 +395,34 @@ func TestGenerateHTMLV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|||||||
path := filepath.Join(t.TempDir(), "daily_report.html")
|
path := filepath.Join(t.TempDir(), "daily_report.html")
|
||||||
report := sampleReportForV1()
|
report := sampleReportForV1()
|
||||||
report.ModelEvents = []ModelEvent{
|
report.ModelEvents = []ModelEvent{
|
||||||
|
{
|
||||||
|
EventType: "official_release",
|
||||||
|
ModelName: "GLM-5",
|
||||||
|
ProviderName: "Zhipu",
|
||||||
|
OperatorName: "Zhipu",
|
||||||
|
TrustLabel: "官方来源 / 一级证据",
|
||||||
|
Baseline: "官方首次发布",
|
||||||
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
||||||
|
SourceKindLabel: "一级官方发布",
|
||||||
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
||||||
|
UpdatedAt: "2026-05-13 08:30",
|
||||||
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
||||||
|
Priority: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventType: "official_release",
|
||||||
|
ModelName: "Doubao Seed 1.8",
|
||||||
|
ProviderName: "ByteDance",
|
||||||
|
OperatorName: "ByteDance Volcano",
|
||||||
|
TrustLabel: "官方来源 / 二级佐证",
|
||||||
|
Baseline: "官方首次发布",
|
||||||
|
Summary: "次级权威报道已形成稳定发布日期信号,适合进入观察池但不应与一级公告混同。",
|
||||||
|
SourceKindLabel: "二级权威佐证发布",
|
||||||
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
||||||
|
UpdatedAt: "2025-12-18 00:00",
|
||||||
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
||||||
|
Priority: 110,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
EventType: "new_model",
|
EventType: "new_model",
|
||||||
ModelName: "DeepSeek-V4-Flash",
|
ModelName: "DeepSeek-V4-Flash",
|
||||||
@@ -384,6 +468,8 @@ func TestGenerateHTMLV3IncludesTencentSubscriptionSection(t *testing.T) {
|
|||||||
"三条行动建议",
|
"三条行动建议",
|
||||||
"今日头条",
|
"今日头条",
|
||||||
"DeepSeek-V4-Flash",
|
"DeepSeek-V4-Flash",
|
||||||
|
"一级官方发布",
|
||||||
|
"二级权威佐证",
|
||||||
"首次出现",
|
"首次出现",
|
||||||
"主来源",
|
"主来源",
|
||||||
"更新时间",
|
"更新时间",
|
||||||
@@ -413,7 +499,7 @@ func TestBuildHeadlineItemsUsesModelEvents(t *testing.T) {
|
|||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "官方首次发布",
|
Baseline: "官方首次发布",
|
||||||
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
Summary: "官方发布新模型,值得优先复查中文通用与推理场景默认选择。",
|
||||||
SourceKindLabel: "官方发布",
|
SourceKindLabel: "一级官方发布",
|
||||||
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
PrimarySource: "https://open.bigmodel.cn/dev/howuse/model",
|
||||||
UpdatedAt: "2026-05-13 08:30",
|
UpdatedAt: "2026-05-13 08:30",
|
||||||
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方文档",
|
||||||
@@ -455,13 +541,13 @@ func TestBuildHeadlineItemsUsesModelEvents(t *testing.T) {
|
|||||||
if len(items) < 2 {
|
if len(items) < 2 {
|
||||||
t.Fatalf("expected at least 2 headline items, got %d", len(items))
|
t.Fatalf("expected at least 2 headline items, got %d", len(items))
|
||||||
}
|
}
|
||||||
if !strings.Contains(items[0].Title, "GLM-5") || items[0].Label != "官方发布" {
|
if !strings.Contains(items[0].Title, "GLM-5") || items[0].Label != "一级官方发布" {
|
||||||
t.Fatalf("expected official release event to rank first, got %+v", items[0])
|
t.Fatalf("expected official release event to rank first, got %+v", items[0])
|
||||||
}
|
}
|
||||||
if items[1].Baseline != "较昨日 -25%" {
|
if items[1].Baseline != "较昨日 -25%" {
|
||||||
t.Fatalf("expected price_cut baseline to be preserved, got %+v", items[1])
|
t.Fatalf("expected price_cut baseline to be preserved, got %+v", items[1])
|
||||||
}
|
}
|
||||||
if items[0].SourceKindLabel != "官方发布" || items[0].PrimarySource != "https://open.bigmodel.cn/dev/howuse/model" {
|
if items[0].SourceKindLabel != "一级官方发布" || items[0].PrimarySource != "https://open.bigmodel.cn/dev/howuse/model" {
|
||||||
t.Fatalf("expected official release evidence fields to be preserved, got %+v", items[0])
|
t.Fatalf("expected official release evidence fields to be preserved, got %+v", items[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,24 +642,27 @@ func TestHeadlineItemFromOfficialReleaseEvent(t *testing.T) {
|
|||||||
TrustLabel: "官方来源",
|
TrustLabel: "官方来源",
|
||||||
Baseline: "官方首次发布",
|
Baseline: "官方首次发布",
|
||||||
Summary: "官方发布新模型。",
|
Summary: "官方发布新模型。",
|
||||||
SourceKindLabel: "官方发布",
|
SourceKindLabel: "一级官方发布",
|
||||||
PrimarySource: "https://docs.anthropic.com/en/release-notes/api",
|
PrimarySource: "https://docs.anthropic.com/en/release-notes/api",
|
||||||
UpdatedAt: "2026-05-13 07:00",
|
UpdatedAt: "2026-05-13 07:00",
|
||||||
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方发布页",
|
EvidenceDetail: "models.release_date = 今日,且 source_url 指向官方发布页",
|
||||||
})
|
})
|
||||||
|
|
||||||
if item.Label != "官方发布" {
|
if item.Label != "一级官方发布" {
|
||||||
t.Fatalf("expected label to be 官方发布, got %+v", item)
|
t.Fatalf("expected label to be 一级官方发布, got %+v", item)
|
||||||
}
|
}
|
||||||
if !strings.Contains(item.Title, "官方发布") {
|
if !strings.Contains(item.Title, "官方发布") {
|
||||||
t.Fatalf("expected title to mention 官方发布, got %+v", item)
|
t.Fatalf("expected title to mention 官方发布, got %+v", item)
|
||||||
}
|
}
|
||||||
if item.SourceKindLabel != "官方发布" {
|
if item.SourceKindLabel != "一级官方发布" {
|
||||||
t.Fatalf("expected source kind label to be 官方发布, got %+v", item)
|
t.Fatalf("expected source kind label to be 一级官方发布, got %+v", item)
|
||||||
}
|
}
|
||||||
if item.PrimarySource != "https://docs.anthropic.com/en/release-notes/api" {
|
if item.PrimarySource != "https://docs.anthropic.com/en/release-notes/api" {
|
||||||
t.Fatalf("expected primary source to be preserved, got %+v", item)
|
t.Fatalf("expected primary source to be preserved, got %+v", item)
|
||||||
}
|
}
|
||||||
|
if item.Tone != "official-primary" {
|
||||||
|
t.Fatalf("expected tone to be official-primary, got %+v", item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeadlineItemFromSecondaryReleaseEvent(t *testing.T) {
|
func TestHeadlineItemFromSecondaryReleaseEvent(t *testing.T) {
|
||||||
@@ -583,16 +672,19 @@ func TestHeadlineItemFromSecondaryReleaseEvent(t *testing.T) {
|
|||||||
TrustLabel: "官方来源 / 二级佐证",
|
TrustLabel: "官方来源 / 二级佐证",
|
||||||
Baseline: "官方首次发布",
|
Baseline: "官方首次发布",
|
||||||
Summary: "模型进入正式发布日期观察池。",
|
Summary: "模型进入正式发布日期观察池。",
|
||||||
SourceKindLabel: "权威佐证发布",
|
SourceKindLabel: "二级权威佐证发布",
|
||||||
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
PrimarySource: "https://developer.volcengine.com/articles/7601918680544641034",
|
||||||
UpdatedAt: "2025-12-18 00:00",
|
UpdatedAt: "2025-12-18 00:00",
|
||||||
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
EvidenceDetail: "models.release_date = 今日,发布日期采用次级权威报道佐证,模型来源页保留官方文档",
|
||||||
})
|
})
|
||||||
|
|
||||||
if item.Label != "权威佐证" {
|
if item.Label != "二级权威佐证" {
|
||||||
t.Fatalf("expected label to be 权威佐证, got %+v", item)
|
t.Fatalf("expected label to be 二级权威佐证, got %+v", item)
|
||||||
}
|
}
|
||||||
if !strings.Contains(item.Title, "权威佐证发布时间线") {
|
if !strings.Contains(item.Title, "权威佐证发布时间线") {
|
||||||
t.Fatalf("expected title to mention 权威佐证发布时间线, got %+v", item)
|
t.Fatalf("expected title to mention 权威佐证发布时间线, got %+v", item)
|
||||||
}
|
}
|
||||||
|
if item.Tone != "secondary-evidence" {
|
||||||
|
t.Fatalf("expected tone to be secondary-evidence, got %+v", item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user