Abstract
Архитектура
Agent (role, goal, backstory)
↓
Tools (@tool функции)
↓
Crew (агент + задача → Process.sequential)
↓
Kickoff → LLM вызов → результат
Подключение к vLLM
from crewai import LLM
llm = LLM(
model="hosted_vllm/my-model",
base_url="http://localhost/v1",
api_key="dummy",
temperature=0.3,
max_tokens=4096,
)vLLM даёт OpenAI-совместимый API — CrewAI работает с ним через hosted_vllm провайдер.
Инструменты (@tool)
from crewai import tool
@tool
def search(query: str) -> str:
"""Поиск в базе знаний"""
# логика
return resultКаждый @tool становится функцией, которую LLM может вызвать. CrewAI оборачивает схему автоматически.
Память (ChromaDB)
import chromadb
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("memories")
collection.add(documents=[text], metadatas=[{"user": uid}], ids=[mid])
results = collection.query(query_texts=[query], n_results=5)Эмбеддинги через Ollama (nomic-embed-text) или vLLM.
Гендер-баг: модель повторяет tool_call
Некоторые модели зацикливаются на tool calls
Gemma 4 и Qwen повторяют
tool_callвместо текста после tool result. Модель не понимает, что инструмент уже отработал, и пытается вызвать его снова.
Fix — subclass LLM, который убирает tools из запроса, если в истории уже есть tool-role message:
class VLLMLLM(LLM):
def call(self, messages, tools=None, *args, **kwargs):
if any(m["role"] == "tool" for m in messages):
tools = None # forced text response
return super().call(messages, tools=tools, *args, **kwargs)Следствие
Только 1 tool call per crew kickoff. Память загружается в контекст заранее, а не через tool calls.
Запуск как systemd
[Unit]
Description=CrewAI Bot
[Service]
ExecStart=/home/user/bot/.venv/bin/python main.py
Restart=on-failure
[Install]
WantedBy=default.targetUser service:
systemctl --user enable --now crewai-botПромпты
Английские промпты = быстрее + компактнее
Кириллица — 2–5x больше токенов на слово. Пишите инструкции на английском, добавляйте
ALWAYS RESPOND IN RUSSIANдля русского вывода.