SkillAgentSearch skills...

Readwren

An adaptive multi-agent system that extracts your literary DNA through conversation and generates actionable reading profiles.

Install / Use

/learn @muratcankoylan/Readwren
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

WREN: AI Literary Interview Agent

An adaptive multi-agent system that extracts your literary DNA through conversation and generates actionable reading profiles.

WREN solves a critical problem for LLM users: you know what you like, but explaining your literary taste to an AI is hard. WREN's interview agent asks the right questions, listens deeply, and builds a structured profile that any LLM can use to generate precisely targeted content.

Built with LangGraph, LangChain, and Kimi K2 Thinking models.


Why WREN?

The Problem: Kimi K2 is a great writer, but users struggle to articulate their literary preferences in a way LLMs can act on. Vague prompts like "write me something good" produce generic results.

The Solution: WREN conducts a 12-turn adaptive interview that:

  • Extracts taste anchors (what you love/hate and why)
  • Maps your style signature (prose density, pacing, tone preferences)
  • Identifies narrative desires (story types you wish existed)
  • Captures implicit signals (vocabulary richness, engagement patterns)
  • Generates a structured, machine-readable profile

The Result: A profile you can hand to any LLM to get content that matches your exact taste.


Architecture: Multi-Agent System

WREN uses a specialized multi-agent architecture with distinct roles:

┌────────────────────────────────────────────────────────────────────┐
│                          CLI Interface                             │
│                       (cli_interview.py)                           │
└────────────────────┬───────────────────────────────────────────────┘
                     │
       ┌─────────────┴──────────────┐
       │                            │
┌──────▼─────────────────┐   ┌──────▼──────────────────────┐
│   InterviewAgent       │   │   ProfileGeneratorAgent     │
│ (kimi-k2-thinking-     │   │   (kimi-k2-thinking)        │
│       turbo)           │   │                             │
│                        │   │ Tools:                      │
│ Tools:                 │   │ ┌─────────────────────────┐ │
│ ┌────────────────────┐ │   │ │ ReasoningExtractor      │ │
│ │ ProfileAnalyzer    │ │   │ │ - Extract thinking      │ │
│ │ - Vocab richness   │ │   │ │ - Format reasoning      │ │
│ │ - Response brevity │ │   │ └─────────────────────────┘ │
│ │ - Engagement level │ │   │                             │
│ └────────────────────┘ │   │ ┌─────────────────────────┐ │
│                        │   │ │ ProfileFormatter        │ │
│ ┌────────────────────┐ │   │ │ - JSON → Markdown       │ │
│ │ConversationAnalyzer│ │   │ │ - Shareable text        │ │
│ │ - Turn tracking    │ │   │ │ - Human-readable        │ │
│ │ - Coverage check   │ │   │ └─────────────────────────┘ │
│ │ - Readiness score  │ │   │                             │
│ └────────────────────┘ │   │ ┌─────────────────────────┐ │
└────────┬───────────────┘   │ │ ProfileSaver            │ │
         │                   │ │ - Create user folders   │ │
         │                   │ │ - Save logs + profiles  │ │
         │                   │ │ - Multiple formats      │ │
         │                   │ └─────────────────────────┘ │
         │                   └──────┬──────────────────────┘
         │                          │
┌────────▼──────────────────────────▼─────────────────────────┐
│                   LangGraph StateGraph                      │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                                                      │   │
│  │  [analyze_node]                                     │   │
│  │      ↓                                              │   │
│  │  Run ProfileAnalyzer + ConversationAnalyzer         │   │
│  │      ↓                                              │   │
│  │  [_should_continue]                                 │   │
│  │      ↓                    ↓                         │   │
│  │  turn < 12           turn >= 12                     │   │
│  │      ↓                    ↓                         │   │
│  │  [generate_question]  [generate_profile]            │   │
│  │                                                      │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  State: {messages, turn_count, analysis, profile_data}      │
└────────┬─────────────────────────────────────────────────────┘
         │
┌────────▼──────────────┐
│ RedisCheckpointSaver  │
│ (State Persistence)   │
│                       │
│ - Pickle serialization│
│ - 24h TTL             │
│ - Resume sessions     │
└───────────────────────┘

Agent 1: InterviewAgent

Role: Conversational interviewer that adapts to user responses

Model: kimi-k2-thinking-turbo (fast, conversational)

