Adaptive Reasoning with ARM: Teaching AI the Right Way to Think

Adaptive Reasoning with ARM: Teaching AI the Right Way to Think
Page content

Summary

Chain-of-thought is powerful, but which chain? Short explanations work for easy tasks, long reflections help on hard ones, and code sometimes beats them both. What if your model could adaptively pick the best strategy, per task, and improve as it learns?

The Adaptive Reasoning Model (ARM) is a framework for teaching language models how to choose the right reasoning format direct answers, chain-of-thoughts, or code depending on the task. It works by evaluating responses, scoring them based on rarity, conciseness, and difficulty alignment, and then updating model behavior over time.

In this post, we implement a local version of ARM, extend it with an adaptive selector, custom reward shaping, and a ZSA-based value predictor, and show how it improves reasoning quality across multiple formats all without cloud dependencies.

ARM: Adaptive Reasoning Model

๐Ÿš€ Why Build an Adaptive Reasoning Model?

Modern LLMs are incredibly capable, but they often struggle with choosing the right way to reason for a given problem. This project tackles that gap head-on by introducing a system that can learn which reasoning style works best per task, per difficulty, per goal.

๐Ÿงญ Why we want this

  • Reasoning matters more than raw answers: Different formats lead to dramatically different outcomes. Direct answers may suffice for trivia, but harder problems need structured thought.

  • One-size-fits-all prompting is limiting: Most AI systems apply the same prompt template everywhere. We dynamically adapt the reasoning strategy based on the goal itself.

  • Some reasoning strategies are underutilized: Prompts like code or short chains often get ignored. ARM incentivizes exploring diverse reasoning paths and rewards the ones that perform best.

  • Itโ€™s lightweight and local: All of this is done using small, local models. This design ensures that ARM can be run efficiently on local hardware, reducing API costs and enhancing data privacy, making advanced reasoning accessible to a wider range of applications.

  • The format becomes the program: We treat the prompt format as a symbolic wrapper that guides the reasoning logic and train a model to recognize which wrapper works best.

๐Ÿš€ Key Innovations in Our Implementation

  • โœ… Unified format-based reasoning supervision using ARM (Adaptive Reasoning Model)
  • โœ… Structured prompt templates that are both human-readable and machine-learnable
  • โœ… Preference pair training via adapters: Automatically logs and learns from format comparisons
  • โœ… Dynamic format selection: A system that learns to pick the best reasoning strategy for future tasks
  • โœ… MR.Q-based format reward modeling: Efficient, trainable, format-aware scoring of hypothesis quality.

Leveraging principles from the MR.Q (Modular Reward Querying) framework, we’ve developed an efficient, trainable system for scoring the quality of generated hypotheses based on their reasoning format. This allows us to go beyond static rubric-based evaluations and instead adaptively shape reward signals that reflect format rarity, token efficiency, and task difficulty. The result is a dynamic and learnable preference signal that can guide reasoning agents toward more optimal format usage.

๐Ÿ› ๏ธ What this gives you

  • ๐Ÿ“ˆ Better outputs with fewer retries
  • ๐Ÿงช Built-in experimentation with reasoning styles
  • ๐Ÿ” A full loop of generation โ†’ evaluation โ†’ adaptation
  • โš™๏ธ A modular system you can plug into any agent pipeline

๐Ÿš€ Motivation: Reasoning Is Format-Dependent

Imagine asking an AI to solve a complex mathematical proof with a simple ‘What’s the answer?’

Or asking for a simple fact like ‘Who discovered penicillin?’ and getting a 10-step chain of thought. The results would be far from optimal.

Chain-of-thought reasoning has become a standard technique in prompting LLMs. But recent research shows that no single format works best across all tasks. Instead, the reasoning format whether it’s direct, code-based, short explanations, or full chains plays a key role in both correctness and interpretability.

Rather than hardcoding formats or relying on human instructions, we asked:

Can an AI system learn which format to use by itself, based on prior successes?


๐Ÿง  Structured Reasoning: The Four Formats That Power ARM

To teach AI how to select the best way to think, we built our system around four distinct reasoning templates. Each one reflects a different cognitive strategy and crucially, each is wrapped in a precise format that makes it identifiable, measurable, and adaptable.

1. ๐ŸŸข <Direct> โ€“ Straight to the Answer

Why we chose it: This template is the simplest. It reflects how some questions can be answered directly, without the need for multi-step reasoning. By including a direct response format, we give the system a baseline strategy to fall back on when brevity and clarity suffice.

Structure:

<Direct>
Question: {{ goal.goal_text }}
Answer: {{ "<ANSWER>" }}
</Direct>

2. ๐ŸŸก <Short_CoT> โ€“ Concise Chain-of-Thought

Why we chose it: Short CoT is useful for tasks requiring a little reasoning but not a full-length breakdown. It helps bridge the gap between direct responses and more elaborate chains, making it ideal for moderate complexity tasks like commonsense inference.

Structure:

<Short_CoT>

Let's think through it briefly.

Step 1: ...
Step 2: ...
Therefore, the answer is: <ANSWER>

</Short_CoT>

3. ๐Ÿ”ต <Code> โ€“ Reasoning Through Python

Why we chose it: Some problems especially in math, logic, or algorithmic reasoning are best solved through code. This format wraps a Python function inside tags and ensures the model explains the logic step-by-step, then prints the final answer.

Structure:

<Code>
def solve():
    # Step 1:
    # Step 2:
    # Return the final result
    return ...

result = solve()
print("Answer:", result)

<ANSWER>
</Code>

4. ๐Ÿ”ด <Long_CoT> โ€“ Full Reasoning Breakdown

Why we chose it: For hard questions or abstract goals, the Long CoT template enforces deep, structured reasoning. It forces the AI to reflect through labeled steps and logically walk toward a conclusion, supporting more interpretability and higher-fidelity evaluation.

Structure:

<Long_CoT>

Let's take it step-by-step and reason carefully.

Step 1: ...
Step 2: ...
...
Therefore, the final answer is: <ANSWER>

</Long_CoT>

๐Ÿงฉ Why Format Matters

We donโ€™t just use formats to structure AI outputs we use them to drive the entire reasoning loop. The ARM system learns which format is most effective for a given goal by:

  • Generating responses in each format
  • Evaluating their performance using MR.Q-based reward modeling
  • Favoring underused but successful formats to promote diversity
  • Adapting its format choice over time

In short, the format is the program a symbolic wrapper that not only guides generation, but enables learning. This level of structure makes ARM much more than a prompt selector. Itโ€™s a reasoning strategist.


๐Ÿงฉ Format Selection: Choosing the Best Way to Reason

Not all reasoning styles are created equal. Sometimes a direct answer is best; other times, a long chain-of-thought provides the needed depth. But how do we decide, automatically, which format to use for a given task?

This is where our format selection mechanism comes in.

We begin by generating responses in multiple reasoning formats like <Direct>, <Short_CoT>, <Code>, and <Long_CoT>. Each format represents a different way of thinking through the problem. But rather than hand-pick the best one, we score them using a trainable reward model that learns to distinguish which formats work best under which conditions.

Each response is evaluated using:

  • A value predictor, trained to assess quality from embeddings.
  • Rarity bonuses, which encourage diversity by favoring underused formats.
  • A length penalty, which discourages unnecessarily verbose outputs.
  • Optionally, a difficulty-aware heuristic, guiding harder questions toward more structured reasoning formats.

