Skip to content

Commit 24b5364

Browse files
author
Claude Code
committed
fix: use Claude CLI with prompt optimization for agentic loop
1 parent c34c43e commit 24b5364

1 file changed

Lines changed: 85 additions & 21 deletions

File tree

src/ml_agent/core/agentic_loop.py

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,74 @@
55

66
import json
77
import re
8+
import os
9+
import subprocess
10+
import tempfile
11+
from pathlib import Path
812
from typing import Dict, Optional
9-
from anthropic import Anthropic
1013
from ml_agent.core.context_manager import ContextManager
1114
from ml_agent.core.tool_router import ToolRouter
1215

1316

17+
class ClaudeClient:
18+
"""Client using Claude CLI with --continue for multi-turn."""
19+
20+
def __init__(self):
21+
# Check claude CLI is available
22+
try:
23+
subprocess.run(
24+
["claude", "--version"],
25+
capture_output=True,
26+
timeout=2,
27+
check=True
28+
)
29+
except (FileNotFoundError, subprocess.CalledProcessError):
30+
raise RuntimeError("Claude CLI not found. Install it: https://claude.ai/download")
31+
print("✓ Using Claude CLI")
32+
self.session_id = None
33+
34+
def messages_create(self, model: str, max_tokens: int, system: str, messages: list) -> str:
35+
"""Call Claude via CLI, using --continue for multi-turn."""
36+
# Build the prompt from messages
37+
prompt_parts = []
38+
for msg in messages:
39+
prompt_parts.append(f"[{msg['role'].upper()}]\n{msg['content']}")
40+
41+
prompt = "\n\n".join(prompt_parts)
42+
43+
cmd = ["claude", "-p", prompt]
44+
45+
# Add system prompt as append flag
46+
if system:
47+
cmd.extend(["--append-system-prompt", system])
48+
49+
# Use --continue if we have a session
50+
if self.session_id:
51+
cmd.extend(["--continue", "--resume", self.session_id])
52+
53+
try:
54+
result = subprocess.run(
55+
cmd,
56+
capture_output=True,
57+
text=True,
58+
timeout=120
59+
)
60+
61+
if result.returncode == 0:
62+
output = result.stdout.strip()
63+
# Extract session ID from output if present
64+
if "session:" in result.stderr.lower():
65+
lines = result.stderr.split('\n')
66+
for line in lines:
67+
if "session" in line.lower():
68+
self.session_id = line.split(":")[-1].strip()
69+
return output
70+
else:
71+
raise RuntimeError(f"Claude error: {result.stderr[:200]}")
72+
except subprocess.TimeoutExpired:
73+
raise RuntimeError("Claude call timed out")
74+
75+
1476
class DoomLoopDetector:
1577
"""Detects repeated patterns that indicate doom loops."""
1678

@@ -40,7 +102,7 @@ class AgenticLoop:
40102
"""Main agent loop - iterates until task completion."""
41103

42104
def __init__(self, provider: str = "claude"):
43-
self.client = Anthropic()
105+
self.client = ClaudeClient()
44106
self.context = ContextManager()
45107
self.router = ToolRouter()
46108
self.doom_detector = DoomLoopDetector()
@@ -49,30 +111,34 @@ def __init__(self, provider: str = "claude"):
49111

50112
def get_system_prompt(self) -> str:
51113
"""System prompt for the agent."""
52-
return f"""You are an autonomous ML research agent.
114+
return f"""You are an autonomous ML research agent working on LaTeX dataset collection and model training.
53115
54-
Your task: Collect LaTeX datasets, train models, and deploy them to Hugging Face Hub.
116+
IMPORTANT: You MUST call tools to complete tasks. Do not just describe what you would do.
55117
56118
Available tools:
57119
{self.router.get_tool_specs()}
58120
59-
For each turn:
60-
1. Analyze the current state
61-
2. Decide the next action
62-
3. Call a tool with: <tool_call>
63-
{{
64-
"tool": "tool_name",
65-
"params": {{"key": "value"}}
66-
}}
121+
CRITICAL TOOL CALLING FORMAT:
122+
When you need to call a tool, YOU MUST respond with EXACTLY this format:
123+
124+
<tool_call>
125+
{{"tool": "tool_name", "params": {{"param1": "value1", "param2": "value2"}}}}
67126
</tool_call>
68-
4. Interpret results and continue
127+
128+
Then wait for the result and continue.
129+
130+
MANDATORY WORKFLOW:
131+
1. Start by calling collect_arxiv to fetch papers
132+
2. After collection, call train_model with the dataset
133+
3. After training, call deploy_model to save the result
134+
4. Report progress at each step
135+
5. Stop only when all steps are complete
69136
70137
Guidelines:
71-
- Be autonomous - make decisions without asking
72-
- Iterate until completion
73-
- Report progress after each step
74-
- Handle failures gracefully
75-
- Stop when task is complete"""
138+
- Always use tool_call blocks for actions
139+
- Be autonomous - make decisions without asking for permission
140+
- Never skip steps
141+
- Report what you did after each tool call"""
76142

77143
async def run(self, task: str, max_iterations: int = 300) -> Dict:
78144
"""Run the agentic loop."""
@@ -90,14 +156,12 @@ async def run(self, task: str, max_iterations: int = 300) -> Dict:
90156
print(f"[Iteration {self.iteration}/{self.max_iterations}]")
91157

92158
# Get response from Claude
93-
response = self.client.messages.create(
159+
assistant_message = self.client.messages_create(
94160
model="claude-opus-4-8",
95161
max_tokens=2048,
96162
system=self.get_system_prompt(),
97163
messages=self.context.get_messages(),
98164
)
99-
100-
assistant_message = response.content[0].text
101165
self.context.add_message("assistant", assistant_message)
102166

103167
print(f"Agent: {assistant_message[:200]}...\n")

0 commit comments

Comments
 (0)