Capabilities:

  • Conducts 12-turn structured interview
  • References previous answers (shows it's listening)
  • Adjusts question depth based on response style
  • Tracks coverage across 5 dimensions
  • Uses LangGraph state machine for conversation flow

Key Innovation: Uses real-time analysis tools to adapt questioning:

  • ProfileAnalyzerTool: Measures vocabulary richness, brevity, engagement
  • ConversationAnalyzerTool: Tracks coverage and determines readiness

Agent 2: ProfileGeneratorAgent

Role: Deep analyst that transforms conversation into structured profile

Model: kimi-k2-thinking (extended reasoning for analysis)

Capabilities:

  • Parses full conversation transcript
  • Generates JSON profile with 40+ data points
  • Scores style preferences on 0-100 scales
  • Provides human-readable explanations
  • Extracts its own reasoning process

Key Innovation: Single-purpose agent runs once, uses expensive model only when needed, includes explanations in second-person for easy sharing.

Agent 3: ReasoningExtractor

Role: Extracts and formats Kimi K2's internal thinking

Capabilities:

  • Pulls reasoning_content from model responses
  • Formats for human readability
  • Saves separately for transparency
  • Enables debugging and insight

LangGraph State Machine

WREN uses LangGraph for stateful conversation management:

class InterviewState(TypedDict):
    messages: Annotated[List[BaseMessage], add]  # Conversation history
    turn_count: int                              # Current turn
    profile_data: Dict[str, Any]                 # Generated profile
    is_complete: bool                            # Completion flag
    current_analysis: Dict[str, Any]             # Real-time metrics

Graph Flow

User Input
    ↓
[analyze_node]
├─> ProfileAnalyzerTool: Analyze response style
├─> ConversationAnalyzerTool: Check coverage
└─> Update state with analysis
    ↓
[_should_continue]
├─> turn_count >= 12? → generate_profile
└─> turn_count < 12?  → generate_question
    ↓
[generate_question_node]
├─> Build prompt with turn context + analysis
├─> Invoke Kimi K2 Thinking Turbo
├─> Extract reasoning from response
└─> Return AIMessage
    ↓
State persisted to Redis → Ready for next turn

Why LangGraph?

  • Built-in state persistence (Redis or in-memory)
  • Clean separation of analysis → decision → generation
  • Resumable sessions (pick up where you left off)
  • Type-safe state transitions

Redis Integration

WREN implements a custom Redis checkpointer for LangGraph:

class RedisCheckpointSaver(BaseCheckpointSaver):
    def put(self, config, checkpoint, metadata, new_versions):
        # Serializes full state with pickle (handles Python objects)
        serialized = pickle.dumps({
            "checkpoint": checkpoint,
            "metadata": metadata,
            "config": config
        })
        self.redis.setex(key, self.ttl, serialized)  # 24h TTL

Why Custom?

  • LangGraph doesn't include Redis checkpointer out of the box
  • Standard JSON serialization fails on Python objects
  • Pickle handles complex state including functions/lambdas

Benefits:

  • Sessions persist across restarts
  • Resume interrupted interviews
  • Inspect state at any point
  • Auto-expiration after 24 hours

Prompt Engineering

Adaptive System Prompt

SYSTEM_PROMPT = """You are a world-class literary profiler conducting 
an adaptive interview.

CORE PRINCIPLES:
- Ask ONE question at a time
- Always reference their specific previous answers
- Adapt follow-ups based on response depth and style
- Continue asking questions until turn 12

STRICT RULES:
- CURRENT TURN: {turn_count} of 12
- If turn < 12: Ask another question (do NOT mention completion)
- If turn = 12: Only then offer to generate their profile
"""

Key Features:

  • Dynamic turn injection prevents premature completion
  • Explicit rules override model's tendency to end early
  • References previous answers (shows listening)
  • Adapts energy level to user responses

Profile Generation Prompt

The system dynamically loads scoring guidelines from PROFILE_RUBRIC.md:

@staticmethod
def get_summary_prompt(conversation: str, include_rubric: bool = True):
    # Load rubric scales from file
    rubric_section = _load_rubric_section()
    
    return f"""Generate JSON profile with:
    
    JSON SCHEMA: [detailed structure]
    
    SCORING GUIDELINES:
    {rubric_section}  ← Dynamically loaded from PROFILE_RUBRIC.md
    
    Conversation:
    {conversation}
    """

Why Dynamic Loading?

  • Single source of truth (update rubric → prompts update automatically)
  • LLM sees detailed scoring guidance
  • Consistent scoring across all profiles

Tools & Analysis

ProfileAnalyzerTool

def _run(self, response_text: str, conversation_history: List) -> Dict:
    """Analyzes individual responses for implicit signals."""
    
    # Calculate metrics
    vocabulary_richness = unique_words / total_words
    response_brevity = 1 / (word_count / 100)  # Normalized
    engagement_level = heuristic(examples, emotion_words, depth)
    
    return {
  
View on GitHub
GitHub Stars190
CategoryDevelopment
Updated2d ago
Forks19

Languages

Python

Security Score

100/100

Audited on Mar 26, 2026

No findings