kaigo-gap
kaigo_mcp
MCP-сервер, который публикует данные о спросе и предложении в японском уходе за пожилыми как инструмент, доступный ИИ-агентам.
При подключении из Claude Code или Claude Desktop можно получить ответ на вопрос «достаточно ли в этом городе домов престарелых со специальным уходом (токуё)» на основе открытых данных.
Статус: работают сервер, агент и eval. Измерены колонка A (локальная 7B), колонка B (локальная 9B) и колонка D (Claude Haiku 4.5).
Что это делает
MCP (Model Context Protocol) — это стандарт для наделения LLM инструментами. Этот репозиторий создаёт сторону инструментов и не содержит саму LLM. Сервер сам по себе не вызывает никаких API и не порождает расходов.
[考える側] [このリポジトリ]
Claude Code / 自作エージェント ←stdio→ kaigo-gap サーバー
「どの道具を使うか」を判断 呼ばれたらデータを返すRelated MCP server: Japan Data MCP
Инструменты
Инструмент | Назначение |
| Общенациональные базовые значения. Основа для оценки отдельных цифр |
| Получение данных о спросе/предложении по названию муниципалитета или страховщика |
| Сортировка по степени нехватки (или достатка) домов токуё |
Основной показатель — число мест в домах токуё на 100 человек, признанных нуждающимися в уходе уровня 3 и выше (по стране 24,6 человека, медиана 26,7). Определение и источники находятся на стороне kaigo_gap_analysis.
Агрегацию здесь не пишем
Данные выгружает вариант 4 (kaigo_gap_analysis) через export_web.py, и мы используем тот же insurers.json, который раздаёт публичная панель, скопировав его. Здесь деление заново не переписываем.
Если держать определение показателя в двух местах, то при расхождении ответа агента и цифр на панели невозможно определить, кто прав. dataset.py в варианте 4 написан по принципу «определение — в одном месте», и мы соблюдаем это, выходя за его пределы. Обновление — через python scripts/sync_data.py.
Что учитывали при проектировании инструментов
По одним цифрам судить нельзя. Если передать «12,4», LLM не поймёт, много это или мало, поэтому в ответ включены общенациональный ранг и общенациональное значение в описании инструмента.
Не закрепляем одноимённые муниципалитеты за одной записью. «Футю» есть и в Токио, и в префектуре Хиросима. Возвращаем оба кандидата, а уточнение оставляем вызывающей стороне.
Не позволяем прочитать «0 мест в токуё» как «худший показатель в стране». Таких страховщиков 90, и все они оказываются на 1-м месте с одинаковым результатом. Обязательно прилагаем число совпадающих, а также примечание: «в малых муниципалитетах это не редкость, жители часто пользуются учреждениями соседних муниципалитетов». Без этого из результата rank_insurers выйдет ошибочный вывод «Синго — худший регион страны».
Если не встроить предметные знания в ответы инструментов, то при каждой смене вызывающей модели та же ошибка прочтения будет воспроизводиться снова. Именно поэтому они помещены не в промпт, а на сторону инструментов.
Использование
pip install -r requirements.txt
python scripts/sync_data.py # 案4からデータを取り込む
python scripts/smoke_test.py # 通信の疎通確認Чтобы использовать из Claude Code, достаточно запустить в этом каталоге — будет прочитан .mcp.json. Настройка для регистрации в других клиентах:
{
"mcpServers": {
"kaigo-gap": {
"command": "python",
"args": ["-m", "kaigo_mcp"],
"cwd": "/path/to/kaigo_mcp"
}
}
}При указании путей Windows разделяйте их символом \ ("C:\projects\kaigo_mcp"). В JSON \p становится недопустимой escape-последовательностью, и файл настроек перестаёт читаться целиком. .mcp.json в этом репозитории действительно был сломан именно так.
Агент
python -m kaigo_mcp.agent --list
python -m kaigo_mcp.agent "尼崎市は特養が足りてる?" --verboseИнструменты вызываются через MCP-сервер (функции напрямую не импортируются). Если обойтись импортом, это не будет прохождением через MCP, и утверждение «создан MCP-сервер» не будет проверено.
Сравниваемые колонки
Колонка | Модель | Место | Настройка | Стоимость за вопрос | Статус |
A | qwen2.5:7b | Основная машина (CPU) | — | 0 иен | Измерено |
B | qwen3.5:9b | RTX 5050 8GB |
| 0 иен | Измерено |
B' | qwen3.5:9b | RTX 5050 8GB |
| 0 иен | Измерено |
C | Qwen3.5-397B-A17B | DeepInfra | — | около 0,8 иены | Отложено (возможно, нужна карта для регистрации) |
D | Claude Haiku 4.5 | Anthropic | — | около 1,4 иены (фактически) | Измерено |
Колонки составлены так, чтобы различие между ними сводилось к одному фактору.
Сравнение | Что позволяет понять |
A → B | Эффект железа и поколения модели (разделено через |
B → B' | Эффект только настройки. Модель и веса одинаковы |
B' → C | Эффект только размера модели (обе из одной линейки Qwen3.5) |
C → D | Разница семейств моделей |
B и B' оставлены обе, потому что при наличии только одной из них часть открытий исчезает.
При той же модели и тех же весах только изменение num_ctx раскалывает долю правильных ответов на 83% и 100%. Если оставить только 8k, выглядит так, будто «локальная 9B отличная», а если только 4k — «локальная непригодна для практики». Ни то, ни другое не соответствует действительности.
Колонка C отложена. Изначально она указывала на Qwen3-235B-A22B, но поколение 3.0 не совпадает с колонкой B, к тому же предоставление уже прекращено. Каталог бесплатного тарифа меняется, поэтому перед использованием проверяйте существование через /models. Бесплатный тариф NVIDIA (C-alt-nvidia) тоже пробовали, но один обмен занял 168 секунд, и для eval он не подошёл.
Цикл пишем только один. Если в одной колонке использовать тул-раннер SDK, а в другой — рукописный, разницу между колонками нельзя будет отделить от разницы в реализации цикла. Бэкенд отвечает только за преобразование истории.
Фактические измерения (колонки A и B)
По 6 запусков, измерено через python scripts/probe_tool_calling.py.
В переходе A→B одновременно меняются железо и поколение модели, поэтому для разделения 7B также запускали на GPU-машине (средняя колонка).
Колонка A qwen2.5:7b (CPU) | qwen2.5:7b (GPU) | Колонка B qwen3.5:9b (GPU) | |
Доля успешных вызовов инструментов (температура по умолчанию) | 4/6 = 67% | 5/6 = 83% | 6/6 = 100% |
Доля успешных вызовов инструментов (температура 0) | 6/6 = 100% | 6/6 = 100% | 6/6 = 100% |
Следование инструкциям (температура 0) | 0/6 = 0% | 0/6 = 0% | 6/6 = 100% |
Время на один запуск (после прогрева) | 7,6 с | 0,4 с | 3,2 с |
Первый запуск (с загрузкой модели) | 102,6 с | 36,1 с | 19,2 с |
Необходимость температуры 0 была особенностью именно 7B
У qwen2.5:7b при температуре по умолчанию ломается открывающий тег <tool_call> (в начале появляется несколько символов вроде olith, pering), парсер ollama не распознаёт его, и вызов просачивается в текст ответа. Примета — остаётся только закрывающий тег </tool_call>. При температуре 0 исчезает.
У qwen3.5:9b даже при температуре по умолчанию 6/6. Эта поломка — не общая проблема локального запуска, а особенность поколения и размера именно этой модели.
Та же 7B на GPU-машине тоже протекает при температуре по умолчанию (5/6), и форма протечки та же (brtc {"name": "lookup_insurer", ...} </tool_call> — несколько символов застревают на месте открывающего тега, остаётся только закрывающий). Смена железа не исправляет. Это привычка модели.
Более крупная разница — «следует ли инструкциям»
Системный промпт предписывает: «перед ответом проверьте общенациональные базовые значения через get_national_baseline». 9B следует в 6/6, 7B не следует ни разу — 0/6. При этом 9B вызывает два инструмента параллельно за один шаг.
7B смогла дать правильный ответ, потому что подхватила общенациональное значение (24,6), встроенное в описание инструмента, но предписанную процедуру не выполнила. Разница не в стабильности, а в следовании инструкциям, и она тем заметнее, чем больше инструментов.
Сработало поколение модели, GPU дал только скорость
Перенос 7B на GPU не сдвинул ни один показатель качества. Поломка тегов при температуре по умолчанию осталась (4/6 → 5/6, при n=6 это в пределах флуктуации), следование инструкциям при температуре 0 осталось 0/6. 7B на GPU-машине при температуре по умолчанию лишь 2 раза из 6 обратилась к базовому значению, а при температуре 0 — ни разу.
Сдвинулись только секунды: 7,6 с → 0,4 с (примерно в 19 раз). То есть из разницы A→B доля успеха и следование инструкциям — это эффект поколения модели, а скорость — эффект железа. Удалось разделить в одной таблице.
Заодно: 9B в 8 раз медленнее 7B (3,2 с против 0,4 с). На том же GPU она параллельно вызывает два инструмента и выдаёт более длинный вывод, потому что следует инструкциям. Скорость и следование инструкциям здесь вступают в прямое противоречие.
Дополнительное подтверждение прошлой поправки о секундах
«По умолчанию 18,9 с → температура 0: 4,4 с» в колонке B было не эффектом температуры, а первой загрузкой. При пересъёмке улучшенным скриптом: первый запуск 19,2 с, далее 3,2 с (при температуре 0 далее 3,9 с) — после прогрева разницы по температуре нет. Та же картина, что и в колонке A: первый запуск 102,6 с, далее 7,6 с; среднее с примесью первого запуска выглядит и как разница по температуре, и как разница по железу.
При прогоне целиком раньше ума упал контекст
Результат не одиночного вызова инструмента, а прогона агента до конца одного вопроса.
«Достаточно ли в Амагасаки домов токуё?» | Колонка A 7B (CPU) | Колонка B 9B (4k) | Колонка B' 9B (8k) |
Шаги | 2 | 2 | 3 |
Секунды | 72,31 | 75–89 | 21,6–37,2 |
Входные / выходные токены | 1 648 / 171 | 1 985 / 3 121 | 3 176 / 601 |
Ответ | Правильный | Пустой (0/4 раза) | Правильный (3/3 раза) |
Раньше всего упёрлись не в VRAM, а в значение по умолчанию num_ctx = 4096 в ollama.
Мы предполагали, что 9B, занимая 6,6 ГБ, упрётся в KV-кэш, но до этого останавливает длина контекста по умолчанию.
Способ остановки трудно заметить. Второй шаг завершается с stop_reason='length': промпт около 1 150 + вывод 2 946 = ровно 4 096. В выводе 3 121 токен, но текст пуст.
Триггер — ошибочный аргумент самой модели. Амагасаки находится в префектуре Хёго, но модель отправляет lookup_insurer({'name': '尼崎市', 'pref': '大阪'}), получает 0 совпадений, затем 2 946 токенов размышляет и исчерпывает контекст, не начав писать ответ. Токены, нужные для самовосстановления, пожирают контекст.
Увеличение num_ctx до 8192 устраняет проблему, но тогда ollama ps показывает 12%/88% CPU/GPU. В 8 ГБ не помещается. При 4k ответа нет, при 8k не влезает в GPU — вот что значит запускать 9B на RTX 5050 8GB.
Сложный вопрос (назвать 3 муниципалитета в префектуре Аомори и сравнить с общенациональными данными, --max-steps 8) завершился и при 4k за 3 шага и 41,06 с.
Контекст определяет не сложность вопроса, а необходимость восстановления.
Это был дефект самого измерения (исправлено)
В loop.py до исправления, если вызова инструмента не было, ставилось stopped_by = "end". Даже при обрыве по length и пустом тексте ставилось "end", поэтому вышеуказанные 4 раза в записях остались как «завершено за 2 шага» — в той же форме, что и успех колонки A. Если пустой ответ выглядит так же, как завершение, сравнивать колонки бессмысленно.
Теперь при length или пустом тексте выставляется truncated. Агрегация смотрит на RunRecord.answered. CLI выводит причину и возвращает код завершения 1.
Дефекты такого рода не обнаруживаются, пока реально не пройдёшь по колонке, которая падает. Если бы гоняли только колонку A, не заметили бы.
И в самой этой правке остались две дыры. Обе срабатывают в момент прогона колонок C и D.
Значение, обозначающее обрыв, у разных компаний разное. У OpenAI-совместимых —
length, у Anthropic —max_tokens. Мы смотрели толькоlength, поэтому в колонке D (Claude) обрыв не обнаруживался. Та же поломка, что считала пустой ответ завершением в колонке B, воспроизводится как есть в колонке D.Случай обрыва посреди вызова инструмента не рассматривался. Проверка находилась внутри ветки «когда вызова инструмента нет». Если перейти дальше с оборванным JSON аргументов, останется только запись о неудаче инструмента, и причина будет выглядеть как «модель плохая».
TRUNCATED_REASONS = {"length", "max_tokens"} проверяется раньше, чем наличие вызова инструмента. Мы реально прошли по max_tokens = 8 (обрыв перед инструментом) и 40 (обрыв посреди вызова инструмента) и заодно убедились, что нормальный путь не превращается в truncated.
Результаты eval (колонки A, B, D)
6 кейсов, измерено через python scripts/eval_agent.py. Все проверки — только те, что можно записать программно (LLM-as-judge не используется). Колонка B — 3 круга (18 записей).
Колонка A 7B (CPU) | Колонка B 9B (4k) | Колонка B' 9B (8k) | Колонка D Haiku 4.5 | |
Правильные ответы | 5/6 = 83% | 15/18 = 83% | 18/18 = 100% | 15/18 = 83% |
Завершение | 6/6 = 100% | 15/18 = 83% | 18/18 = 100% | 18/18 = 100% |
Следование инструкциям | 2/6 = 33% | 18/18 = 100% | 18/18 = 100% | 17/18 = 94% |
Секунд на вопрос (медиана) | 72–85 с | 16,9 с | 24,0 с | 6,5–8,5 с |
Стоимость | 0 иен | 0 иен | 0 иен | 25 иен |
num_ctx — это не настройка скорости, а настройка доли правильных ответов
Все 3 потери при 4k — это truncated/length в amagasaki-basic. Механизм — тот же, что уже видели в предыдущем разделе (При прогоне целиком раньше ума упал контекст), он просто проявился и в eval; это не новая поломка.
Сквозь eval выяснилось: при той же модели и тех же весах только настройка раскалывает долю правильных ответов на 83% и 100%. 8k просто даёт запас на восстановление; модель не меняется нисколько.
«Локальная модель превзошла Haiku» — так писать нельзя
В этих 6 кейсах колонка B' (8k) выше (правильные ответы 18/18 против 15/18, следование инструкциям 18/18 против 17/18). Однако для такого прочтения это слишком слабо.
Всего 6 кейсов × 3 круга. Малая выборка.
Потери Haiku не связаны с возможностями модели. Как сказано ниже в «Примечание сработало слишком сильно», среди них есть круг, где модель прочитала встроенное в инструмент примечание и исключила 4 записи с нулевой вместимостью. Считать потери, вызванные нашим дизайном, слабостью модели — ошибка.
Секунды в 3 раза больше (24,0 с против 6,5–8,5 с). Это не одна арена.
Можно сказать лишь: «в этих 6 вопросах локальной 9B при 8k хватило». Учитывая условие «0 иен и не выходит из дома», это само по себе достаточный результат.
Одинаковая доля правильных ответов, разное содержание (колонка A)
Колонки A, B и D дают по 83% правильных ответов, но состав разный. Колонка A случайно попадает, подхватывая общенациональное значение, встроенное в описание инструмента, и не выполняет предписанную проверку базового значения (следование инструкциям 2/6). Колонки B и D обращаются к нему каждый раз. Чем больше инструментов, тем заметнее эта разница.
Потери колонки B — это упомянутый выше truncated: не «не смогла ответить», а «не успела дописать ответ». При одинаковых 83% причины недостижения у всех трёх разные. Если выстроить одну цифру доли правильных ответов, эта разница исчезает.
Примечание на стороне инструмента сработало, но слишком сильно
В ответ rank_insurers встроено: «нулевая вместимость не редкость в малых муниципалитетах, не судите по одному этому». Колонка D прочитала это и исключила 4 записи с нулевой вместимостью из ответа (1 раз из 3 кругов). Цифры соответствуют реальным данным, это не выдумка.
Ошибочное прочтение предотвращено, но вместо этого информация скрылась. «Не судите» было истолковано как «не касайтесь». Решение встраивать предметные знания в ответы инструментов работает, но иногда срабатывает слишком сильно.
Дальнейшие планы
Колонка B — решено публиковать обе настройки 4k / 8k (B и B'). Суть в том, что раскалывается только настройкой.
Колонка C отложена. Возможно, для регистрации в DeepInfra нужна карта.
Бесплатный тариф NVIDIA для eval не годится (скорость не гарантирована; был обмен в 168 секунд).
Связанное
This server cannot be deployed
Maintenance
Related MCP Connectors
Search Japanese company credit, financials, subsidies, and procurement via AI agents.
Local government intelligence for AI agents.
Japan data tools for AI agents: calendar (rokuyo), address, name splitting, corporate number lookup
Raw Japanese regulatory data for AI agents: pension, gazette, gBizINFO. x402-metered (USDC).
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceEnables LLMs to search and retrieve Japanese real estate data through unified access to the MLIT Real Estate Information Library API, supporting over 25 geospatial datasets for interactive queries.189MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to query Japanese public data (laws, corporations, statistics) from official government APIs, returning normalized English metadata with source attribution.1MIT
- FlicenseNot gradedqualityDmaintenanceProvides AI agents with comprehensive Japanese market intelligence through 27 MCP tools, covering corporate data, macroeconomics, financials, and environmental data from 14 integrated sources.-
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to search Japanese government subsidies and grants, including archived closed calls and recurring application periods based on historical data.10 npm1MIT