-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
259 lines (225 loc) · 12 KB
/
main.py
File metadata and controls
259 lines (225 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# --- START OF FILE main.py ---
import os
import re
import time
import shutil
import requests
# --- LM Studio設定 ---
LM_STUDIO_BASE_URL = "http://localhost:1234"
LM_STUDIO_MODEL = "lfm2-8b-a1b"
# --- ダミークライアントフラグ (互換性のため) ---
client = True # LM Studioは常に利用可能とみなす
# --- LM Studioで利用可能なモデル ---
AVAILABLE_GROQ_MODELS = [
LM_STUDIO_MODEL,
]
### ▼▼▼ 変更点: 対応言語の定義 ▼▼▼ ###
SUPPORTED_LANGUAGES = ["Python", "JavaScript", "Go", "HTML/CSS", "Ruby", "TypeScript"]
# プロジェクト設定
PROJECT_DIR = "Project"
REQUEST_FILE = "request.txt"
# --- ヘルパー関数 (変更なし) ---
def clean_project_dir():
if not os.path.exists(PROJECT_DIR): return
for filename in os.listdir(PROJECT_DIR):
file_path = os.path.join(PROJECT_DIR, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path): os.unlink(file_path)
elif os.path.isdir(file_path): shutil.rmtree(file_path)
except Exception as e: print(f"Error while deleting file/directory: {e}")
def create_project_dir():
if not os.path.exists(PROJECT_DIR): os.makedirs(PROJECT_DIR)
def read_file(filepath):
try:
with open(filepath, "r", encoding="utf-8") as f: return f.read()
except FileNotFoundError: return ""
def write_file(filepath, content):
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, "w", encoding="utf-8") as f: f.write(content)
# --- AIエージェントの定義 ---
def ai_call(system_prompt, user_prompt, model_id, max_retries=3):
# messagesをsystem_prompt + user_promptに変換
# LM Studio /api/v1/chat は system_prompt と input を受け取る形式
payload = {
"model": model_id,
"system_prompt": system_prompt,
"input": user_prompt,
}
for attempt in range(max_retries):
try:
print(f"🧠 AI ({model_id}) is thinking...")
response = requests.post(
f"{LM_STUDIO_BASE_URL}/api/v1/chat",
headers={"Content-Type": "application/json"},
json=payload,
timeout=120,
)
response.raise_for_status()
data = response.json()
# レスポンス形式: {"choices": [{"message": {"content": "..."}}]} または {"response": "..."}
if "choices" in data:
content = data["choices"][0]["message"]["content"]
elif "response" in data:
content = data["response"]
else:
content = str(data)
print("✅ AI response received.")
return content.strip()
except Exception as e:
print(f"⚠️ API呼び出しエラー (試行 {attempt + 1}/{max_retries}): {e}")
time.sleep(10)
print("❌ AI呼び出しに失敗しました。")
return None
def president_ai(user_request, model_id):
system_prompt = (
"You are the President of a software company. "
"Based on the user's request, define the project's core direction and provide concise instructions to the Project Manager. "
"Output in Markdown format. Do not include greetings, signatures, or any extra text."
)
user_prompt = (
f"User's development request:\n---\n{user_request}\n---\n"
"Create instructions for the Project Manager based on the above request."
)
print("\n===== 👑 President AI's Turn =====")
instruction = ai_call(system_prompt, user_prompt, model_id)
if instruction: print("▶️ PresidentからPMへの指示:\n", instruction)
return instruction
### ▼▼▼ 変更点: PMに開発言語を伝える ▼▼▼ ###
def project_manager_ai(president_instruction, model_id, language):
system_prompt = (
"You are a skilled Project Manager. "
"Based on the President's instructions, create a concrete task list to be written into README.md. "
"Important: Every task must include the target filename wrapped in backticks (e.g. `main.py`, `index.js`) and a `[ ]` checkbox. "
"Good example: - [ ] Write the basic server setup in `app.js`\n"
"Output only the Markdown task list. Do not include any extra text."
)
user_prompt = (
f"President's instructions:\n---\n{president_instruction}\n---\n"
"Convert the above instructions into a concrete task list where every task includes a filename."
)
print("\n===== 📋 Project Manager AI's Turn =====")
new_readme_content = ai_call(system_prompt, user_prompt, model_id)
if new_readme_content:
new_readme_content = re.sub(r'^```(markdown)?\n', '', new_readme_content, flags=re.IGNORECASE)
new_readme_content = re.sub(r'\n```$', '', new_readme_content)
write_file(os.path.join(PROJECT_DIR, "README.md"), new_readme_content)
print("✅ README.md を作成/更新しました。")
else:
print("❌ PMがREADMEの生成に失敗しました。")
return new_readme_content is not None
### ▼▼▼ 変更点: Engineerに言語を伝え、プロンプトを動的に生成 ▼▼▼ ###
def engineer_ai(task, engineer_id, fallback_filename, model_id, language):
system_prompt = (
"You are a skilled software engineer. Generate or modify code as instructed. "
"Your output must be a single Markdown code block containing only the complete file content. "
"Do not include explanations, greetings, or any text outside the code block."
)
readme_content = read_file(os.path.join(PROJECT_DIR, "README.md"))
match = re.search(r'`([^`]+)`', task)
if match:
target_file = match.group(1)
elif fallback_filename:
print(f"⚠️ No filename found in task. Using fallback file `{fallback_filename}`.")
target_file = fallback_filename
else:
print(f"❌ Could not find target filename in task and no fallback available. Skipping: {task}")
return False
target_filepath = os.path.join(PROJECT_DIR, target_file)
existing_code = read_file(target_filepath)
user_prompt = (
f"You are Engineer #{engineer_id}. Execute the following task precisely.\n\n"
f"## Project README:\n---\n{readme_content}\n---\n\n"
f"## Your task:\n- {task}\n\n"
f"## Target file: `{target_file}`\n\n"
f"## Current file content:\n```\n{existing_code}\n```\n\n"
f"Generate the complete updated content for `{target_file}` as a Markdown code block."
)
print(f"\n===== 👷 Engineer AI #{engineer_id}'s Turn on: {task} =====")
code = ai_call(system_prompt, user_prompt, model_id)
if code:
code = re.sub(r'^```[a-zA-Z]*\n', '', code)
code = re.sub(r'\n```$', '', code)
write_file(target_filepath, code)
print(f"✅ Engineer #{engineer_id}が `{target_filepath}` を更新しました。")
return True
else:
print(f"❌ Engineer #{engineer_id}がコードの生成に失敗しました。")
return False
# --- 設定選択関数 ---
def select_model():
print(f"\n--- AIモデル設定 ---")
print(f"LM Studio モデル: {LM_STUDIO_MODEL}")
return LM_STUDIO_MODEL
### ▼▼▼ 変更点: 言語選択機能を追加 ▼▼▼ ###
def select_language():
"""開発に使用するプログラミング言語を選択させる"""
print("\n--- 開発言語の選択 ---")
for i, lang in enumerate(SUPPORTED_LANGUAGES):
print(f" {i+1}: {lang}")
while True:
try:
choice = input(f"\n使用する言語の番号を選択してください (1-{len(SUPPORTED_LANGUAGES)}) [Enterで1]: ").strip()
if not choice: choice = "1"
choice_num = int(choice)
if 1 <= choice_num <= len(SUPPORTED_LANGUAGES):
return SUPPORTED_LANGUAGES[choice_num - 1]
else:
print(f"無効な番号です。1から{len(SUPPORTED_LANGUAGES)}の間で入力してください。")
except ValueError:
print("数値を入力してください。")
def main():
print("ようこそ!組織的AIコーディングシステム (AI-Code-Swarm) LM Studio版へ。")
selected_language = select_language()
selected_model = select_model()
print("\n--- 開発設定の確認 ---")
print(f"言語: {selected_language}")
print(f"モデル: {selected_model}")
print("------------------------\n")
if os.path.exists(PROJECT_DIR) and os.listdir(PROJECT_DIR):
print(f"⚠️ 警告: '{PROJECT_DIR}' ディレクトリには既にファイルが存在します。")
while True:
choice = input("開始前に中身を全て削除しますか? (y/n): ").lower().strip()
if choice in ['y', 'yes']: print(f"🧹 '{PROJECT_DIR}' ディレクトリの中身を削除しています..."); clean_project_dir(); print("✅ 削除が完了しました。"); break
elif choice in ['n', 'no']: print("📂 既存のファイルを保持して処理を続行します。"); break
else: print("無効な入力です。'y' または 'n' を入力してください。")
if not client: print("エラー: LM Studioクライアントが初期化されていません。"); return
user_request = read_file(REQUEST_FILE)
if not user_request: print(f"エラー: 開発要求ファイル '{REQUEST_FILE}' が見つからないか、内容が空です。"); return
print(f"\n📄 '{REQUEST_FILE}' から開発要求を読み込みました:\n---\n{user_request}\n---")
create_project_dir()
president_instruction = president_ai(user_request, selected_model)
if not president_instruction: print("Presidentが指示を出せませんでした。処理を中断します。"); return
### ▼▼▼ 変更点: PMの呼び出しに言語を渡す ▼▼▼ ###
if not project_manager_ai(president_instruction, selected_model, selected_language):
print("Project Managerがタスク計画を立てられませんでした。処理を中断します。")
return
engineer_id_counter, main_filename = 1, None
while True:
readme_content = read_file(os.path.join(PROJECT_DIR, "README.md"))
tasks = re.findall(r'-\s*\[\s*\]\s*(.*)', readme_content)
if not tasks: print("\n🎉 全てのタスクが完了しました!"); break
current_task_text = tasks[0]
if not main_filename:
all_tasks_in_readme = re.findall(r'-\s*\[[\s|x]\]\s*(.*)', readme_content)
for t in all_tasks_in_readme:
match = re.search(r'`([^`]+)`', t)
if match: main_filename = match.group(1); print(f"💡 プロジェクトのメインファイルを `{main_filename}` と推定しました。"); break
engineer_id = (engineer_id_counter - 1) % 2 + 1
### ▼▼▼ 変更点: Engineerの呼び出しに言語を渡す ▼▼▼ ###
success = engineer_ai(current_task_text, engineer_id, main_filename, selected_model, selected_language)
engineer_id_counter += 1
if success:
current_task_line = f"- [ ] {current_task_text}"
new_readme_content = readme_content.replace(current_task_line, f"- [x] {current_task_text}", 1)
write_file(os.path.join(PROJECT_DIR, "README.md"), new_readme_content)
print(f"✅ タスクを完了済みに更新: {current_task_text}")
else:
print(f"❌ タスクの処理に失敗しました。処理を中断します: {current_task_text}"); break
time.sleep(1)
print("\n===== 最終的なプロジェクト構成 =====")
for root, _, files in os.walk(PROJECT_DIR):
for name in files: print(os.path.join(root, name).replace('\\', '/'))
print("====================================")
print("開発を終了します。")
if __name__ == "__main__":
main()