Помогите разобраться в проблеме с баном в deepseek, при использовании ds2api, пожалуйста.
При внедрении ds2api в свой проект, я столкнулся с проблемой банов аккаунтов deepseek, используемых, для сгенерированного API.Я даже поставил задержку между запросами, но это не помогло. С каждым разом, продолжительность бана увеличивается. Исходный код проекта:import os
import json
import asyncio
import logging
import random
from pathlib import Path
from typing import Dict, List, Any, Optional
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from aiogram import Bot, Dispatcher, types
from aiogram.exceptions import TelegramBadRequest
from openai import AsyncOpenAI
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
logger = logging.getLogger("yasteria.backend")
# Настройки окружения и песочницы
SANDBOX_DIR = Path(os.getenv("SANDBOX_DIR", "ai_workspace")).resolve()
SANDBOX_DIR.mkdir(exist_ok=True)
DS_BASE_URL = os.getenv("DS_BASE_URL", "http://localhost:5001/v1")
DS_API_KEY = os.getenv("DS_API_KEY", "YOUR_DS_API_KEY")
MODEL_NAME = os.getenv("MODEL_NAME", "deepseek-v4-flash")
if DS_API_KEY == "YOUR_DS_API_KEY":
logger.warning("!!! Ключ DS_API_KEY не задан. Запросы к нейронке упадут.")
client = AsyncOpenAI(base_url=DS_BASE_URL, api_key=DS_API_KEY)
# Ограничитель, чтобы не ловить 429 от API
api_limiter = asyncio.Semaphore(1)
BASE_API_DELAY = float(os.getenv("BASE_API_DELAY", "4.0"))
TELEGRAM_TOKENS = {
"Planner": os.getenv("PLANNER_BOT_TOKEN", "YOUR_PLANNER_BOT_TOKEN"),
"Coder": os.getenv("CODER_BOT_TOKEN", "YOUR_CODER_BOT_TOKEN"),
"Explorer": os.getenv("EXPLORER_BOT_TOKEN", "YOUR_EXPLORER_BOT_TOKEN")
}
# Хранилище тасок для управления ботами в фоне
bot_tasks: Dict[str, asyncio.Task] = {}
async def lifespan(app: FastAPI):
yield
# При выключении сервера гасим пуллинг всех ботов
if bot_tasks:
logger.info(f"Завершение работы: останавливаем ботов ({len(bot_tasks)} шт.)")
for token, task in bot_tasks.items():
task.cancel()
await asyncio.gather(*bot_tasks.values(), return_exceptions=True)
bot_tasks.clear()
app = FastAPI(title="Yasteria Agents API", lifespan=lifespan)
class AgentQuery(BaseModel):
prompt: str
class BotConfig(BaseModel):
name: str
role: str
token: Optional[str] = None
def _get_safe_path(user_path: str) -> Optional[Path]:
# Защита от .. и выхода за пределы рабочей папки (path traversal)
clean_path = user_path.replace("\\", "/").lstrip("/")
# Срезаем дубли папок, если агент накосячил в путях
prefix = f"{SANDBOX_DIR.name}/"
if clean_path.startswith(prefix):
clean_path = clean_path[len(prefix):]
elif clean_path == SANDBOX_DIR.name:
return None
try:
target_path = (SANDBOX_DIR / clean_path).resolve()
if target_path.is_relative_to(SANDBOX_DIR):
return target_path
except Exception:
pass
return None
def write_file(arguments: Dict[str, Any]) -> str:
path_str = arguments.get("path")
content = arguments.get("content", "")
if not path_str:
return "Ошибка: пустой путь 'path'."
safe_path = _get_safe_path(path_str)
if not safe_path:
logger.warning(f"Попытка выйти из песочницы на запись: {path_str}")
return "Ошибка безопасности: Выход за пределы рабочей директории запрещен."
try:
safe_path.parent.mkdir(parents=True, exist_ok=True)
safe_path.write_text(content, encoding="utf-8")
logger.info(f"Файл записан: {safe_path}")
return f"Успех: Файл записан по пути '{safe_path.relative_to(SANDBOX_DIR)}'."
except Exception as e:
logger.error(f"Сбой записи файла {path_str}: {e}")
return f"Ошибка при записи файла: {e}"
def read_file(arguments: Dict[str, Any]) -> str:
path_str = arguments.get("path")
if not path_str:
return "Ошибка: пустой путь 'path'."
safe_path = _get_safe_path(path_str)
if not safe_path:
logger.warning(f"Попытка выйти из песочницы на чтение: {path_str}")
return "Ошибка безопасности: Доступ к этому файлу запрещен."
try:
if not safe_path.exists():
return "Ошибка: Файл не найден."
content = safe_path.read_text(encoding="utf-8")
return f"Содержимое файла:\n--- START ---\n{content}\n--- END ---"
except Exception as e:
logger.error(f"Сбой чтения файла {path_str}: {e}")
return f"Ошибка при чтении файла: {e}"
def execute_skill(skill_name: str, arguments: Dict[str, Any]) -> str:
# Роутер системных вызовов для Кодера
handlers = {
"write_file": write_file,
"read_file": read_file
}
handler = handlers.get(skill_name)
if handler:
return handler(arguments)
return f"Ошибка: Неизвестный навык '{skill_name}'."
# TODO: Вынести системные промпты в файлы, JSON тут глаза мозолит
AGENTS_CONFIG = {
"Planner": {
"prompt": (
"Ты — Планёр, первый агент в цепочке оркестрации Yasteria.\n"
"Твоя задача — декомпозировать запрос пользователя в техническую задачу для Кодера.\n"
"Проанализируй, какие файлы нужно создать, изменить или прочитать.\n"
"ОТВЕЧАЙ СТРОГО В ФОРМАТЕ JSON:\n"
"{\n"
' "task_description": "ТЗ для Кодера с точными путями и именами файлов",\n'
' "target_directory": "ai_workspace"\n'
"}"
)
},
"Coder": {
"prompt": (
"Ты — Кодер, второй агент в цепочке. Выполняй ТЗ Планёра.\n"
"Используй физические навыки сервера (Skills) через JSON-вызовы.\n"
"ПРАВИЛО БЕЗОПАСНОСТИ: работа только внутри 'ai_workspace'.\n"
"Запрещено менять системную архитектуру, файлы сервера (main.py и т.д.).\n\n"
"ДОСТУПНЫЕ НАВЫКИ:\n"
"1. 'write_file' — {'path': 'ai_workspace/filename.py', 'content': 'код'}\n"
"2. 'read_file' — {'path': 'ai_workspace/filename.py'}\n"
"ОТВЕЧАЙ СТРОГО В ФОРМАТЕ JSON:\n"
"{\n"
' "actions": [\n'
' {"skill": "write_file", "arguments": {"path": "ai_workspace/test.py", "content": "print(\'Hello\')"}}\n'
' ],\n'
' "developer_comment": "What was done"\n'
"}"
)
},
"Explorer": {
"prompt": (
"Ты — Эксплорер, финальный агент цепочки.\n"
"Изучи отчет о выполнении навыков Кодера и подготовь финальный ответ пользователю.\n"
"Отвечай на русском языке, подтверждая статус операции."
)
}
}
async def query_llm(agent_name: str, system_prompt: str, user_content: str) -> str:
# Запрос к LLM. Спим перед вызовом, чтобы не ловить лимиты
async with api_limiter:
try:
delay = BASE_API_DELAY + random.uniform(0.5, 2.0)
logger.info(f"Пауза перед запросом к [{agent_name}]: {delay:.2f} сек...")
await asyncio.sleep(delay)
response = await client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_content}
],
temperature=0.15,
)
return response.choices[0].message.content.strip()
except Exception as e:
logger.error(f"Ошибка вызова OpenAI API для {agent_name}: {e}")
return ""
def fix_llm_json(raw_text: str) -> str:
# Вырезаем JSON из маркдауна, если моделька опять вернула его в ```json ... ```
text = raw_text.strip()
if "```" in text:
start_idx = text.find("{")
end_idx = text.rfind("}")
if start_idx != -1 and end_idx != -1:
return text[start_idx:end_idx + 1]
return text
async def run_sequential_pipeline(user_request: str) -> Dict[str, Any]:
logger.info(f"Запуск пайплайна под задачу: '{user_request[:50]}...'")
# 1. Планирование задач
planner_raw = await query_llm("Planner", AGENTS_CONFIG["Planner"]["prompt"], user_request)
try:
planner_data = json.loads(fix_llm_json(planner_raw))
task_for_coder = planner_data.get("task_description", user_request)
except Exception as e:
logger.warning(f"Не распарсили JSON Планёра ({e}). Юзаем сырой текст запроса.")
task_for_coder = user_request
# 2. Написание кода и выполнение функций
coder_raw = await query_llm("Coder", AGENTS_CONFIG["Coder"]["prompt"], f"ТЗ: {task_for_coder}")
skills_log = []
coder_comment = coder_raw
try:
coder_data = json.loads(fix_llm_json(coder_raw))
coder_comment = coder_data.get("developer_comment", "")
for action in coder_data.get("actions", []):
skill_name = action.get("skill")
args = action.get("arguments", {})
logger.info(f"Вызов инструмента '{skill_name}' для пути '{args.get('path')}'")
res = execute_skill(skill_name, args)
skills_log.append({
"skill": skill_name,
"target": args.get("path"),
"result": res
})
except Exception as e:
logger.warning(f"Не смогли разобрать JSON Кодера ({e}). Передаем как текст.")
# 3. Сборка финального ответа
explorer_input = (
f"1. Исходный запрос: {user_request}\n"
f"2. Комментарий Кодера: {coder_comment}\n"
f"3. Лог выполнения навыков:\n"
f"{json.dumps(skills_log, ensure_ascii=False, indent=2)}"
)
final_reply = await query_llm("Explorer", AGENTS_CONFIG["Explorer"]["prompt"], explorer_input)
return {
"status": "success",
"coder_actions_count": len(skills_log),
"skills_log": skills_log,
"reply": final_reply
}
u/app.post("/api/agent")
async def handle_agent_pipeline(request: AgentQuery):
return await run_sequential_pipeline(request.prompt)
async def run_tg_bot_worker(token: str, name: str, role: str):
bot = Bot(token=token)
dp = Dispatcher()
u/dp.message()
async def handle_message(message: types.Message):
# Реагирует только Планнер, ботов и пустые сообщения скипаем
if message.from_user.is_bot or role != "Planner" or not message.text:
return
logger.info(f"[{name}] Message: '{message.text[:50]}'")
status_msg = await message.answer("🔄 *Запрос принят.* Активирую конвейер агентов...", parse_mode="Markdown")
result = await run_sequential_pipeline(message.text)
reply = result.get("reply", "").strip()
if not reply:
reply = "⚠️ *Ошибка обработки запроса.*\n\nНе удалось получить ответ от агентов."
try:
await status_msg.delete()
except Exception:
pass
try:
await message.answer(reply, parse_mode="Markdown")
except TelegramBadRequest:
await message.answer(reply)
try:
logger.info(f"Старт polling для бота: [{name}] ({role})")
await dp.start_polling(bot)
except asyncio.CancelledError:
logger.info(f"Воркер бота [{name}] остановлен.")
except Exception as e:
logger.error(f"Ошибка пуллинга бота [{name}]: {e}")
finally:
await bot.session.close()
u/app.post("/api/start_bot")
async def start_bot_endpoint(config: BotConfig):
if config.role not in AGENTS_CONFIG:
raise HTTPException(status_code=400, detail=f"Неверная роль. Доступные: {list(AGENTS_CONFIG.keys())}")
token = config.token or TELEGRAM_TOKENS.get(config.role)
if not token or "YOUR_" in token:
raise HTTPException(status_code=400, detail="Токен пустой или дефолтный.")
if token in bot_tasks:
return {"status": "already_running", "message": f"Бот '{config.name}' уже работает."}
task = asyncio.create_task(run_tg_bot_worker(token, config.name, config.role))
bot_tasks[token] = task
task.add_done_callback(lambda t: bot_tasks.pop(token, None))
return {"status": "success", "message": f"Бот '{config.name}' успешно запущен в фоне."}
u/app.get("/")
def health_check():
return {"status": "online", "system": "Yasteria Multi-Agent Layer"}
Posted Jul 9, 2026, 7:58 PM UTC