Explorar el Código

feat(web): Chinese business annotations for all config page fields

- 新增 web/lib/config/fieldLabels.ts:字段→中文业务含义字典,注释原文取自
  规则包映射配置表.xlsx「字段注释」表与游走策略配置表.xlsx 数据字典行
  (权威来源,非编写);query prompts 字段含义取自 M2 brief 合同
- 配置页表头改为中文名+英文字段名+悬浮注释;每张表附「字段业务含义说明」
  折叠清单;dispatch/映射/binding/边目录补 notes 列,原因码表补业务解释列;
  Prompts 区字段行内带含义

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sam Lee hace 3 días
padre
commit
de3db6b8e2
Se han modificado 3 ficheros con 165 adiciones y 14 borrados
  1. 32 0
      web/app/globals.css
  2. 38 14
      web/features/config/ConfigPage.tsx
  3. 95 0
      web/lib/config/fieldLabels.ts

+ 32 - 0
web/app/globals.css

@@ -1302,3 +1302,35 @@ a {
   background: rgba(125, 135, 155, 0.1);
   font-weight: 600;
 }
+
+.config-table th small {
+  display: block;
+  font-weight: 400;
+  font-family: var(--mono, ui-monospace, monospace);
+  font-size: 10px;
+  opacity: 0.65;
+}
+.field-annotations {
+  margin-top: 8px;
+  font-size: 12px;
+}
+.field-annotations dl {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 6px 18px;
+  margin: 8px 0 0;
+}
+.field-annotations dt {
+  font-weight: 600;
+}
+.field-annotations dt small {
+  margin-left: 6px;
+  font-weight: 400;
+  font-family: var(--mono, ui-monospace, monospace);
+  opacity: 0.65;
+}
+.field-annotations dd {
+  margin: 0 0 4px;
+  color: inherit;
+  opacity: 0.8;
+}

+ 38 - 14
web/features/config/ConfigPage.tsx

