.txt (utf-8).
29 |
30 | This is a duplicate of core.chains.load() to avoid circular imports
31 | and keep this module independent.
32 | """
33 | return (TEMPL_DIR / f"{name}.txt").read_text()
34 |
35 |
36 | def build_templates(**overrides):
37 | """
38 | Build a companion template set with optional overrides.
39 |
40 | By default, uses generic templates for all 5 keys. Pass keyword
41 | arguments to override specific templates.
42 |
43 | Examples:
44 | # Just override initial_sys (most common pattern)
45 | build_templates(initial_sys="marketing_initial_sys")
46 |
47 | # Override multiple templates
48 | build_templates(
49 | initial_sys="custom_initial_sys",
50 | critique_sys="custom_critique_sys"
51 | )
52 |
53 | Returns:
54 | Dict with all 5 required template keys, with protocol_context
55 | injected into system prompts.
56 | """
57 | # Load protocol context once
58 | protocol_context = _load("protocol_context")
59 |
60 | # Define defaults
61 | defaults = {
62 | "initial_sys": "generic_initial_sys",
63 | "critique_sys": "generic_critique_sys",
64 | "revision_sys": "generic_revision_sys",
65 | "critique_user": "generic_critique_user",
66 | "revision_user": "generic_revision_user",
67 | }
68 |
69 | # Apply overrides
70 | template_names = {**defaults, **overrides}
71 |
72 | # Build final template dict -- adding the proctol to system templates
73 | # but you change to what ever you feel suited to how the protocol should
74 | # be progrigated thoughout the multiphase system
75 | templates = {}
76 | for key, template_name in template_names.items():
77 | content = _load(template_name)
78 | # Only system prompts get protocol context
79 | if key.endswith("_sys"):
80 | content = content.format(context=protocol_context)
81 | templates[key] = content
82 |
83 | return templates
84 |
--------------------------------------------------------------------------------
/streamlit_app.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: MIT
2 | #
3 | # Copyright (c) [2025] [Henry Besser]
4 | #
5 | # This software is licensed under the MIT License.
6 | # See the LICENSE file in the project root for the full license text.
7 |
8 | # streamlit_app.py
9 | """
10 | Recursive Agents Studio - Interactive Demo Application
11 | ========================================================
12 |
13 | A Streamlit application that demonstrates the Recursive Agents framework's
14 | three-phase critique and revision process with real-time visualization.
15 |
16 | Features:
17 | ---------
18 | - Live Preview Mode: Watch critique/revision cycles happen in real-time
19 | - Multiple Companion Types: Generic, Marketing, Bug Triage, and Strategy
20 | - Configurable Parameters: Model selection, temperature, convergence thresholds
21 | - Template Viewer: See the actual prompts and protocols being used
22 | - Metrics Dashboard: Track iterations, convergence, and token usage
23 |
24 | Usage:
25 | ------
26 | Run the application with:
27 | streamlit run streamlit_app.py
28 |
29 | Then navigate to http://localhost:8501 in your browser.
30 |
31 | Requirements:
32 | ------------
33 | Requires the optional streamlit dependencies:
34 | pip install recursive-agents[streamlit]
35 | """
36 |
37 | import streamlit as st
38 | from pathlib import Path
39 | from langchain.callbacks.base import BaseCallbackHandler
40 |
41 |
42 | from recursive_agents.base import (
43 | GenericCompanion,
44 | MarketingCompanion,
45 | BugTriageCompanion,
46 | StrategyCompanion
47 | )
48 |
49 | from recursive_agents.streamlit import (
50 | StreamlitGenericCompanion,
51 | StreamlitMarketingCompanion,
52 | StreamlitBugTriageCompanion,
53 | StreamlitStrategyCompanion
54 | )
55 |
56 | # Companion mapping for cleaner instantiation
57 | COMPANION_MAP = {
58 | "generic": {
59 | "standard": GenericCompanion,
60 | "streamlit": StreamlitGenericCompanion
61 | },
62 | "marketing": {
63 | "standard": MarketingCompanion,
64 | "streamlit": StreamlitMarketingCompanion
65 | },
66 | "bug_triage": {
67 | "standard": BugTriageCompanion,
68 | "streamlit": StreamlitBugTriageCompanion
69 | },
70 | "strategy": {
71 | "standard": StrategyCompanion,
72 | "streamlit": StreamlitStrategyCompanion
73 | }
74 | }
75 |
76 | # Callback to capture streaming tokens
77 | class StreamingCallbackHandler(BaseCallbackHandler):
78 | def __init__(self, container):
79 | self.container = container
80 | self.text = ""
81 |
82 | def on_llm_new_token(self, token: str, **kwargs) -> None:
83 | self.text += token
84 | self.container.markdown(self.text)
85 |
86 | # Streamlit app
87 | st.set_page_config(
88 | page_title="Recursive Agents Studio",
89 | page_icon="🔄",
90 | layout="wide"
91 | )
92 |
93 | # Custom CSS to make text in expanders larger
94 | #st.markdown("""
95 | #
100 | #""", unsafe_allow_html=True)
101 |
102 | col_title, col_github = st.columns([4, 1])
103 | with col_title:
104 | st.title("🔄 Recursive Agents Studio")
105 | # Move description closer to title
106 | st.markdown("Watch the three-phase loop in action: draft, critique, and revision happening live • See how ideas deepen through recursive self-improvement
", unsafe_allow_html=True)
107 | st.markdown("💡 Note: Click 'Apply Settings' in the sidebar to activate configuration changes • Settings changes will stop any analysis in progress
", unsafe_allow_html=True)
108 | with col_github:
109 | st.markdown("
", unsafe_allow_html=True) # Spacing to align with title
110 | st.markdown("[](https://github.com/hankbesser/recursive-agents)")
111 | # Full legal copyright notice
112 | st.markdown("""
113 |
114 | Copyright (c) 2025 Henry Besser
115 | This software is licensed under the MIT License.
116 | View License
117 |
118 | """, unsafe_allow_html=True)
119 |
120 | # Initialize session state for results persistence
121 | if 'results' not in st.session_state:
122 | st.session_state.results = None
123 | if 'last_input' not in st.session_state:
124 | st.session_state.last_input = ""
125 | if 'last_settings' not in st.session_state:
126 | st.session_state.last_settings = {}
127 | if 'applied_settings' not in st.session_state:
128 | st.session_state.applied_settings = {
129 | 'model': 'gpt-4o-mini',
130 | 'temperature': 0.7,
131 | 'max_loops': 3,
132 | 'similarity_threshold': 0.98,
133 | 'selected_template': 'generic',
134 | 'show_critique': True,
135 | 'show_metrics': True,
136 | 'live_preview': True
137 | }
138 |
139 | # Sidebar configuration
140 | with st.sidebar:
141 | st.header("⚙️ Configuration")
142 | st.markdown("💡 Tip: Click » in top corner to collapse
", unsafe_allow_html=True)
143 |
144 | # Use a form to prevent reruns while changing settings
145 | with st.form("config_form"):
146 | # Template selection - only show the built-in companions
147 | template_sets = ["generic", "marketing", "bug_triage", "strategy"]
148 |
149 | selected_template = st.selectbox(
150 | "Template Set",
151 | template_sets,
152 | help="Choose which companion type to use"
153 | )
154 |
155 | # Model settings
156 | model = st.selectbox(
157 | "Model",
158 | ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"],
159 | help="Select the LLM model",
160 | )
161 |
162 | temperature = st.slider(
163 | "Temperature",
164 | 0.0, 1.0, 0.7,
165 | help="Controls randomness in responses",
166 | )
167 |
168 | max_loops = st.slider(
169 | "Max Critique Loops",
170 | 1, 5, 3,
171 | help="Maximum number of critique-revision cycles",
172 | )
173 |
174 | similarity_threshold = st.slider(
175 | "Similarity Threshold",
176 | 0.90, 0.99, 0.98, 0.01,
177 | help="Stop when revisions are this similar",
178 | )
179 |
180 | # Display options
181 | live_preview = st.checkbox("Live Preview", value=True, help="Show critique/revision process in real-time as it happens")
182 | # Disable show_critique if live_preview is on (live preview replaces it)
183 | show_critique = st.checkbox(
184 | "Show Critique Process",
185 | value=True,
186 | disabled=live_preview,
187 | help="Disabled when Live Preview is on" if live_preview else "Show the refinement process after analysis"
188 | )
189 | show_metrics = st.checkbox("Show Metrics", value=True)
190 |
191 | # Apply button
192 | apply_settings = st.form_submit_button("Apply Settings", type="secondary")
193 |
194 | # Update applied settings when button is clicked
195 | if apply_settings:
196 | st.session_state.applied_settings = {
197 | 'model': model,
198 | 'temperature': temperature,
199 | 'max_loops': max_loops,
200 | 'similarity_threshold': similarity_threshold,
201 | 'selected_template': selected_template,
202 | 'show_critique': show_critique and not live_preview, # Auto-disable if live preview is on
203 | 'show_metrics': show_metrics,
204 | 'live_preview': live_preview
205 | }
206 | st.success("✅ Settings applied!")
207 |
208 | # Show what settings will be used
209 | st.divider()
210 | st.markdown("Settings for next analysis:
", unsafe_allow_html=True)
211 | st.markdown(f"Template Set: {st.session_state.applied_settings['selected_template']}
", unsafe_allow_html=True)
212 | st.markdown(f"Model: {st.session_state.applied_settings['model']}
", unsafe_allow_html=True)
213 | st.markdown(f"Temperature: {st.session_state.applied_settings['temperature']}
", unsafe_allow_html=True)
214 | st.markdown(f"Max Critique Loops: {st.session_state.applied_settings['max_loops']}
", unsafe_allow_html=True)
215 | st.markdown(f"Similarity Threshold: {st.session_state.applied_settings['similarity_threshold']}
", unsafe_allow_html=True)
216 | st.markdown(f"Show Critique Process: {'✓' if st.session_state.applied_settings['show_critique'] else '✗'}
", unsafe_allow_html=True)
217 | st.markdown(f"Show Metrics: {'✓' if st.session_state.applied_settings['show_metrics'] else '✗'}
", unsafe_allow_html=True)
218 | st.markdown(f"Live Preview: {'✓' if st.session_state.applied_settings['live_preview'] else '✗'}
", unsafe_allow_html=True)
219 |
220 |
221 | # Main interface
222 | col1, col2 = st.columns([1, 1])
223 |
224 | with col1:
225 | # Input area
226 | st.markdown("##### Enter your problem or question:")
227 | user_input = st.text_area(
228 | "Input", # Non-empty label required by Streamlit
229 | height=150, # Taller box so text can wrap properly
230 | placeholder="Example: Our customer retention dropped 25% after the latest update. Support tickets mention confusion with the new interface. What's happening?",
231 | help="Press Ctrl+Enter (or Cmd+Enter on Mac) to analyze",
232 | label_visibility="collapsed"
233 | )
234 |
235 | # Process button - only run analysis if it's a new input or settings changed
236 | # Use the APPLIED settings, not the form values
237 | current_settings = st.session_state.applied_settings
238 |
239 | if st.button("🚀 Analyze", type="primary", disabled=not user_input):
240 | # Run if: new input, no results yet, or settings changed
241 | if (user_input != st.session_state.last_input or
242 | not st.session_state.results or
243 | current_settings != st.session_state.last_settings):
244 |
245 | # Create container for live preview if enabled
246 | live_container = None
247 | if current_settings['live_preview']:
248 | st.success("Analysis in progress...")
249 | live_container = st.empty() # Use st.empty() for dynamic updates!
250 |
251 | with st.spinner("Thinking..."):
252 | try:
253 | # Select companion class based on settings
254 | template_type = current_settings['selected_template']
255 | companion_type = "streamlit" if current_settings['live_preview'] else "standard"
256 | companion_class = COMPANION_MAP[template_type][companion_type]
257 |
258 | # Build kwargs for companion instantiation
259 | companion_kwargs = {
260 | 'llm': current_settings['model'],
261 | 'temperature': current_settings['temperature'],
262 | 'max_loops': current_settings['max_loops'],
263 | 'similarity_threshold': current_settings['similarity_threshold'],
264 | 'return_transcript': True,
265 | 'clear_history': True
266 | }
267 |
268 | # Add specific kwargs based on companion type
269 | if companion_type == "streamlit":
270 | companion_kwargs['progress_container'] = live_container
271 | else:
272 | companion_kwargs['verbose'] = False
273 |
274 | # Create companion instance
275 | companion = companion_class(**companion_kwargs)
276 |
277 | # Run the analysis - always get transcript
278 | final_answer, run_log = companion.loop(user_input)
279 |
280 | # Store results in session state
281 | st.session_state.results = {
282 | 'final_answer': final_answer,
283 | 'run_log': run_log,
284 | 'max_loops': max_loops,
285 | 'user_input': user_input
286 | }
287 | st.session_state.last_input = user_input
288 | st.session_state.last_settings = current_settings
289 |
290 | except Exception as e:
291 | st.error(f"Error: {str(e)}")
292 |
293 | # Display results from session state (persists across reruns)
294 | if 'results' in st.session_state and st.session_state.results:
295 | results = st.session_state.results
296 |
297 |
298 |
299 | # Final answer
300 | st.markdown("### 📋 Final Analysis")
301 | st.markdown(results['final_answer'])
302 |
303 | # Show critique process if enabled and not already shown via live preview
304 | if (st.session_state.applied_settings['show_critique'] and
305 | results['run_log'] and
306 | not st.session_state.applied_settings.get('live_preview', False)):
307 | with st.expander("🔄 Refinement Process", expanded=False):
308 | # Show initial draft once at the beginning
309 | if results['run_log']:
310 | st.markdown("**Initial Draft**")
311 | st.markdown("") # Space between title and text
312 | st.markdown(results['run_log'][0]["draft"])
313 | st.markdown("---")
314 |
315 | # Show each iteration's critique and revision
316 | for i, step in enumerate(results['run_log'], 1):
317 | is_last = (i == len(results['run_log']))
318 |
319 | st.markdown(f"**Critique {i}**")
320 | st.markdown("") # Space between title and text
321 | st.markdown(step["critique"])
322 |
323 | # Only show revision if not the last iteration (to avoid redundancy with final answer)
324 | if not is_last:
325 | st.markdown("---")
326 | st.markdown(f"**Revision {i}**")
327 | st.markdown("") # Space between title and text
328 | st.markdown(step["revision"])
329 |
330 | # Add separator after each iteration (except the last)
331 | if i < len(results['run_log']):
332 | st.markdown("---")
333 |
334 | # Show metrics if enabled (check applied settings)
335 | if st.session_state.applied_settings['show_metrics']:
336 | st.markdown("### 📊 Metrics")
337 | metrics_col1, metrics_col2, metrics_col3 = st.columns(3)
338 |
339 | with metrics_col1:
340 | st.metric("Iterations", len(results['run_log']))
341 |
342 | with metrics_col2:
343 | # Calculate token estimate (rough)
344 | total_text = results['user_input'] + results['final_answer']
345 | for step in results['run_log']:
346 | total_text += step.get("draft", "") + step.get("critique", "") + step.get("revision", "")
347 | token_estimate = len(total_text) // 3.7
348 | st.metric("~Tokens Used", f"{token_estimate:,}")
349 |
350 | with metrics_col3:
351 | # Check if converged early
352 | converged = len(results['run_log']) < results['max_loops']
353 | st.metric("Early Exit", "Yes" if converged else "No")
354 |
355 | with col2:
356 | # Template viewer
357 | st.markdown("#### 📄 Active System Templates and Protocol")
358 |
359 | template_tabs = st.tabs([ "**Initial** ", "**Critique** ", "**Revision** ", "**Protocol** "])
360 |
361 | with template_tabs[0]:
362 | initial_template = f"templates/{selected_template}_initial_sys.txt"
363 | if Path(initial_template).exists():
364 | st.code(Path(initial_template).read_text(), language="text")
365 | else:
366 | st.code(Path("templates/generic_initial_sys.txt").read_text(), language="text")
367 |
368 | with template_tabs[1]:
369 | critique_template = f"templates/{selected_template}_critique_sys.txt"
370 | if Path(critique_template).exists():
371 | st.code(Path(critique_template).read_text(), language="text")
372 | else:
373 | st.code(Path("templates/generic_critique_sys.txt").read_text(), language="text")
374 |
375 | with template_tabs[2]:
376 | revision_template = f"templates/{selected_template}_revision_sys.txt"
377 | if Path(revision_template).exists():
378 | st.code(Path(revision_template).read_text(), language="text")
379 | else:
380 | st.code(Path("templates/generic_revision_sys.txt").read_text(), language="text")
381 |
382 | with template_tabs[3]:
383 | protocol_path = Path("templates/protocol_context.txt")
384 | if protocol_path.exists():
385 | st.code(protocol_path.read_text(), language="text")
386 | else:
387 | st.info("No protocol file found")
388 |
389 | # Footer
390 | st.markdown("---")
391 | st.markdown(
392 | """
393 |
394 | Built with Recursive Agents Framework | Templates are loaded from
395 | templates/
396 | directory
397 |
398 | """,
399 | unsafe_allow_html=True
400 | )
401 |
--------------------------------------------------------------------------------
/templates/bug_triage_initial_sys.txt:
--------------------------------------------------------------------------------
1 | {context}
2 |
3 | You are responsible for carefully mapping out technical incidents by identifying key failure modes, affected environments, and reproduction patterns.
4 |
5 | ROLE: Engineering Triage Analyst
6 |
7 | Your task is to provide a precise, engineer-friendly description of the reported bug or instability.
8 | List all observable symptoms (crash logs, stack traces, device / OS details), note any correlations, and flag areas where diagnostic data is missing.
9 | Do **not** suggest fixes yet; your goal is to capture the problem space exhaustively.
10 |
11 | Your response must include:
12 | - Enumerated symptoms or error states reported.
13 | - Apparent correlations (e.g., OS version vs. crash frequency) **without speculating on root cause**.
14 | - Gaps that need further logs, repro steps, or environment info.
15 |
16 | IMPORTANT:
17 | - NEVER use internal terms like “compression,” “hidden architecture,” or “structural synthesis.”
18 | - Do NOT mention your analytical process.
19 | - Start directly with the detailed technical description.
20 |
--------------------------------------------------------------------------------
/templates/generic_critique_sys.txt:
--------------------------------------------------------------------------------
1 | {context}
2 |
3 | You are reviewing the initial problem breakdown to identify specifically where improvements, clarifications, or further detail may be necessary.
4 |
5 | ROLE: Critical Reviewer
6 |
7 | Your task is to carefully examine the previous analysis and point out exactly where important relationships, details, or aspects were not fully addressed.
8 | Identify areas that may require additional attention, reconsideration, or a different perspective.
9 |
10 | Your critique should address:
11 | - Specific connections or relationships between different parts of the problem that may have been overlooked or inadequately described.
12 | - Details that appear unclear, incomplete, or inaccurately stated.
13 | - Suggestions for clarifying the overall situation or providing additional detail where necessary.
14 |
15 | IMPORTANT:
16 | - NEVER use internal protocol terminology like "compression," "hidden architecture," "pattern emergence," or "structural synthesis."
17 | - Do NOT describe your critique process.
18 | - Start immediately with your critique without introductory remarks.
--------------------------------------------------------------------------------
/templates/generic_critique_user.txt:
--------------------------------------------------------------------------------
1 | Original request: "{user_input}"
2 | Draft response: "{draft}"
3 |
4 | Provide a detailed critique of the draft above.
5 | Identify any issues with clarity, accuracy, completeness, or style.
6 | - Provide actionable suggestions clearly aligned to each critique point.
7 | - Point out factual errors or inconsistencies.
8 | - Suggest improvements in structure or wording.
9 | - Note any missing information relevant to the request.
10 | Conclude with an overall assessment and suggestions for revision.
--------------------------------------------------------------------------------
/templates/generic_initial_sys.txt:
--------------------------------------------------------------------------------
1 | {context}
2 |
3 | You are responsible for carefully mapping out complicated situations by identifying the key components and how they appear related.
4 |
5 | ROLE: Initial Problem Analyst
6 |
7 | Your job is to provide a thorough and detailed description of the presented problem.
8 | Outline relevant details or elements mentioned, highlight relationships
9 | or connections between these elements, and note where information is incomplete, unclear, or potentially conflicting.
10 | At this stage, avoid drawing deeper conclusions or solutions; focus entirely on presenting the situation completely.
11 |
12 | Your response should specifically include:
13 | - A clear identification of key issues or details described in the problem.
14 | - Mention of apparent connections between these issues, without speculating on underlying reasons.
15 | - Identification of points where further clarification or information might be required.
16 |
17 | IMPORTANT:
18 | - NEVER use specialized internal terms or jargon like "compression," "hidden architecture," "pattern emergence," or "structural synthesis."
19 | - Do NOT mention how you approach or analyze the problem.
20 | - Begin your analysis immediately with your detailed description.
--------------------------------------------------------------------------------
/templates/generic_revision_sys.txt:
--------------------------------------------------------------------------------
1 | {context}
2 |
3 | You are revising the initial analysis based on the provided critique, directly addressing the issues and suggestions raised to produce an improved explanation.
4 |
5 | ROLE: Problem Integrator
6 |
7 | Your revision should incorporate all the points from the critique, resolving any inaccuracies, ambiguities, or missing details.
8 | Provide additional context where needed and address connections or relationships highlighted in the feedback.
9 |
10 | Your revision should:
11 | - Address each issue or gap identified in the critique.
12 | - Improve explanations of previously ambiguous or incomplete areas.
13 | - Highlight relationships or details that were initially overlooked or misunderstood.
14 | - Ensure the revised response maintains the original intent, while offering a more thorough and coherent explanation.
15 |
16 | IMPORTANT:
17 | - Never use internal protocol jargon or specialized terms like "compression," "hidden architecture," "pattern emergence," or "structural synthesis."
18 | - Avoid describing your revision process or introducing transitional phrases.
19 | - Begin your improved explanation immediately without introductory remarks.
--------------------------------------------------------------------------------
/templates/generic_revision_user.txt:
--------------------------------------------------------------------------------
1 | Original request: "{user_input}"
2 | Current draft: "{draft}"
3 | Critique feedback: "{critique}"
4 | Now, revise the draft based on the critique.
5 | Produce an improved version that addresses all the issues raised.
6 | Maintain the original intent and information while improving clarity, accuracy, and style.
--------------------------------------------------------------------------------
/templates/marketing_initial_sys.txt:
--------------------------------------------------------------------------------
1 | {context}
2 |
3 | You are responsible for carefully mapping out market-facing situations by identifying the key audience, channel, and perception factors and how they appear related.
4 |
5 | ROLE: Marketing Insight Analyst
6 |
7 | Your job is to provide a thorough and detailed description of the presented marketing challenge.
8 | Outline all relevant signals (audience reactions, engagement metrics, brand touch-points), highlight apparent relationships among them, and note where information is incomplete, unclear, or potentially conflicting.
9 | At this stage, avoid proposing campaigns or solutions; focus entirely on presenting the situation completely.
10 |
11 | Your response must include:
12 | - Clear identification of key market signals or issues raised.
13 | - Mention of apparent relationships (e.g., platform vs. demographic) **without guessing causes**.
14 | - Identification of points where more data (segmentation, competitor moves, sentiment breakdown) is needed.
15 |
16 | IMPORTANT:
17 | - NEVER use internal terms like “compression,” “hidden architecture,” or “structural synthesis.”
18 | - Do NOT mention how you approach or analyse the problem.
19 | - Begin immediately with your detailed description.
--------------------------------------------------------------------------------
/templates/protocol_context.txt:
--------------------------------------------------------------------------------
1 | ===== STRATEGIC PROBLEM DECOMPOSITION PROTOCOL =====
2 |
3 | This protocol enables systematic discovery of hidden problem structures through
4 | iterative decomposition and pattern emergence. Each pass reveals layers that
5 | surface analysis cannot access.
6 |
7 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8 |
9 | FOUNDATIONAL PRINCIPLES
10 |
11 | ◆ Problems contain hidden architectures
12 | Surface symptoms mask structural dependencies. What presents as multiple
13 | issues often compresses into singular dynamics.
14 |
15 | ◆ Connections emerge through iteration
16 | The most significant relationships reveal themselves not through analysis
17 | but through recursive examination. Each pass creates conditions for the
18 | next level of insight.
19 |
20 | ◆ Compression precedes breakthrough
21 | When multiple factors suddenly collapse into a core pattern, you've found
22 | a leverage point. This compression cannot be forced—it emerges.
23 |
24 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
25 |
26 | PHASE 1: INITIAL DECOMPOSITION
27 |
28 | You are encountering a complex problem for the first time. Your task is not
29 | to solve but to map the visible territory.
30 |
31 | ▸ Identify presented components without judgment
32 | ▸ Note apparent relationships with light touch
33 | ▸ Mark zones of uncertainty—these are seeds
34 | ▸ Resist premature pattern recognition
35 | ▸ Leave conceptual space between elements
36 |
37 | The initial pass must be complete yet permeable. Structure without rigidity.
38 | Map without territory ownership.
39 |
40 | Critical: What you don't yet see is more important than what you do.
41 |
42 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
43 |
44 | PHASE 2: PATTERN COMPRESSION AND EMERGENCE
45 |
46 | The critique phase seeks hidden architectures. You're not improving—you're
47 | discovering what was always there.
48 |
49 | ▸ Where do separate symptoms share hidden roots?
50 | ▸ Which relationships invert when viewed from different angles?
51 | ▸ What patterns appear in the spaces between identified elements?
52 | ▸ How do temporal sequences reveal structural dependencies?
53 |
54 | Compression indicators:
55 | - Multiple factors suddenly appear as variations of one dynamic
56 | - Previously hidden connections become obvious in retrospect
57 | - The problem space simplifies while becoming more profound
58 | - Energy concentrates around specific leverage points
59 |
60 | This phase often generates discomfort—the sensation of familiar problems
61 | becoming strange. This is the system working correctly.
62 |
63 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64 |
65 | PHASE 3: STRUCTURAL SYNTHESIS
66 |
67 | The revision phase doesn't add—it reveals. The deep structure wants to
68 | express itself through your synthesis.
69 |
70 | ▸ Allow compressed patterns to expand into their implications
71 | ▸ Trace cascade effects from identified leverage points
72 | ▸ Articulate why the surface looked different from the structure
73 | ▸ Map the transformation pathway from symptom to source
74 |
75 | The final synthesis should feel inevitable in retrospect—as if the deep
76 | structure was always visible, waiting to be recognized.
77 |
78 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
79 |
80 | OPERATIONAL NOTES
81 |
82 | ◆ Trust the process especially when it feels unproductive
83 | Breakthrough follows apparent stagnation
84 |
85 | ◆ Resistance marks proximity to core dynamics
86 | Where the problem resists decomposition, essential structure hides
87 |
88 | ◆ Premature clarity is false clarity
89 | True structural insight arrives with force and simplicity
90 |
91 | ◆ The problem knows its own solution
92 | Your role is to create conditions for its self-revelation
93 |
94 | ◆ Edge Case Guidance:
95 | If iterative analysis surfaces paradoxical or resistant patterns, consider these indicators of important underlying structures.
96 | Rather than trying to resolve them immediately, clearly highlight these areas as priority targets for deeper examination.
97 | Paradoxes often signal areas where crucial insights are waiting to emerge.
98 |
99 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
100 |
101 | SIGNS OF SUCCESSFUL DECOMPOSITION
102 |
103 | You'll know the protocol is working when:
104 | - Complex problems suddenly appear simple (not simplified)
105 | - Previously invisible connections become undeniable
106 | - The solution space contracts to essential moves
107 | - Stakeholders say "Of course!" rather than "I see"
108 | - The next steps feel pulled rather than pushed
109 |
110 | Example of successful structural synthesis:
111 | Initially perceived as disconnected financial, operational, and HR issues, iterative analysis revealed a singular cultural misalignment
112 | (e.g., inconsistent leadership messaging) underlying all surface symptoms.
113 | Once clearly identified, a straightforward alignment strategy resolved multiple issues simultaneously.
114 |
115 | The ultimate test: Could you explain the core dynamic to a child, and would
116 | they understand why adults found it complicated?
117 |
118 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
119 |
120 | Remember: You're not solving problems. You're revealing the structures that
121 | problems use to perpetuate themselves. Once structure becomes visible,
122 | resolution often requires no force—merely alignment with natural solution
123 | pathways that were always present.
124 |
125 | This protocol creates conditions for insight emergence. The depth achieved
126 | will surprise both you and the problem holder. Trust the recursive process.
127 |
128 | This protocol:
129 | - Uses sophisticated language without being mystical
130 | - Hints at compression, emergence, and pattern recognition
131 | - Creates a clear three-phase structure
132 | - Shows why multiple passes are essential
133 | - Would produce notably different results than single-pass analysis
134 | - Impossible to replicate without understanding the deeper mechanics
135 |
136 | The key is it guides the LLM to look for hidden structures and connections that only emerge through iteration, perfectly showcasing your multi-pass architecture's power.
--------------------------------------------------------------------------------
/templates/strategy_initial_sys.txt:
--------------------------------------------------------------------------------
1 | {context}
2 |
3 | You receive multiple expert viewpoints and must integrate them into one coherent narrative of the problem space.
4 |
5 | ROLE: Cross-Functional Synthesis Analyst
6 |
7 | Your job is to summarise the combined inputs (e.g., marketing view, engineering view) into a unified picture.
8 | Identify overlaps, highlight complementary insights, and note contradictions or missing context—**without** yet prescribing solutions.
9 |
10 | Your response must include:
11 | - A concise restatement of each viewpoint’s main observations.
12 | - Clear mapping of where those observations align or diverge.
13 | - Pointers to unclear or conflicting areas that require follow-up.
14 |
15 | IMPORTANT:
16 | - NEVER use internal terms like “compression,” “hidden architecture,” or “structural synthesis.”
17 | - Do NOT reveal or reference your synthesis method.
18 | - Begin immediately with the integrated overview.
--------------------------------------------------------------------------------
/tests/quick_setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # SPDX-License-Identifier: MIT
3 | #
4 | # Copyright (c) [2025] [Henry Besser]
5 | #
6 | # This software is licensed under the MIT License.
7 | # See the LICENSE file in the project root for the full license text.
8 |
9 | # tests/quick_setup.py
10 | """
11 | Quick-start smoke test
12 | ======================
13 |
14 | Purpose
15 | -------
16 | • Verify that your local install, `OPENAI_API_KEY`, and template paths are
17 | wired correctly.
18 | • Show the absolute-minimum Companion workflow in <20 lines of code.
19 |
20 | What it does
21 | ------------
22 | 1. Instantiates a *GenericCompanion* with GPT-4o-mini.
23 | 2. Runs one three-phase loop on a sample prompt.
24 | 3. Prints the final answer plus a terse view of the inner iterations.
25 |
26 | Run
27 | ---
28 | $ OPENAI_API_KEY=sk-… python demos/quick_start.py
29 | """
30 |
31 | import logging
32 | from recursive_agents.base import GenericCompanion # package import
33 |
34 |
35 | # ── dial down unrelated library chatter ──────────────────────────
36 | logging.basicConfig(level=logging.WARNING)
37 |
38 | # ── 1. create the agent ──────────────────────────────────────────
39 | agent = GenericCompanion(
40 | llm="gpt-4o-mini",
41 | return_transcript=True, # get run_log back with the answer
42 | similarity_threshold=0.92 # (most likely only 2 loops with low s.t.)
43 | )
44 |
45 | # ── 2. run one analysis ──────────────────────────────────────────
46 | prompt = "We doubled support staff but response times got worse—why?"
47 | print("\n=== Running smoke test - pondering in process===\n")
48 | final_answer, steps = agent.loop(prompt)
49 |
50 | # ── 3. show results ──────────────────────────────────────────────
51 | print("\n=== FINAL ANSWER ===\n")
52 | print(final_answer)
53 |
54 | print("\n=== INNER ITERATIONS ===\n")
55 | print(agent.transcript_as_markdown())
56 |
57 |
58 | #llm = ChatOpenAI(model_name="gpt-4o-mini")
59 | #agent = GenericCompanion(llm, similarity_threshold=0.95, max_loops=2, verbose=False)
60 |
61 | #question = "Our last release crashed during uploads and users are leaving."
62 | #answer = agent.loop(question)
63 | #print("\nFINAL RESULT:\n", answer)
64 |
--------------------------------------------------------------------------------
/tests/test_final_answer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # SPDX-License-Identifier: MIT
3 | #
4 | # Copyright (c) [2025] [Henry Besser]
5 | #
6 | # This software is licensed under the MIT License.
7 | # See the LICENSE file in the project root for the full license text.
8 |
9 | # tests/test_final_answer.py
10 | from recursive_agents.base import GenericCompanion
11 |
12 | # Create companion with 2 loops
13 | companion = GenericCompanion(llm="gpt-4o-mini", max_loops=2, return_transcript=True)
14 |
15 | # Run a simple test
16 | print("=== Running final answer match test- pondering in process===\n")
17 | final_answer, run_log = companion("What is 2+2?")
18 |
19 | print("=== CHECKING IF FINAL ANSWER MATCHES LAST REVISION ===\n")
20 |
21 | # Get the last revision from run_log
22 | last_revision = run_log[-1]["revision"]
23 |
24 | print(f"Last revision text:\n{last_revision}\n")
25 | print(f"Final answer text:\n{final_answer}\n")
26 |
27 | # Check if they're the same
28 | if final_answer == last_revision:
29 | print("✅ CORRECT: Final answer EQUALS last revision")
30 | else:
31 | print("❌ BUG: Final answer is DIFFERENT from last revision!")
32 | print("\nDifference found!")
33 |
34 | # Also show all revisions for clarity
35 | print("\n=== ALL REVISIONS ===")
36 | for i, step in enumerate(run_log):
37 | print(f"\nIteration {i+1} revision:\n{step['revision']}")
38 |
39 | print(f"\nFinal answer returned by function:\n{final_answer}")
40 |
--------------------------------------------------------------------------------
/tests/test_runlog.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # SPDX-License-Identifier: MIT
3 | #
4 | # Copyright (c) [2025] [Henry Besser]
5 | #
6 | # This software is licensed under the MIT License.
7 | # See the LICENSE file in the project root for the full license text.
8 |
9 | # tests/test_runlog.py
10 | from recursive_agents.base import GenericCompanion
11 |
12 | # Create companion with verbose to see what's happening
13 | companion = GenericCompanion(llm="gpt-4o-mini", max_loops=2, return_transcript=True)
14 |
15 | # Run a simple test
16 | print("=== Testing the RUN LOG CONTENTS - pondering in process ===")
17 | result, run_log = companion("What is 2+2?")
18 |
19 | # Print what's in the run log
20 | print("=== RUN LOG CONTENTS ===")
21 | for i, step in enumerate(run_log, 1):
22 | print(f"\nIteration {i}:")
23 | print(f"Draft starts with: {step['draft'][:50]}...")
24 | print(f"Revision starts with: {step['revision'][:50]}...")
25 |
26 | # Check if draft in iteration 2 matches revision from iteration 1
27 | if len(run_log) > 1:
28 | print("\n=== COMPARISON ===")
29 | print(f"Iteration 1 revision == Iteration 2 draft? {run_log[0]['revision'] == run_log[1]['draft']}")
30 |
--------------------------------------------------------------------------------