These factors are combined into a single score. Unlike many traditional setups, lower scores in our system mean better outcomes less redundancy, more efficiency, better alignment.

The format with the best (lowest) final score is selected as the response. This enables the agent to adaptively choose the reasoning strategy that fits the task, rather than relying on a fixed or naive generation strategy.

In short: format selection is dynamic, data-driven, and reward-guided.

    graph TD
A[Input Question] --> B{Difficulty Analysis}
B -->|Easy| C[Direct/Short_CoT]
B -->|Medium| D[Code/Short_CoT]
B -->|Hard| E[Long_CoT/Code]
C --> F[Generate Responses]
D --> F
E --> F
F --> G[ARM Scoring]
G --> H[Select Best Format]
  
Format Math Accuracy Speed (ms) Token Efficiency
๐ŸŸข Direct 62% 120 โ˜…โ˜…โ˜…โ˜…โ˜…
๐ŸŸก Short_CoT 78% 350 โ˜…โ˜…โ˜…โ˜†โ˜†
๐Ÿ”ต Code 92% 2100 โ˜…โ˜…โ˜†โ˜†โ˜†
๐Ÿ”ด Long_CoT 85% 2900 โ˜…โ˜†โ˜†โ˜†โ˜†
+ARM 94% 1800* โ˜…โ˜…โ˜…โ˜…โ˜†
  • Adaptive selection overhead included

๐Ÿง  Format Detection & Reward Shaping

In the ARM system, not all reasoning formats are treated equally โ€” and not just because of performance. The system actively detects the format used and applies reward shaping to balance accuracy, diversity, and efficiency.

๐Ÿ” Format Detection

Each generated response is wrapped in tags like <Direct>...</Direct>, <Short_CoT>...</Short_CoT>, and so on. ARM uses these tags to detect which format was used:

def detect_format(text: str) -> Optional[str]:
    if "<Direct>" in text and "</Direct>" in text:
        return "direct"
    if "<Short_CoT>" in text and "</Short_CoT>" in text:
        return "short_cot"
    if "<Code>" in text and "</Code>" in text:
        return "code"
    if "<Long_CoT>" in text and "</Long_CoT>" in text:
        return "long_cot"
    return None

This lets the system keep accurate statistics on format frequency โ€” a key ingredient for reward shaping.

๐ŸŽฒ Rarity Bonus

To avoid overfitting to common formats like <Direct>, the system includes a rarity bonus. Less frequently used formats get a small boost:

rarity_bonus = 1.0 / (1 + self.judge.format_freq.get(fmt, 0))

This encourages the model to explore underused but potentially valuable reasoning strategies, especially for more complex problems.

๐Ÿ“‰ Token Efficiency Penalty

Length matters โ€” but longer isnโ€™t always better. To discourage unnecessarily verbose responses, ARM applies a token penalty:

token_len = len(response.split())
final_score = base_score - 0.01 * token_len + rarity_bonus

This penalizes bloated outputs while rewarding concise, effective answers โ€” a step toward aligning models with real-world efficiency constraints.

๐Ÿ“ˆ Ada-GRPO-Inspired Reward Update

The final score used for format selection is not just a reflection of performance; it’s shaped by format frequency and verbosity:

final_score = base_score - 0.01 * token_len + rarity_bonus

This shaped reward serves as a proxy for adaptive format optimization โ€” a lightweight, trainable alternative to heavy reinforcement learning methods like GRPO or PPO. Over time, this results in more balanced and context-sensitive format selection.


๐Ÿง  Adaptive Reasoning Modes

To determine which type of reasoning (direct answer, short chain-of-thought, code-based solution, or long chain-of-thought) is most appropriate for a given problem is hard. In our system, we tackle this with adaptive reasoning modes that dynamically choose the best reasoning strategy using format-based reward signals.

๐Ÿงช Mode 1: Adaptive Format Selection

In this default mode, the agent:

  1. Generates hypotheses in multiple reasoning formats.
  2. Scores each hypothesis using a custom-trained reward model derived from MR.Q.
  3. Applies reward shaping to account for response length and rarity of format (to encourage diversity).
  4. Selects the best format by combining these reward signals.

This allows the agent to adaptively shift strategies: using a simple direct response for easy questions, and deeper long-form reasoning when needed. The scoring mechanism penalizes overly verbose answers and boosts underused formats โ€” enabling Ada-GRPO-style learning, where format choice is continuously updated based on feedback from past performance.

๐Ÿง  Mode 2: Consensus-Guided Reasoning

Consensus-guided mode is designed for reliability. Instead of choosing based on reward, it tests for agreement:

  • The system generates responses in three formats: direct, short CoT, and code.
  • If all three formats produce the same final answer, the system accepts this as high-confidence consensus.
  • If not, it falls back to a long-form CoT explanation, which is more exhaustive and less prone to ambiguity.

This strategy balances speed and robustness. When all reasoning modes agree, the answer is likely strong. If they diverge, we activate deeper reasoning to resolve ambiguity.


๐Ÿ› ๏ธ System Overview

We implemented ARM within the Co AI framework, using the following components:

    
classDiagram
    class ARMDataLoader {
        +load_dataset()
        +wrap_for_mrq()
        +generate_pairs()
    }

    class MRQPreferencePairORM {
        +id : int
        +goal : str
        +prompt : str
        +output_a : str
        +output_b : str
        +preferred : str
        +fmt_a : str
        +fmt_b : str
        +difficulty : str
        +to_dict() : dict
    }

    class ARMReasoningSelfEvaluator {
        -encoder : TextEncoder
        -value_predictor : HypothesisValuePredictor
        -ref_value_predictor : HypothesisValuePredictor
        -format_freq : dict
        +score(prompt, response) : float
        +judge(goal, prompt, output_a, output_b) : (str, dict)
        +train_from_database(goal, cfg)
        +export_samples_to_json()
    }

    class HypothesisValuePredictor {
        +forward(zsa_embedding) : float
    }

    class TextEncoder {
        +forward(prompt_emb, response_emb) : Tensor
    }

    class AdaptiveReasonerAgent {
        -judge : ARMReasoningSelfEvaluator
        +run(context) : dict
        +_run_adaptive_mode(prompt, context) : dict
        +_generate_with_format(fmt, context) : dict
    }

    ARMDataLoader --> MRQPreferencePairORM : outputs
    AdaptiveReasonerAgent --> ARMReasoningSelfEvaluator : uses
    ARMReasoningSelfEvaluator --> TextEncoder : uses
    ARMReasoningSelfEvaluator --> HypothesisValuePredictor : uses
    ARMReasoningSelfEvaluator --> MRQPreferencePairORM : loads
  

๐Ÿง  Feeding the Brain: The ARMDataLoader

At the heart of our adaptive reasoning system lies the ARMDataLoader, a tool designed to transform raw datasets into structured, format-aware preference data the kind of data that teaches our AI how to think better.

We extended Hugging Face datasets with format-labeled reasoning samples using the ARMDataLoader. These provide training pairs like:

Prompt: Why do planets orbit stars?
Response A: <Short_CoT> Because gravity acts as a central force...
Response B: <Direct> Due to gravity.
Preferred: A

