Guest User

Untitled

a guest
May 6th, 2026
475
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 27.79 KB | None | 0 0
  1. # Универсальный анализатор кода и текста (Smart Analyzer)
  2.  
  3. 📘 Полная документация и руководство пользователя
  4.  
  5. **Дата:** 2026-05-06
  6. **Устройство:** Infinix X676C (Android 12, Termux)
  7. **Основная цель:** исследование безопасности Android-приложений, смысловой анализ исходного кода и текстов с автоматическим выбором лучшей доступной языковой модели.
  8. **Актуальная версия:** smart_analyze.py (CLI) + web_analyzer.py (веб-интерфейс)
  9.  
  10. ***
  11.  
  12. ## 1. Обзор системы
  13.  
  14. Система состоит из двух компонентов:
  15.  
  16. - **smart_analyze.py** – CLI-скрипт для анализа файлов/текста через командную строку
  17. - **web_analyzer.py** – веб-сервер с графическим интерфейсом (http://127.0.0.1:5000)
  18.  
  19. **Общие возможности:**
  20. - Автоматический выбор backend (Groq → OpenRouter → Ollama → локальная модель)
  21. - 5 режимов: `audit`, `find_secrets`, `text_analysis`, `search`, `explain_code`
  22. - Работает офлайн с Qwen2.5-1.5B-Instruct через llama.cpp
  23. - Потоковый вывод без зависаний
  24. - Принудительный выбор backend и параметров генерации
  25.  
  26. ***
  27.  
  28. ## 2. Архитектура
  29.  
  30. ### 2.1. Динамический выбор backend
  31.  
  32. Приоритет проверки:
  33. 1. **Groq** (GROQ_API_KEY) – лучшая бесплатная модель по формуле (лимит/день × 0.7) + (качество × 0.3)
  34. 2. **OpenRouter** (OPENROUTER_API_KEY) – бесплатные модели (input_price=0)
  35. 3. **Ollama** (OLLAMA_BASE_URL) – локальный сервер
  36. 4. **Local** (llama.cpp) – fallback
  37.  
  38. **Оценка качества:** размер модели + бонусы за семейства (Llama-4, Qwen3, DeepSeek-R1 и др.)
  39.  
  40. ### 2.2. Локальный запуск (pty)
  41. - `pty.fork()` для потокового вывода
  42. - Флаги llama-completion: `-st -no-cnv --simple-io`
  43. - `select()` для чтения без блокировок
  44.  
  45.  
  46. ## 3. Установка
  47.  
  48. ### 3.1. Требования
  49. ```
  50. pkg install python requests clang cmake ninja git flask
  51. ```
  52.  
  53. ### 3.2. Сборка llama.cpp
  54. ```
  55. git clone https://github.com/ggerganov/llama.cpp
  56. cd llama.cpp
  57. cmake . -DLLAMA_CURL=OFF -DLLAMA_VULKAN=OFF -DLLAMA_OPENCL=OFF -B build -DCMAKE_BUILD_TYPE=Release
  58. cmake --build build --config Release -j4
  59. cp build/bin/llama-completion ~/.local/bin/
  60. ```
  61.  
  62. ### 3.3. Модель
  63. ```
  64. mkdir ~/models
  65. # Скачайте qwen2.5-1.5b-instruct-q4_k_m.gguf в ~/models/
  66. ```
  67.  
  68. ### 3.4. Переменные окружения (~/.bashrc)
  69. ```bash
  70. export GROQ_API_KEY="gsk_..."
  71. export OPENROUTER_API_KEY="sk-or-v1-..."
  72. export OLLAMA_BASE_URL="http://localhost:11434"
  73. export LOCAL_BIN="$HOME/.local/bin/llama-completion"
  74. export LOCAL_MODEL_PATH="$HOME/models/qwen2.5-1.5b-instruct-q4_k_m.gguf"
  75. export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
  76. ```
  77.  
  78. ### 3.5. Развертывание скриптов
  79. ```bash
  80. # CLI-скрипт
  81. cat > ~/bin/smart_analyze.py << 'EOF'
  82. #!/usr/bin/env python3
  83. """
  84. Универсальный анализатор кода/текста с динамическим выбором лучшей бесплатной модели.
  85. Версия без детектора повторов, с корректной обработкой завершения локальной модели (Errno 5).
  86. """
  87. import os, sys, re, argparse, pty, select, io, errno
  88. import requests
  89. from typing import Optional, List, Dict, Tuple
  90.  
  91. BACKENDS_PRIORITY = ["groq", "openrouter", "ollama", "local"]
  92. LOCAL_BIN = os.getenv("LOCAL_BIN", os.path.expanduser("~/.local/bin/llama-completion"))
  93. LOCAL_MODEL = os.getenv("LOCAL_MODEL_PATH", os.path.expanduser("~/models/qwen2.5-1.5b-instruct-q4_k_m.gguf"))
  94. TIMEOUT = 10
  95.  
  96. PROMPTS = {
  97. "audit": """Ты — эксперт по безопасности приложений. Проанализируй предоставленный исходный код.
  98. Найди все потенциальные уязвимости: SQL-инъекции, небезопасные криптоалгоритмы, хардкод секретов, утечки данных в логи, небезопасное хранение.
  99. Опиши каждую проблему кратко: место, тип уязвимости, возможную эксплуатацию.""",
  100. "find_secrets": """Извлеки из кода все потенциальные секреты: API-ключи, токены, криптографические ключи, пароли, хэши.""",
  101. "text_analysis": """Выполни смысловой анализ текста: ключевые темы, тональность, скрытые намерения.""",
  102. "search": """Найди в коде все места, удовлетворяющие запросу: "{query}". Объясни каждое.""",
  103. "explain_code": """Объясни, что делает следующий код и как он работает. Опиши алгоритм, назначение функций и возможные особенности реализации."""
  104. }
  105.  
  106. def estimate_quality(name: str) -> float:
  107. score = 50.0
  108. m = re.search(r'(\d+\.?\d*)\s*[bB]', name.lower())
  109. if m: score += min(float(m.group(1)) * 1.5, 60)
  110. for s, bonus in [('llama-4',20), ('llama-3.3',18), ('qwen3',19), ('deepseek-r1',20), ('mixtral',15), ('mistral',15), ('gemma-4',17), ('nemotron',16), ('gpt-oss',18), ('ling',15)]:
  111. if s in name.lower(): score += bonus; break
  112. return min(score, 100.0)
  113.  
  114. def fetch_groq_free_models(api_key):
  115. try:
  116. resp = requests.get("https://api.groq.com/openai/v1/models", headers={"Authorization": f"Bearer {api_key}"}, timeout=TIMEOUT)
  117. if resp.status_code!=200: return []
  118. models = []
  119. for m in resp.json().get('data', []):
  120. mid = m['id']
  121. if any(x in mid for x in ['whisper','guard','spec-decoding','tool-use']): continue
  122. rpm, rpd = (30,14400) if ('70b' in mid or 'deepseek' in mid) else (100,14400) if any(x in mid for x in ['8b','9b']) else (60,14400)
  123. q = estimate_quality(mid)
  124. models.append({"id":mid,"backend":"groq","rpm":rpm,"rpd":rpd,"quality":q,"priority": rpd*0.7 + q*0.3})
  125. models.sort(key=lambda x: x["priority"], reverse=True)
  126. return models
  127. except: return []
  128.  
  129. def fetch_openrouter_free_models(api_key):
  130. try:
  131. resp = requests.get("https://openrouter.ai/api/v1/models", headers={"Authorization": f"Bearer {api_key}"}, timeout=TIMEOUT)
  132. if resp.status_code!=200: return []
  133. models = []
  134. for m in resp.json().get('data', []):
  135. if float(m.get('pricing',{}).get('input',0))==0.0:
  136. q = estimate_quality(m['id'])
  137. models.append({"id":m['id'],"backend":"openrouter","rpm":20,"rpd":50,"quality":q,"priority":50*0.7+q*0.3})
  138. models.sort(key=lambda x: x["priority"], reverse=True)
  139. return models
  140. except: return []
  141.  
  142. def fetch_ollama_models(base_url, api_key=None):
  143. try:
  144. headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
  145. resp = requests.get(f"{base_url.rstrip('/')}/api/tags", headers=headers, timeout=TIMEOUT)
  146. if resp.status_code!=200: return []
  147. models = []
  148. for m in resp.json().get('models',[]):
  149. q = estimate_quality(m['name'])
  150. models.append({"id":m['name'],"backend":"ollama","quality":q,"priority":q})
  151. models.sort(key=lambda x: x["priority"], reverse=True)
  152. return models
  153. except: return []
  154.  
  155. def select_best_model(force_backend=None):
  156. candidates = []
  157. if not force_backend or force_backend=="groq":
  158. key = os.getenv("GROQ_API_KEY")
  159. if key:
  160. lst = fetch_groq_free_models(key)
  161. if lst:
  162. candidates.append(lst[0])
  163. print(f"[+] Groq: {len(lst)} моделей, лучшая: {lst[0]['id']}", file=sys.stderr)
  164. if not force_backend or force_backend=="openrouter":
  165. key = os.getenv("OPENROUTER_API_KEY")
  166. if key:
  167. lst = fetch_openrouter_free_models(key)
  168. if lst:
  169. candidates.append(lst[0])
  170. print(f"[+] OpenRouter: {len(lst)} моделей, лучшая: {lst[0]['id']}", file=sys.stderr)
  171. if not force_backend or force_backend=="ollama":
  172. base = os.getenv("OLLAMA_BASE_URL","http://localhost:11434")
  173. key = os.getenv("OLLAMA_API_KEY")
  174. lst = fetch_ollama_models(base, key)
  175. if lst:
  176. candidates.append(lst[0])
  177. print(f"[+] Ollama: {len(lst)} моделей, лучшая: {lst[0]['id']}", file=sys.stderr)
  178. if not force_backend or force_backend=="local":
  179. if os.path.isfile(LOCAL_MODEL):
  180. q = estimate_quality(LOCAL_MODEL)
  181. candidates.append({"id":"local-model","backend":"local","priority":q})
  182. print(f"[+] Локальная модель: {LOCAL_MODEL}", file=sys.stderr)
  183. if not candidates:
  184. print("[x] Нет доступных моделей.", file=sys.stderr)
  185. sys.exit(1)
  186. candidates.sort(key=lambda x: x["priority"], reverse=True)
  187. best = candidates[0]
  188. params = {}
  189. if best["backend"]=="groq": params["api_key"]=os.getenv("GROQ_API_KEY")
  190. elif best["backend"]=="openrouter": params["api_key"]=os.getenv("OPENROUTER_API_KEY")
  191. elif best["backend"]=="ollama": params.update({"base_url":os.getenv("OLLAMA_BASE_URL","http://localhost:11434"),"api_key":os.getenv("OLLAMA_API_KEY")})
  192. print(f"[🏆] Выбрана {best['backend']}/{best['id']} (приоритет {best['priority']:.2f})", file=sys.stderr)
  193. return best["backend"], best["id"], params
  194.  
  195. def query_groq(prompt, api_key, model, temp=0.15, max_tok=2048):
  196. r = requests.post("https://api.groq.com/openai/v1/chat/completions", headers={"Authorization":f"Bearer {api_key}","Content-Type":"application/json"}, json={"model":model,"messages":[{"role":"user","content":prompt}],"temperature":temp,"max_tokens":max_tok}, timeout=60)
  197. if r.status_code==429: raise RuntimeError("Groq quota exceeded")
  198. r.raise_for_status()
  199. return r.json()["choices"][0]["message"]["content"]
  200.  
  201. def query_openrouter(prompt, api_key, model, temp=0.15, max_tok=2048):
  202. r = requests.post("https://openrouter.ai/api/v1/chat/completions", headers={"Authorization":f"Bearer {api_key}","Content-Type":"application/json","HTTP-Referer":"http://localhost","X-Title":"SmartAnalyzer"}, json={"model":model,"messages":[{"role":"user","content":prompt}],"temperature":temp,"max_tokens":max_tok}, timeout=60)
  203. if r.status_code==429: raise RuntimeError("OpenRouter quota exceeded")
  204. r.raise_for_status()
  205. return r.json()["choices"][0]["message"]["content"]
  206.  
  207. def query_ollama(prompt, base_url, model, api_key=None, temp=0.15, max_tok=2048):
  208. headers = {"Content-Type":"application/json"}
  209. if api_key: headers["Authorization"]=f"Bearer {api_key}"
  210. r = requests.post(f"{base_url.rstrip('/')}/api/chat", headers=headers, json={"model":model,"messages":[{"role":"user","content":prompt}],"stream":False,"options":{"temperature":temp,"num_predict":max_tok}}, timeout=120)
  211. if r.status_code==429: raise RuntimeError("Ollama quota exceeded")
  212. r.raise_for_status()
  213. return r.json()["message"]["content"]
  214.  
  215. def query_local(prompt: str, model_path=LOCAL_MODEL, ctx_size=2048, temp=0.7, max_tokens=256) -> str:
  216. master_fd, slave_fd = pty.openpty()
  217. cmd = [
  218. LOCAL_BIN, "-m", model_path,
  219. "-t", "4", "-c", str(ctx_size),
  220. "--temp", str(temp), "--repeat-penalty", "1.1",
  221. "-n", str(max_tokens), "--no-display-prompt",
  222. "-no-cnv", "--simple-io", "-st",
  223. "-p", prompt
  224. ]
  225. env = os.environ.copy()
  226. env["LLAMA_LOG_VERBOSITY"] = "0"
  227. env["LLAMA_LOG_COLORS"] = "off"
  228.  
  229. pid = os.fork()
  230. if pid == 0:
  231. os.setsid()
  232. os.dup2(slave_fd, 0)
  233. os.dup2(slave_fd, 1)
  234. os.dup2(slave_fd, 2)
  235. os.close(master_fd)
  236. os.execvpe(cmd[0], cmd, env)
  237. else:
  238. os.close(slave_fd)
  239. output = io.BytesIO()
  240. line_buf = b""
  241. while True:
  242. try:
  243. r, w, e = select.select([master_fd], [], [], 1.0)
  244. except OSError:
  245. break
  246. if r:
  247. try:
  248. data = os.read(master_fd, 1024)
  249. except OSError as e:
  250. if e.errno == errno.EIO: # pty закрыт дочерним процессом – нормальное завершение
  251. break
  252. raise
  253. if not data:
  254. break
  255. output.write(data)
  256. line_buf += data
  257. while b'\n' in line_buf:
  258. line_bytes, line_buf = line_buf.split(b'\n', 1)
  259. line = line_bytes.decode('utf-8', errors='replace').strip()
  260. if not any(line.startswith(s) for s in ['main:', 'common_', 'llama_', 'load_', 'print_info', 'sched_reserve', 'common_init']):
  261. sys.stderr.write(line + '\n')
  262. try:
  263. wpid, wstatus = os.waitpid(pid, os.WNOHANG)
  264. if wpid == pid:
  265. break
  266. except (ChildProcessError, OSError):
  267. pass
  268. os.close(master_fd)
  269.  
  270. result = output.getvalue().decode('utf-8', errors='replace').strip()
  271. for token in ['<|user|>', '<|endoftext|>', '<|assistant|>', '<|system|>', '<|im_end|>']:
  272. result = result.replace(token, '')
  273. lines = result.split('\n')
  274. clean_lines = [l for l in lines if not any(l.startswith(s) for s in ['main:', 'common_', 'llama_', 'load_', 'print_info', 'sched_reserve', 'common_init'])]
  275. # убираем только последние строго идентичные дубликаты
  276. final_lines = []
  277. seen = set()
  278. for l in clean_lines:
  279. if l not in seen:
  280. final_lines.append(l)
  281. seen.add(l)
  282. return '\n'.join(final_lines).strip()
  283.  
  284. def main():
  285. parser = argparse.ArgumentParser(description="Универсальный анализатор кода/текста")
  286. parser.add_argument("--file", help="Путь к файлу")
  287. parser.add_argument("--stdin", action="store_true", help="Читать из stdin")
  288. parser.add_argument("--task", default="audit", choices=["audit","find_secrets","text_analysis","search","explain_code"])
  289. parser.add_argument("--query", help="Поисковый запрос (для task=search)")
  290. parser.add_argument("--temperature", type=float, default=0.15)
  291. parser.add_argument("--max-tokens", type=int, default=2048)
  292. parser.add_argument("--ctx-size", type=int, default=4096)
  293. parser.add_argument("--backend", choices=["groq","openrouter","ollama","local"])
  294. args = parser.parse_args()
  295.  
  296. if args.file:
  297. with open(args.file, "r", encoding="utf-8", errors="ignore") as f:
  298. content = f.read()
  299. elif args.stdin:
  300. content = sys.stdin.read()
  301. else:
  302. parser.print_help()
  303. sys.exit(1)
  304.  
  305. if not content.strip():
  306. print("[x] Пустой ввод.", file=sys.stderr)
  307. sys.exit(1)
  308.  
  309. if args.task=="search":
  310. if not args.query:
  311. sys.exit("Нужен --query")
  312. base_prompt = PROMPTS["search"].format(query=args.query)
  313. else:
  314. base_prompt = PROMPTS[args.task]
  315.  
  316. full_prompt = f"{base_prompt}\n\n### ИСХОДНЫЙ КОД / ТЕКСТ:\n{content}"
  317. backend, model, creds = select_best_model(args.backend)
  318.  
  319. try:
  320. if backend=="groq": result = query_groq(full_prompt, creds["api_key"], model, args.temperature, args.max_tokens)
  321. elif backend=="openrouter": result = query_openrouter(full_prompt, creds["api_key"], model, args.temperature, args.max_tokens)
  322. elif backend=="ollama": result = query_ollama(full_prompt, creds["base_url"], model, creds.get("api_key"), args.temperature, args.max_tokens)
  323. elif backend=="local": result = query_local(full_prompt, LOCAL_MODEL, args.ctx_size, args.temperature, args.max_tokens)
  324. else: sys.exit(f"Unknown backend {backend}")
  325. print(result)
  326. except Exception as e:
  327. print(f"[x] Ошибка: {e}", file=sys.stderr)
  328. sys.exit(1)
  329.  
  330. if __name__=="__main__":
  331. main()
  332. EOF
  333. chmod +x ~/bin/smart_analyze.py
  334. ```
  335. # Веб-сервер (обновленная версия с max_tokens)
  336. ```bash
  337. cat > ~/bin/web_analyzer.py << 'EOF'
  338. #!/usr/bin/env python3
  339. """
  340. Веб-интерфейс для smart_analyze.py с потоковым выводом и настройкой max_tokens
  341. """
  342. import os, sys, tempfile, subprocess
  343. from flask import Flask, request, render_template_string, Response, stream_with_context
  344.  
  345. app = Flask(__name__)
  346.  
  347. HTML = r"""
  348. <!DOCTYPE html>
  349. <html>
  350. <head>
  351. <meta charset="utf-8">
  352. <meta name="viewport" content="width=device-width, initial-scale=1">
  353. <title>Smart Analyzer</title>
  354. <style>
  355. body { font-family: sans-serif; max-width: 800px; margin: 0 auto; padding: 1em; }
  356. label { display: block; margin-top: 1em; }
  357. textarea, select, input { width: 100%; box-sizing: border-box; }
  358. textarea { height: 200px; }
  359. #result { margin-top: 1em; white-space: pre-wrap; background: #f4f4f4; padding: 1em; border-radius: 4px; min-height: 50px; }
  360. .spinner { display: none; margin-left: 10px; }
  361. .advanced { margin-top: 1em; border-top: 1px solid #ccc; padding-top: 1em; }
  362. </style>
  363. </head>
  364. <body>
  365. <h2>Smart Analyzer</h2>
  366. <form id="form" method="post" enctype="multipart/form-data">
  367. <label>Файл (выберите или перетащите):</label>
  368. <input type="file" name="file" id="fileInput">
  369.  
  370. <label>Или вставьте текст/код:</label>
  371. <textarea name="text" id="textInput" placeholder="Вставьте сюда код или текст для анализа..."></textarea>
  372.  
  373. <label>Режим:</label>
  374. <select name="task">
  375. <option value="audit">Аудит безопасности</option>
  376. <option value="explain_code">Объяснение кода</option>
  377. <option value="find_secrets">Поиск секретов</option>
  378. <option value="text_analysis">Смысловой анализ текста</option>
  379. <option value="search">Поиск по коду (введите запрос ниже)</option>
  380. </select>
  381.  
  382. <label>Бэкенд:</label>
  383. <select name="backend">
  384. <option value="auto">Автоматический</option>
  385. <option value="groq">Groq</option>
  386. <option value="openrouter">OpenRouter</option>
  387. <option value="ollama">Ollama</option>
  388. <option value="local">Локальная модель</option>
  389. </select>
  390.  
  391. <div id="searchQueryDiv" style="display:none;">
  392. <label>Поисковый запрос:</label>
  393. <input type="text" name="query" id="queryInput" placeholder="Что искать в коде?">
  394. </div>
  395.  
  396. <div class="advanced">
  397. <label>Максимальное количество токенов (для облачных API):</label>
  398. <input type="number" name="max_tokens" value="4096" min="256" max="16384" step="256">
  399. <small>Больше токенов — длиннее ответ. По умолчанию 4096.</small>
  400. </div>
  401.  
  402. <button type="submit" style="margin-top:1em;">Запустить анализ</button>
  403. <span class="spinner" id="spinner">⏳ Анализируем...</span>
  404. </form>
  405.  
  406. <h3>Результат</h3>
  407. <div id="result">Здесь появится ответ...</div>
  408.  
  409. <script>
  410. const form = document.getElementById('form');
  411. const fileInput = document.getElementById('fileInput');
  412. const textInput = document.getElementById('textInput');
  413. const taskSelect = document.getElementsByName('task')[0];
  414. const searchQueryDiv = document.getElementById('searchQueryDiv');
  415. const spinner = document.getElementById('spinner');
  416. const resultDiv = document.getElementById('result');
  417.  
  418. taskSelect.addEventListener('change', function() {
  419. searchQueryDiv.style.display = this.value === 'search' ? 'block' : 'none';
  420. });
  421. if (taskSelect.value === 'search') searchQueryDiv.style.display = 'block';
  422.  
  423. fileInput.addEventListener('change', function() {
  424. if (fileInput.files.length > 0) textInput.value = '';
  425. });
  426. textInput.addEventListener('input', function() {
  427. if (textInput.value.trim() !== '') fileInput.value = '';
  428. });
  429.  
  430. form.addEventListener('submit', async function(e) {
  431. e.preventDefault();
  432. spinner.style.display = 'inline';
  433. resultDiv.textContent = '';
  434. const formData = new FormData(form);
  435. try {
  436. const response = await fetch('/analyze', {
  437. method: 'POST',
  438. body: formData
  439. });
  440. if (!response.ok) {
  441. resultDiv.textContent = 'Ошибка HTTP: ' + response.status;
  442. return;
  443. }
  444. const reader = response.body.getReader();
  445. const decoder = new TextDecoder();
  446. let buffer = '';
  447. while (true) {
  448. const { done, value } = await reader.read();
  449. if (done) break;
  450. buffer += decoder.decode(value, { stream: true });
  451. let lines = buffer.split('\n');
  452. buffer = lines.pop() || '';
  453. for (const line of lines) {
  454. resultDiv.textContent += line + '\n';
  455. }
  456. }
  457. if (buffer) {
  458. resultDiv.textContent += buffer;
  459. }
  460. } catch (err) {
  461. resultDiv.textContent = 'Ошибка: ' + err.message;
  462. } finally {
  463. spinner.style.display = 'none';
  464. }
  465. });
  466. </script>
  467. </body>
  468. </html>
  469. """
  470.  
  471. @app.route('/')
  472. def index():
  473. return render_template_string(HTML, result=None)
  474.  
  475. @app.route('/analyze', methods=['POST'])
  476. def analyze():
  477. task = request.form.get('task', 'audit')
  478. backend = request.form.get('backend', 'auto')
  479. query = request.form.get('query', '')
  480. max_tokens = request.form.get('max_tokens', '4096')
  481.  
  482. content = ''
  483. if 'file' in request.files and request.files['file'].filename != '':
  484. file = request.files['file']
  485. content = file.read().decode('utf-8', errors='ignore')
  486. elif request.form.get('text', '').strip():
  487. content = request.form['text'].strip()
  488. else:
  489. return Response("Не указан ни файл, ни текст", status=400)
  490.  
  491. if not content:
  492. return Response("Пустой ввод", status=400)
  493.  
  494. tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8')
  495. tmp.write(content)
  496. tmp_path = tmp.name
  497. tmp.close()
  498.  
  499. def generate():
  500. cmd = ['smart_analyze.py', '--file', tmp_path, '--task', task, '--max-tokens', max_tokens]
  501. if backend != 'auto':
  502. cmd += ['--backend', backend]
  503. if task == 'search' and query:
  504. cmd += ['--query', query]
  505. try:
  506. proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
  507. text=True, bufsize=1, universal_newlines=True)
  508. for line in iter(proc.stdout.readline, ''):
  509. if not line:
  510. break
  511. yield line
  512. proc.stdout.close()
  513. proc.wait()
  514. if proc.returncode != 0:
  515. yield f"\n[Процесс завершился с кодом {proc.returncode}]"
  516. except Exception as e:
  517. yield f"\n[Ошибка: {e}]"
  518. finally:
  519. os.unlink(tmp_path)
  520.  
  521. return Response(stream_with_context(generate()), mimetype='text/plain')
  522.  
  523. if __name__ == '__main__':
  524. print("Веб-сервер с потоковым выводом: http://127.0.0.1:5000")
  525. app.run(host='127.0.0.1', port=5000, debug=False, threaded=True)
  526. EOF
  527. chmod +x ~/bin/web_analyzer.py
  528. ```
  529.  
  530. ***
  531.  
  532. ## 4. Режимы анализа
  533.  
  534. | Режим | Описание |
  535. |-------|----------|
  536. | `audit` | Аудит безопасности: SQLi, крипто, хардкод, утечки |
  537. | `find_secrets` | API-ключи, токены, пароли, хэши |
  538. | `text_analysis` | Темы, тональность, намерения |
  539. | `search` | Поиск по коду (требует `--query`) |
  540. | `explain_code` | Объяснение алгоритма |
  541.  
  542. ***
  543.  
  544. ## 5. CLI-команды
  545.  
  546. ### 5.1. Анализ файла
  547. ```bash
  548. smart_analyze.py --file app.java --task audit
  549. smart_analyze.py --file RsaUtils.java --task explain_code --backend local
  550. ```
  551.  
  552. ### 5.2. stdin
  553. ```bash
  554. cat code.java | smart_analyze.py --stdin --task audit
  555. echo "код" | smart_analyze.py --stdin --task find_secrets
  556. ```
  557.  
  558. ### 5.3. Поиск
  559. ```bash
  560. smart_analyze.py --file app.java --task search --query "RC4"
  561. ```
  562.  
  563. ### 5.4. Backend и параметры
  564. ```bash
  565. smart_analyze.py --file code.java --backend groq --temperature 0.2 --max-tokens 8192 --ctx-size 4096
  566. ```
  567.  
  568. ***
  569.  
  570. ## 6. Веб-интерфейс
  571.  
  572. ### 6.1. Запуск
  573. ```bash
  574. python3 ~/bin/web_analyzer.py
  575. ```
  576. **Адрес:** http://127.0.0.1:5000
  577.  
  578. ### 6.2. Возможности GUI
  579. - Загрузка файла или ввод текста
  580. - Выбор режима, backend, max_tokens (256-16384)
  581. - Поисковый запрос (для `search`)
  582. - **Потоковый вывод** в реальном времени
  583. - Автоочистка при новом запросе
  584.  
  585. ### 6.3. Устранение неполадок
  586. - **Failed to fetch:** сервер не запущен → `python3 ~/bin/web_analyzer.py`
  587. - **JSON.parse:** очистите кеш (Ctrl+Shift+R)
  588.  
  589. ***
  590.  
  591. ## 7. Особенности и лимиты
  592.  
  593. ### 7.1. Локальная модель
  594. - **Qwen2.5-1.5B** – оптимально (8-10 токенов/с)
  595. - Загрузка: 4-5 сек
  596.  
  597. ### 7.2. Облачные лимиты
  598. | Сервис | Лимит/мин | Лимит/день |
  599. |--------|-----------|------------|
  600. | Groq 70B | 30 | 14400 |
  601. | Groq 8-9B | 100 | 14400 |
  602. | OpenRouter | - | 50 (бесплатно) |
  603.  
  604. **Автопереключение** при 429
  605.  
  606. ### 7.3. Конфиденциальность
  607. **Облако:** код уходит на сервер
  608. **Решение:** `--backend local` или обфускация
  609.  
  610. ### 7.4. Доступ к файлам
  611. `/sdcard` → `~/storage/` в Termux
  612.  
  613. ***
  614.  
  615. ## 8. Будущие улучшения
  616.  
  617. - Chat-шаблоны из метаданных
  618. - llama-server для кэширования
  619. - Hugging Face интеграция
  620. - Server-Sent Events
  621. - Подсветка синтаксиса
  622. - История запросов
  623.  
  624. ***
  625.  
  626. **Система полностью готова!** Передайте этот документ любой LLM для контекста.
  627.  
Advertisement
Add Comment
Please, Sign In to add comment