Generate pairwise explanations such as 'you chose 06 but skipped 08'.
(selected: Set[int], skipped: Set[int], papers: List[Dict])
| 141 | |
| 142 | |
| 143 | def _build_contrastive_signals(selected: Set[int], skipped: Set[int], papers: List[Dict]) -> List[str]: |
| 144 | """Generate pairwise explanations such as 'you chose 06 but skipped 08'.""" |
| 145 | contrastive_signals: List[str] = [] |
| 146 | selected_candidates = [] |
| 147 | skipped_candidates = [] |
| 148 | |
| 149 | for paper_num in sorted(selected): |
| 150 | paper = papers[paper_num - 1] |
| 151 | topics = _paper_topic_candidates(paper) |
| 152 | if topics: |
| 153 | selected_candidates.append((paper_num, paper, topics)) |
| 154 | |
| 155 | for paper_num in sorted(skipped): |
| 156 | paper = papers[paper_num - 1] |
| 157 | topics = _paper_topic_candidates(paper) |
| 158 | if topics: |
| 159 | skipped_candidates.append((paper_num, paper, topics)) |
| 160 | |
| 161 | for selected_num, selected_paper, selected_topics in selected_candidates: |
| 162 | selected_category = str(selected_paper.get("category") or "") |
| 163 | if selected_category not in {"maybe_interested", "edge_relevant", "high_relevant"}: |
| 164 | continue |
| 165 | |
| 166 | for skipped_num, skipped_paper, skipped_topics in skipped_candidates: |
| 167 | if skipped_num == selected_num: |
| 168 | continue |
| 169 | skipped_category = str(skipped_paper.get("category") or "") |
| 170 | if skipped_category not in {"must_read", "high_relevant", "maybe_interested"}: |
| 171 | continue |
| 172 | |
| 173 | preferred_topics = [topic for topic in selected_topics if topic not in skipped_topics] |
| 174 | deprioritized_topics = [topic for topic in skipped_topics if topic not in selected_topics] |
| 175 | if not preferred_topics or not deprioritized_topics: |
| 176 | continue |
| 177 | |
| 178 | preferred_label = format_direction_label(preferred_topics[0], prefer_chinese=True) |
| 179 | deprioritized_label = format_direction_label(deprioritized_topics[0], prefer_chinese=True) |
| 180 | signal = ( |
| 181 | f"✓ 你选了 {selected_num:02d}({preferred_label})但跳过了 {skipped_num:02d}({deprioritized_label})" |
| 182 | f" → 当前更偏 {preferred_label} 而非 {deprioritized_label}" |
| 183 | ) |
| 184 | if signal not in contrastive_signals: |
| 185 | contrastive_signals.append(signal) |
| 186 | if len(contrastive_signals) >= 2: |
| 187 | return contrastive_signals |
| 188 | break |
| 189 | |
| 190 | return contrastive_signals |
| 191 | |
| 192 | |
| 193 | def create_reading_reports_for_selection( |
no test coverage detected