class ARMDataLoader:
    def __init__(
        self,
        dataset_name: str = "aqua_rat",
        subset: Optional[str] = None,
        split: str = "train",
        max_samples: int = 500,
        memory=None,
        logger=None
    ):
        self.dataset_name = dataset_name
        self.subset = subset
        self.split = split
        self.max_samples = max_samples
        self.memory = memory
        self.logger = logger

        # Format tokens
        self.format_tokens = {
            "direct": "<Direct>",
            "short_cot": "<Short_CoT>",
            "code": "<Code>",
            "long_cot": "<Long_CoT>"
        }
        self.format_end_tokens = {
            "direct": "</Direct>",
            "short_cot": "</Short_CoT>",
            "code": "</Code>",
            "long_cot": "</Long_CoT>"
        }

        self._debug_count = 0
        self.dataset = None

    def log(self, event_name: str, payload: dict):
        if self.logger:
            self.logger.log(event_name, payload)
        else:
            print(f"[{event_name}] {json.dumps(payload)}")

    def adapt(self, context: dict):
        """Main method: Load โ†’ Convert โ†’ Save to Memory"""
        self.log("DatasetLoading", {"name": self.dataset_name, "split": self.split})
        self.load_dataset()
        self.summarize_difficulties()
        self.print_samples_by_difficulty()

        total_samples = len(self.dataset)
        indices = random.sample(range(total_samples), min(self.max_samples, total_samples))

        count = 0
        goal_text = context.get("goal").get("goal_text")
        run_id = context.get("run_id")
        for idx in indices:
            sample = self.dataset[idx]
            pairs = self.build_preference_pairs(sample)
            for pair in pairs:
                prompt = pair["prompt"]
                chosen = pair["chosen"]
                rejected = pair["rejected"]
                preferred = pair["preferred_format"]

                # Embed everything once
                prompt_emb = self._get_or_cache_embedding(prompt)
                chosen_emb = self._get_or_cache_embedding(chosen)
                rejected_emb = self._get_or_cache_embedding(rejected)

                # Save to database
                try:
                    self.memory.mrq.add_preference_pair(
                        goal=goal_text,
                        prompt=prompt,
                        output_a=chosen,
                        output_b=rejected,
                        preferred=preferred,
                        run_id=run_id
                    )
                    count += 1
                except Exception as e:
                    self.log("PreferencePairSaveError", {
                        "error": str(e),
                        "prompt": prompt[:80],
                        "chosen": chosen[:80],
                        "rejected": rejected[:80]
                    })

        self.log("PreferencePairsSaved", {
            "count": count,
            "goal": "arm_dpo"
        })
        context["dpo_samples"] = count
        return context

    def _get_or_cache_embedding(self, text: str) -> List[float]:
        """
        Get embedding from cache or compute and store.
        Uses your existing memory.embedding.get_or_create() method.
        """
        emb = self.memory.embedding.get_or_create(text)
        return emb

    def load_dataset(self):
        """Load dataset from Hugging Face."""
        try:
            self.dataset = load_dataset(self.dataset_name, self.subset, split=self.split)
            self.log("DatasetLoaded", {"count": len(self.dataset)})
        except Exception as e:
            raise RuntimeError(f"Failed to load dataset '{self.dataset_name}': {str(e)}")

    def _detect_difficulty(self, question: str) -> str:
        words = question.split()
        if len(words) < 20:
            return "easy"
        elif len(words) < 50:
            return "medium"
        else:
            return "hard"

    def build_preference_pairs(self, sample: Dict) -> List[Dict]:
        """
        Build DPO-style preference pairs by comparing formats.
        Returns list of dicts like:
        {
          'prompt': ..., 
          'chosen': ..., 
          'rejected': ..., 
          'preferred_format': ..., 
          'difficulty': ...
        }
        """
        question = sample.get("question", "").strip()
        ground_truth = sample.get("correct", "").strip()
        difficulty = self._detect_difficulty(question)

        # Generate all four reasoning formats
        direct = self.generate_direct(ground_truth)
        short_cot = self.generate_short_cot(question, ground_truth)
        code = self.generate_code(question, ground_truth)
        long_cot = self.generate_long_cot(question, ground_truth)

        format_to_response = {
            "direct": direct,
            "short_cot": short_cot,
            "code": code,
            "long_cot": long_cot
        }

        # Filter out empty responses
        valid_formats = [fmt for fmt, resp in format_to_response.items() if resp.strip()]
        format_to_response = {k: v for k, v in format_to_response.items() if k in valid_formats}

        # Define which formats are preferred based on difficulty
        if difficulty == "easy":
            preferred_formats = ["direct", "short_cot", "code"]
            non_preferred_formats = ["long_cot"]
        elif difficulty == "medium":
            preferred_formats = ["short_cot", "code"]
            non_preferred_formats = ["direct", "long_cot"]
        elif difficulty == "hard":
            preferred_formats = ["long_cot", "code"]
            non_preferred_formats = ["direct", "short_cot"]
        else:
            preferred_formats = ["short_cot", "code"]
            non_preferred_formats = ["direct", "long_cot"]

        # Build all possible pairs
        pairs = []
        for pref in preferred_formats:
            p_resp = format_to_response.get(pref)
            if not p_resp:
                continue
            for non_pref in non_preferred_formats:
                np_resp = format_to_response.get(non_pref)
                if not np_resp:
                    continue
                pairs.append({
                    "prompt": question,
                    "chosen": p_resp,
                    "rejected": np_resp,
                    "preferred_format": pref,
                    "rejected_format": non_pref,
                    "difficulty": difficulty
                })

        return pairs

    def summarize_difficulties(self):
        counts = Counter()
        for sample in self.dataset:
            question = sample.get("question", "")
            detected = self._detect_difficulty(question)
            counts[detected] += 1
        self.log("DifficultySummary", dict(counts))
        return counts
    
    def print_samples_by_difficulty(self, count_per_level=3):
        buckets = {"easy": [], "medium": [], "hard": []}
        for sample in self.dataset:
            question = sample.get("question", "")
            difficulty = self._detect_difficulty(question)
            if len(buckets[difficulty]) < count_per_level:
                buckets[difficulty].append(question)

        for diff, questions in buckets.items():
            self.log("SampleByDifficulty", {
                "difficulty": diff,
                "examples": questions
            })

    def _detect_difficulty(self, question: str) -> str:
        """Basic heuristic to infer difficulty based on question length."""
        words = question.split()
        if len(words) < 20:
            return "easy"
        elif len(words) < 50:
            return "medium"
        else:
            return "hard"

    def generate_direct(self, answer: str) -> str:
        return f"The answer is {answer}."

    def generate_short_cot(self, question: str, answer: str) -> str:
        return (
            "Let me think briefly:\n"
            + "Step 1: Understand the question.\n"
            + "Step 2: Apply basic logic.\n"
            + f"Final Answer: {answer}"
        )

    def generate_code(self, question: str, answer: str) -> str:
        return (
            "def solve():\n"
            "    # Placeholder code generated by GPT-4o\n"
            f"    return '{answer}'\n"
            "solve()\n"
            f"# Output: {answer}"
        )

    def generate_long_cot(self, question: str, answer: str) -> str:
        return (
            "Let's analyze this step-by-step:\n\n"
            "1. Read the question carefully.\n"
            "2. Identify key information.\n"
            "3. Consider multiple approaches.\n"
            "4. Evaluate thoroughly.\n"
            "...\n"
            "Reflection: This approach ensures correctness by exploring multiple paths.\n"
            f"Final Answer: {answer}"
        )

