Spaces:
Sleeping
Sleeping
Nikolay Ponomarev commited on
Commit ·
5183c26
1
Parent(s): bf83283
inance help
Browse files- Dockerfile +1 -7
- app.py +104 -150
- start.sh +1 -1
Dockerfile
CHANGED
|
@@ -19,15 +19,10 @@ RUN chmod +x /app/start.sh
|
|
| 19 |
# Ollama server
|
| 20 |
ENV OLLAMA_HOST=0.0.0.0:11434
|
| 21 |
|
| 22 |
-
# CPU-friendly
|
| 23 |
ENV OLLAMA_CONTEXT_LENGTH=4096
|
| 24 |
-
|
| 25 |
-
# CPU-friendly: не раздувать параллелизм и память :contentReference[oaicite:5]{index=5}
|
| 26 |
ENV OLLAMA_NUM_PARALLEL=1
|
| 27 |
ENV OLLAMA_MAX_LOADED_MODELS=1
|
| 28 |
-
|
| 29 |
-
# Можно держать модель в памяти подольше (уменьшает “долгий первый ответ”),
|
| 30 |
-
# но это увеличивает расход RAM. Документация/faq упоминают keep_alive как env в некоторых сборках. :contentReference[oaicite:6]{index=6}
|
| 31 |
ENV OLLAMA_KEEP_ALIVE=10m
|
| 32 |
|
| 33 |
# Gradio on Spaces
|
|
@@ -40,6 +35,5 @@ ENV PIPELINE=single
|
|
| 40 |
ENV NUM_CTX=4096
|
| 41 |
ENV MAX_TOKENS=1024
|
| 42 |
ENV LITELLM_TIMEOUT=3600
|
| 43 |
-
ENV AGENT_MAX_STEPS=1
|
| 44 |
|
| 45 |
CMD ["/app/start.sh"]
|
|
|
|
| 19 |
# Ollama server
|
| 20 |
ENV OLLAMA_HOST=0.0.0.0:11434
|
| 21 |
|
| 22 |
+
# CPU-friendly
|
| 23 |
ENV OLLAMA_CONTEXT_LENGTH=4096
|
|
|
|
|
|
|
| 24 |
ENV OLLAMA_NUM_PARALLEL=1
|
| 25 |
ENV OLLAMA_MAX_LOADED_MODELS=1
|
|
|
|
|
|
|
|
|
|
| 26 |
ENV OLLAMA_KEEP_ALIVE=10m
|
| 27 |
|
| 28 |
# Gradio on Spaces
|
|
|
|
| 35 |
ENV NUM_CTX=4096
|
| 36 |
ENV MAX_TOKENS=1024
|
| 37 |
ENV LITELLM_TIMEOUT=3600
|
|
|
|
| 38 |
|
| 39 |
CMD ["/app/start.sh"]
|
app.py
CHANGED
|
@@ -2,33 +2,29 @@ import os
|
|
| 2 |
import re
|
| 3 |
import html as ihtml
|
| 4 |
import textwrap
|
|
|
|
| 5 |
import requests
|
| 6 |
import gradio as gr
|
| 7 |
|
| 8 |
-
from smolagents import
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
# ----------------------------
|
| 12 |
# Config (через env)
|
| 13 |
# ----------------------------
|
| 14 |
-
# CPU-safe дефолт: небольшая модель из Ollama library
|
| 15 |
-
# Можно заменить в Space Variables: MODEL_NAME=qwen2.5-coder:7b или llama3.1:8b (но на CPU будет медленно)
|
| 16 |
MODEL_NAME = os.getenv("MODEL_NAME", "qwen2.5-coder:3b")
|
| 17 |
-
|
| 18 |
OLLAMA_BASE = os.getenv("OLLAMA_URL", "http://127.0.0.1:11434").rstrip("/")
|
| 19 |
|
| 20 |
-
# CPU-
|
| 21 |
NUM_CTX = int(os.getenv("NUM_CTX", "4096"))
|
| 22 |
MAX_TOKENS = int(os.getenv("MAX_TOKENS", "1024"))
|
|
|
|
| 23 |
|
| 24 |
-
#
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
# На CPU лучше избегать многошаговых агентов
|
| 28 |
-
AGENT_MAX_STEPS = int(os.getenv("AGENT_MAX_STEPS", "1"))
|
| 29 |
-
|
| 30 |
-
# single = 1 вызов модели (лучше для CPU)
|
| 31 |
-
# multi = ваш 3-агентный пайплайн (дольше)
|
| 32 |
PIPELINE = os.getenv("PIPELINE", "single").strip().lower()
|
| 33 |
|
| 34 |
|
|
@@ -43,8 +39,20 @@ def make_model():
|
|
| 43 |
)
|
| 44 |
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
def _strip_html(raw_html: str) -> str:
|
| 47 |
-
"""Грубое преобразование HTML -> текст, чтобы агент не тащил сырой HTML в ответ."""
|
| 48 |
raw_html = ihtml.unescape(raw_html)
|
| 49 |
raw_html = re.sub(r"(?is)<(script|style).*?>.*?</\1>", " ", raw_html)
|
| 50 |
raw_html = re.sub(r"(?is)<br\s*/?>", "\n", raw_html)
|
|
@@ -55,10 +63,9 @@ def _strip_html(raw_html: str) -> str:
|
|
| 55 |
return raw_html.strip()
|
| 56 |
|
| 57 |
|
| 58 |
-
@tool
|
| 59 |
def web_search(query: str) -> str:
|
| 60 |
"""
|
| 61 |
-
Ищет краткую информацию в интернете по текстовому запросу (
|
| 62 |
и возвращает очищенный текст (без сырого HTML).
|
| 63 |
|
| 64 |
Args:
|
|
@@ -80,83 +87,74 @@ def web_search(query: str) -> str:
|
|
| 80 |
|
| 81 |
def _friendly_error(e: Exception) -> str:
|
| 82 |
return (
|
| 83 |
-
"### Ошибка
|
| 84 |
-
"
|
| 85 |
-
"
|
| 86 |
-
"-
|
| 87 |
-
"-
|
| 88 |
-
"-
|
| 89 |
"Текст ошибки:\n"
|
| 90 |
f"```text\n{repr(e)}\n```"
|
| 91 |
)
|
| 92 |
|
| 93 |
|
| 94 |
-
def
|
| 95 |
-
if allow_internet:
|
| 96 |
-
|
| 97 |
-
Интернет доступен ТОЛЬКО через инструмент web_search(query: str).
|
| 98 |
-
ОБЯЗАТЕЛЬНО: минимум 2 раза вызвать web_search:
|
| 99 |
-
1) "emergency financial assistance <регион>" или "rent assistance <регион>";
|
| 100 |
-
2) "how to avoid financial aid scams" или "debt relief scams warning".
|
| 101 |
-
Не вставляй сырой HTML — только извлечённые выводы.
|
| 102 |
-
"""
|
| 103 |
-
actions = """
|
| 104 |
-
Интернет — только через web_search(query: str).
|
| 105 |
-
Используй результаты поиска, чтобы дополнить список вариа��тов помощи и добавить предупреждения о мошенничестве.
|
| 106 |
-
"""
|
| 107 |
-
writer = """
|
| 108 |
-
Интернет — только через web_search(query: str).
|
| 109 |
-
Можно уточнить формулировки и типовые источники помощи, но не вставляй сырой HTML.
|
| 110 |
-
"""
|
| 111 |
-
return triage, actions, writer
|
| 112 |
-
else:
|
| 113 |
-
common = """
|
| 114 |
-
Интернет недоступен, инструмент web_search отсутствует.
|
| 115 |
-
Опирайся только на свои знания и общие принципы.
|
| 116 |
-
"""
|
| 117 |
-
return common, common, common
|
| 118 |
|
|
|
|
|
|
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
agent = CodeAgent(
|
| 130 |
-
tools=tools,
|
| 131 |
-
model=model,
|
| 132 |
-
add_base_tools=False,
|
| 133 |
-
max_steps=AGENT_MAX_STEPS,
|
| 134 |
-
)
|
| 135 |
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
{
|
|
|
|
| 140 |
|
| 141 |
-
Сформируй ГОТОВЫЙ ОТЧЁТ ДЛЯ ЧЕЛОВЕКА в формате Markdown.
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
Структура
|
| 149 |
# План финансовой помощи
|
| 150 |
## Важно
|
| 151 |
-
- дисклеймер: не юр/фин совет; при угрозе выселения/насилия/суицида —
|
| 152 |
-
- не сообщать коды, CVV, пароли; осторожно с предоплатами и
|
| 153 |
## Сводка ситуации
|
| 154 |
## Приоритеты
|
| 155 |
### Сегодня (24–72 часа)
|
| 156 |
### На неделе
|
| 157 |
### В течение месяца
|
| 158 |
## Варианты помощи в регионе
|
| 159 |
-
-
|
| 160 |
## Пошаговый план
|
| 161 |
## Мини-бюджет на 30 дней
|
| 162 |
## Анти-мошенничество
|
|
@@ -167,33 +165,22 @@ def run_fin_aid_multi_agent(case_description: str, region: str, urgency: str, al
|
|
| 167 |
Регион: {region}
|
| 168 |
Описание ситуации:
|
| 169 |
{case_description}
|
|
|
|
|
|
|
| 170 |
"""
|
| 171 |
try:
|
| 172 |
-
|
| 173 |
-
return str(out).strip() if out is not None else ""
|
| 174 |
except Exception as e:
|
| 175 |
return _friendly_error(e)
|
| 176 |
|
| 177 |
# ----------------------------
|
| 178 |
-
# Multi
|
| 179 |
# ----------------------------
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
triage = CodeAgent(tools=tools, model=model, add_base_tools=False, max_steps=AGENT_MAX_STEPS)
|
| 183 |
-
triage_prompt = f"""
|
| 184 |
-
Ты агент Triage.
|
| 185 |
-
Всегда отвечай на РУССКОМ языке.
|
| 186 |
-
{web_hint_triage}
|
| 187 |
-
|
| 188 |
-
Цель: быстро разобрать ситуацию человека и определить приоритеты.
|
| 189 |
-
Правила:
|
| 190 |
-
- НЕ проси чувствительные данные: номера карт, CVV, пароли, SMS-коды, полный паспорт/ID.
|
| 191 |
-
- Можно безопасные категории: диапазоны сумм, тип дохода, примерные расходы, статус аренды.
|
| 192 |
-
|
| 193 |
-
Сформируй структуру:
|
| 194 |
- Краткое резюме (2–4 предложения)
|
| 195 |
- Допущения (список)
|
| 196 |
-
- Приоритеты:
|
| 197 |
- Риски (выселение/отключения/штрафы/коллекторы/мошенники/перегрузка)
|
| 198 |
- Какие данные подготовить (безопасный список)
|
| 199 |
- Вопросы для уточнения (до 8)
|
|
@@ -202,54 +189,21 @@ def run_fin_aid_multi_agent(case_description: str, region: str, urgency: str, al
|
|
| 202 |
Регион: {region}
|
| 203 |
Описание:
|
| 204 |
{case_description}
|
| 205 |
-
"""
|
| 206 |
-
triage_result = triage.run(textwrap.dedent(triage_prompt))
|
| 207 |
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
Ты агент Actions.
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
Поля:
|
| 217 |
-
- "bucket": "urgent" | "short_term" | "mid_term"
|
| 218 |
-
- "title"
|
| 219 |
-
- "steps" (3–8)
|
| 220 |
-
- "expected_outcome"
|
| 221 |
-
- "documents"
|
| 222 |
-
- "warnings"
|
| 223 |
-
|
| 224 |
-
Дополнительно: список resources (тоже питоновский список словарей):
|
| 225 |
-
- "type": "government" | "ngo" | "debt_counseling" | "housing" | "utilities" | "other"
|
| 226 |
-
- "name"
|
| 227 |
-
- "what_it_helps_with"
|
| 228 |
-
- "how_to_start"
|
| 229 |
-
- "notes"
|
| 230 |
-
|
| 231 |
-
Вход (Triage):
|
| 232 |
-
{triage_result}
|
| 233 |
"""
|
| 234 |
-
actions_struct = actions_agent.run(textwrap.dedent(actions_prompt))
|
| 235 |
|
| 236 |
-
|
| 237 |
-
writer = CodeAgent(tools=tools, model=model, add_base_tools=False, max_steps=AGENT_MAX_STEPS)
|
| 238 |
-
writer_prompt = f"""
|
| 239 |
Ты агент Writer.
|
| 240 |
-
|
| 241 |
-
{web_hint_writer}
|
| 242 |
-
|
| 243 |
-
Сгенерируй Python-код, который создаёт переменную report (Markdown строка)
|
| 244 |
-
и возвращает её как последнее выражение.
|
| 245 |
|
| 246 |
-
Формат:
|
| 247 |
-
report = \"\"\"
|
| 248 |
-
...markdown...
|
| 249 |
-
\"\"\"
|
| 250 |
-
report
|
| 251 |
-
|
| 252 |
-
Структура отчёта:
|
| 253 |
# План финансовой помощи
|
| 254 |
## Важно
|
| 255 |
## Сводка ситуации
|
|
@@ -262,32 +216,31 @@ report
|
|
| 262 |
## Мини-бюджет на 30 дней
|
| 263 |
## Анти-мошенничество
|
| 264 |
## Что подготовить
|
| 265 |
-
## Вопросы для уточнения
|
| 266 |
-
|
| 267 |
-
ВАЖНО:
|
| 268 |
-
- НЕ вставляй Python-структуры dict/list в Markdown — только текст.
|
| 269 |
-
- НЕ запрашивай чувствительные данные.
|
| 270 |
-
|
| 271 |
-
Вход (Triage):
|
| 272 |
-
{triage_result}
|
| 273 |
|
| 274 |
-
|
| 275 |
-
{actions_struct}
|
| 276 |
"""
|
| 277 |
-
result = writer.run(textwrap.dedent(writer_prompt))
|
| 278 |
-
return str(result).strip() if result is not None else ""
|
| 279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
except Exception as e:
|
| 281 |
return _friendly_error(e)
|
| 282 |
|
| 283 |
|
| 284 |
-
with gr.Blocks(title="Financial Aid Navigator (CPU
|
| 285 |
gr.Markdown("# Financial Aid Navigator (Ollama on CPU)")
|
| 286 |
gr.Markdown(
|
| 287 |
f"- Model: `{MODEL_NAME}`\n"
|
| 288 |
f"- Ollama: `{OLLAMA_BASE}`\n"
|
| 289 |
-
f"- PIPELINE: `{PIPELINE}` (single
|
| 290 |
-
f"- NUM_CTX: `{NUM_CTX}`, MAX_TOKENS: `{MAX_TOKENS}`, TIMEOUT: `{LITELLM_TIMEOUT}`
|
| 291 |
)
|
| 292 |
|
| 293 |
region = gr.Textbox(
|
|
@@ -309,7 +262,7 @@ with gr.Blocks(title="Financial Aid Navigator (CPU-friendly)") as demo:
|
|
| 309 |
),
|
| 310 |
)
|
| 311 |
allow_internet = gr.Checkbox(
|
| 312 |
-
label="Разрешить
|
| 313 |
value=False,
|
| 314 |
)
|
| 315 |
|
|
@@ -317,11 +270,12 @@ with gr.Blocks(title="Financial Aid Navigator (CPU-friendly)") as demo:
|
|
| 317 |
output = gr.Markdown()
|
| 318 |
|
| 319 |
run_btn.click(
|
| 320 |
-
fn=
|
| 321 |
inputs=[case_description, region, urgency, allow_internet],
|
| 322 |
outputs=[output],
|
| 323 |
)
|
| 324 |
|
|
|
|
| 325 |
demo.queue(max_size=20)
|
| 326 |
|
| 327 |
|
|
|
|
| 2 |
import re
|
| 3 |
import html as ihtml
|
| 4 |
import textwrap
|
| 5 |
+
import warnings
|
| 6 |
import requests
|
| 7 |
import gradio as gr
|
| 8 |
|
| 9 |
+
from smolagents import LiteLLMModel
|
| 10 |
+
|
| 11 |
+
# (опционально) меньше шума в логах
|
| 12 |
+
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
| 13 |
|
| 14 |
|
| 15 |
# ----------------------------
|
| 16 |
# Config (через env)
|
| 17 |
# ----------------------------
|
|
|
|
|
|
|
| 18 |
MODEL_NAME = os.getenv("MODEL_NAME", "qwen2.5-coder:3b")
|
|
|
|
| 19 |
OLLAMA_BASE = os.getenv("OLLAMA_URL", "http://127.0.0.1:11434").rstrip("/")
|
| 20 |
|
| 21 |
+
# CPU-friendly дефолты
|
| 22 |
NUM_CTX = int(os.getenv("NUM_CTX", "4096"))
|
| 23 |
MAX_TOKENS = int(os.getenv("MAX_TOKENS", "1024"))
|
| 24 |
+
LITELLM_TIMEOUT = int(os.getenv("LITELLM_TIMEOUT", "3600"))
|
| 25 |
|
| 26 |
+
# single = 1 вызов модели (рекомендуется на CPU)
|
| 27 |
+
# multi = 3 вызова модели (triage/actions/writer) — медленнее
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
PIPELINE = os.getenv("PIPELINE", "single").strip().lower()
|
| 29 |
|
| 30 |
|
|
|
|
| 39 |
)
|
| 40 |
|
| 41 |
|
| 42 |
+
def llm_text(model: LiteLLMModel, user_prompt: str, system_prompt: str | None = None) -> str:
|
| 43 |
+
messages = []
|
| 44 |
+
if system_prompt:
|
| 45 |
+
messages.append({"role": "system", "content": system_prompt})
|
| 46 |
+
messages.append({"role": "user", "content": user_prompt})
|
| 47 |
+
|
| 48 |
+
resp = model(messages)
|
| 49 |
+
# smolagents модели иногда возвращают объект с .content, иногда уже строку
|
| 50 |
+
if hasattr(resp, "content"):
|
| 51 |
+
return (resp.content or "").strip()
|
| 52 |
+
return str(resp).strip()
|
| 53 |
+
|
| 54 |
+
|
| 55 |
def _strip_html(raw_html: str) -> str:
|
|
|
|
| 56 |
raw_html = ihtml.unescape(raw_html)
|
| 57 |
raw_html = re.sub(r"(?is)<(script|style).*?>.*?</\1>", " ", raw_html)
|
| 58 |
raw_html = re.sub(r"(?is)<br\s*/?>", "\n", raw_html)
|
|
|
|
| 63 |
return raw_html.strip()
|
| 64 |
|
| 65 |
|
|
|
|
| 66 |
def web_search(query: str) -> str:
|
| 67 |
"""
|
| 68 |
+
Ищет краткую информацию в интернете по текстовому запросу (DuckDuckGo HTML)
|
| 69 |
и возвращает очищенный текст (без сырого HTML).
|
| 70 |
|
| 71 |
Args:
|
|
|
|
| 87 |
|
| 88 |
def _friendly_error(e: Exception) -> str:
|
| 89 |
return (
|
| 90 |
+
"### Ошибка\n\n"
|
| 91 |
+
"Запрос к Ollama/модели не выполнился.\n\n"
|
| 92 |
+
"На CPU чаще всего помогает:\n"
|
| 93 |
+
"- Модель поменьше (`qwen2.5-coder:1.5b` или `qwen2.5-coder:3b`)\n"
|
| 94 |
+
"- `NUM_CTX=2048..4096`\n"
|
| 95 |
+
"- `MAX_TOKENS=512..1024`\n\n"
|
| 96 |
"Текст ошибки:\n"
|
| 97 |
f"```text\n{repr(e)}\n```"
|
| 98 |
)
|
| 99 |
|
| 100 |
|
| 101 |
+
def build_web_context(region: str, allow_internet: bool) -> str:
|
| 102 |
+
if not allow_internet:
|
| 103 |
+
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
+
q1 = f"emergency financial assistance {region}"
|
| 106 |
+
q2 = "how to avoid financial aid scams"
|
| 107 |
|
| 108 |
+
try:
|
| 109 |
+
r1 = web_search(q1)
|
| 110 |
+
except Exception as e:
|
| 111 |
+
r1 = f"(web_search ошибка по запросу '{q1}': {repr(e)})"
|
| 112 |
|
| 113 |
+
try:
|
| 114 |
+
r2 = web_search(q2)
|
| 115 |
+
except Exception as e:
|
| 116 |
+
r2 = f"(web_search ошибка по запросу '{q2}': {repr(e)})"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
+
return (
|
| 119 |
+
"## Результаты web_search (для внутреннего использования в ответе)\n"
|
| 120 |
+
f"### Поиск 1: {q1}\n{r1}\n\n"
|
| 121 |
+
f"### Поиск 2: {q2}\n{r2}\n"
|
| 122 |
+
)
|
| 123 |
|
|
|
|
| 124 |
|
| 125 |
+
def run_fin_aid(
|
| 126 |
+
case_description: str,
|
| 127 |
+
region: str,
|
| 128 |
+
urgency: str,
|
| 129 |
+
allow_internet: bool,
|
| 130 |
+
):
|
| 131 |
+
model = make_model()
|
| 132 |
+
web_ctx = build_web_context(region, allow_internet)
|
| 133 |
+
|
| 134 |
+
system = (
|
| 135 |
+
"Ты помощник по финансовой навигации. Всегда отвечай на РУССКОМ.\n"
|
| 136 |
+
"Правила безопасности:\n"
|
| 137 |
+
"- НЕ проси и НЕ предлагай вводить чувствительные данные: номера карт, CVV, пароли, коды из SMS, полный паспорт/ID.\n"
|
| 138 |
+
"- Можно спрашивать безопасные категории: диапазоны сумм, сроки, тип дохода, примерные расходы.\n"
|
| 139 |
+
"- Никаких инвестсоветов. Фокус: кризисная поддержка, бюджет, варианты помощи.\n"
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
if PIPELINE != "multi":
|
| 143 |
+
prompt = f"""
|
| 144 |
+
Сформируй ГОТОВЫЙ отчёт для человека в Markdown.
|
| 145 |
|
| 146 |
+
Структура:
|
| 147 |
# План финансовой помощи
|
| 148 |
## Важно
|
| 149 |
+
- дисклеймер: не юр/фин совет; при угрозе выселения/насилия/суицида — местные службы
|
| 150 |
+
- не сообщать коды, CVV, пароли; осторожно с предоплатами и "мгновенными списаниями"
|
| 151 |
## Сводка ситуации
|
| 152 |
## Приоритеты
|
| 153 |
### Сегодня (24–72 часа)
|
| 154 |
### На неделе
|
| 155 |
### В течение месяца
|
| 156 |
## Варианты помощи в регионе
|
| 157 |
+
- 5–10 пунктов (гос/НКО/жильё/коммуналка/долги/соцслужбы). Если точных названий нет — категории.
|
| 158 |
## Пошаговый план
|
| 159 |
## Мини-бюджет на 30 дней
|
| 160 |
## Анти-мошенничество
|
|
|
|
| 165 |
Регион: {region}
|
| 166 |
Описание ситуации:
|
| 167 |
{case_description}
|
| 168 |
+
|
| 169 |
+
{web_ctx}
|
| 170 |
"""
|
| 171 |
try:
|
| 172 |
+
return llm_text(model, textwrap.dedent(prompt), system_prompt=system)
|
|
|
|
| 173 |
except Exception as e:
|
| 174 |
return _friendly_error(e)
|
| 175 |
|
| 176 |
# ----------------------------
|
| 177 |
+
# Multi pipeline (3 вызова модели) — медленнее на CPU
|
| 178 |
# ----------------------------
|
| 179 |
+
triage_prompt = f"""
|
| 180 |
+
Ты агент Triage. Сформируй структуру:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
- Краткое резюме (2–4 предложения)
|
| 182 |
- Допущения (список)
|
| 183 |
+
- Приоритеты по срочности: сегодня / на неделе / в течение месяца
|
| 184 |
- Риски (выселение/отключения/штрафы/коллекторы/мошенники/перегрузка)
|
| 185 |
- Какие данные подготовить (безопасный список)
|
| 186 |
- Вопросы для уточнения (до 8)
|
|
|
|
| 189 |
Регион: {region}
|
| 190 |
Описание:
|
| 191 |
{case_description}
|
|
|
|
|
|
|
| 192 |
|
| 193 |
+
{web_ctx}
|
| 194 |
+
"""
|
| 195 |
+
actions_prompt = """
|
| 196 |
Ты агент Actions.
|
| 197 |
+
На основе Triage дай:
|
| 198 |
+
1) Список конкретных действий по 3 корзинам: urgent / short_term / mid_term.
|
| 199 |
+
2) Список вариантов помощи (resources): government/ngo/debt_counseling/housing/utilities/other.
|
| 200 |
+
Формат — Markdown, без Python-структур.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
"""
|
|
|
|
| 202 |
|
| 203 |
+
writer_prompt = """
|
|
|
|
|
|
|
| 204 |
Ты агент Writer.
|
| 205 |
+
Собери финальный отчёт в Markdown по шаблону:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
# План финансовой помощи
|
| 208 |
## Важно
|
| 209 |
## Сводка ситуации
|
|
|
|
| 216 |
## Мини-бюджет на 30 дней
|
| 217 |
## Анти-мошенничество
|
| 218 |
## Что подготовить
|
| 219 |
+
## Вопросы для уточнения (до 8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
|
| 221 |
+
НЕ проси чувствительные данные.
|
|
|
|
| 222 |
"""
|
|
|
|
|
|
|
| 223 |
|
| 224 |
+
try:
|
| 225 |
+
triage = llm_text(model, textwrap.dedent(triage_prompt), system_prompt=system)
|
| 226 |
+
actions = llm_text(model, actions_prompt + "\n\nВход (Triage):\n" + triage, system_prompt=system)
|
| 227 |
+
final_report = llm_text(
|
| 228 |
+
model,
|
| 229 |
+
writer_prompt + "\n\nВход (Triage):\n" + triage + "\n\nВход (Actions):\n" + actions,
|
| 230 |
+
system_prompt=system,
|
| 231 |
+
)
|
| 232 |
+
return final_report
|
| 233 |
except Exception as e:
|
| 234 |
return _friendly_error(e)
|
| 235 |
|
| 236 |
|
| 237 |
+
with gr.Blocks(title="Financial Aid Navigator (CPU)") as demo:
|
| 238 |
gr.Markdown("# Financial Aid Navigator (Ollama on CPU)")
|
| 239 |
gr.Markdown(
|
| 240 |
f"- Model: `{MODEL_NAME}`\n"
|
| 241 |
f"- Ollama: `{OLLAMA_BASE}`\n"
|
| 242 |
+
f"- PIPELINE: `{PIPELINE}` (single рекомендуем на CPU)\n"
|
| 243 |
+
f"- NUM_CTX: `{NUM_CTX}`, MAX_TOKENS: `{MAX_TOKENS}`, TIMEOUT: `{LITELLM_TIMEOUT}`"
|
| 244 |
)
|
| 245 |
|
| 246 |
region = gr.Textbox(
|
|
|
|
| 262 |
),
|
| 263 |
)
|
| 264 |
allow_internet = gr.Checkbox(
|
| 265 |
+
label="Разрешить поиск в интернете (DuckDuckGo через requests)",
|
| 266 |
value=False,
|
| 267 |
)
|
| 268 |
|
|
|
|
| 270 |
output = gr.Markdown()
|
| 271 |
|
| 272 |
run_btn.click(
|
| 273 |
+
fn=run_fin_aid,
|
| 274 |
inputs=[case_description, region, urgency, allow_internet],
|
| 275 |
outputs=[output],
|
| 276 |
)
|
| 277 |
|
| 278 |
+
# В вашей версии Gradio concurrency_count не поддерживается — оставляем совместимо
|
| 279 |
demo.queue(max_size=20)
|
| 280 |
|
| 281 |
|
start.sh
CHANGED
|
@@ -36,7 +36,7 @@ else
|
|
| 36 |
echo "[start.sh] Model already present. Skipping pull."
|
| 37 |
fi
|
| 38 |
|
| 39 |
-
# Warmup
|
| 40 |
echo "[start.sh] Warming up model..."
|
| 41 |
python3 - << 'PY'
|
| 42 |
import os, json, urllib.request
|
|
|
|
| 36 |
echo "[start.sh] Model already present. Skipping pull."
|
| 37 |
fi
|
| 38 |
|
| 39 |
+
# Warmup
|
| 40 |
echo "[start.sh] Warming up model..."
|
| 41 |
python3 - << 'PY'
|
| 42 |
import os, json, urllib.request
|