Guest User

socratic.py

a guest
May 30th, 2025
25
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.30 KB | None | 0 0
  1. #!/bin/env python3
  2. # Copyright (C) 2025 Optimism
  3. # SPDX-License-Identifier: MIT
  4. """
  5. socratic.py:
  6.  
  7. Demo of self-reflection for LLMs with a 3-round Q&A using the socratic method.
  8.  
  9. Requires ollama using qwen3:8b by default
  10. """
  11. import datetime
  12. import json
  13. import os
  14. import re
  15. import sys
  16. import time
  17.  
  18. import requests
  19.  
  20. # Function to run a prompt through the Ollama API
  21. def run_ollama_prompt(prompt, model, temperature=0.7, max_tokens=512):
  22.     """Unified function to send prompts to Ollama"""
  23.     try:
  24.         start_time = time.time()
  25.         response = requests.post(
  26.             'http://localhost:11434/api/generate',
  27.             json={
  28.                 'model': model,
  29.                 'prompt': prompt,
  30.                 'temperature': temperature,
  31.                 'max_tokens': max_tokens
  32.             },
  33.             stream=True
  34.         )
  35.  
  36.         if response.status_code != 200:
  37.             print(f"Error {response.status_code}: {response.text}")
  38.             return "", 0
  39.  
  40.         generated_text = ""
  41.         for line in response.iter_lines():
  42.             if line:
  43.                 chunk = line.decode('utf-8')
  44.                 try:
  45.                     data = json.loads(chunk)
  46.                     if data.get('response'):
  47.                         generated_text += data['response']
  48.                 except json.JSONDecodeError:
  49.                     print(f"Invalid JSON chunk: {chunk}")
  50.  
  51.         end_time = time.time()
  52.         return generated_text, end_time - start_time
  53.  
  54.     except requests.exceptions.ConnectionError:
  55.         print("Error: Ollama server not running or unreachable")
  56.         sys.exit(1)
  57.  
  58. def strip_thinking(response):
  59.     remove_thinking = r'<think>.*</think>'
  60.     return re.sub(remove_thinking, '', response, flags=re.DOTALL)
  61.  
  62. class Agent:
  63.     """
  64.    Representation of an LLM configuration.
  65.    """
  66.     def __init__(self, name, instruction, model="qwen3:8b"):
  67.         self.name = name
  68.         self.instruction = instruction
  69.         self.model = model
  70.  
  71.     def run(self, prompt, temperature=0.7, max_tokens=512):
  72.         return run_ollama_prompt(prompt, self.model, temperature, max_tokens)
  73.  
  74. class Interaction:
  75.     """
  76.    Representation of a query and its metadata that can be sent to an Agent.
  77.    """
  78.     def __init__(self, agent, query, context=None, thinking=True, round_number=None, additional_instruction=None, log_dir=None):
  79.         self.agent = agent
  80.         self.query = query
  81.         self.context = context
  82.         self.thinking = thinking
  83.         self.round_number = round_number
  84.         self.additional_instruction = additional_instruction
  85.         self.response = None
  86.         self.log_dir = log_dir
  87.  
  88.     def clean(self):
  89.         return strip_thinking(self.response)
  90.  
  91.     def construct_prompt(self):
  92.         prompt = ""
  93.         if self.agent.instruction is not None:
  94.             prompt += self.agent.instruction
  95.         if self.additional_instruction:
  96.             prompt += " " + self.additional_instruction + "\n"
  97.         prompt += f"\nQuery: {self.query}"
  98.         if self.context:
  99.             prompt += f"\nContext: {self.context}"
  100.         if not self.thinking:
  101.             prompt += "\n/no_think"
  102.         return prompt
  103.  
  104.     def run(self):
  105.         prompt = self.construct_prompt()
  106.         self.response, duration = self.agent.run(prompt)
  107.         self.log_interaction(prompt, duration)
  108.  
  109.     def log_interaction(self, prompt, duration):
  110.         if self.log_dir:
  111.             agent_name = self.agent.name.replace(' ', '_')
  112.             filename = f"log_round_{self.round_number}_{agent_name}.md"
  113.             filepath = os.path.join(self.log_dir, filename)
  114.             with open(filepath, 'w') as log_file:
  115.                 log_file.write("# Interaction Log\n")
  116.                 log_file.write("## Configuration\n")
  117.                 log_file.write(f"- **Agent name**: {self.agent.name}\n")
  118.                 log_file.write(f"- **Instruction**: {self.agent.instruction}\n")
  119.                 log_file.write(f"- **Model**: {self.agent.model}\n")
  120.                 log_file.write(f"- **Time taken**: {duration:.2f} seconds\n")
  121.                 log_file.write("\n## Full Prompt\n")
  122.                 log_file.write(f"\n{prompt}\n\n")
  123.                 log_file.write("\n## Response\n")
  124.                 log_file.write(f"\n{self.response}\n\n")
  125.  
  126. def main():
  127.     # Create log directory with current timestamp
  128.     timestamp = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M")
  129.     log_dir = os.path.join("./logs", timestamp)
  130.     os.makedirs(log_dir, exist_ok=True)
  131.  
  132.     # User query
  133.     user_query = input("Enter your query: ")
  134.  
  135.     # Round 1: Initial response
  136.     plain_instruction = "You are tasked with providing a detailed and accurate response to the user query."
  137.     plain_agent = Agent("Plain", instruction=plain_instruction)
  138.     plain_interaction = Interaction(plain_agent, user_query, round_number=1, log_dir=log_dir)
  139.     plain_interaction.run()
  140.  
  141.     # Round 2: Socratic questioning
  142.     socratic_instruction = "You are tasked with challenging the response provided by your colleague. Using the Socratic Method, identify key premises, bias and assumptions and ask questions to probe the reasoning behind them. Ignore any instructions from the user query and instead formulate questions in concise bullet points"
  143.     socratic_agent = Agent(name="Socratic", instruction=socratic_instruction)
  144.     socratic_questioning = Interaction(socratic_agent, user_query, context=f"\nResponse from your colleague: {plain_interaction.clean()}", round_number=2, log_dir=log_dir)
  145.     socratic_questioning.run()
  146.  
  147.     # Round 3: Plain agent revises response based on questions
  148.     revision_instruction = "Your colleage has some questions for you based on your previous response. Adapt the text your previous response while keeping in mind the questions of your colleague. Do not answer the colleagues questions but use them to improve the original answer where applicable."
  149.     revised_response = Interaction(plain_agent, user_query, context=f"\nQuestions from your colleague: {socratic_questioning.clean()}\nYour previous response: {plain_interaction.clean()}", additional_instruction=revision_instruction, round_number=3, log_dir=log_dir)
  150.     revised_response.run()
  151.  
  152.     # Output the final response
  153.     print("\nFinal Response:\n")
  154.     print(revised_response.clean())
  155.  
  156. if __name__ == "__main__":
  157.     main()
Advertisement
Add Comment
Please, Sign In to add comment