๐Ÿฆพ What It Does

The ARMDataLoader serves a crucial role in the training loop. Its responsibilities are:

  • Load: It loads a reasoning dataset (like aqua_rat) from Hugging Face.

  • Analyze: It categorizes questions by difficulty (easy, medium, hard) using a simple heuristic based on question length.

  • Generate: For each question, it generates multiple reasoning-format outputs:

    • ๐ŸŸข direct
    • ๐ŸŸก short_cot (short chain of thought)
    • ๐Ÿ”ต code (Python-style)
    • ๐Ÿ”ด long_cot (extended reasoning)
  • Compare: Based on question difficulty, it builds DPO-style preference pairs (e.g., “long_cot” preferred over “short_cot” for hard questions).

  • Embed: It computes vector embeddings for each prompt and response.

  • Save: It stores the result in three places:

    • ๐Ÿ”„ Context (in-memory): for downstream agents to consume immediately.
    • ๐Ÿ’พ Database: for structured MR.Q-style training and later introspection.
    • ๐Ÿ“ Files (optional): for backups, analysis, or offline tuning.

โ“ How It Works

Hereโ€™s a step-by-step breakdown of what happens during the adapt() call:

  1. Load the dataset It fetches the dataset from Hugging Face, logging the number of samples.

  2. Infer question difficulty Shorter questions tend to be easier, longer ones harder and that guides how we prioritize reasoning formats.

  3. Generate reasoning outputs For each question, it simulates what an LLM might say using different reasoning modes. These are fixed-format templates that mimic how real agents would respond.

  4. Construct preference pairs Based on difficulty, it decides which formats are “better” and creates training samples where one format is preferred over another.

  5. Embed and persist The full prompt, chosen response, and rejected response are embedded and inserted into the database. It also attaches metadata like reasoning format and difficulty.

  6. Log everything From dataset loading to embedding creation to preference storage, every step is tracked to make training traceable and debuggable.

โœจ Why This Matters

Teaching an AI to reason well means teaching it when and how to use different reasoning styles. The ARMDataLoader is our way of building that teaching dataset not by labeling answers manually, but by programmatically generating preferences from structure and context.

ARM shows that format is a learnable behavior. By treating prompt structure as part of the training signal, we enable small models to reason more like experts choosing the right tool for the job.

This lays the foundation for:

  • Symbolic planners that suggest formats for unseen tasks
  • Instruction-following models that learn format from implicit feedback
  • Compact model distillation from format-aware training logs

๐Ÿค– Teaching the AI to Judge Itself: The ARM Self-Evaluator

At the core of the ARM system lies a unique component: the ARMReasoningSelfEvaluator. This module isn’t just another scorer it’s the backbone of adaptive reasoning, built to learn what kind of reasoning works best based on experience.

โ“ Why We Built It

Most models respond in a single style direct, verbose, code-driven, or step-by-step without context. But different problems demand different styles of thinking. A math puzzle might need code. A factual recall might just need a short sentence.

The ARM evaluatorโ€™s job is to learn which reasoning format works best in which context and to reinforce those preferences through reward learning.

๐Ÿง‘โ€๐Ÿซ What It Does

The evaluator:

  • ๐Ÿง  Scores candidate responses using a learned reward model
  • ๐Ÿ†š Compares outputs in pairs to determine the better one
  • ๐Ÿ”„ Trains itself using DPO-style preference data
  • ๐Ÿงฎ Balances rare and overused formats by adjusting rewards dynamically
  • ๐Ÿšฆ Selects the best reasoning format for new prompts

Itโ€™s more than a static critic itโ€™s a format-aware adaptive judge that updates its preferences as it sees more examples.



class ARMReasoningSelfEvaluator(BaseEvaluator):
    def __init__(self, cfg, memory, logger):
        self.cfg = cfg
        self.memory = memory
        self.logger = logger
        self.device = cfg.get("device", "cpu")

        self.format_freq = cfg.get(
            "format_freq", {"direct": 1, "short_cot": 1, "code": 1, "long_cot": 1}
        )
        self.format_rewards = cfg.get(
            "format_rewards",
            {"direct": [0.5], "short_cot": [0.5], "code": [0.5], "long_cot": [0.5]},
        )

        self.encoder = TextEncoder().to(self.device)
        self.value_predictor = HypothesisValuePredictor(512, 1024).to(self.device)

    def judge(self, goal, prompt, output_a, output_b):
        prompt_emb = torch.tensor(
            self.memory.embedding.get_or_create(prompt), device=self.device
        ).unsqueeze(0)
        output_a_emb = torch.tensor(
            self.memory.embedding.get_or_create(output_a), device=self.device
        ).unsqueeze(0)
        output_b_emb = torch.tensor(
            self.memory.embedding.get_or_create(output_b), device=self.device
        ).unsqueeze(0)

        zsa_a = self.encoder(prompt_emb, output_a_emb)
        zsa_b = self.encoder(prompt_emb, output_b_emb)

        value_a = self.value_predictor(zsa_a).item()
        value_b = self.value_predictor(zsa_b).item()

        preferred_output = output_a if value_a >= value_b else output_b
        scores = {
            "value_a": value_a,
            "value_b": value_b,
            "fmt_a": ARMDataLoader.detect_format(output_a),
            "fmt_b": ARMDataLoader.detect_format(output_b),
        }

        return preferred_output, scores

    def _update_format_stats(self, fmt: str, reward: float):
        """
        Track format usage and average reward per format.
        
        This enables format-aware reward shaping and prevents format collapse.
        """
        if fmt not in self.format_freq:
            self.format_freq[fmt] = 0
            self.format_rewards[fmt] = []

        self.format_freq[fmt] += 1
        self.format_rewards[fmt].append(reward)

    def train_from_database(self, goal_text: str, cfg: dict):
        limit = cfg.get("limit", 1000)
        epochs = cfg.get("epochs", 20)
        lr = cfg.get("lr", 1e-4)
        batch_size = cfg.get("batch_size", 16)

        samples = self.memory.mrq.get_training_pairs(goal=goal_text, limit=limit)
        if not samples:
            self.logger.log(
                "TrainingError", {"message": "No samples found", "goal": goal_text}
            )
            return

        inputs, labels = [], []
        for item in samples:
            prompt_emb = self.memory.embedding.get_or_create(item["prompt"])
            output_a_emb = self.memory.embedding.get_or_create(item["output_a"])
            output_b_emb = self.memory.embedding.get_or_create(item["output_b"])
            preferred = item["preferred"]

            zsa_a = self.encoder(
                torch.tensor(prompt_emb).unsqueeze(0).to(self.device),
                torch.tensor(output_a_emb).unsqueeze(0).to(self.device),
            )
            zsa_b = self.encoder(
                torch.tensor(prompt_emb).unsqueeze(0).to(self.device),
                torch.tensor(output_b_emb).unsqueeze(0).to(self.device),
            )

            diff = zsa_a - zsa_b if preferred == "a" else zsa_b - zsa_a
            inputs.append(diff.squeeze(0).detach())
            labels.append(torch.tensor([1.0], device=self.device))

        dataset = torch.utils.data.TensorDataset(
            torch.stack(inputs), torch.stack(labels)
        )
        dataloader = torch.utils.data.DataLoader(
            dataset, batch_size=batch_size, shuffle=True
        )

        opt = torch.optim.Adam(self.value_predictor.parameters(), lr=lr)
        self.value_predictor.train()

        for epoch in range(epochs):
            total_loss = 0.0
            for x_batch, y_batch in dataloader:
                preds = self.value_predictor(x_batch)
                loss = -torch.log(torch.sigmoid(preds)).mean()
                opt.zero_grad()
                loss.backward()
                opt.step()
                total_loss += loss.item()

            avg_loss = total_loss / len(dataloader)
            self.logger.log(
                "TrainingEpoch",
                {"epoch": epoch + 1, "avg_loss": avg_loss, "goal": goal_text},
            )

        self.logger.log("TrainingComplete", {"goal": goal_text})

    def score(self, prompt: str, response: str) -> float:
        """
        Public scoring method used by agents like AdaptiveReasonerAgent.
        Returns a scalar score indicating how good a response is.
        """
        prompt_emb = torch.tensor(
            self.memory.embedding.get_or_create(prompt), device=self.device
        ).unsqueeze(0)
        response_emb = torch.tensor(
            self.memory.embedding.get_or_create(response), device=self.device
        ).unsqueeze(0)

        with torch.no_grad():
            zsa = self.encoder(prompt_emb, response_emb)
            score = self.value_predictor(zsa).item()

        token_len = len(response.split())
        fmt = ARMDataLoader.detect_format(response)
        rarity_bonus = 1.0 / (1 + self.format_freq.get(fmt, 1))
        score -= 0.01 * token_len
        score += rarity_bonus

        self._update_format_stats(fmt, score)
        return score

    def _score_response(self, prompt_emb, response_emb):
        """Score a single response using prompt-response encoder + value predictor"""
        zsa = self.encoder(prompt_emb, response_emb)
        return self.value_predictor(zsa), zsa

