qwenAgent / app.py
BIBLETUM's picture
Update app.py
f50677b verified
import os
import textwrap
import requests
import gradio as gr
import litellm
from smolagents import CodeAgent, LiteLLMModel, tool, LogLevel
MODEL_NAME = os.getenv("MODEL_NAME", "danielsheep/Qwen3-Coder-30B-A3B-Instruct-1M-Unsloth:UD-IQ3_XXS")
OLLAMA_BASE = os.getenv("OLLAMA_URL", "http://127.0.0.1:11434")
def make_model():
return LiteLLMModel(
model_id=f"ollama_chat/{MODEL_NAME}",
api_base=OLLAMA_BASE,
num_ctx=16384,
temperature=0.2,
max_tokens=4096,
)
@tool
def web_search(query: str) -> str:
"""Ищет краткую информацию в интернете по текстовому запросу.
Args:
query: Текстовый поисковый запрос.
"""
resp = requests.get(
"https://duckduckgo.com/html/",
params={"q": query},
timeout=8,
headers={"User-Agent": "qwen-agent-demo"},
)
resp.raise_for_status()
text = resp.text
return text[:4000]
def run_multi_agent(spec: str, level: str, allow_internet: bool):
model = make_model()
# какие инструменты доступны агентам
tools = [web_search] if allow_internet else []
# куски промптов, зависящие от allow_internet
if allow_internet:
web_hint_planner = """
В сгенерированном коде НЕ используй import requests и другие сетевые библиотеки.
Для доступа в интернет у тебя есть ТОЛЬКО инструмент web_search(query: str).
По фичам авторизации/безопасности ОБЯЗАТЕЛЬНО:
- хотя бы один раз вызвать web_search с запросом вроде
"mobile app login test cases", "best practices login screen testing";
- использовать найденную информацию, чтобы дополнить риски и сценарии.
"""
web_hint_tester = """
В сгенерированном коде НЕ используй import requests.
Для доступа в интернет у тебя есть ТОЛЬКО инструмент web_search(query: str).
Если фича связана с авторизацией/безопасностью:
- вызови web_search минимум один раз, чтобы найти типичные позитивные,
негативные и граничные кейсы;
- добавь их в набор тестов.
"""
web_hint_reviewer = """
В сгенерированном коде НЕ используй import requests.
Для доступа в интернет у тебя есть ТОЛЬКО инструмент web_search(query: str).
Можешь использовать его, чтобы уточнить формулировки нефункциональных
требований (безопасность, производительность, устойчивость) и включить
их в отчёт. Не вставляй сырой HTML.
"""
else:
web_hint_common = """
Интернет недоступен, инструмент web_search отсутствует.
Не пытайся импортировать requests или вызывать web_search в коде.
Опирайся только на своё знание модели.
"""
web_hint_planner = web_hint_common
web_hint_tester = web_hint_common
web_hint_reviewer = web_hint_common
# Агент 1: планировщик
planner = CodeAgent(
tools=tools,
model=model,
add_base_tools=False,
max_steps=5,
)
plan_prompt = f"""
Ты агент Planner.
Всегда отвечай на РУССКОМ языке.
{web_hint_planner}
По описанию фичи составь план тестирования и список предположений.
Структура результата (внутренняя, для следующих агентов):
- Предположения (список)
- Риски (список)
- Декомпозиция сценариев (список коротких сценариев).
Уровень детализации: {level}.
Описание фичи:
{spec}
"""
plan = planner.run(textwrap.dedent(plan_prompt))
# Агент 2: генератор тестов
tester = CodeAgent(
tools=tools,
model=model,
add_base_tools=False,
max_steps=5,
)
tests_prompt = f"""
Ты агент Tester.
Всегда отвечай на РУССКОМ языке.
{web_hint_tester}
На основе плана Planner сгенерируй НАБОР ТЕСТОВ как ПИТОНОВСКИЙ СПИСОК словарей.
Каждый тест = словарь с полями:
- "type" (positive / negative / boundary / nonfunctional),
- "name",
- "short_description".
Не нужно писать текст отчёта, только структуру данных.
Уровень детализации: {level}.
План Planner (для анализа):
{plan}
"""
tests = tester.run(textwrap.dedent(tests_prompt))
# Агент 3: ревьюер — формирует финальный Markdown
reviewer = CodeAgent(
tools=tools,
model=model,
add_base_tools=False,
max_steps=3, # Достаточно шагов для исправления ошибок, но промпт явно указывает на финальный ответ
)
reviewer_prompt = f"""
Ты агент Reviewer.
Всегда отвечай на РУССКОМ языке.
{web_hint_reviewer}
Твоя задача — СФОРМИРОВАТЬ ГОТОВЫЙ ОТЧЁТ ДЛЯ ЧЕЛОВЕКА в формате Markdown.
КРИТИЧЕСКИ ВАЖНО: Это ФИНАЛЬНЫЙ шаг. Ты должен сгенерировать Python-код, который создаёт переменную report с Markdown-отчётом и возвращает её. После выполнения этого кода задача будет завершена.
Формат кода должен быть:
report = \"\"\"
## Предположения
- ...
## Чеклист
- ...
## Тесты
### Позитивные
- Название: ... – краткое описание
### Негативные
- ...
### Граничные
- ...
### Нефункциональные
- ...
\"\"\"
report
Используй следующую структуру отчёта:
## Предположения
- ...
## Чеклист
- ... (краткий чеклист для ручного прогона)
## Тесты
### Позитивные
- Название: ... – краткое описание
### Негативные
- ...
### Граничные
- ...
### Нефункциональные
- ...
Важные требования:
- Сгенерируй Python-код с переменной report, содержащей Markdown-текст.
- Используй тройные кавычки \"\"\" для многострочной строки.
- В конце кода должна быть переменная report (без print, без return - просто report как последнее выражение).
- Если код выполнился успешно и переменная report создана - это ФИНАЛЬНЫЙ ответ, больше ничего делать не нужно.
- Если в коде была ошибка - исправь её и попробуй снова, но после успешного выполнения сразу остановись.
- CodeAgent вернёт значение переменной report как финальный результат.
- Все заголовки и описания на русском языке.
- Используй информацию из Planner и Tester.
- НЕ используй структуры dict/list в Markdown, только читабельный текст.
- НЕ вызывай инструменты после создания report - это финальный шаг.
- Используй final_answer() после получения достаточного результата
План Planner:
{plan}
Структура тестов Tester:
{tests}
"""
result = reviewer.run(textwrap.dedent(reviewer_prompt))
# CodeAgent возвращает результат последнего выражения в коде
final_markdown = str(result).strip() if result is not None else ""
return final_markdown
with gr.Blocks(title="Multi-agent QA Assistant (Ollama + Qwen3)") as demo:
gr.Markdown("# Multi-agent QA Assistant (Ollama + Qwen3)")
gr.Markdown(
f"- Model: `{MODEL_NAME}`\n"
f"- Ollama: `{OLLAMA_BASE}`"
)
spec = gr.Textbox(
label="Описание фичи / сценария",
lines=8,
placeholder="Например: экран авторизации по номеру телефона с маской, автозаполнением, проверкой зарегистрированности..."
)
level = gr.Dropdown(
["низкий", "средний", "высокий"],
value="средний",
label="Детализация"
)
allow_internet = gr.Checkbox(
label="Разрешить агенту выход в интернет (web_search)",
value=False,
)
run_btn = gr.Button("Запустить мультиагента")
output = gr.Markdown()
run_btn.click(
fn=run_multi_agent,
inputs=[spec, level, allow_internet],
outputs=[output],
)
def main():
demo.launch()
if __name__ == "__main__":
main()