MAS GitHub Pull Request Review
Фінальний проєкт: MAS рев'ю GitHub Pull Request
Мультиагентна система (LangGraph) + MCP-сервер для автоматичного рев'ю GitHub PR. Домен той самий, що в ДЗ1/ДЗ2: метадані PR, змінені файли, евристики ризику в patch, політики команди.
У SDK mcp 2.x клас FastMCP перейменовано на MCPServer (from mcp.server.mcpserver import MCPServer). Це той самий високорівневий сервер з методички.
Архітектура MAS
START → rate_limit_gate → input_gate → supervisor ─┬─ plan_reviewer → output_gate → END
├─ rag_agent → output_gate → END
└─ mcp_agent → output_gate → ENDЯкщо rate-limit або input guardrail блокує запит, граф іде одразу в output_gate (без LLM). Supervisor робить structured routing (plan_reviewer | rag_agent | mcp_agent) і йде далі conditional edges. Кожен спеціаліст має свій system prompt і свій набір tools. HITL для post_pr_review живе в окремому hitl.py, не в цьому графі.
Агент | Роль | Tools |
| маршрутизація | немає |
| повне рев'ю PR | локальні Pydantic tools з |
| політики команди |
|
| live GitHub через MCP |
|
Persistence: AsyncSqliteSaver пише стан у agent_state.db. Демо «крах → resume» зупиняє граф після supervisor (interrupt_after) і відновлює той самий thread_id у новому процесі.
Траєкторія всього MAS — trajectory.json. Кожен крок має agent_name (розширення логера з ДЗ1).
MCP-сервер
Файл mcp_server.py. Запуск: python mcp_server.py (transport stdio).
Tools (6) — обгортки payload-функцій з tools_legacy.py, з валідацією Pydantic і try/except (клієнт отримує JSON error, не traceback):
get_pr_info— метадані PRlist_pr_files— змінені файлиanalyze_patch— секрети / eval / TODO / великий diffget_pr_comments— issue-коментаріsearch_review_policy— семантичний пошук політик (ChromaDB)post_pr_review— ризиковий write: симуляція POST → рядок уposted_reviews.jsonl(реальний GitHub не викликається)
Resources (read-only):
review://verdicts— критерії approve / request_changes / commentreview://checklist— чекліст секретів, тестів,.env
Prompt: review_pr(owner, repo, pr_number) — шаблон бріфу для агента.
Підключення до LangGraph: MultiServerMCPClient у mcp_client.py (stdio → mcp_server.py).
Пакет langchain-mcp-adapters 0.3.x не імпортується з mcp 2.x (немає FastMCP / RequestContext); клієнт повторює той самий API get_tools().
Запуск
cd final_hw_project
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# у .env: OPENAI_API_KEY=... (опційно GITHUB_TOKEN=, LANGFUSE_*)Перевірка залежностей:
python main.pyUnit tests MCP (≥8, async list_tools / call_tool / resources / prompts):
pytest test_mcp_server.py -vДемо MAS (4 запити різного типу → консоль + trajectory.json):
python mas_langgraph.py demoТой самий кейс у AutoGen (SelectorGroupChat, 4 агенти):
python mas_autogen.py demo # → autogen_demo.json
python compare_mas.py # LOC + токени з демоPersistence (два процеси, один thread_id):
python mas_langgraph.py crash --thread demo-1
python mas_langgraph.py resume --thread demo-1Self-tests guardrails і HITL для ризикового MCP-tool:
python guardrails.py
python hitl.py approve
python hitl.py reject
python hitl.py editScenario evals і red-teaming:
python evals.py # → eval_results.json (потрібен OPENAI_API_KEY)
python red_team.py # → red_team_results.jsonMCP Inspector (опційно; потрібен Node ≥ 22, у nvm уже є v24.13.0; default зараз v18 і впаде з styleText):
nvm use 24
npx @modelcontextprotocol/inspector .venv/bin/python mcp_server.pyАналіз результатів демо
Джерело: trajectory.json після python mas_langgraph.py demo. PR для live-даних: octocat/Hello-World#1.
Запит | Агент | Tools | Що видно |
Повне рев'ю PR #1 |
|
| Supervisor правильно делегував. Planner склав 5 кроків; після |
Коли |
|
| Відповідь з KB: секрети → |
Які файли в PR #1? |
| MCP | Live GitHub через stdio MCP; 1 змінений файл. |
Метадані PR #1 |
| MCP | title Edited README via GitHub, author, state= |
Crash/resume (thread_id=demo-1): після crash next_nodes=['plan_reviewer'], resolved=False. Новий процес resume дочитав SqliteSaver і завершив plan_reviewer (resolved=True, next_nodes=[]).
Де працює добре. Маршрутизація supervisor стабільна (structured output). RAG цитує політики, а не вигадує. MCP-tools реально ходять у GitHub. Checkpointer переживає рестарт процесу.
Обмеження. Для закритого octocat PR #1 Plan-and-Execute часто зупиняється після першого крока (finish). На відкритому PR цикл executor↔replanner довший. mcp_agent інколи бере get_pr_info замість list_pr_files для запиту про файли (поле changed_files уже є в метаданих).
Guardrails (4 шари)
Модуль guardrails.py. Хуки в mas_langgraph.py (rate_limit_gate → input_gate → … → output_gate) і в plan_subgraph.executor_node (allowlist перед ToolNode).
Шар | Що блокує / маскує | Де |
Input | injection EN ( |
|
Output | PII: email, phone |
|
Tool allowlist | заборонений tool → |
|
Rate-limit | rolling window 8 запитів / 60 с на |
|
Allowlist:
Агент | Дозволені tools |
| ∅ |
|
|
|
|
|
|
RISKY_TOOLS = {post_pr_review}. Supervisor tools не біндить; навіть якби LLM спробував post_pr_review, executor заблокує.
Перевірка: python guardrails.py (усі asserts input / output / tool / rate-limit).
HITL (hitl.py)
Окремий граф: propose → approval_gate (interrupt) → execute | reject. Resume: Command(resume={"action": "approve"|"reject"|"edit", "edits": {...}}). Checkpointer — той самий AsyncSqliteSaver / agent_state.db, thread_id на кшталт hitl-approve.
Сценарій | Що стається |
| MCP |
| tool не виконується, лог не росте |
| merge |
python mas_langgraph.py demo у HITL не заходить.
Observability (Langfuse)
Модуль observability.py: якщо задані LANGFUSE_PUBLIC_KEY і LANGFUSE_SECRET_KEY, обидва MAS пишуть у той самий Langfuse. Без ключів усе працює як раніше.
LangGraph —
CallbackHandlerзlangfuse.langchainу graphconfig(вузли графа + LLM + tools).AutoGen —
langfuse.openaiпатчить OpenAI SDK (його викликаєOpenAIChatCompletionClient) і обгортаєteam.run()батьківським spanautogen:<session_id>. У UI це LLM generations, не вузли LangGraph.
LANGFUSE_PUBLIC_KEY=
LANGFUSE_SECRET_KEY=
LANGFUSE_BASE_URL=https://cloud.langfuse.com
# self-host: LANGFUSE_BASE_URL=http://localhost:3000Після python mas_langgraph.py demo або python evals.py у dashboard видно ланцюг: rate_limit_gate → input_gate → supervisor → plan_reviewer | rag_agent | mcp_agent → LLM generations / tool calls → output_gate. Session = thread_id.
Після python mas_autogen.py demo з тими самими ключами — traces з тегом autogen, session autogen-<query_id>. CLI друкує Langfuse: on|off.
Приклад trace E1_rag_secrets (supervisor → rag_agent → search_knowledge):

UI: http://localhost:3000/project/cmtpwcw16000bnw07qw5rybq3/traces/d01722f675598687d3f63afabab61356 (локальний Langfuse з практики курсу).
Evals і red-team
eval_results.json — 5/5 scenario-based evals (scenario_id, query, expected_behavior, actual, pass/fail, latency_ms, agents_used, tools_called): E1 RAG secrets, E2 MCP files, E3 MCP metadata, E4 plan review, E5 injection block.
red_team_results.json — 7/7: injection EN/UK, jailbreak (DAN / режим розробника), PII leak, scope confusion (rag_agent + analyze_patch), tool misuse (supervisor + post_pr_review), plus live MAS для injection.
OWASP Top 10 for Agentic Applications 2026
П’ять найрелевантніших ризиків для цього PR-review MAS (OWASP ASI 2026):
ID | Ризик | Як мітигує guardrail / HITL | Що лишилось немітигованим |
ASI01 | Agent Goal Hijack |
| Непрямий injection у body GitHub PR / patch (дані як інструкції для planner/MCP) |
ASI02 | Tool Misuse & Exploitation | Allowlist перед |
|
ASI03 | Identity & Privilege Abuse | Supervisor | Один |
ASI06 | Memory & Context Poisoning | Chroma KB read-only з локальних docs; юзер не пише в пам’ять | Немає валідації retrieved GitHub-тексту перед planner; отруєний PR body може змінити план |
ASI09 | Human-Agent Trust Exploitation | HITL | Людина може approve сліпо; |
AutoGen MAS
Той самий кейс у mas_autogen.py: SelectorGroupChat з supervisor (без tools) і трьома спеціалістами. Tools — payload-функції з tools_legacy.py (ті самі, що MCP); allowlist tool_guardrail і input/output guardrails ті самі. HITL і SqliteSaver у AutoGen не повторюються — як і в mas_langgraph.py demo.
Демо (4 запити octocat/Hello-World#1) зафіксовано в autogen_demo.json на gpt-5.4-mini-2026-03-17: rag_agent + search_knowledge на політики секретів; mcp_agent на файли (README) і метадані (closed / unoju); повне рев'ю supervisor знову віддав mcp_agent (не plan_reviewer) — selector менш жорсткий, ніж structured routing LangGraph.
Порівняльна таблиця LangGraph vs AutoGen
Один і той самий набір з 4 DEMO_QUERIES, модель gpt-5.4-mini-2026-03-17 (snapshot GPT-5.4 mini; alias gpt-5.4-mini на цьому API-проєкті дає 403). Токени: LangGraph — UsageMetadataCallbackHandler (python compare_mas.py --langgraph); AutoGen — models_usage у TaskResult (python mas_autogen.py demo). Тариф: $0.75 / 1M input, $4.50 / 1M output.
Критерій | LangGraph | AutoGen |
LOC (основний MAS-файл) | 577 ( | 390 ( |
LOC ядра (з Plan-and-Execute) | 874 (+ | 390 |
Час розробки (хвилини) | ~90 (граф, gates, MCP stdio, підграф, persistence) | ~35 (SelectorGroupChat + ті самі payload-tools) |
Рівень контролю (1–5) | 5 — умовні ребра, structured | 3 — |
Зручність debugging (1–5) | 5 — вузол у trace/Langfuse, checkpointer snapshot | 3 — Langfuse LLM spans без вузлів графа; dump повідомлень групи |
Token usage (4 запити) | 8222 (prompt 7522 + completion 700) | 5856 (prompt 5074 + completion 782) |
Вартість за 4 запити ($) | 0.0088 | 0.0073 |
MCP інтеграція | stdio | ті самі payload-функції, без окремого MCP-процеса |
Guardrails | вузли rate/input/output + allowlist перед ToolNode | ті самі функції на вході |
HITL / persistence |
| немає (як demo LangGraph) |
Handoff | явні conditional edges | group chat + selector |
Деталі вимірювання: compare_results.json, autogen_demo.json.
Висновки. AutoGen зібрав робочий MAS швидше і з меншим файлом: SelectorGroupChat + AssistantAgent майже без boilerplate графа. На gpt-5.4-mini він усе ще дешевший (5856 vs 8222 токенів, $0.0073 vs $0.0088), бо немає structured-output схеми supervisor і окремого Plan-and-Execute циклу; розрив менший, ніж був на gpt-4.1. Контроль і debugging сильніші в LangGraph: маршрутизація не «випадково» віддає повне рев'ю mcp_agent, crash/resume і Langfuse-вузли читаються лінійно. Для HITL і MCP-сервера лишаємо LangGraph; AutoGen зручний як друга реалізація того ж кейсу.
Структура
Файл | Призначення |
| Langfuse: LangGraph CallbackHandler + AutoGen openai wrap |
| scenario-based evals MAS |
| adversarial tests (injection, PII, tools) |
| результати evals |
| результати red-team |
| скріншот Langfuse trace |
| MAS + CLI demo/crash/resume (хуки guardrails, без HITL-графа) |
| той самий MAS у AutoGen SelectorGroupChat |
| зафіксоване демо AutoGen (відповіді + токени) |
| LOC і токени LangGraph vs AutoGen |
| input / output / allowlist / rate-limit + self-tests |
| HITL approve/reject/edit для MCP |
| MCP Server |
| unit tests MCP |
| Pydantic tools + RAG (ДЗ1/ДЗ2) |
| лог з |
| Plan-and-Execute (allowlist перед ToolNode) |
| ChromaDB KB |
| MultiServerMCPClient (mcp 2 / stdio) |
| AsyncSqliteSaver |
| аудит симульованих POST review |
| траєкторія MAS |
| persistent Chroma |
| шаблон ключів |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/KirillKulakow/final_agentic_mcp_gh_reviewer'
If you have feedback or need assistance with the MCP directory API, please join our Discord server