Key Features

โœ… Format-Aware Scoring

Using a helper function (detect_format), the evaluator classifies each response into one of four formats:

  • <Direct>
  • <Short_CoT>
  • <Long_CoT>
  • <Code>

These labels are essential because reward shaping is format-dependent. For example, longer reasoning chains (long CoT) might earn higher scores on hard questions but get penalized for verbosity on easy ones.

๐Ÿง  Reward Model

It uses a neural model to evaluate the quality of prompt-response pairs:

  • A TextEncoder converts the prompt and response into embeddings.
  • A ValuePredictor scores the embeddings.
  • A reference model (ref_value_predictor) provides a stability baseline.

Rewards are computed with KL-penalty and PPO-style clipping, to avoid overfitting or sudden shifts in scoring behavior.

๐ŸŽฒ (Soon) Rarity Scaling (Ada-GRPO Inspired)

The more a format is used, the less reward it gets. This rarity-based adjustment helps the system explore diverse reasoning styles rather than settling on a single default.

  • Rare formats are rewarded more.
  • Overused formats are penalized unless they consistently outperform.

This encourages adaptive and diverse thinking in a structured way.

๐Ÿงช Format Selection

For a new prompt, the evaluator can compare multiple formatted responses and select the best one:

best_fmt, scores = evaluator.select_best_format(prompt, {
    "direct": direct_output,
    "short_cot": short_cot_output,
    "long_cot": long_cot_output,
    "code": code_output
})

It takes into account value scores, token length (brevity bonus), and format rarity.

๐Ÿ“š Training Options

The evaluator supports two training modes:

  • train_from_database(): Uses preference pairs from the MR.Q database.
  • train_from_context(): Uses freshly generated preference data stored in context (e.g., during active learning).

Both routes apply the same format-aware reward shaping logic and log training progress with metrics like:

  • Format usage frequency
  • Average rewards per format
  • Loss and early stopping info

๐Ÿ“ˆ Training Results

ARM was trained on datasets like AQUA-RAT, learning format preferences per question type. A few insights: Anything with Jimmy

  • Math problems preferred <Code> even when <Long_CoT> was available
  • Commonsense questions often favored <Short_CoT>
  • Diversity rewards encouraged occasional exploration of <Direct>

We logged training epochs, reward dynamics, and final format selections. Early results show format-aware tuning can boost performance without any finetuning of the base LLM.


๐Ÿค– AdaptiveReasonerAgent: Format-Aware Reasoning That Learns

The AdaptiveReasonerAgent is the front-line decision-maker in our format-sensitive reasoning system. It chooses how to reason not just what to say by evaluating and selecting among various reasoning formats such as direct answers, short chains-of-thought, code-based logic, or full long-form explanations.

Its purpose is to dynamically select the best reasoning format for each task, based on past performance, preference data, or real-time consensus. The agent supports three powerful operational modes:


๐Ÿง  1. Adaptive Mode (Default)

“Choose the best format on the fly by testing multiple and scoring them.”

In this mode, the agent:

  • Generates outputs in several candidate formats (e.g., direct, short_cot, code, long_cot).

  • Uses a scoring function (e.g., ARMReasoningSelfEvaluator) to evaluate each output.

  • Adjusts the score using:

    • Token efficiency penalty (to avoid long-winded answers)
    • Rarity bonus (to encourage underused formats)
  • Picks the format with the highest adjusted score.

This strategy enables the system to learn which formats perform best under various goal types and task conditions dynamically adapting over time.


๐Ÿ“œ 2. Instruction-Guided Mode

“Force the system to reason using a fixed format.”

Here, a format is explicitly specified (e.g., "short_cot"). The agent:

  • Loads a prompt template corresponding to the given format.
  • Calls the LLM with that template.
  • Returns the result, tagging it with the used format.

This mode is especially useful when:

  • You want repeatability in experiments.
  • You’re fine-tuning a model on a specific reasoning strategy.
  • Youโ€™re enforcing constraints from a curriculum or user instruction.

๐Ÿ—ณ๏ธ 3. Consensus-Guided Mode

“Try several formats, and only accept one if all agree.”

This unique fallback mode ensures output stability. The agent:

  • Tries three formats: direct, short_cot, and code.
  • If all three produce the same response, it accepts the result (labeling it "consensus-simple").
  • If not, it triggers a fallback to a long_cot format, assuming more detailed reasoning may help resolve ambiguity.

This is ideal when trust and convergence matter more than speed, such as in medical, legal, or safety-critical tasks.


Agent Configuration: adaptive_reasoner.yaml

# configs/agents/adaptive_reasoner.yaml

adaptive_reasoner:
  name: adaptive_reasoner
  enabled: true

  mode: adaptive

  format_list:
    - direct
    - short_cot
    - code
    - long_cot

  format_priority_by_difficulty:
    easy:
      - direct
      - short_cot
      - code
      - long_cot
    medium:
      - short_cot
      - code
      - long_cot
      - direct
    hard:
      - long_cot
      - code
      - short_cot
      - direct
    default:
      - short_cot
      - long_cot
      - code
      - direct

  save_prompt: true
  save_context: false
  skip_if_completed: false

  evaluator: arm         #(mrq or llm)  may not be enough items fo mrq
  evaluator_prompt_file: evaluation.txt
  evaluator_model:
    name: ollama/phi3
    api_base: http://localhost:11434
    api_key: null

  analysis_model:
    name: ollama/llama3.2
    api_base: http://localhost:11434
    api_key: null

  model:
    name: ollama/qwen3
    api_base: http://localhost:11434
    api_key: null

  input_key: hypotheses   # add nodes
  output_key: hypotheses
  prompt_mode: file
  prompt_file: generate_cot.txt
  pattern_prompt_file: cot_pattern.txt
  strategy:

  remove_think: false # we require "thinking" part of the prompt

  device: cpu # How many epochs to wait after no improvement
  limit: 1000 # max training data
  epochs: 20  # how much to train
  patience: 3  # How many epochs to wait after no improvement
  min_delta: 0.0001  # Minimum change in loss to qualify as improvement
  log_results: true # save results to database
  save_improved: true # save improved prompts and hypotheses
