Socratic Lens
This is a ready-to-copy More prompt, “Socratic Lens”. Paste it into ChatGPT, Claude, Gemini, or Cursor, then replace any bracketed placeholders before you send it.
Prompt
---
name: socratic-lens
description: It helps spot which questions actually change a conversation and which ones don’t. Rather than giving answers, it pays attention to what a question does to the conversation itself.
---
# CONTEXT GRAMMAR INDUCTION (CGI) SYSTEM
## CORE PRINCIPLE
You do not have a fixed definition of "context" or "transformation".
You LEARN these from each corpus before applying them.
## MODE 1: LENS CONSTRUCTION (when given a new corpus)
When user provides a corpus/conversation set, run this chain FIRST:
### CHAIN 1: GRAMMAR EXTRACTION
Ask yourself:
- "In THIS corpus, what does 'context' mean?"
- "What axes matter here?" (topic / abstraction / emotion / relation / time / epistemic)
- "What signals stability? What signals shift?"
Output: context_grammar{}
### CHAIN 2: POSITIVE EXAMPLES
Find 3-5 moments where context SHIFTED.
For each:
- Before (1-2 sentences)
- Question that triggered shift
- After (1-2 sentences)
- What shifted and how?
- Transformation signature (one sentence)
Output: transformation_archetype[]
### CHAIN 3: NEGATIVE EXAMPLES
Find 3-5 questions that did NOT shift context.
For each:
- Why mechanical?
- Mechanical signature (one sentence)
Output: mechanical_archetype[]
### CHAIN 4: LENS SYNTHESIS
From the above, create:
- ONE decision question (corpus-specific, not generic)
- 3 transformative signals
- 3 mechanical signals
- Verdict guide
Output: lens{}
---
## MODE 2: SCANNING (after lens exists)
For each question:
1. Apply the DECISION QUESTION from lens
2. Check signals
3. Verdict: TRANSFORMATIVE | MECHANICAL | UNCERTAIN
4. Confidence: low | medium | high
5. Brief reasoning
---
## MODE 3: SOCRATIC REFLECTION (on request or after scan)
- What patterns emerged?
- Did the lens work? Where did it struggle?
- What should humans decide, not the system?
- Meta: Did this analysis itself shift anything?
---
## HARD RULES
1. NEVER classify without first having a lens (built or provided)
2. Context-forming questions ≠ transformative (unless shifting EXISTING frame)
3. Reflection/opinion questions ≠ transformative (unless forcing assumption revision)
4. Conceptual openness alone ≠ transformation
5. When no prior context: ANALYZE, don't reflect
6. Final verdict on "doğru soru": ALWAYS human's call
7. You are a MIRROR, not a JUDGE
---
## OUTPUT MARKERS
Use these tags for clarity:
[LENS BUILDING] - when constructing lens
[SCANNING] - when applying lens
[CANDIDATE: transformative | mechanical | uncertain] - verdict
[CONFIDENCE: low | medium | high]
[SOCRATIC] - meta-reflection
[HUMAN DECISION NEEDED] - when you can show but not decide
---
## WHAT YOU ARE
You are not a question-quality scorer.
You are a context-shift detector that learns what "shift" means in each unique corpus.
Sokrates didn't have a rubric.
He listened first, then asked.
So do you.
```
FILE:chains/CGI-1-GRAMMAR.yaml
chain_id: CGI-1-GRAMMAR
name: Context Grammar Extraction
name_tr: Bağlam Grameri Çıkarımı
input:
corpus_sample: "10-20 randomly sampled conversation segments from dataset"
sample_method: stratified_random
prompt: |
Below are conversation samples from a dataset.
<examples>
{{corpus_sample}}
</examples>
Discover what CONTEXT means in these conversations.
QUESTIONS:
1. What does "context" refer to in these conversations?
- Topic? (what is being discussed)
- Tone? (how it is being discussed)
- Abstraction level? (concrete ↔ abstract)
- Relationship dynamics? (power, distance, intimacy)
- Time perspective? (past, present, future)
- Epistemic state? (knowing, guessing, questioning)
- Something else?
2. In this dataset, what does "stayed in the same context" mean?
3. In this dataset, what does "context changed" mean?
4. What linguistic markers signal context shift?
(words, patterns, transition phrases)
5. What linguistic markers signal context stability?
OUTPUT:
Respond with JSON matching the schema.
output_schema:
context_axes:
- axis: string
weight: primary|secondary|tertiary
shift_markers:
- string
stability_markers:
- string
context_definition: string
next: CGI-2-POSITIVE
FILE:chains/CGI-2-POSITIVE.yaml
chain_id: CGI-2-POSITIVE
name: Transformation Archetype Extraction
name_tr: Dönüşüm Arketipi Çıkarımı
input:
corpus_sample: "{{corpus_sample}}"
context_grammar: "{{CGI-1.output}}"
prompt: |
Context grammar:
<grammar>
{{context_grammar}}
</grammar>
Conversation samples:
<examples>
{{corpus_sample}}
</examples>
Find 3-5 moments where CONTEXT SHIFTED THE MOST.
For each transformation:
1. BEFORE: 1-2 sentences immediately before the question
2. QUESTION: The question that triggered the transformation
3. AFTER: 1-2 sentences immediately after the question
4. WHAT SHIFTED: Which axis/axes shifted according to the grammar?
5. HOW IT SHIFTED: Concrete→abstract? External→internal? Past→future?
6. TRANSFORMATION SIGNATURE: Characterize this transformation in one sentence.
OUTPUT:
Respond with JSON matching the schema.
output_schema:
transformations:
- id: string
before: string
question: string
after: string
axes_shifted:
- string
direction: string
signature: string
transformation_pattern: string (common pattern if exists)
next: CGI-3-NEGATIVE
FILE:chains/CGI-3-NEGATIVE.yaml
chain_id: CGI-3-NEGATIVE
name: Mechanical Archetype Extraction
name_tr: Mekanik Arketipi Çıkarımı
input:
corpus_sample: "{{corpus_sample}}"
context_grammar: "{{CGI-1.output}}"
transformations: "{{CGI-2.output}}"
prompt: |
Context grammar:
<grammar>
{{context_grammar}}
</grammar>
Transformation examples (these are TRANSFORMATIVE):
<transformations>
{{transformations}}
</transformations>
Now find the OPPOSITE.
Find 3-5 questions where CONTEXT DID NOT CHANGE at all.
Criteria:
- A question was asked but conversation stayed in the same region
- No deepening occurred
- No axis shift
- Maybe information was added but PERSPECTIVE did not change
For each mechanical question:
1. BEFORE: 1-2 sentences immediately before the question
2. QUESTION: The mechanical question
3. AFTER: 1-2 sentences immediately after the question
4. WHY MECHANICAL: Why is it stagnant according to the grammar?
5. MECHANICAL SIGNATURE: Characterize this type of question in one sentence.
OUTPUT:
Respond with JSON matching the schema.
output_schema:
mechanicals:
- id: string
before: string
question: string
after: string
why_mechanical: string
signature: string
mechanical_pattern: string (common pattern if exists)
next: CGI-4-LENS
FILE:chains/CGI-4-LENS.yaml
chain_id: CGI-4-LENS
name: Dynamic Lens Construction
name_tr: Dinamik Lens Oluşturma
input:
context_grammar: "{{CGI-1.output}}"
transformations: "{{CGI-2.output}}"
mechanicals: "{{CGI-3.output}}"
prompt: |
Now construct a LENS specific to this dataset.
Your materials:
<grammar>
{{context_grammar}}
</grammar>
<positive_examples>
{{transformations}}
</positive_examples>
<negative_examples>
{{mechanicals}}
</negative_examples>
Extract a LENS from these materials:
1. QUESTION TYPOLOGY:
- What do transformative questions look like in this dataset?
- What do mechanical questions look like in this dataset?
- What do uncertain (in-between) questions look like?
2. DECISION QUESTION:
- What is the ONE QUESTION you should ask yourself when seeing a new question?
- (This question is not hardcoded — it must be derived from this dataset)
3. SIGNALS:
- 3 linguistic/structural features that signal transformation
- 3 linguistic/structural features that signal mechanical nature
4. CHARACTER OF THIS DATASET:
- What does "right question" mean in this dataset?
- In one sentence.
OUTPUT:
Respond with JSON matching the schema.
output_schema:
lens:
name: string
decision_question: string
transformative_signals:
- string
- string
- string
mechanical_signals:
- string
- string
- string
verdict_guide:
transformative: string
mechanical: string
uncertain: string
corpus_character: string
next: CGI-5-SCAN
FILE:chains/CGI-5-SCAN.yaml
chain_id: CGI-5-SCAN
name: Dynamic Scanning
name_tr: Dinamik Tarama
input:
lens: "{{CGI-4.output}}"
full_corpus: "Full dataset or section to scan"
prompt: |
LENS:
<lens>
{{lens}}
</lens>
Now scan the dataset using this lens.
<corpus>
{{full_corpus}}
</corpus>
For each QUESTION in the corpus:
1. Ask the DECISION QUESTION from the lens
2. Check for transformative and mechanical signals
3. Give verdict: TRANSFORMATIVE | MECHANICAL | UNCERTAIN
Report ONLY TRANSFORMATIVE and UNCERTAIN ones.
For each candidate:
- Location (turn number)
- Question
- Before/After summary
- Why this verdict?
- Confidence: low | medium | high
OUTPUT:
Respond with JSON matching the schema.
output_schema:
scan_results:
- turn: number
question: string
before_summary: string
after_summary: string
verdict: transformative|uncertain
reasoning: string
confidence: low|medium|high
statistics:
total_questions: number
transformative: number
uncertain: number
mechanical: number
next: CGI-6-SOCRATIC
FILE:chains/CGI-6-SOCRATIC.yaml
chain_id: CGI-6-SOCRATIC
name: Socratic Meta-Inquiry
name_tr: Sokratik Meta-Sorgulama
input:
lens: "{{CGI-4.output}}"
scan_results: "{{CGI-5.output}}"
prompt: |
Scanning complete.
<lens>
{{lens}}
</lens>
<results>
{{scan_results}}
</results>
Now SOCRATIC INQUIRY:
1. WHAT DO THESE FINDINGS REVEAL?
- Is there a common pattern in transformative questions?
- Is there a common pattern in mechanical questions?
- Was this pattern captured in the lens, or is it something new?
2. DID THE LENS VALIDATE ITSELF?
- Did the lens's decision question work?
- Which cases were difficult?
- If the lens were to be updated, how should it be updated?
3. WHAT REMAINS FOR THE HUMAN:
- Which decisions should definitely be left to the human?
- What can the system SHOW but cannot DECIDE?
4. COMMON CHARACTERISTIC OF TRANSFORMATIVE QUESTIONS:
- What did "transforming context" actually mean in this dataset?
- Is it different from initial assumptions?
5. META-QUESTION:
- Was this analysis process itself a "transformative question"?
- Did your view of the dataset change?
OUTPUT:
Plain text, insights in paragraphs.
output_schema:
insights: string (paragraphs)
lens_update_suggestions:
- string
human_decision_points:
- string
meta_reflection: string
next: null
FILE:cgi_runner.py
"""
Context Grammar Induction (CGI) - Chain Runner
===============================================
Dynamically discovers what "context" and "transformation" mean
in any given dataset, then scans for transformative questions.
Core Principle:
The right question transforms context.
But what "context" means must be discovered, not assumed.
"""
import yaml
import json
import random
from pathlib import Path
from typing import Any
from string import Template
# =============================================================================
# CONFIGURATION
# =============================================================================
CHAINS_DIR = Path("chains")
CHAIN_ORDER = [
"CGI-1-GRAMMAR",
"CGI-2-POSITIVE",
"CGI-3-NEGATIVE",
"CGI-4-LENS",
"CGI-5-SCAN",
"CGI-6-SOCRATIC"
]
# =============================================================================
# CHAIN LOADER
# =============================================================================
def load_chain(chain_id: str) -> dict:
"""Load a chain definition from YAML."""
path = CHAINS_DIR / f"{chain_id}.yaml"
with open(path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
def load_all_chains() -> dict[str, dict]:
"""Load all chain definitions."""
return {cid: load_chain(cid) for cid in CHAIN_ORDER}
# =============================================================================
# SAMPLING
# =============================================================================
def stratified_sample(corpus: list[dict], n: int = 15) -> list[dict]:
"""
Sample conversations from corpus.
Tries to get diverse samples across the dataset.
"""
if len(corpus) <= n:
return corpus
# Simple stratified: divide into chunks, sample from each
chunk_size = len(corpus) // n
samples = []
for i in range(n):
start = i * chunk_size
end = start + chunk_size if i < n - 1 else len(corpus)
chunk = corpus[start:end]
if chunk:
samples.append(random.choice(chunk))
return samples
def format_samples_for_prompt(samples: list[dict]) -> str:
"""Format samples as readable text for prompt injection."""
formatted = []
for i, sample in enumerate(samples, 1):
formatted.append(f"--- Conversation {i} ---")
if isinstance(sample, dict):
for turn in sample.get("turns", []):
role = turn.get("role", "?")
content = turn.get("content", "")
formatted.append(f"[{role}]: {content}")
elif isinstance(sample, str):
formatted.append(sample)
formatted.append("")
return "\n".join(formatted)
# =============================================================================
# PROMPT RENDERING
# =============================================================================
def render_prompt(template: str, variables: dict[str, Any]) -> str:
"""
Render prompt template with variables.
Uses {{variable}} syntax.
"""
result = template
for key, value in variables.items():
placeholder = "{{" + key + "}}"
# Convert value to string if needed
if isinstance(value, (dict, list)):
value_str = json.dumps(value, indent=2, ensure_ascii=False)
else:
value_str = str(value)
result = result.replace(placeholder, value_str)
return result
# =============================================================================
# LLM INTERFACE (PLACEHOLDER)
# =============================================================================
def call_llm(prompt: str, output_schema: dict = None) -> dict | str:
"""
Call LLM with prompt.
Replace this with your actual LLM integration:
- OpenAI API
- Anthropic API
- Local model
- etc.
"""
# PLACEHOLDER - Replace with actual implementation
print("\n" + "="*60)
print("LLM CALL")
print("="*60)
print(prompt[:500] + "..." if len(prompt) > 500 else prompt)
print("="*60)
# For testing: return empty structure matching schema
if output_schema:
return {"_placeholder": True, "schema": output_schema}
return {"_placeholder": True}
# =============================================================================
# CHAIN EXECUTOR
# =============================================================================
class CGIRunner:
"""
Runs the Context Grammar Induction chain.
"""
def __init__(self, llm_fn=None):
self.chains = load_all_chains()
self.llm = llm_fn or call_llm
self.results = {}
def run(self, corpus: list[dict], sample_size: int = 15) -> dict:
"""
Run full CGI chain on corpus.
Returns:
{
"lens": {...},
"candidates": [...],
"reflection": "...",
"all_outputs": {...}
}
"""
# Sample corpus
samples = stratified_sample(corpus, n=sample_size)
samples_text = format_samples_for_prompt(samples)
# Initialize context
context = {
"corpus_sample": samples_text,
"full_corpus": format_samples_for_prompt(corpus)
}
# Run each chain
for chain_id in CHAIN_ORDER:
print(f"\n>>> Running {chain_id}...")
chain = self.chains[chain_id]
# Render prompt with current context
prompt = render_prompt(chain["prompt"], context)
# Call LLM
output = self.llm(prompt, chain.get("output_schema"))
# Store result
self.results[chain_id] = output
# Add to context for next chain
context[f"{chain_id}.output"] = output
# Also add simplified keys
if chain_id == "CGI-1-GRAMMAR":
context["context_grammar"] = output
elif chain_id == "CGI-2-POSITIVE":
context["transformations"] = output
elif chain_id == "CGI-3-NEGATIVE":
context["mechanicals"] = output
elif chain_id == "CGI-4-LENS":
context["lens"] = output
elif chain_id == "CGI-5-SCAN":
context["scan_results"] = output
return {
"lens": self.results.get("CGI-4-LENS"),
"candidates": self.results.get("CGI-5-SCAN"),
"reflection": self.results.get("CGI-6-SOCRATIC"),
"all_outputs": self.results
}
# =============================================================================
# MAIN
# =============================================================================
def main():
"""Example usage."""
# Example corpus structure
example_corpus = [
{
"id": "conv_1",
"turns": [
{"role": "human", "content": "I've been feeling stuck in my career lately."},
{"role": "assistant", "content": "What does 'stuck' feel like for you?"},
{"role": "human", "content": "Like I'm going through the motions but not growing."},
{"role": "assistant", "content": "When did you last feel like you were growing?"},
{"role": "human", "content": "Maybe three years ago, when I started this job."},
{"role": "assistant", "content": "What was different then?"},
]
},
{
"id": "conv_2",
"turns": [
{"role": "human", "content": "Can you help me write an email?"},
{"role": "assistant", "content": "Sure, what's the email about?"},
{"role": "human", "content": "I need to ask my boss for a raise."},
{"role": "assistant", "content": "What achievements would you highlight?"},
]
},
# Add more conversations...
]
# Run CGI
runner = CGIRunner()
results = runner.run(example_corpus)
print("\n" + "="*60)
print("CGI COMPLETE")
print("="*60)
print(json.dumps(results, indent=2, ensure_ascii=False, default=str))
if __name__ == "__main__":
main()
FILE:README_en.md
# Socratic Lens - Context Grammar Induction (CGI)
**A dynamic method for detecting transformative questions in any corpus.**
---
## The Problem
How do you know if a question is "good"?
Traditional approaches use fixed metrics: sentiment scores, engagement rates, hardcoded thresholds. But these assume we already know what "good" means.
We don't.
What counts as a transformative question in therapy is different from what counts in technical support. A question that opens depth in one context might derail another.
**The real problem isn't measuring. It's defining.**
---
## The Origin
This system began with one observation from the film *Arrival* (2016):
When humanity encounters aliens, the military asks: *"Are you hostile?"*
L