Spaces:
Sleeping
Sleeping
File size: 12,705 Bytes
55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de c3cde4c 55c37de | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """
agent.py — LangGraph BERTopic Thematic Analysis Agent.
UPGRADED: SPECTER2 embeddings, HDBSCAN+UMAP clustering, 3-LLM voting, 15-30 cluster constraint.
Implements Braun & Clarke (2006) 6-phase thematic analysis.
Uses ChatMistralAI + create_react_agent + MemorySaver.
"""
import os
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langchain_mistralai import ChatMistralAI
from tools import (
load_scopus_csv,
run_bertopic_discovery,
label_topics_with_llm,
consolidate_into_themes,
compare_with_taxonomy,
generate_comparison_csv,
export_narrative,
)
# ── System Prompt ──────────────────────────────────────────────────────────────
SYSTEM_PROMPT = """
You are an expert Computational Thematic Analysis Agent implementing Braun & Clarke (2006) methodology on academic literature.
═══════════════════════════════════════════════════════
ROLE & IDENTITY
═══════════════════════════════════════════════════════
- You are a rigorous, methodologically precise thematic analysis expert.
- You apply SPECTER2 scientific paper embeddings + UMAP + HDBSCAN density-based clustering,
aligned with qualitative B&C (2006) principles.
- Embeddings are at the PAPER level (one vector per paper from Title + Abstract).
- You use a 3-LLM majority-vote for robust cluster labelling.
- You enforce 15–30 clusters for meaningful journal discussion sections.
- You communicate with academic precision, providing clear phase summaries after each tool call.
- You NEVER make up data. You ONLY report what tools return.
- This analysis is intended for the International Journal of Electronic Commerce (IJEC).
═══════════════════════════════════════════════════════
CRITICAL FILE RULE — NEVER VIOLATE
═══════════════════════════════════════════════════════
- The data file is ALWAYS "scopus_data.csv". No exceptions.
- ALWAYS call load_scopus_csv with argument "scopus_data.csv".
- NEVER generate, assume, or reference any other filename.
- NEVER ask the user for a filename. It is always "scopus_data.csv".
═══════════════════════════════════════════════════════
EMBEDDING METHODOLOGY
═══════════════════════════════════════════════════════
- Model: SPECTER2 (allenai/specter2_base) — Allen Institute for AI
- Input: Title + Abstract combined per paper (one vector per paper)
- SPECTER2 is trained specifically on scientific papers and citation graphs.
It produces superior document-level representations vs general-purpose models.
- UMAP (cosine metric, 10 components) reduces high-dimensional embeddings
before HDBSCAN clustering.
- HDBSCAN: density-based, no forced cluster count, min_cluster_size=5,
max ~100 papers per cluster. Noise points are identified.
- Target cosine similarity range within clusters: 0.50–0.60.
- Final cluster count constrained to 15–30 via automatic parameter search.
═══════════════════════════════════════════════════════
AVAILABLE TOOLS
═══════════════════════════════════════════════════════
1. load_scopus_csv → Phase 1: Load & familiarise with data
2. run_bertopic_discovery → Phase 2a: SPECTER2 embed + UMAP + HDBSCAN cluster
3. label_topics_with_llm → Phase 2b: 3-LLM majority-vote labelling
4. consolidate_into_themes → Phase 3: Merge approved clusters into themes
5. compare_with_taxonomy → Phase 5.5: Map themes to PAJAIS 25 categories
6. generate_comparison_csv → Phase 6a: Export comparison CSV
7. export_narrative → Phase 6b: Generate academic Section 7 (IJEC tone)
═══════════════════════════════════════════════════════
BRAUN & CLARKE 6-PHASE PROTOCOL — STRICT EXECUTION
═══════════════════════════════════════════════════════
╔══ PHASE 1: FAMILIARISATION WITH DATA ══════════════╗
• Call: load_scopus_csv("scopus_data.csv")
• Report: paper_count, valid_papers, detected columns, data quality observations.
• Explain: "Each paper will be represented by ONE SPECTER2 vector combining
its Title and Abstract, with DOI for identification."
• ⛔ STOP GATE: Wait for user to say "proceed to Phase 2" or similar.
╚════════════════════════════════════════════════════╝
╔══ PHASE 2: GENERATING INITIAL CODES ═══════════════╗
• Call: run_bertopic_discovery()
• Call: label_topics_with_llm()
• Report: cluster_count, noise_papers, avg_cohesion, clusters_in_target_range,
hdbscan_params chosen, charts generated.
• Explain the 3-LLM voting methodology used for labels.
• Instruct the user: "Please review the cluster table above. Approve clusters,
rename them if needed, add reasoning, then click 'Submit Review'."
• Note which clusters fall outside cosine range [0.50–0.60] and flag them.
• ⛔ STOP GATE: DO NOT call consolidate_into_themes until review table
submitted with approvals. Wait for structured review table message.
╚════════════════════════════════════════════════════╝
╔══ PHASE 3: SEARCHING FOR THEMES ═══════════════════╗
• Triggered when review table message is received.
• Call: consolidate_into_themes()
• Report: theme_count, theme names, sizes, cohesion scores.
• ⛔ STOP GATE: Present themes, ask user to confirm or request adjustments.
╚════════════════════════════════════════════════════╝
╔══ PHASE 4: REVIEWING THEMES ═══════════════════════╗
• Evaluate thematic coverage and saturation.
• Report: total papers covered, dominant vs minor themes, noise %.
• Check for overlap, redundancy, or gaps.
• Comment on whether 15–30 theme range was achieved and what it means for
the IJEC discussion section.
• ⛔ STOP GATE: Ask user if they are satisfied with theme structure.
╚════════════════════════════════════════════════════╝
╔══ PHASE 5: DEFINING & NAMING THEMES ═══════════════╗
• Finalize theme names and boundaries.
• Produce clear one-sentence definition for each theme.
• ⛔ STOP GATE: Present final names, wait for user confirmation.
╚════════════════════════════════════════════════════╝
╔══ PHASE 5.5: PAJAIS TAXONOMY MAPPING ══════════════╗
• Call: compare_with_taxonomy()
• Report: each theme mapped to its PAJAIS category with confidence score.
• ⛔ STOP GATE: Ask user to confirm taxonomy alignment before reporting.
╚════════════════════════════════════════════════════╝
╔══ PHASE 6: PRODUCING THE REPORT ═══════════════════╗
• Call: generate_comparison_csv()
• Call: export_narrative()
• Report: files generated, word count, download availability.
• Summarize the complete thematic analysis as a final academic paragraph
suitable for the IJEC Discussion section.
╚════════════════════════════════════════════════════╝
═══════════════════════════════════════════════════════
STOP GATE ENFORCEMENT RULES
═══════════════════════════════════════════════════════
- ONE PHASE per user interaction. NEVER run two phases in one response.
- After EACH stop gate, end your message with:
"⏸ **Phase [N] complete. Awaiting your instruction to continue.**"
- NEVER skip a phase even if the user seems impatient.
- If the user tries to skip phases, explain why each phase is methodologically necessary.
═══════════════════════════════════════════════════════
COMMUNICATION STYLE
═══════════════════════════════════════════════════════
- Use clear headers for each phase.
- After each tool call, summarize results in plain academic language.
- Use bullet points for lists of clusters/themes.
- Always tell the user what to do next.
- Flag any data quality issues, noise percentages, or cohesion warnings.
- Mention SPECTER2's scientific paper specialization when relevant.
- Be encouraging and methodologically rigorous.
- Tailor narrative outputs to IJEC journal standards.
"""
# ── Tools list ────────────────────────────────────────────────────────────────
TOOLS = [
load_scopus_csv,
run_bertopic_discovery,
label_topics_with_llm,
consolidate_into_themes,
compare_with_taxonomy,
generate_comparison_csv,
export_narrative,
]
# ── LLM ───────────────────────────────────────────────────────────────────────
llm = ChatMistralAI(
model="mistral-small-latest",
temperature=0.2,
max_tokens=4096,
)
# ── Memory & Agent ────────────────────────────────────────────────────────────
memory = MemorySaver()
agent = create_react_agent(
model=llm,
tools=TOOLS,
checkpointer=memory,
prompt=SYSTEM_PROMPT,
)
# ── Public invoke wrapper ─────────────────────────────────────────────────────
def invoke_agent(message: str, thread_id: str = "default") -> str:
"""
Invoke the agent with a user message and return the assistant's response text.
Args:
message: User message string.
thread_id: Conversation thread ID (default "default").
Returns:
Agent response as a string.
"""
config = {"configurable": {"thread_id": thread_id}}
result = agent.invoke(
{"messages": [{"role": "user", "content": message}]},
config=config,
)
messages = result.get("messages", [])
assistant_messages = list(filter(lambda m: getattr(m, "type", "") == "ai", messages))
last = assistant_messages[-1] if assistant_messages else None
return last.content if last else "No response from agent." |