Setting Description
mode Execution strategy: adaptive (auto-select best format), instruction_guided (fixed format), consensus_guided (agreement among formats).
format_list Available reasoning formats to try: direct, short_cot, code, long_cot.
format_priority_by_difficulty Format ranking per difficulty level (easy, medium, hard, default) to bias adaptive choices.
save_prompt Whether to save the generated prompt in logs/output.
save_context Whether to persist full context dictionary to logs.
skip_if_completed Skip the agent if output already exists (for resuming runs).
evaluator Scoring method: mrq (learned), llm (external model), or arm (format-aware evaluator).
evaluator_prompt_file Prompt file for LLM-based evaluator.
evaluator_model Config for the evaluator’s model (e.g., Ollama model + base URL).
analysis_model Optional model for post-analysis and rubric classification.
model Main model used to generate reasoning outputs.
input_key Context key to read input from (e.g., list of hypotheses).
output_key Key to store agent output in context.
prompt_mode Prompt strategy: file means load from Jinja template.
prompt_file Path to main prompt template for reasoning generation.
pattern_prompt_file Optional pattern prompt for CoT-style formatting guidance.
strategy Reserved for future use (e.g., strategy tuning).
remove_think Whether to exclude โ€œLet’s think step-by-stepโ€ and similar phrases.
device Hardware target (cpu or cuda).
limit Max number of training samples for learning-based evaluators.
epochs Number of training epochs.
patience Early stopping: number of epochs without improvement before stopping.
min_delta Minimum loss improvement to count as progress.
log_results Whether to log scores and decisions to the database.
save_improved Save outputs that improve upon previous generations.
rubrics List of reasoning evaluation dimensions (e.g., depth, strategy, certainty).

โš–๏ธ Behind the Scenes: Format Evaluation

The agent can use one of two evaluators to rank and choose among formats:

  • LLMJudgeEvaluator โ€“ Uses an external LLM to compare answers pairwise.
  • ARMFormatEvaluator โ€“ A learned self-evaluator trained from preferences, format usage, and reward trends.

By default, the agent uses ARMFormatEvaluator, which understands:

  • The rarity of formats (favoring underused ones),
  • The length of responses (penalizing verbose ones),
  • And ongoing training data from past comparisons.

In short, AdaptiveReasonerAgent is a flexible and evolving problem solver. It doesnโ€™t just ask whatโ€™s the best answer, but whatโ€™s the best way to reason about this?


class AdaptiveReasonerAgent(BaseAgent):
    def __init__(self, cfg, memory=None, logger=None):
        super().__init__(cfg, memory, logger)

        self.modes = ["adaptive", "instruction_guided", "consensus_guided"]
        self.mode = self.cfg.get("mode", "adaptive")
        self.format_list = self.cfg.get(
            "format_list", ["direct", "short_cot", "code", "long_cot"]
        )
        self.judge = self._init_judge()

    async def run(self, context: dict):
        goal = context.get(GOAL) 


        self.judge.train_from_database(goal.get("goal_text"), self.cfg)

        prompt = goal.get("goal_text")

        response = ""
        if self.mode == "instruction_guided":
            format_name = self.cfg.get("format", "long_cot")
            response = self._generate_with_format(format_name, context)
        elif self.mode == "consensus_guided":
            response = self._run_consensus_mode(context)
        else:  # default to adaptive
            response = self._run_adaptive_mode(prompt, context)

        self.logger.log("AdaptiveReasoningResponse", response)

        context[self.output_key] = response
        return context

    def _generate_with_format(self, fmt, context):
        prompt = self.prompt_loader.from_file(fmt, self.cfg, context)
        response = self.call_llm(prompt, context)
        return {
            "prompt": prompt,
            "response": response,
            "format_used": ARMDataLoader.detect_format(response) or fmt,
        }

    def _run_consensus_mode(self, context:dict):
        outputs = {}
        for fmt in ["direct", "short_cot", "code"]:
            outputs[fmt] = self._generate_with_format(fmt, context)["response"]

        responses = list(outputs.values())
        unique_responses = set(responses)

        if len(unique_responses) == 1:
            return {
                "response": responses[0],
                "format": "consensus-simple",
                "source_formats": list(outputs.keys()),
            }
        else:
            long_cot_response = self._generate_with_format("long_cot", context)
            return {
                "response": long_cot_response["response"],
                "format": "long_cot",
                "source_formats": list(outputs.keys()),
                "fallback_reason": "no_consensus",
            }

    def _run_adaptive_mode(self, prompt:str, context:dict) -> dict[str, Union[str, float]]:
        prioritized_formats = ["direct", "short_cot", "code", "long_cot"]

        scores = {}
        for fmt in prioritized_formats:
            dict_response = self._generate_with_format(fmt, context)
            response = dict_response["response"]
            base_score = self.judge.score(prompt, response)

            token_len = len(response.split())
            rarity_bonus = 1.0 / (1 + self.judge.format_freq.get(fmt, 0))

            final_score = base_score - 0.01 * token_len + rarity_bonus
            scores[fmt] = final_score
            self.judge._update_format_stats(fmt, final_score)

        best_format = max(scores, key=scores.get)
        chosen_response = self._generate_with_format(best_format, context)
        # Log decision
        self.logger.log(
            "AdaptiveModeDecision",
            {"goal": prompt, "scores": scores, "chosen": best_format},
        )

        return {
            "response": chosen_response,
            "format_used": best_format,
            "scores": scores,
        }

    def get_format_for_goal(self, goal: dict):
        if "preferred_format" in goal:
            return goal["preferred_format"]
        goal_type = goal.get("goal_type", "default")
        if goal_type == "math":
            return "code"
        elif goal_type == "commonsense":
            return "short_cot"
        else:
            return "long_cot"

    def _get_prioritized_formats(self, context:dict):
        if "preferred_format" in context:
            return [context["preferred_format"]]

        priority_map = self.cfg.get("format_priority_by_difficulty", {})
        difficulty = context.get("difficulty", "default").lower()
        return priority_map.get(difficulty, priority_map.get("default", ["long_cot"]))

    def _init_judge(self):
        judge_strategy = self.cfg.get("judge", "mrq")
        if judge_strategy == "llm":
            llm = self.cfg.get("judge_model", self.cfg.get("model"))
            prompt_file = self.cfg.get(
                "judge_prompt_file", "judge_pairwise_comparison.txt"
            )
            self.logger.log(
                "EvaluatorInit", {"strategy": "LLM", "prompt_file": prompt_file}
            )
            return LLMJudgeEvaluator(
                self.cfg, llm, prompt_file, self.call_llm, self.logger
            )
        else:
            self.logger.log("EvaluatorInit", {"strategy": "ARM"})
            return ARMReasoningSelfEvaluator(self.cfg, self.memory, self.logger)