@@ -9,6 +9,7 @@ import {
 import type { ConfigFileResponse } from "@/lib/api/types";
 import { AppShell } from "@/components/layout/AppShell";
 import { FactGrid } from "@/components/cards/FactGrid";
+import { fieldAnnotation } from "@/lib/config/fieldLabels";
 
 const TABS = [
   { id: "rule-packs", label: "规则包" },
@@ -49,9 +50,15 @@ function ConfigTable({
           <table className="config-table">
             <thead>
               <tr>
-                {fields.map((field) => (
-                  <th key={field}>{field}</th>
-                ))}
+                {fields.map((field) => {
+                  const annotation = fieldAnnotation(field);
+                  return (
+                    <th key={field} title={annotation.desc}>
+                      {annotation.label}
+                      {annotation.label !== field ? <small>{field}</small> : null}
+                    </th>
+                  );
+                })}
               </tr>
             </thead>
             <tbody>
@@ -64,6 +71,23 @@ function ConfigTable({
               ))}
             </tbody>
           </table>
+          <details className="field-annotations">
+            <summary className="muted">字段业务含义说明</summary>
+            <dl>
+              {fields.map((field) => {
+                const annotation = fieldAnnotation(field);
+                return (
+                  <div key={field}>
+                    <dt>
+                      {annotation.label}
+                      <small>{field}</small>
+                    </dt>
+                    <dd>{annotation.desc || "—"}</dd>
+                  </div>
+                );
+              })}
+            </dl>
+          </details>
         </div>
       )}
     </section>
@@ -91,7 +115,7 @@ function RulePacksTab({ data }: { data: Record<string, unknown> }) {
         title="Dispatch(按对象选择规则包)"
         note="dispatch_enabled=否 即 future 包:已归属未运行(M3 拍板,启用需打开 dispatch)。"
         rows={dispatch}
-        fields={["dispatch_id", "target_entity", "content_format", "runtime_stage", "dispatch_enabled", "rule_pack_id", "rule_pack_version"]}
+        fields={["dispatch_id", "target_entity", "content_format", "runtime_stage", "dispatch_enabled", "rule_pack_id", "rule_pack_version", "notes"]}
       />
       <ConfigTable
         title={`Hard Gates(${String(contentPack.rule_pack_id || "Content 包")})`}
@@ -118,12 +142,12 @@ function RulePacksTab({ data }: { data: Record<string, unknown> }) {
         title="效果状态映射(effect_status_mapping)"
         note="含 M3 新增的 hard gate KEEP→pending 映射(map_keep_for_review_pending_hard_gate)。"
         rows={asRows(data.effect_status_mapping)}
-        fields={["mapping_id", "decision_action", "reason_category", "is_hard_gate", "content_effect_status", "query_effect_status", "priority", "enabled"]}
+        fields={["mapping_id", "decision_action", "reason_category", "is_hard_gate", "content_effect_status", "query_effect_status", "priority", "enabled", "notes"]}
       />
       <ConfigTable
         title="决策原因码"
         rows={asRows(data.decision_reason_codes)}
-        fields={["decision_reason_code", "reason_label", "reason_category", "is_hard_gate"]}
+        fields={["decision_reason_code", "reason_label", "reason_category", "is_hard_gate", "business_explanation"]}
       />
     </>
   );
@@ -136,7 +160,7 @@ function WalkStrategyTab({ data }: { data: Record<string, unknown> }) {
         title="边→规则包归属(walk_rule_pack_binding)"
         note="M4 增补 path_stop / decision_to_asset 后共 8 条;归属包写在 walk action 顶层 rule_pack_id,实际执行包写在 raw_payload.rule_pack_execution。"
         rows={asRows(data.walk_rule_pack_binding)}
-        fields={["binding_id", "edge_id", "target_entity", "rule_pack_id", "dispatch_policy", "required"]}
+        fields={["binding_id", "edge_id", "target_entity", "rule_pack_id", "dispatch_policy", "required", "notes"]}
       />
       <ConfigTable
         title="游走边目录(walk_edge_catalog)"
@@ -169,13 +193,13 @@ function QueryPromptsTab({ data }: { data: Record<string, unknown> }) {
             <h3 className="section-title">Profile · {name}</h3>
             <FactGrid
               rows={[
-                ["prompt_version", profile.prompt_version],
-                ["temperature", profile.temperature],
-                ["max_tokens", profile.max_tokens],
-                ["variants_per_seed", `${cell(profile.variants_per_seed)}(>1 暂不支持,M2 拍板)`],
-                ["evidence_fields", `${asRows(profile.evidence_fields).length || (profile.evidence_fields as unknown[] | undefined)?.length || 0} 个字段`],
-                ["泛词过滤 queries", ((genericFilter.queries as unknown[]) || []).join("、")],
-                ["泛词过滤 tokens", ((genericFilter.tokens as unknown[]) || []).join("、")]
+                [`${fieldAnnotation("prompt_version").label}(prompt_version)`, profile.prompt_version],
+                [`${fieldAnnotation("temperature").label}(temperature)`, `${cell(profile.temperature)} — ${fieldAnnotation("temperature").desc}`],
+                [`${fieldAnnotation("max_tokens").label}(max_tokens)`, profile.max_tokens],
+                [`${fieldAnnotation("variants_per_seed").label}(variants_per_seed)`, `${cell(profile.variants_per_seed)} — ${fieldAnnotation("variants_per_seed").desc}`],
+                [`${fieldAnnotation("evidence_fields").label}(evidence_fields)`, `${(profile.evidence_fields as unknown[] | undefined)?.length || 0} 个字段 — ${fieldAnnotation("evidence_fields").desc}`],
+                [`${fieldAnnotation("generic_filter").label} queries`, ((genericFilter.queries as unknown[]) || []).join("、")],
+                [`${fieldAnnotation("generic_filter").label} tokens`, ((genericFilter.tokens as unknown[]) || []).join("、")]
               ]}
             />
             <details>

+ 95 - 0
web/lib/config/fieldLabels.ts

@@ -0,0 +1,95 @@
+// 字段中文业务含义。来源(权威,勿自行编写):
+// - tech_documents/规则包映射/规则包映射配置表.xlsx 的「字段注释」sheet 与各 sheet 数据字典行
+// - tech_documents/游走策略/游走策略配置表.xlsx 各 sheet 数据字典行
+// - query prompts 字段含义来自 M2 implementation brief(prompt 配置合同)
+export type FieldAnnotation = { label: string; desc: string };
+
+export const FIELD_ANNOTATIONS: Record<string, FieldAnnotation> = {
+  // ===== 通用 =====
+  rule_pack_id: { label: "规则包 ID", desc: "规则包 ID,用来标识某个具体规则包。" },
+  rule_pack_version: { label: "规则包版本", desc: "规则包版本。" },
+  priority: { label: "优先级", desc: "优先级,数字越小越先执行或越先作为主原因。" },
+  enabled: { label: "是否启用", desc: "这条配置是否存在并允许维护,不等于当前运行启用。" },
+  notes: { label: "注释", desc: "中文业务注释。" },
+  platform: { label: "平台", desc: "规则适用的平台,例如抖音。" },
+  field_path: { label: "字段路径", desc: "在 EvidenceBundle 或运行上下文中的字段路径。" },
+  operator: { label: "操作符", desc: "判断操作符,例如 is_empty、in、not_in、eq、gte。" },
+  expected_value: { label: "目标值", desc: "触发规则时匹配的目标值。" },
+  decision_action: { label: "判定动作", desc: "规则输出动作,例如入池、待复看、淘汰。" },
+  decision_reason_code: { label: "原因码", desc: "规则输出原因码,用于复盘和报表。" },
+  effect_status: { label: "效果状态", desc: "内容级效果状态,例如 success、pending、failed、rule_blocked。" },
+  target_entity: { label: "判断对象", desc: "规则判断对象,例如内容、作者、tag、路径或预算。" },
+  runtime_stage: { label: "适用阶段", desc: "适用阶段,例如 V1.0 或 future。" },
+  strategy_version: { label: "策略版本", desc: "策略版本,用来区分不同阶段或不同实验版本。" },
+  edge_id: { label: "游走边", desc: "游走边 ID(绑定/预算/停止规则适用的边)。" },
+
+  // ===== rule_pack_dispatch =====
+  dispatch_id: { label: "路由 ID", desc: "规则路由 ID,用来标识一条选择规则包的路由。" },
+  content_format: { label: "内容形态", desc: "内容形态,例如 video。" },
+  dispatch_enabled: { label: "运行时启用", desc: "当前运行链路是否真正会选择这条规则;false 即 future 包(已归属未运行)。" },
+  fallback_policy: { label: "兜底策略", desc: "路由失败或多条路由同时命中时的处理策略。" },
+
+  // ===== hard_gate_rules =====
+  gate_id: { label: "硬规则 ID", desc: "Hard Gate 规则 ID。" },
+  label: { label: "中文名称", desc: "Hard Gate 中文名称。" },
+  when: { label: "触发条件", desc: "字段路径 + 操作符 + 目标值组成的触发条件。" },
+  severity: { label: "严重程度", desc: "规则严重程度,例如 fatal 表示命中后强拦截,review 表示进入待复看。" },
+  stop_scoring: { label: "停止评分", desc: "命中后是否停止后续评分。" },
+
+  // ===== scorecard =====
+  key: { label: "维度 key", desc: "评分维度 key。" },
+  dimension_key: { label: "所属维度", desc: "评分维度 key。" },
+  max_score: { label: "最高分", desc: "该维度或阈值最高分。" },
+  weight_percent: { label: "权重 %", desc: "该维度在总分中的权重。" },
+  runtime_status: { label: "运行状态", desc: "运行状态,例如 active、future、deprecated。" },
+  scoring_rule_id: { label: "评分细则 ID", desc: "评分细则 ID。" },
+  score_value: { label: "得分", desc: "命中该评分细则后给出的分数。" },
+  min_score: { label: "分数下限", desc: "分数下限。" },
+
+  // ===== effect_status_mapping =====
+  mapping_id: { label: "映射 ID", desc: "映射关系 ID。" },
+  target_level: { label: "映射层级", desc: "映射层级,例如 content 或 query。" },
+  reason_category: { label: "原因分类", desc: "原因分类。" },
+  is_hard_gate: { label: "硬规则触发", desc: "是否由 Hard Gate 触发。" },
+  content_effect_status: { label: "内容效果状态", desc: "内容级效果状态。" },
+  query_effect_status: { label: "query 效果状态", desc: "query 级效果状态。" },
+
+  // ===== decision_reason_codes =====
+  reason_label: { label: "原因中文名", desc: "原因码中文名称。" },
+  business_explanation: { label: "业务解释", desc: "原因码的业务解释。" },
+
+  // ===== walk_rule_pack_binding =====
+  binding_id: { label: "绑定 ID", desc: "边→规则包绑定 ID。" },
+  required: { label: "是否必须", desc: "是否必须通过该规则判断。" },
+  dispatch_policy: { label: "调用方式", desc: "调用方式,如 required / optional / advisory。" },
+
+  // ===== walk_edge_catalog =====
+  edge_type: { label: "边类型", desc: "边类型。" },
+  can_loop: { label: "可循环", desc: "是否允许循环或多轮。" },
+
+  // ===== walk_budget_policy =====
+  budget_id: { label: "预算规则 ID", desc: "预算规则 ID。" },
+  max_total_actions: { label: "单 run 上限", desc: "单 run 最多执行次数。" },
+  max_per_query: { label: "每 query 上限", desc: "每条 query 最多执行次数。" },
+  max_per_content: { label: "每内容上限", desc: "每条内容最多执行次数。" },
+  max_pages: { label: "最大页数", desc: "最大页数。" },
+  max_depth: { label: "最大深度", desc: "最大游走深度。" },
+  max_tag_hops: { label: "tag 跳数上限", desc: "最大 tag 扩散跳数。" },
+
+  // ===== walk_stop_policy =====
+  stop_policy_id: { label: "停止规则 ID", desc: "停止规则 ID。" },
+  condition_label: { label: "停止条件", desc: "停止条件名称。" },
+  stop_action: { label: "停止动作", desc: "停止后的动作(stop_path 停整条路径 / stop_edge 停这条边)。" },
+
+  // ===== query prompts(M2 brief 合同) =====
+  prompt_version: { label: "Prompt 版本", desc: "prompt 配置版本,decision 记录中的 prompt 真相源。" },
+  temperature: { label: "温度", desc: "LLM 采样温度,与 V1 行为逐字复刻(0.4)。" },
+  max_tokens: { label: "最大 token", desc: "LLM 输出 token 上限。" },
+  variants_per_seed: { label: "每种子变体数", desc: "每个种子词生成的 query 数;>1 暂不支持(M2 拍板,配置大于 1 直接报错)。" },
+  evidence_fields: { label: "证据字段", desc: "拼装 LLM 输入 evidence_context 时读取的字段清单(17+ 个,顺序固定)。" },
+  generic_filter: { label: "泛词过滤", desc: "生成结果命中泛词(如「内容」「热门」)时丢弃,避免无效搜索词。" }
+};
+
+export function fieldAnnotation(field: string): FieldAnnotation {
+  return FIELD_ANNOTATIONS[field] || { label: field, desc: "" };
+}