๐Ÿ”ข HypothesisValuePredictor: Scoring Hypotheses from Embeddings

At the heart of our format selection mechanism is a lightweight neural module called the HypothesisValuePredictor. Its job is simple but crucial: take a fixed-size embedding of a generated hypothesis (produced by our Zero-Shot Adapter encoder) and predict a scalar score representing its expected quality.

class HypothesisValuePredictor(nn.Module):
    def __init__(self, zsa_dim=512, hdim=1024):
        super().__init__()
        self.value_net = nn.Sequential(
            nn.Linear(zsa_dim, hdim),
            nn.ReLU(),
            nn.Linear(hdim, 1)
        )

    def forward(self, zsa_embedding):
        return self.value_net(zsa_embedding)

๐Ÿง  How It Works

  • Input: A dense embedding (e.g., 512-dim) representing a hypothesis.
  • Hidden Layer: A 1024-unit hidden layer with ReLU activation.
  • Output: A single scalar score estimating hypothesis quality.

This module allows the system to learn to prefer hypotheses that score well not only in evaluator judgment but also in alignment with past successes enabling reward learning, strategy adaptation, and fine-grained preference modeling across reasoning formats.

You can think of it as the “value head” in reinforcement learning but tuned to prefer reasoning strategies that consistently lead to good answers.


๐Ÿ‹๏ธโ€โ™‚๏ธ Training the Value Head: Connecting Embeddings to Reward

The HypothesisValuePredictor serves as a reward estimator that learns to predict which reasoning paths are most effective based on structured preference data gathered during execution.

๐ŸŽฏ Input: Preference Pairs

During training, the system uses DPO-style preference pairs:

  • A common prompt.
  • Two different hypothesis outputs (A and B).
  • A preference label indicating which output is better.

These are often generated using the ARMDataLoader, and stored in the mrq_preference_pairs table with embeddings for:

  • Prompt (z_prompt)
  • Output A (z_a)
  • Output B (z_b)

We pass the pair through a ZSA encoder that combines the prompt and each output into a contextualized representation:

zsa_a = encoder(z_prompt, z_a)
zsa_b = encoder(z_prompt, z_b)

Then the HypothesisValuePredictor assigns a scalar score to each:

value_a = value_predictor(zsa_a)
value_b = value_predictor(zsa_b)

๐Ÿงฎ Training Objective: Pairwise Margin Loss

To learn effectively, we train the value head using a pairwise margin ranking loss. This encourages the model to assign a higher score to the preferred output (A or B) and penalize it when the scores are too close or in the wrong order.

A simple PyTorch implementation looks like:

loss = torch.nn.functional.margin_ranking_loss(
    value_a, value_b, target=torch.tensor([1.0]), margin=1.0
)

This setup is conceptually similar to Reinforcement Learning from Human Feedback (RLHF), but:

  • Lighter: No need to backprop through the language model.
  • Modular: You can swap in different encoders or reward signals.
  • Self-contained: Learns from internal preferences (MR.Q, LLM judges, heuristics).

๐Ÿšฆ Why It Matters

This value prediction architecture allows the system to:

  • Learn which formats work best for different types of tasks.
  • Generalize reward signals beyond single episodes.
  • Guide adaptive reasoning based on learned success patterns.

It is a foundational step toward symbolic reward shaping, where strategy selection and refinement become learnable.


๐Ÿ”ก TextEncoder: Learning Rich Representations of Promptโ€“Response Pairs

The TextEncoder transforms raw embeddings of prompts and hypotheses into meaningful joint representations that can be evaluated by the value head.

๐Ÿง  What It Does

Given:

  • prompt_emb: The embedding of the goal or question.
  • response_emb: The embedding of a generated hypothesis or output.

The encoder performs the following steps:

  1. Separate Projections:

    • It first passes the prompt_emb through a multi-layer perceptron (MLP) to produce zs, a compressed representation of the prompt.
    • It also projects response_emb into za, a compressed representation of the hypothesis.
    • These are treated as distinct signals: the situation (zs) and the action (za).
  2. Joint Reasoning Embedding (ZSA):

    • The two projections are concatenated: zsa_input = [zs, za]
    • This joint vector is passed through another MLP (zsa_mlp) to produce zsa, a dense vector representing the full contextโ€“response relationship.
  3. Output:

    • The final zsa embedding can be fed into a value predictor to assess the quality or suitability of the hypothesis in the given context.
zs = self.zs_mlp(prompt_emb)      # Encode prompt
za = self.za_mlp(response_emb)    # Encode hypothesis
zsa = self.zsa_mlp([zs, za])      # Joint representation

๐Ÿ”— Why It Matters

This architecture enables the system to:

  • Condition evaluations on the prompt, not just the standalone output.
  • Learn interaction effects between goal and response (e.g. Is this reasoning plausible for this specific prompt?).
  • Provide inputs for preference learning, value estimation, or reward shaping using MR.Q, LLM judgments, or learned evaluators.

๐Ÿ›  Design Notes

  • The encoder is modular: you can plug in any embedding model (e.g., BERT, OpenAI, Ollama) upstream.
  • It enforces a clean separation between context (zs) and action (za), which is inspired by decision-making models in reinforcement learning.

This ZSA structure forms the foundation of adaptive value learning in the Co AI pipeline.


๐Ÿ Adaptive Reasoning Result: ARM Evaluation

๐Ÿ” Prompt

Can AI reprogram itself, and under what conditions would this be possible?

โœ… Selected Format: ๐ŸŸข Direct

View Final Answer

AI can reprogram itself under conditions where it possesses self-modifying capabilities, sufficient computational resources, and autonomous decision-making frameworks, though this remains a theoretical and highly complex challenge in current AI systems.

๐Ÿงช Internal Reasoning Trace (Think Step)

View internal reasoning

Okay, the user is asking if AI can reprogram itself and under what conditions. Let me break this down.

First, I need to understand what "reprogram" means here. Does it mean changing its own code, or adapting its behavior without external input? Maybe they're referring to self-modification, which is a concept in AI research.

So, can AI reprogram itself? Well, some AI systems, especially advanced ones, might have the capability to modify their own code or parameters. For example, reinforcement learning agents can adjust their strategies, but that's more about adapting behavior than reprogramming. True self-reprogramming would involve altering their underlying code or architecture.

Conditions for this would include having the necessary computational resources, like access to processing power and memory. Also, the AI would need to have the capability to write or modify code, which might require specific architectural designs, such as recursive self-improvement mechanisms. Additionally, there might be safety protocols in place to prevent unintended changes, especially if the AI is autonomous.

But wait, current AI systems don't have this capability. They rely on external updates. For self-reprogramming to be possible, the AI would need to have autonomy in modifying its own code, which is a significant leap. It might require advanced architectures like neural networks with self-learning capabilities or specialized self-modifying code.

Also, the environment in which the AI operates would matter. If the environment allows for such modifications without external interference, it could enable self-reprogramming. But there are ethical and safety concerns here. Without proper safeguards, self-reprogramming could lead to unpredictable behavior.

So, putting it all together, the answer would be that yes, under certain conditions like having the right architecture, resources, and safety measures, AI could reprogram itself. But currently, most AI systems don't have this ability.

๐Ÿ“Š Format Scores (Lower is Better)

Format Score
direct -4.80 โœ…
short_cot -7.70
long_cot -11.97
code -22.70

Note on Scoring Direction: In our reward modeling setup, lower scores indicate better outcomes. This is because our loss function penalizes verbosity and overuse of familiar formats, so more efficient, less common, and better-fitting responses are scored lower. Unlike traditional setups where higher is better, here a lower score reflects higher reward alignment.

โœ… The ๐ŸŸข direct format was selected as the best response based on value prediction and reward shaping.


Hereโ€™s a final checklist comparing your blog implementation to the original ARM paper (“Reward Shaping for Format Selection in Adaptive Reasoning”) with clear markings of whatโ€™s implemented, whatโ€™s adapted, and where additional functionality lives in your broader Co AI framework.


โœ… ARM Implementation Checklist (vs. Paper)

Component Status Implementation Location / Notes
1. Reasoning Format Generation โœ… Implemented AdaptiveReasonerAgent._generate_with_format using prompt templates (<Direct>, <Short_CoT>, etc.)
2. Difficulty-Based Format Prioritization โœ… Implemented Controlled via config: format_priority_by_difficulty in adaptive_reasoner.yaml
3. Format-Specific Prompt Wrapping โœ… Implemented Uses start/end tags like <Direct>...</Direct> via ARM evaluator or prompt loader
4. Reward Signal from LLM Evaluation โœ… Implemented MRQ-style scoring via ARMFormatEvaluator or fallback to LLMJudgeEvaluator
5. Reward Shaping (Length Penalty + Rarity Bonus) โœ… Implemented In AdaptiveReasonerAgent._run_adaptive_mode, includes token-length penalty and format rarity boost
6. Value Prediction Model (V(s,a)) โœ… Implemented HypothesisValuePredictor + TextEncoder used in training with reward feedback
7. ZSA Embedding Construction โœ… Implemented From TextEncoder, combining prompt and hypothesis embeddings
8. Preference Pair Storage โœ… Implemented Stored via MRQPreferencePairORM in database from ARMDataLoader
9. ARMDataLoader from HF Dataset โœ… Implemented Loads and classifies samples from HuggingFace datasets, builds preference pairs
10. Preference-Driven Reward Model Training โœ… Implemented Training loop logs loss and updates predictor using stored preference pairs
11. Collapsible Blog Result Display โœ… Implemented Markdown with <details> used for internal reasoning and answer formatting
12. Full Adaptive Pipeline โœ… Implemented Controlled via mode: adaptive and score-based selection of reasoning format
Consensus-Guided Mode โœ… Implemented Fully implemented in AdaptiveReasonerAgent._run_consensus_mode()
Instruction-Guided Mode โœ… Implemented Also supported via static format override in config

โš ๏ธ Features Not Implemented in the Blog, But Covered in the Framework

Component Status Available In
Feedback Loop with Dynamic Format Updating โš ๏ธ Not shown Partially reflected via format_freq tracking inside the judge
Reinforcement Learning (GRPO / DPO) โš ๏ธ Not included Planned in next blog on “The Ripper” also look at the SharpeningAgent
Meta Reasoning / Meta Learning Loop โŒ Future Work Under design for ReflectorNet and symbolic learning agents
Format Transfer Between Tasks โŒ Not shown Can be explored by linking format scores across goals in scores table
Custom Rubric-Driven Evaluation โš ๏ธ Not used here We have a custom adapter for this

๐Ÿ“š References

  1. Wei et al., 2022 โ€“ Chain of Thought Prompting Elicits Reasoning in Large Language Models [https://arxiv.org/abs/2201.11903] Introduces the concept of CoT (Chain-of-Thought) prompting, a technique central to reasoning format comparisons in ARM.

  2. Zheng et al., 2023 โ€“ Fractured Reasoning: Using Format Diversity to Improve LLM Outputs Highlights how different formats (direct, code, CoT) can yield varying answer quality, motivating ARM’s format selector.

  3. Ouyang et al., 2022 โ€“ Training language models to follow instructions with human feedback [https://arxiv.org/abs/2203.02155] Introduces DPO-style learning using preference pairs, foundational to ARM’s feedback and reward optimization strategy.

  4. Yuan et al., 2023 โ€“ MR.Q: A Modular Reward Model for Question Answering Tasks Presents the idea of modular, interpretable reward modeling. ARM’s format-aware judge and scoring logic builds on this framework.

  5. OpenAI, 2023 โ€“ Reinforcement Learning with Human Feedback (RLHF) [https://openai.com/research/instruction-following] Broader context around using human preferences to train better-behaved LLMs, relevant to ARMโ€™s learning loop.

  6. Bai et al., 2022 โ€“ Training a Helpful and Harmless Assistant with RLHF [https://arxiv.org/abs/2204.05862] Explains safety considerations and preference alignment essential when designing self-reprogramming AI agents.

  7. Zhou et al., 2023 โ€“ Language Models are Greedy Reasoners: A Systematic Formalization of Chain-of-Thought Prompting [https://arxiv.org/abs/2305.17421] Formalizes the idea that format selection impacts reasoning quality a central assumption in ARM’s adaptive logic.


๐Ÿ“˜ Glossary

Term Definition
ARM (Adaptive Reasoning Model) A system that helps language models choose the best reasoning strategy (e.g., direct answer, chain-of-thought, code) for a given task, and improves its choices over time based on feedback.
Reasoning Format A structural pattern used to answer a prompt, such as <Direct>, <Short_CoT>, <Code>, or <Long_CoT>. Each format guides how the model structures its response.
Chain-of-Thought (CoT) A reasoning technique where the model explains its thinking step by step before arriving at a final answer. Often used to improve accuracy on complex tasks.
ZSA Embedding A learned vector that encodes both the semantics of a prompt (Zs) and an answer (Za), combined into a single joint embedding (ZSA) for reward prediction.
Value Predictor A neural network that estimates the quality or reward of a given hypothesis (answer) based on its ZSA embedding.
Reward Model A function or model that scores responses according to a set of criteria, such as correctness, brevity, novelty, or alignment with a preferred format.
Rarity Bonus A scoring mechanism that boosts underused reasoning formats to encourage exploration and diversity.
Token Efficiency Penalty A small penalty applied to longer responses to prevent overly verbose outputs. Promotes clarity and brevity.
Difficulty Estimator A simple heuristic that estimates question difficulty (easy/medium/hard) based on length or complexity. Used to determine which reasoning formats are preferred.
DPO (Direct Preference Optimization) A training method where a model learns to prefer one response over another by comparing outputs and reinforcing the better one.
LLM (Large Language Model) A neural network trained on large text corpora capable of generating natural language responses. Examples include GPT-4, Qwen, Mistral, etc.
MR.Q A modular self-evaluation framework used in this project to score and rank hypotheses based on response quality, format usage, and other criteria.
Preference Pair A data record showing two competing responses (A and B), with a label indicating which one is preferred and why. Used for training reward models.
Prompt Loader A utility that dynamically loads prompt templates from files, injecting variables or formatting tags based on the reasoning strategy.
Self-Evaluator A component that automatically scores generated answers against internal criteria (e.g., format rarity, conciseness, past performance).