{"id":482945,"date":"2026-06-09T08:49:48","date_gmt":"2026-06-09T08:49:48","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=482945"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=482945","title":{"rendered":"redb.Route 3.1.0 \u2014 LLM \u043a\u0430\u043a \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442: .To(\u00abllm:\/\/claude\u00bb) \u0438 .AsLlmTool()"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a9a\/7e9\/f32\/a9a7e9f32beebf1ef50f4b78bc8d078d.webp\" alt=\"redb.route llm AI\" title=\"redb.route llm AI\" width=\"832\" height=\"1248\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a9a\/7e9\/f32\/a9a7e9f32beebf1ef50f4b78bc8d078d.webp 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a9a\/7e9\/f32\/a9a7e9f32beebf1ef50f4b78bc8d078d.webp 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>redb.route llm AI<\/figcaption><\/div>\n<\/figure>\n<p><strong>\u0421\u0435\u0440\u0438\u044f:<\/strong> redb ecosystem (\u0430\u043d\u043e\u043d\u0441, \u0440\u0430\u0437\u0431\u043e\u0440 \u043f\u043e\u0437\u0436\u0435)<\/p>\n<p>\u0412 3.1.0 \u0443\u00a0<a href=\"https:\/\/github.com\/redbase-app\/redb-route\" rel=\"noopener noreferrer nofollow\">redb.Route<\/a>\u00a0\u0432\u044b\u0448\u043b\u043e\u00a0<strong>\u0434\u0432\u0430 \u043d\u043e\u0432\u044b\u0445 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430<\/strong>:\u00a0<code>redb.Route.Llm<\/code>\u00a0(24-\u0439) \u0438\u00a0<code>redb.Route.Exec<\/code>\u00a0(25-\u0439). LLM \u0442\u0435\u043f\u0435\u0440\u044c \u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 endpoint \u043d\u0430\u0440\u0430\u0432\u043d\u0435 \u0441 Kafka, RabbitMQ \u0438 HTTP: \u0432\u044b\u0437\u043e\u0432 \u043c\u043e\u0434\u0435\u043b\u0438 \u2014 \u044d\u0442\u043e \u0448\u0430\u0433\u00a0<code>.To(\"llm:\/\/claude\")<\/code>, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0430\u0433\u0435\u043d\u0442\u0430 \u2014 \u044d\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0441\u00a0<code>.AsLlmTool(\"shell\")<\/code>, \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0433\u0435\u043d\u0442 \u2014\u00a0<code>From(\"llm:\/\/factory?schedule=5m\")<\/code>. Exec \u2014 \u0441\u043f\u0430\u0432\u043d\u0435\u0440 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0441 allowlist, working-dir \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c; \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u043a\u0430\u043a backend shell-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0430\u0433\u0435\u043d\u0442\u0430, \u0438 \u043a\u0430\u043a \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0439 scheduled consumer (cron-less health-probes, \u0431\u044d\u043a\u0430\u043f\u044b \u0438 \u0442.\u043f.). \u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u00ab\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 AI-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0440\u044f\u0434\u043e\u043c \u0441 ESB\u00bb: \u0432\u0441\u0451 \u0432\u043d\u0443\u0442\u0440\u0438 \u0442\u043e\u0439 \u0436\u0435 DSL, \u0442\u0435\u0445 \u0436\u0435 retry\/throttle\/circuit-breaker\/audit, \u0442\u0435\u0445 \u0436\u0435 OpenTelemetry-\u0442\u0440\u0435\u0439\u0441\u043e\u0432.<\/p>\n<p>\u042d\u0442\u043e \u0430\u043d\u043e\u043d\u0441. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u0440\u0430\u0437\u0431\u043e\u0440 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439 \u043f\u043e\u0437\u0436\u0435. \u0417\u0434\u0435\u0441\u044c \u2014 \u0447\u0442\u043e \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c, \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432 \u043a\u043e\u0434\u0435, \u0438 \u0447\u0442\u043e \u0447\u0435\u0441\u0442\u043d\u043e \u0435\u0449\u0451 \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043d\u043e.<\/p>\n<blockquote>\n<p><strong>\u0415\u0441\u043b\u0438 \u0447\u0438\u0442\u0430\u0435\u0442\u0435 \u043f\u0440\u043e\u00a0<\/strong><code><strong>redb.Route<\/strong><\/code><strong>\u00a0\u0432\u043f\u0435\u0440\u0432\u044b\u0435<\/strong>\u00a0\u2014 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u0435\u0439 \u0441\u0435\u0440\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/1042392\/\" rel=\"noopener noreferrer nofollow\">redb.Route \u2014 Apache Camel \u0434\u043b\u044f .NET<\/a>\u00a0\u2014 \u0437\u0430\u0447\u0435\u043c \u0432\u043e\u043e\u0431\u0449\u0435, \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u00abApache Camel \u043f\u043e\u0434 .NET\u00bb<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/1042872\/\" rel=\"noopener noreferrer nofollow\">redb.Route \u0438\u0437\u043d\u0443\u0442\u0440\u0438: \u0447\u0435\u0442\u044b\u0440\u0435 in-memory \u043a\u0430\u043d\u0430\u043b\u0430 \u0438 Exchange<\/a>\u00a0\u2014 \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d runtime<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/1043332\/\" rel=\"noopener noreferrer nofollow\">redb.Route 3.0.1 \u2014 \u043f\u043b\u043e\u0441\u043a\u0430\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u043f\u043e DSL, \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 CRTP \u0438 \u0442\u0438\u0445\u0438\u0439 null<\/a>\u00a0\u2014 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043f\u0430\u0442\u0447 \u043f\u0435\u0440\u0435\u0434 3.1.0<\/p>\n<\/li>\n<\/ul>\n<\/blockquote>\n<hr\/>\n<h3>\u0421\u0430\u043c\u043e\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u043e\u0431\u044a\u044f\u0441\u043d\u0435\u043d\u0438\u0435<\/h3>\n<pre><code>From(\"kafka:\/\/orders\")    .To(Llm.Factory(\"claude\").Temperature(0.2).MaxTokens(1024).AsUri())    .To(\"kafka:\/\/orders.translated\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u0430 \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u2014 \u043f\u043e\u043b\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 LLM:<\/p>\n<ul>\n<li>\n<p>\u0442\u0435\u043b\u043e \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0443\u0445\u043e\u0434\u0438\u0442 \u043a\u0430\u043a user-\u043f\u0440\u043e\u043c\u043f\u0442;<\/p>\n<\/li>\n<li>\n<p>\u0430\u0433\u0435\u043d\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043a\u0440\u0443\u0433 (\u043c\u043e\u0434\u0435\u043b\u044c \u2192 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e tool-use \u2192 \u043c\u043e\u0434\u0435\u043b\u044c \u2192 \u2026) \u0434\u043e\u00a0<code>EndTurn<\/code>\u00a0\u0438\u043b\u0438\u00a0<code>MaxIterations<\/code>;<\/p>\n<\/li>\n<li>\n<p>assistant-\u043e\u0442\u0432\u0435\u0442 \u043b\u043e\u0436\u0438\u0442\u0441\u044f \u0432\u00a0<code>exchange.Out.Body<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432, id \u043c\u043e\u0434\u0435\u043b\u0438, \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438, \u0447\u0438\u0441\u043b\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u2014 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>OpenTelemetry-\u0442\u0440\u0435\u0439\u0441\u044b \u0438 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>endpoint \u0432\u0438\u0434\u0435\u043d \u0432 dashboard&#8217;\u0435 tsak.web \u0441 messages\/sec, \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e, error rate \u0438 last-error \u2014 \u043a\u0430\u043a \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u043e \u0432\u0435\u0441\u044c \u0441\u043c\u044b\u0441\u043b \u00abLLM \u043a\u0430\u043a \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440, \u0430 \u043d\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u00bb. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c Apache Camel-\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 ESB \u0441 retry, breaker, idempotent consumer \u0438 audit, \u0442\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0442\u044c LLM \u0432 \u0435\u0449\u0451 \u043e\u0434\u0438\u043d endpoint \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0447\u0435\u0441\u0442\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0442\u0440\u0430\u0438, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0442\u0430\u0449\u0438\u0442\u044c \u00abAI-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443\u00bb \u0440\u044f\u0434\u043e\u043c \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439.<\/p>\n<hr\/>\n<h3>\u041e\u0434\u0438\u043d \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440-\u0430\u0434\u0430\u043f\u0442\u0435\u0440, 14 OpenAI-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0445 API<\/h3>\n<p>\u0412 \u043f\u0430\u043a\u0435\u0442\u0435 \u0434\u0432\u0430 production-\u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430:<\/p>\n<ul>\n<li>\n<p><code><strong>OpenAiProvider<\/strong><\/code>\u00a0\u2014 \u043e\u0434\u0438\u043d \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 \u0434\u043b\u044f 14 OpenAI-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0445 API:\u00a0<code>openai<\/code>,\u00a0<code>anthropic<\/code>\/<code>claude<\/code>\u00a0(\u0447\u0435\u0440\u0435\u0437 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 OpenAI-compat endpoint Anthropic),\u00a0<code>groq<\/code>,\u00a0<code>cerebras<\/code>,\u00a0<code>openrouter<\/code>,\u00a0<code>gemini<\/code>\u00a0(OpenAI-compat),\u00a0<code>github-models<\/code>,\u00a0<code>mistral<\/code>,\u00a0<code>together<\/code>,\u00a0<code>huggingface<\/code>,\u00a0<code>deepseek<\/code>,\u00a0<code>ollama<\/code>,\u00a0<code>lmstudio<\/code>\u00a0+ \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439\u00a0<code>custom<\/code>\u00a0\u0434\u043b\u044f self-hosted \u0448\u043b\u044e\u0437\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><code><strong>StubProvider<\/strong><\/code>\u00a0\u2014 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 echo \u0434\u043b\u044f unit-\u0442\u0435\u0441\u0442\u043e\u0432 \u0431\u0435\u0437 \u043a\u043b\u044e\u0447\u0435\u0439.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430\u0442\u0438\u0432\u043d\u044b\u0439\u00a0<code>AnthropicProvider<\/code>\u00a0(Messages API) \u2014 \u043d\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0434\u043b\u044f \u0444\u0438\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0435\u0442 \u0432 OpenAI-compat surface.<\/p>\n<p>\u0421\u043c\u0435\u043d\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u2014 \u044d\u0442\u043e \u0441\u043c\u0435\u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 DI:<\/p>\n<pre><code>services.AddLlmConnectionFactory(\"groq\", f =&gt;{    f.Provider = \"groq\";    f.ModelId  = \"llama-3.3-70b-versatile\";    f.ApiKey   = Environment.GetEnvironmentVariable(\"REDB_LLM_GROQ_KEY\");});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441\u00a0<code>provider = \"anthropic\"<\/code>\u00a0\u0438\u00a0<code>modelId = \"claude-haiku-4-5\"<\/code>\u00a0\u2014 \u0438 \u0442\u043e\u0442 \u0436\u0435\u00a0<code>.To(\"llm:\/\/...\")<\/code>\u00a0\u0443\u0436\u0435 \u0437\u0432\u043e\u043d\u0438\u0442 \u0432 Claude. DSL \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0438 \u043d\u0430 \u0441\u0438\u043c\u0432\u043e\u043b.<\/p>\n<hr\/>\n<h3>Tools \u2014 \u044d\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b<\/h3>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435:\u00a0<strong>\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0430\u0433\u0435\u043d\u0442\u0430 \u2014 \u044d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u044b\u0439\u00a0<\/strong><code><strong>RouteBuilder<\/strong><\/code><strong>-\u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u043b\u044e\u0441 \u043e\u0434\u0438\u043d DSL-\u0430\u0441\u043f\u0435\u043a\u0442\u00a0<\/strong><code><strong>.AsLlmTool(\"name\")<\/strong><\/code>. \u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u043f\u0430\u0434\u0430\u0435\u0442 \u0447\u0435\u0442\u044b\u0440\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0431\u044b \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e.<\/p>\n<pre><code>From(\"direct:tool-shell\")    .AsLlmTool(\"shell\")        .Description(\"Run a small shell command on the host. Input: {\\\"command\\\":\\\"&lt;name&gt;\\\",\\\"args\\\":[\\\"...\\\"]}.\")        .Input(\"\"\"            {              \"type\":\"object\",              \"properties\":{                \"command\":{\"type\":\"string\"},                \"args\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}              },              \"required\":[\"command\"]            }            \"\"\")        .SideEffect(ToolSideEffect.ReadOnly)        .Cost(ToolCostClass.Cheap)    .Then()    .To(ExecDsl.Run()        .AllowedCommands(\"cmd\", \"pwsh\")        .WorkingDirectory(scratchDir)        .TimeoutMs(5_000)        .MaxStdoutBytes(8_192)        .MaxStderrBytes(8_192));<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e:<\/p>\n<ol>\n<li>\n<p><strong>Tools-as-routes<\/strong>\u00a0\u2014 \u0432\u043d\u0443\u0442\u0440\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0432\u0441\u0435 30+ EIP-\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432 (Splitter, Aggregator, CircuitBreaker, Throttle, Filter, TryCatch). Tool, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0431\u0430\u0437\u0443, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u00a0<code>Transaction()<\/code>, breaker \u0438 retry, \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u043d\u0438 \u0441\u0442\u0440\u043e\u043a\u0438 runtime-\u043a\u043e\u0434\u0430 \u0440\u0443\u043a\u0430\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Tool \u0438\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430<\/strong>\u00a0\u2014 \u0431\u0435\u0440\u0451\u0448\u044c \u043b\u044e\u0431\u043e\u0439\u00a0<code>From(\"http:\/\/...\")<\/code>\u00a0\u0438\u043b\u0438\u00a0<code>From(\"sql:\/\/...\")<\/code>\u00a0\u0438 \u043d\u0430\u0432\u0435\u0448\u0438\u0432\u0430\u0435\u0448\u044c\u00a0<code>.AsLlmTool(\"name\")<\/code>. \u0423 \u043d\u0435\u0433\u043e \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u0430\u0443\u0434\u0438\u0442, \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u2014 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442.<\/p>\n<\/li>\n<li>\n<p><strong>Inventory-as-data<\/strong>\u00a0\u2014\u00a0<code>IToolDescriptorRegistry<\/code>\u00a0\u0437\u043d\u0430\u0435\u0442 \u0432\u0441\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0441 JSON-schema. \u041c\u043e\u0436\u043d\u043e \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c\u00a0<code>?tools=*<\/code>\u00a0\u0438\u043b\u0438\u00a0<code>?tools=lookup,shell<\/code>, \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u00ab\u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0433\u0435\u043d\u0442\u0430 \u0438\u0437 \u0432\u0441\u0435\u0445 read-only-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\u00bb \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>Zero bumps \u043d\u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u044b.<\/strong>\u00a0\u0410\u0441\u043f\u0435\u043a\u0442\u00a0<code>AsLlmTool()<\/code>\u00a0\u0436\u0438\u0432\u0451\u0442 \u0432\u00a0<code>redb.Route.Llm.Abstractions<\/code>. \u041b\u044e\u0431\u043e\u0439 \u0438\u0437 22 \u0434\u0440\u0443\u0433\u0438\u0445 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0435\u0433\u043e \u0431\u0435\u0437 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0432\u043e\u0438\u0445 NuGet-\u043f\u0430\u043a\u0435\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ol>\n<hr\/>\n<h3>\u0420\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440: standalone-\u0434\u0435\u043c\u043e \u00abcurl \u2192 Claude \u2192 shell \u2192 \u043e\u0442\u0432\u0435\u0442\u00bb<\/h3>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/786\/872\/cf4\/786872cf444c7698968276d2d0506149.png\" width=\"1280\" height=\"288\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/786\/872\/cf4\/786872cf444c7698968276d2d0506149.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/786\/872\/cf4\/786872cf444c7698968276d2d0506149.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u042d\u0442\u043e \u043f\u043e\u043b\u043d\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430. \u0414\u0432\u0430 \u0444\u0430\u0439\u043b\u0430 \u2014\u00a0<code>Llm.HttpShell.csproj<\/code>\u00a0\u0438\u00a0<code>Program.cs<\/code>, top-level statements, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 host-\u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0439 \u0441\u0432\u0435\u0440\u0445\u0443. \u0412\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0448\u044c \u0441\u0432\u043e\u0439 Anthropic-\u043a\u043b\u044e\u0447 \u0432 \u043e\u0434\u043d\u043e \u043c\u0435\u0441\u0442\u043e \u0438\u00a0<code>dotnet run<\/code>\u00a0\u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u0442 HTTP-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043d\u0430 5088, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c Claude Haiku \u0443\u043c\u0435\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c shell \u043d\u0430 \u0445\u043e\u0441\u0442\u0435 \u0438 \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0440\u0435\u043f\u043b\u0438\u043a.<\/p>\n<h4>csproj \u2014 \u043f\u044f\u0442\u044c NuGet-\u043f\u0430\u043a\u0435\u0442\u043e\u0432<\/h4>\n<pre><code class=\"xml\">&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;  &lt;PropertyGroup&gt;    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;    &lt;TargetFramework&gt;net9.0&lt;\/TargetFramework&gt;    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;    &lt;Nullable&gt;enable&lt;\/Nullable&gt;  &lt;\/PropertyGroup&gt;  &lt;ItemGroup&gt;    &lt;PackageReference Include=\"redb.Route\" Version=\"3.1.0\" \/&gt;    &lt;PackageReference Include=\"redb.Route.Http\" Version=\"3.1.0\" \/&gt;    &lt;PackageReference Include=\"redb.Route.Llm\" Version=\"3.1.0\" \/&gt;    &lt;PackageReference Include=\"redb.Route.Llm.Abstractions\" Version=\"3.1.0\" \/&gt;    &lt;PackageReference Include=\"redb.Route.Exec\" Version=\"3.1.0\" \/&gt;    &lt;PackageReference Include=\"Microsoft.Extensions.Logging.Console\" Version=\"9.0.0\" \/&gt;  &lt;\/ItemGroup&gt;&lt;\/Project&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>redb.Route<\/code>\u00a0\u2014 \u044f\u0434\u0440\u043e ESB,\u00a0<code>redb.Route.Http<\/code>\u00a0\u2014 HTTP-\u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442,\u00a0<code>redb.Route.Llm[.Abstractions]<\/code>\u00a0\u2014 LLM-\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440 \u0438 DSL-\u0430\u0441\u043f\u0435\u043a\u0442\u00a0<code>.AsLlmTool()<\/code>,\u00a0<code>redb.Route.Exec<\/code>\u00a0\u2014 \u0441\u043f\u0430\u0432\u043d\u0435\u0440 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0441 allowlist-\u043e\u043c \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e\u00a0<code>redb.Core<\/code>\/<code>redb.Postgres<\/code>\u00a0\u2014 \u0434\u0435\u043c\u043e \u0441\u0430\u043c\u043e\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u0435\u043d \u0438 \u0434\u0435\u0440\u0436\u0438\u0442 conversation memory \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<h4>1. \u041a\u043b\u044e\u0447<\/h4>\n<pre><code>const string ApiKey = \"&lt;your-key&gt;\";<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u043e\u0442, \u0447\u0442\u043e \u0432\u0438\u0434\u0435\u043d \u0432\u00a0<a href=\"https:\/\/console.anthropic.com\/\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/console.anthropic.com\/<\/code><\/a>. \u041f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0430 Groq \u2014 \u044d\u0442\u043e\u00a0<code>Provider = \"groq\"<\/code>,\u00a0<code>ModelId = \"llama-3.3-70b-versatile\"<\/code>\u00a0\u0438 \u043a\u043b\u044e\u0447 \u0438\u0437\u00a0<a href=\"http:\/\/console.groq.com\" rel=\"noopener noreferrer nofollow\"><code>console.groq.com<\/code><\/a>; DSL \u043d\u0438\u0436\u0435 \u043d\u0435 \u0434\u0432\u0438\u043d\u0435\u0442\u0441\u044f.<\/p>\n<h4>2. DI \u0438\u00a0RouteContext<\/h4>\n<pre><code>RouteContext ctx = null!;var services = new ServiceCollection();services.AddLogging(b =&gt; b    .AddSimpleConsole(o =&gt; { o.SingleLine = true; o.TimestampFormat = \"HH:mm:ss \"; })    .SetMinimumLevel(LogLevel.Information));services.AddSingleton&lt;IRouteContext&gt;(_ =&gt; ctx);services.AddSingleton&lt;ILogger&gt;(sp =&gt; sp.GetRequiredService&lt;ILoggerFactory&gt;().CreateLogger(\"redb.Route\"));var sp = services.BuildServiceProvider();ctx = new RouteContext(sp, contextId: \"llm-http-shell\");ctx.AddService(typeof(ILoggerFactory), sp.GetRequiredService&lt;ILoggerFactory&gt;());<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>RouteContext<\/code>\u00a0\u2014 runtime-\u0441\u043e\u0441\u0443\u0434: \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b, \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0441\u0435\u0440\u0432\u0438\u0441\u044b. \u0417\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435\u00a0<code>_ =&gt; ctx<\/code>\u00a0\u2014 \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00a0<code>IRouteContext<\/code>\u00a0\u043a\u0430\u043a DI-\u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0441\u0430\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u0437\u0434\u0430\u043d (\u043e\u043d \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u00a0<code>IServiceProvider<\/code>). \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u2014 \u043a\u043b\u0430\u0434\u0451\u043c\u00a0<code>ILoggerFactory<\/code>\u00a0\u0432 context \u0440\u0443\u043a\u0430\u043c\u0438; \u0431\u0435\u0437 \u044d\u0442\u043e\u0433\u043e\u00a0<code>.Log(...)<\/code>-\u0448\u0430\u0433\u0438 \u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u0445 \u043c\u043e\u043b\u0447\u0430 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 no-op (\u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u2014 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435).<\/p>\n<h4>3. \u0422\u0440\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430<\/h4>\n<pre><code>ctx.AddComponent(new HttpComponent { ServerManager = new SharedHttpServerManager() });ctx.AddComponent(new LlmComponent());ctx.AddComponent(new ExecComponent());<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0432 redb.Route \u2014 \u043f\u043b\u0430\u0433\u0438\u043d \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043b\u0430\u0434\u0435\u0435\u0442 URI-\u0441\u0445\u0435\u043c\u043e\u0439:\u00a0<code>http:\/\/<\/code>,\u00a0<code>llm:\/\/<\/code>,\u00a0<code>exec:\/\/<\/code>. \u041f\u0440\u043e\u0441\u0442\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f; \u043d\u0438\u0447\u0435\u0433\u043e \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0442\u0443\u0442 \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442.<\/p>\n<h4>4. Connection factory \u0434\u043b\u044f Claude Haiku<\/h4>\n<pre><code>ctx.AddToRegistry(\"haiku\", new LlmConnectionFactory{    Name        = \"haiku\",    Provider    = \"anthropic\",    ModelId     = \"claude-haiku-4-5\",    ApiKey      = ApiKey,    Temperature = 0.0,    MaxTokens   = 512});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>\"haiku\"<\/code>\u00a0\u2014 \u043b\u0435\u0439\u0431\u043b, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u043e\u0442\u043e\u043c \u0441\u043a\u0430\u0436\u0435\u0442\u00a0<code>Llm.Factory(\"haiku\")<\/code>. \u0422\u043e\u0442 \u0436\u0435 \u043f\u0440\u0438\u0451\u043c, \u0447\u0442\u043e \u0443 redb.Route \u043d\u0430 all-connectors \u0443\u0440\u043e\u0432\u043d\u0435 \u0434\u043b\u044f \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 connection-factory.<\/p>\n<h4>5. Agent engine + tool registry<\/h4>\n<pre><code>var toolRegistry = new ToolDescriptorRegistry();ctx.AddService(typeof(IToolDescriptorRegistry), toolRegistry);var producerTemplate = new ProducerTemplate(ctx);ctx.AddService(typeof(IProducerTemplate), producerTemplate);var engine = new AgentEngine(    logger:           null,    producerTemplate: producerTemplate,    observer:         new NoopAgentObserver(),    budget:           new NoopBudgetEnforcer(),    approval:         new AutoApproveGate(),    redaction:        new NoopRedactionFilter(),    shadow:           new NoopShadowRunner(),    conversation:     new InMemoryConversationStore(),    idempotency:      null,    approvalStore:    null);ctx.AddService(typeof(IAgentEngine), engine);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u0435 state-\u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u0438 \u0430\u0433\u0435\u043d\u0442\u0430 \u2014 observability, budget, approval, redaction, shadow, conversation, idempotency \u2014 \u0437\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u043c\u0438. \u0412 \u0434\u0435\u043c\u043e \u0432\u0441\u0435 Noop\/InMemory: agent-loop \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043d\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0435\u0440\u0441\u0438\u0441\u0442\u0438\u0442\u0441\u044f. \u0411\u043e\u0435\u0432\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u2014\u00a0<code>AddRedbLlmStorage()<\/code>\u00a0(\u0441\u043c. \u0441\u0435\u043a\u0446\u0438\u044e \u043d\u0438\u0436\u0435): \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442\u00a0<code>InMemoryConversationStore<\/code>\u00a0\u043d\u0430\u00a0<code>RedbConversationStore<\/code>, \u043f\u0440\u0438\u0446\u0435\u043f\u043b\u044f\u0435\u0442\u00a0<code>RedbAuditObserver<\/code>\u00a0\u0438 \u0442.\u0434.<\/p>\n<h4>6a. HTTP-\u043c\u0430\u0440\u0448\u0440\u0443\u0442<\/h4>\n<pre><code>var isWindows  = OperatingSystem.IsWindows();var allowed    = isWindows ? new[] { \"cmd\", \"pwsh\", \"powershell\" } : new[] { \"sh\", \"bash\" };var scratchDir = Path.Combine(Path.GetTempPath(), \"redb-llm-shell\");Directory.CreateDirectory(scratchDir);var systemPrompt =    \"You can run small shell commands through the 'shell' tool. \" +    $\"The host is {(isWindows ? \"Windows (use cmd \/c)\" : \"Linux (use sh -c)\")}. \" +    \"Use the tool when the user asks about the system, files, or commands; \" +    \"then summarise what you learned in one short sentence.\";ctx.AddRoutes(r =&gt;{    r.From(\"http:0.0.0.0:5088\/api\/llm\/shell?inOut=true\")        .RouteId(\"llm-http-shell\")        .ConvertBody&lt;string&gt;()        .Process(e =&gt;        {            e.In.Headers[LlmHeaders.SystemPrompt] = systemPrompt;            e.In.Headers[LlmHeaders.ConversationId] =                e.In.Headers.TryGetValue(\"X-Chat-Id\", out var v) &amp;&amp; v is string s &amp;&amp; s.Length &gt; 0                    ? s : \"default\";        })        .Log(\"[LLM-SHELL] \u25b6 chat=${header.llm.conversation.id} prompt=${body}\")        .To(LlmDsl.Factory(\"haiku\")            .Tools(\"shell\")            .ConversationFromHeader()            .MaxIterations(10)            .Temperature(0.0)            .AsUri())        .Log(\"[LLM-SHELL] \u25c0 iters=${header.llm.tool.iterations} stop=${header.llm.stop_reason} \" +             \"tokensIn=${header.llm.tokens.in} tokensOut=${header.llm.tokens.out}\")        .Log(\"[LLM-SHELL] \u25c0 reply=${body}\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043b\u043e POST&#8217;\u0430 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f user-\u043f\u0440\u043e\u043c\u043f\u0442\u043e\u043c.\u00a0<code>Process<\/code>\u00a0\u0441\u0442\u0430\u0432\u0438\u0442 system-\u043f\u0440\u043e\u043c\u043f\u0442 \u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\u00a0<code>LlmHeaders.SystemPrompt<\/code>\u00a0\u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0438\u0442 conversation id \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e\u00a0<code>X-Chat-Id<\/code>\u00a0(\u0431\u0435\u0437 \u043d\u0435\u0433\u043e \u2014\u00a0<code>default<\/code>).\u00a0<code>.Tools(\"shell\")<\/code>\u00a0\u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 LLM-specific knob: \u00ab\u0434\u0430\u0439 \u0430\u0433\u0435\u043d\u0442\u0443 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441 \u044d\u0442\u0438\u043c \u0438\u043c\u0435\u043d\u0435\u043c\u00bb.\u00a0<code>MaxIterations(10)<\/code>\u00a0\u2014 \u043f\u043e\u0442\u043e\u043b\u043e\u043a tool-loop&#8217;\u0430 (\u0438\u043d\u0430\u0447\u0435 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u043f\u0438\u043d\u0433 \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0435\u043b\u044c\u044e \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c).<\/p>\n<h4>6b. \u0421\u0430\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 \u044d\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442<\/h4>\n<pre><code>    r.From(\"direct:tool-shell\")        .AsLlmTool(\"shell\")            .Description(                \"Run a small shell command on the host. Input: \" +                \"{\\\"command\\\":\\\"&lt;name&gt;\\\",\\\"args\\\":[\\\"...\\\"]}. Output: \" +                \"{\\\"stdout\\\":\\\"...\\\",\\\"stderr\\\":\\\"...\\\",\\\"exitCode\\\":N}. \" +                $\"Allowed commands: {string.Join(\", \", allowed)}. \" +                $\"Working directory is pinned to '{scratchDir}'.\")            .Input(\"\"\"                {                  \"type\": \"object\",                  \"properties\": {                    \"command\": { \"type\": \"string\" },                    \"args\":    { \"type\": \"array\", \"items\": { \"type\": \"string\" } }                  },                  \"required\": [\"command\"]                }                \"\"\")            .SideEffect(ToolSideEffect.ReadOnly)            .Cost(ToolCostClass.Cheap)        .Then()        .Log(\"[SHELL-TOOL] \u25b6 in=${body}\")        .To(ExecDsl.Run()            .AllowedCommands(allowed)            .WorkingDirectory(scratchDir)            .TimeoutMs(5_000)            .MaxStdoutBytes(8_192)            .MaxStderrBytes(8_192))        .Log(\"[SHELL-TOOL] \u25c0 exit=${header.redbExec.ExitCode} body=${body}\");});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>Description<\/code>\u00a0+ JSON-\u0441\u0445\u0435\u043c\u0430 \u2014 \u0442\u043e, \u0447\u0442\u043e Claude \u0443\u0432\u0438\u0434\u0438\u0442 \u043a\u0430\u043a \u00ab\u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u043d\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430, \u0432\u043e\u0442 \u0435\u0451 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430\u00bb.\u00a0<code>SideEffect.ReadOnly<\/code>\u00a0\u0438\u00a0<a href=\"http:\/\/Cost.Cheap\" rel=\"noopener noreferrer nofollow\"><code>Cost.Cheap<\/code><\/a>\u00a0\u2014 \u0433\u0430\u0439\u0434\u043b\u0430\u0439\u043d\u044b \u0434\u043b\u044f governance-\u0445\u0443\u043a\u043e\u0432 (\u0431\u044e\u0434\u0436\u0435\u0442\u044b, \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0435 \u0430\u043f\u0440\u0443\u0432\u0430). \u041f\u043e\u0441\u043b\u0435\u00a0<code>.Then()<\/code>\u00a0\u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 route:\u00a0<code>Log<\/code>\u00a0\u2192\u00a0<a href=\"http:\/\/ExecDsl.Run\" rel=\"noopener noreferrer nofollow\"><code>ExecDsl.Run<\/code><\/a><code>()<\/code>\u00a0\u0441 allowlist&#8217;\u043e\u043c, \u0440\u0430\u0431\u043e\u0447\u0438\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u043c, \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c 5 \u0441\u0435\u043a \u0438 cap-\u043e\u043c \u043d\u0430 stdout\/stderr \u2192\u00a0<code>Log<\/code>. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e LLM-specific \u043a\u043e\u0434\u0430 \u043d\u0438\u0436\u0435\u00a0<code>.Then()<\/code>. \u042d\u0442\u043e \u0432\u0441\u0451 \u0435\u0449\u0451 \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u2014 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u043d\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u043d\u0443\u0442\u044c\u00a0<code>CircuitBreaker<\/code>,\u00a0<code>Throttle<\/code>,\u00a0<code>Transaction<\/code>,\u00a0<code>WireTap<\/code>-shadow, \u0447\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e \u0438\u0437 30+ EIP-\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432 redb.Route.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1fb\/22a\/415\/1fb22a41526db59a9124db88b4ca07c0.png\" width=\"1280\" height=\"590\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/1fb\/22a\/415\/1fb22a41526db59a9124db88b4ca07c0.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1fb\/22a\/415\/1fb22a41526db59a9124db88b4ca07c0.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>7. \u0421\u0442\u0430\u0440\u0442 \u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<pre><code>await ctx.Start();producerTemplate.Start();var stop = new ManualResetEventSlim();Console.CancelKeyPress += (_, e) =&gt; { e.Cancel = true; stop.Set(); };stop.Wait();await ctx.DisposeAsync();<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u0417\u0430\u043f\u0443\u0441\u043a<\/h4>\n<pre><code class=\"bash\">dotnet runcurl -d \"how much free disk space?\" http:\/\/localhost:5088\/api\/llm\/shellcurl -d \"what did you just say?\" -H \"X-Chat-Id: my-chat\" http:\/\/localhost:5088\/api\/llm\/shell<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 Claude \u0432\u0438\u0434\u0438\u0442 system-\u043f\u0440\u043e\u043c\u043f\u0442 \u00ab\u0443 \u0442\u0435\u0431\u044f \u0435\u0441\u0442\u044c\u00a0<code>shell<\/code>\u00bb, \u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e, \u0434\u0451\u0440\u0433\u0430\u0435\u0442 tool \u0441 \u0447\u0435\u043c-\u0442\u043e \u0432\u0440\u043e\u0434\u0435\u00a0<code>{\"command\":\"cmd\",\"args\":[\"\/c\",\"fsutil\",\"volume\",\"diskfree\",\"C:\"]}<\/code>, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 stdout, \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u0443. \u0412\u0442\u043e\u0440\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u0441\u00a0<code>X-Chat-Id: my-chat<\/code>\u00a0\u2014 \u0432\u0438\u0434\u0438\u0442 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043e\u0431\u043c\u0435\u043d \u0432\u00a0<code>InMemoryConversationStore<\/code>\u00a0\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435.<\/p>\n<p>\u0412 \u043b\u043e\u0433\u0430\u0445 \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0438\u0434\u043d\u043e \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c:\u00a0<code>[LLM-SHELL] \u25b6 prompt=...<\/code>\u00a0\u2192\u00a0<code>[SHELL-TOOL] \u25b6 in={\"command\":\"cmd\",...}<\/code>\u00a0\u2192\u00a0<code>[SHELL-TOOL] \u25c0 exit=0 body={\"stdout\":\"...\",\"exitCode\":0}<\/code>\u00a0\u2192\u00a0<code>[LLM-SHELL] \u25c0 iters=2 stop=end_turn tokensIn=... tokensOut=... reply=...<\/code>. \u042d\u0442\u043e \u0432\u0441\u0451 \u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0439\u00a0<code>.Log()<\/code>-\u0448\u0430\u0433 redb.Route, \u043d\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 LLM-tracing.<\/p>\n<p>Allowlist \u043a\u043e\u043c\u0430\u043d\u0434 (<code>cmd<\/code>,\u00a0<code>pwsh<\/code>,\u00a0<code>sh<\/code>,\u00a0<code>bash<\/code>) \u2014 security envelope: \u0432\u0441\u0451 \u0432\u043d\u0435 \u0441\u043f\u0438\u0441\u043a\u0430\u00a0<code>redb.Route.Exec<\/code>\u00a0\u043e\u0442\u0432\u0435\u0440\u0433\u0430\u0435\u0442 \u0435\u0449\u0451 \u0434\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<hr\/>\n<h3>redb.Route.Exec\u00a0\u2014 \u0441\u043f\u0430\u0432\u043d\u0435\u0440 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u043a\u0430\u043a 25-\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442<\/h3>\n<p>\u0412 \u0434\u0435\u043c\u043e \u0432\u044b\u0448\u0435\u00a0<a href=\"http:\/\/ExecDsl.Run\" rel=\"noopener noreferrer nofollow\"><code>ExecDsl.Run<\/code><\/a><code>()<\/code>\u00a0\u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043a\u0430\u043a \u00ab\u0431\u044d\u043a\u0435\u043d\u0434\u00a0<code>shell<\/code>-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u00bb, \u043d\u043e \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0448\u0435\u043b \u0432 3.1.0 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0441\u00a0<code>redb.Route.Llm<\/code>. \u0417\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043a\u0443\u0447\u043d\u044b\u0439, \u043d\u043e \u043f\u043e\u0432\u0441\u0435\u043c\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0431\u0435\u043b: framework \u0443\u0436\u0435 \u0443\u043c\u0435\u043b \u0432 22+ \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430 (HTTP, Kafka, SQL, \u2026), \u0430 \u0434\u043e \u0441\u0430\u043c\u043e\u0439 \u041e\u0421 \u2014 \u043d\u0435\u0442.<\/p>\n<p><strong>Producer \u2014\u00a0<\/strong><code><strong>.To(<\/strong><\/code><a href=\"http:\/\/ExecDsl.Run\" rel=\"noopener noreferrer nofollow\"><code><strong>ExecDsl.Run<\/strong><\/code><\/a><code><strong>(...))<\/strong><\/code>\u00a0\u2014 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0441\u043f\u0430\u0432\u043d. \u041a\u043e\u043c\u0430\u043d\u0434\u0443 \u0440\u0435\u0437\u043e\u043b\u0432\u0438\u0442 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435: JSON body \u2192 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438\u00a0<code>redbExec.Command<\/code>\/<code>redbExec.Args<\/code>\u00a0\u2192 URI-\u043e\u043f\u0446\u0438\u0438\u00a0<code>?command=...<\/code>. \u0422\u0435\u043b\u043e Out \u2014 JSON, \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0434\u043b\u044f LLM-tool ABI:<\/p>\n<pre><code class=\"json\">{ \"stdout\": \"...\", \"stderr\": \"...\", \"exitCode\": 0, \"timedOut\": false }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u043e\u0432\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 shell-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0432 \u0434\u0435\u043c\u043e \u2014 \u044d\u0442\u043e\u00a0<code>.To(<\/code><a href=\"http:\/\/ExecDsl.Run\" rel=\"noopener noreferrer nofollow\"><code>ExecDsl.Run<\/code><\/a><code>().AllowedCommands(...))<\/code>\u00a0\u0431\u0435\u0437 \u0435\u0434\u0438\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441\u043a\u043b\u0435\u0435\u0447\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u041c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u0438\u0441\u044b\u043b\u0430\u0435\u0442\u00a0<code>{\"command\":\"cmd\",\"args\":[...]}<\/code>, \u043f\u0440\u043e\u0434\u044c\u044e\u0441\u0435\u0440 \u043f\u0430\u0440\u0441\u0438\u0442, \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u0442, \u043e\u0442\u0434\u0430\u0451\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 JSON. \u041c\u0430\u0440\u0448\u0440\u0443\u0442\u043d\u044b\u0435 \u0444\u0438\u0447\u0438 (CircuitBreaker, Throttle, OnException, audit) \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u2014 \u044d\u0442\u043e endpoint \u043a\u0430\u043a \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439.<\/p>\n<p><strong>Consumer \u2014\u00a0<\/strong><code><strong>From(<\/strong><\/code><a href=\"http:\/\/ExecDsl.Run\" rel=\"noopener noreferrer nofollow\"><code><strong>ExecDsl.Run<\/strong><\/code><\/a><code><strong>(...).Schedule(\"5m\"))<\/strong><\/code>\u00a0\u2014 \u0442\u043e\u0442 \u0436\u0435 \u0441\u043f\u0430\u0432\u043d\u0435\u0440, \u043d\u043e \u043a\u0430\u043a \u043f\u0435\u0440\u0432\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 source-endpoint \u0441\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c \u0448\u0435\u0434\u0443\u043b\u0435\u0440\u043e\u043c. \u0411\u0435\u0437 cron, \u0431\u0435\u0437 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e worker&#8217;\u0430:<\/p>\n<pre><code>routes.From(ExecDsl.Run(\".\/scripts\/health-check.sh\")                   .Schedule(\"5m\")                   .TimeoutMs(30_000))      .Choice()        .When(e =&gt; e.In.Headers[\"redbExec.ExitCode\"]?.ToString() != \"0\")          .To(\"http:\/\/alerts.internal\/oncall\")        .Otherwise()          .To(\"kafka:\/\/metrics.healthy\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u044b\u0435 5 \u043c\u0438\u043d\u0443\u0442 \u2014 \u0437\u0430\u043f\u0443\u0441\u043a \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e exit code, \u0432 \u043e\u0434\u043d\u0443 \u0441\u0442\u043e\u0440\u043e\u043d\u0443 HTTP-\u0432\u0435\u0431\u0445\u0443\u043a \u0434\u0435\u0436\u0443\u0440\u043d\u043e\u043c\u0443, \u0432 \u0434\u0440\u0443\u0433\u0443\u044e \u2014 Kafka-\u0442\u043e\u043f\u0438\u043a.\u00a0<code>?schedule=<\/code>\u00a0\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b (<code>500ms<\/code>,\u00a0<code>30s<\/code>,\u00a0<code>5m<\/code>,\u00a0<code>1h<\/code>). \u0414\u043b\u044f cron \u2014\u00a0<code>From(\"quartz:\/\/&lt;cron&gt;\").To(<\/code><a href=\"http:\/\/ExecDsl.Run\" rel=\"noopener noreferrer nofollow\"><code>ExecDsl.Run<\/code><\/a><code>(...))<\/code>\u00a0(Quartz \u0443\u0436\u0435 \u0448\u0435\u0434\u0443\u043b\u0435\u0440, \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u043d\u0443\u0442\u0440\u0438 Exec \u043d\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430).<\/p>\n<p><strong>Security-\u043e\u0431\u0432\u044f\u0437\u043a\u0430<\/strong>\u00a0\u2014 \u0442\u043e, \u0447\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u0434\u0430\u0451\u0442 shell-as-tool \u043f\u0440\u0430\u0432\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c:<\/p>\n<ul>\n<li>\n<p><code>AllowedCommands<\/code>\u00a0\u2014 case-insensitive \u043f\u043e file-name,\u00a0<code>\/usr\/bin\/git<\/code>\u00a0\u0438\u00a0<code>git.exe<\/code>\u00a0\u043e\u0431\u0430 \u043c\u0430\u0442\u0447\u0430\u0442\u00a0<code>git<\/code>. \u041a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u043d\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u0442\u0432\u0435\u0440\u0433\u0430\u044e\u0442\u0441\u044f\u00a0<code>UnauthorizedAccessException<\/code>\u00a0\u0435\u0449\u0451 \u0434\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p><code>WorkingDirectory<\/code>\u00a0\u2014 \u043f\u0438\u043d\u0438\u0442 cwd; \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0439\u0442\u0438 \u0437\u0430 \u043d\u0435\u0451 \u0441\u0430\u043c.<\/p>\n<\/li>\n<li>\n<p><code>EnvironmentOverrides<\/code>\u00a0+\u00a0<code>ScrubEnvironment<\/code>\u00a0\u2014 \u0441\u0442\u0430\u0440\u0442 \u0441 \u043f\u0443\u0441\u0442\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u044b\u0435\u00a0<code>KEY=VALUE<\/code>.<\/p>\n<\/li>\n<li>\n<p><code>TimeoutMs<\/code>\u00a0\u2014 wall-clock kill-switch, \u0443\u0431\u0438\u0432\u0430\u0435\u0442 \u0432\u0435\u0441\u044c process tree.<\/p>\n<\/li>\n<li>\n<p><code>MaxStdoutBytes<\/code>\u00a0\/\u00a0<code>MaxStderrBytes<\/code>\u00a0\u2014 cap, \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 host \u043e\u0442 runaway-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u043e\u0447\u0435\u043c\u0443 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442, \u0430 \u043d\u0435 \u0447\u0430\u0441\u0442\u044c\u00a0<\/strong><code><strong>redb.Route.Llm<\/strong><\/code>\u00a0\u2014 \u0442\u0440\u0438 \u043f\u0440\u0438\u0447\u0438\u043d\u044b: (1) Exec \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0438 \u0431\u0435\u0437 LLM (scheduled health-probes, log rotation, deploy-glue, \u0431\u044d\u043a\u0430\u043f\u044b); (2)\u00a0<code>redb.Route.Llm<\/code>\u00a0\u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0442\u0430\u0449\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043d\u0430 \u0441\u043f\u0430\u0432\u043d \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0433\u0434\u0435 \u0430\u0433\u0435\u043d\u0442 \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e HTTP\/SQL-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b; (3) \u0442\u043e\u0442 \u0436\u0435 allowlist\/timeout\/cap-\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d \u0432 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430\u0445 \u0441 \u0442\u0435\u043c \u0436\u0435 \u043a\u043b\u0430\u0441\u0441\u043e\u043c security-\u043f\u0440\u043e\u0431\u043b\u0435\u043c.<\/p>\n<hr\/>\n<h3>From(&#171;llm:\/\/&#8230;&#187;)\u00a0\u2014 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0433\u0435\u043d\u0442-\u043a\u043e\u043d\u0441\u044c\u044e\u043c\u0435\u0440<\/h3>\n<p>\u042d\u0442\u043e \u0442\u0430 \u0444\u0438\u0447\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435\u0442 \u0432 Camel\u00a0<code>langchain4j-*<\/code>\u00a0(\u0442\u0430\u043c LLM \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0434\u044c\u044e\u0441\u0435\u0440):\u00a0<strong>LLM \u043a\u0430\u043a \u043f\u0435\u0440\u0432\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 source-endpoint<\/strong>, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0443\u0436\u0435 \u0441\u0438\u0434\u0438\u0442 \u0448\u0435\u0434\u0443\u043b\u0435\u0440.<\/p>\n<pre><code>From(\"llm:\/\/groq?schedule=5m\" +     \"&amp;systemPromptRef=#watchdog-system\" +     \"&amp;initialBodyRef=#daily-brief\" +     \"&amp;tools=*\")    .To(\"rabbitmq:\/\/alerts\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u044b\u0435 5 \u043c\u0438\u043d\u0443\u0442 \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0430\u0433\u0435\u043d\u0442\u0430 \u0441 system-\u043f\u0440\u043e\u043c\u043f\u0442\u043e\u043c \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430\u00a0<code>#watchdog-system<\/code>\u00a0\u0438 user-\u043f\u0440\u043e\u043c\u043f\u0442\u043e\u043c \u0438\u0437\u00a0<code>#daily-brief<\/code>. \u041e\u0442\u0432\u0435\u0442 \u0443\u0445\u043e\u0434\u0438\u0442 \u0432 RabbitMQ.\u00a0<code>?schedule=<\/code>\u00a0\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b (<code>500ms<\/code>,\u00a0<code>30s<\/code>,\u00a0<code>5m<\/code>,\u00a0<code>1h<\/code>); \u0434\u043b\u044f cron \u2014\u00a0<code>From(\"quartz:\/\/...\").To(\"llm:\/\/...\")<\/code>, \u0443 Quartz \u044d\u0442\u043e \u0443\u0436\u0435 \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u0430.<\/p>\n<p>\u0427\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u043b\u043e\u0436\u0438\u0442\u0441\u044f \u0432 \u044d\u0442\u0443 \u0444\u043e\u0440\u043c\u0443: watchdog-\u0430\u0433\u0435\u043d\u0442\u044b, \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u0447\u0451\u0442\u043e\u0432, self-improving \u0430\u0433\u0435\u043d\u0442\u044b \u0441 conversation memory (\u0442\u0430 \u0436\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043c\u0438).<\/p>\n<hr\/>\n<h3>#-\u043f\u0440\u043e\u043c\u043f\u0442\u044b \u2014 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0440\u0435\u0435\u0441\u0442\u0440<\/h3>\n<p>\u041f\u0440\u0435\u0444\u0438\u043a\u0441\u00a0<code>#<\/code>\u00a0\u043d\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442 URI-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 lookup. \u041b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u043c\u043f\u0442 \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u2014 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0430\u0433\u0435\u043d\u0442\u0430 \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u0438\u0442 \u0441\u0432\u0435\u0436\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u0435\u0437 \u0440\u0435\u0434\u0435\u043f\u043b\u043e\u044f:<\/p>\n<pre><code>host.Context.AddToRegistry(\"style.terse\", \"Reply in fewer than 5 words.\");r.From(\"direct:chat\")    .To(\"llm:\/\/scripted?systemPromptRef=#style.terse\")    .To(\"mock:done\");\/\/ ... \u043f\u043e\u0437\u0436\u0435, \u0434\u0440\u0443\u0433\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 ...host.Context.AddToRegistry(\"style.terse\", \"Reply in French only.\");\/\/ \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0443\u0432\u0438\u0434\u0438\u0442 \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u0435\u0437\u043e\u043b\u0432\u0438\u043d\u0433 \u0438\u0434\u0451\u0442 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0447\u0435\u0440\u0435\u0437\u00a0<code>IPromptTemplateRegistry<\/code>\u00a0(\u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u2014 \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f replay \u0432 eval-\u043f\u0440\u043e\u0433\u043e\u043d\u0430\u0445), \u043f\u043e\u0442\u043e\u043c \u0447\u0435\u0440\u0435\u0437 generic-registry \u0441 \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439. \u0411\u0435\u0437\u00a0<code>#<\/code>\u00a0\u2014 \u043b\u0438\u0442\u0435\u0440\u0430\u043b, \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043a\u0430\u043a \u0435\u0441\u0442\u044c.<\/p>\n<p>\u042d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435\u00a0<code>#name<\/code>-\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 redb.Route framework-wide \u0434\u043b\u044f connection-factory: \u0435\u0434\u0438\u043d\u0430\u044f \u043a\u043e\u043d\u0432\u0435\u043d\u0446\u0438\u044f, \u043d\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u00abpromptref-DSL\u00bb.<\/p>\n<hr\/>\n<h3>\u041f\u0430\u043c\u044f\u0442\u044c \u0430\u0433\u0435\u043d\u0442\u0430 \u2014\u00a0AddRedbLlmStorage()<\/h3>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432\u0441\u0435 state-\u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u0438 \u2014 in-memory (\u0441\u0442\u0435\u043d\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432, tool idempotency, approvals, cost ledgers, audit).\u00a0<code>AddRedbRouteLlm()<\/code>\u00a0\u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f zero-dependency \u2014 \u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 stateless-\u0430\u0433\u0435\u043d\u0442\u043e\u0432, \u043d\u043e \u0432\u0441\u0451 \u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0435.<\/p>\n<p>\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u2014 \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430:<\/p>\n<pre><code>services.AddRedbRoute(route =&gt;{    route.Services.AddRedbRouteLlm();    route.Services.AddRedbIdempotentRepository();   \/\/ \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f idempotency    route.Services.AddRedbLlmStorage();             \/\/ \u2190 REDB-\u0441\u0442\u043e\u0440\u0430\u0434\u0436\u0438 \u043d\u0430 \u0432\u0441\u0435 \u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u0438});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>AddRedbLlmStorage()<\/code>\u00a0\u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0435 \u0441\u0438\u043d\u0433\u043b\u0442\u043e\u043d\u044b:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441<\/p>\n<\/th>\n<th>\n<p align=\"left\">Default<\/p>\n<\/th>\n<th>\n<p align=\"left\">REDB store<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>IConversationStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>InMemoryConversationStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RedbConversationStore<\/code>\u00a0\u2014 \u0434\u0435\u0440\u0435\u0432\u043e, parent-id \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439, message-id \u043d\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439\u00a0<code>value_string<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>IToolIdempotencyStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>InMemoryToolIdempotencyStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RedbToolIdempotencyStore<\/code>\u00a0(\u043f\u043e\u0432\u0435\u0440\u0445\u00a0<code>IIdempotentRepository<\/code>)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>IApprovalStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>InMemoryApprovalStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RedbApprovalStore<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>ICostBudgetStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>InMemoryCostBudgetStore<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RedbCostBudgetStore<\/code>\u00a0\u2014 running totals per tenant\/window<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><code>IAgentObserver<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>NoopAgentObserver<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RedbAuditObserver<\/code>\u00a0\u2014 \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 tool-call<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>Per-row \u0431\u0438\u0437\u043d\u0435\u0441-\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043b\u0435\u0436\u0430\u0442 \u043d\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439\u00a0<code>value_string<\/code>, \u043d\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 JSON. Lookup \u2014 O(log n) \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043b\u043e\u043d\u043a\u0435, \u043d\u0435 full-scan-with-JSON-decode. Tree integrity \u0434\u043b\u044f transcripts \u2014 \u0447\u0435\u0440\u0435\u0437 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439\u00a0<code>parent_id<\/code>\u00a0(<code>IRedbService.CreateChildAsync<\/code>), \u043d\u0435 soft FK \u0432 props. \u0421\u0442\u043e\u0440\u0430\u0434\u0436\u0438 lazy-\u0441\u0438\u043d\u043a\u0430\u044e\u0442 \u0441\u0445\u0435\u043c\u0443 \u043d\u0430 \u043f\u0435\u0440\u0432\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u2014 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e migration step.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a4d\/a63\/e8c\/a4da63e8cc5980543e9a7e6bb2c55eae.png\" width=\"1280\" height=\"1153\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a4d\/a63\/e8c\/a4da63e8cc5980543e9a7e6bb2c55eae.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a4d\/a63\/e8c\/a4da63e8cc5980543e9a7e6bb2c55eae.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<hr\/>\n<h3>\u0427\u0442\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0434\u0432\u0438\u0436\u043a\u0430 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e<\/h3>\n<p>\u042d\u0442\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0437\u0430 \u00ab\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440, \u0430 \u043d\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00bb. \u0412\u0441\u0451, \u0447\u0442\u043e redb.Route \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442, \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043a LLM-\u0432\u044b\u0437\u043e\u0432\u0430\u043c \u0431\u0435\u0437 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">Concern<\/p>\n<\/th>\n<th>\n<p align=\"left\">DSL-\u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Retry \/ backoff<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RedeliveryPolicy<\/code>,\u00a0<code>OnException<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Rate limiting<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Throttle<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Resilience<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>CircuitBreaker<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Idempotency<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>IdempotentConsumer<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Compensation<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Saga<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Audit \/ shadow<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>WireTap<\/code>,\u00a0<code>Multicast<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Tracing &amp; \u043c\u0435\u0442\u0440\u0438\u043a\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>RouteActivitySource<\/code>,\u00a0<code>RouteMetrics<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041f\u0435\u0440\u0441\u0438\u0441\u0442\u0435\u043d\u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>redb<\/code>\u00a0schemes (typed object engine)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>Tool, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0434\u0438\u0442 \u0432 \u0434\u043e\u0440\u043e\u0433\u043e\u0439 API \u2014 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0448\u044c \u0432\u00a0<code>CircuitBreaker<\/code>\u00a0\u0438\u00a0<code>Throttle<\/code>. \u0425\u043e\u0447\u0435\u0448\u044c shadow-\u0437\u0430\u043f\u0443\u0441\u043a \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u043c\u043f\u0442\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441\u043e \u0441\u0442\u0430\u0440\u044b\u043c \u2014\u00a0<code>Multicast<\/code>\u00a0+\u00a0<code>WireTap<\/code>. \u042d\u0442\u043e \u043d\u0435 \u00ab\u0444\u0438\u0447\u0438 LLM-\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430\u00bb, \u044d\u0442\u043e \u0434\u0432\u0438\u0436\u043e\u043a, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 LLM \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043a\u0430\u043a endpoint.<\/p>\n<hr\/>\n<h3>\u0427\u0442\u043e live-\u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e, \u0430 \u0447\u0442\u043e \u2014 \u043d\u0435\u0442<\/h3>\n<p>\u0427\u0435\u0441\u0442\u043d\u043e \u043f\u0440\u043e \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432 (\u044d\u0442\u043e \u0432 README, \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e \u0442\u0443\u0442):<\/p>\n<ul>\n<li>\n<p><strong>Groq + Llama 3.3 70B<\/strong>\u00a0\u2014 \u0441\u0430\u043c\u044b\u0439 \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0439 free-tier \u0438\u0437 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445. \u0418\u0434\u0451\u0442 \u0441\u043e strict-\u0430\u0441\u0441\u0435\u0440\u0442\u043e\u043c (\u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u0434\u0430\u0442\u044c \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0435 \u0441\u043b\u043e\u0432\u043e) \u0432\u00a0<code>BasicChatTests<\/code>\u00a0\u0438\u00a0<code>ToolRouteTests<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>Mistral small-latest<\/strong>\u00a0\u2014 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 short-form, \u043d\u043e tool-use \u043d\u0430 free-tier \u043f\u043b\u0430\u0432\u0430\u0435\u0442; \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u0441 tools \u0430\u0441\u0441\u0435\u0440\u0442\u0438\u043c \u0444\u0438\u043b\u043e\u0441\u043e\u0444\u0441\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Gemini 2.0 Flash<\/strong>\u00a0\u2014 15 RPM \u043d\u0430 free-tier, \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Anthropic Claude<\/strong>\u00a0(Haiku 4.5 \/ Sonnet 4.6) \u2014 \u0447\u0435\u0440\u0435\u0437 OpenAI-compat endpoint, live-\u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0432\u00a0<code>ClaudeChatTests<\/code>\u00a0\u0438 \u0432\u00a0<code>redb.Route.Demo<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>OpenRouter \/ Cerebras<\/strong>\u00a0\u2014 \u043a\u0430\u0440\u043a\u0430\u0441 \u0440\u0430\u0431\u043e\u0447\u0438\u0439, \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 free-\u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u0430\u044e\u0442 rate-limit, \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u044b\u00a0<code>[EnvFact]<\/code>\u00a0\u0438 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u0431\u0435\u0437 \u043a\u043b\u044e\u0447\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>Honest skip-list, \u0447\u0442\u043e \u0435\u0449\u0451 \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043d\u043e:<\/p>\n<ul>\n<li>\n<p><strong>Embeddings \u0438 vector store<\/strong>\u00a0\u2014 Phase 2 (<code>embed:\/\/<\/code>,\u00a0<code>vector:\/\/<\/code>\u00a0\u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u044b).<\/p>\n<\/li>\n<li>\n<p><strong>RAG-\u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u044b, document loaders, web search<\/strong>\u00a0\u2014 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442 (\u0434\u043b\u044f web search \u0435\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 Tavily-tool \u0432\u00a0<a href=\"http:\/\/redb.Route.Llm.Tools\" rel=\"noopener noreferrer nofollow\"><code>redb.Route.Llm.Tools<\/code><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0439\u00a0<code>.AsLlmTool(\"web_search\")<\/code>).<\/p>\n<\/li>\n<li>\n<p><strong>Sliding-window memory shapes<\/strong>\u00a0(window-by-N-messages, window-by-K-tokens) \u2014 \u043f\u043e\u043a\u0430 \u043d\u0435 first-class. Persistent transcripts \u0447\u0435\u0440\u0435\u0437\u00a0<code>AddRedbLlmStorage()<\/code>\u00a0\u0435\u0441\u0442\u044c; windowed-shape \u043f\u043e\u0432\u0435\u0440\u0445 \u043d\u0438\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u043c\u00a0<code>Process<\/code>\u00a0+ conversation store, \u043d\u043e \u043d\u0435 \u043e\u0434\u043d\u043e\u0439 \u043e\u043f\u0446\u0438\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>Native AnthropicProvider<\/strong>\u00a0\u2014 OpenAI-compat surface \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432; \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 Messages API \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u0444\u0438\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0442\u0430\u043c \u043d\u0435\u0442.<\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<h3>\u0421\u0441\u044b\u043b\u043a\u0438<\/h3>\n<blockquote>\n<p><strong>GitHub \u0432\u043f\u0435\u0440\u0435\u0434\u0438 NuGet.<\/strong>\u00a0\u0421\u0432\u0435\u0436\u0438\u0435 \u0431\u0430\u0433-\u0444\u0438\u043a\u0441\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u0447\u0438\u043d\u043a\u0430\u00a0<code>tool_use<\/code>\/<code>tool_result<\/code>\u00a0\u043f\u0430\u0440 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0434\u0438\u0430\u043b\u043e\u0433\u0430 \u0438 \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 OEM- \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043e\u043a Windows \u0432\u00a0<code>redb.Route.Exec<\/code>) \u0443\u0436\u0435 \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432\u00a0<code>main<\/code>\u00a0\u043f\u043e\u0434 \u0432\u0435\u0440\u0441\u0438\u0435\u0439\u00a0<strong>3.1.1<\/strong>\u00a0\u2014 \u0441\u043c.\u00a0<a href=\"http:\/\/CHANGELOG.md\" rel=\"noopener noreferrer nofollow\"><code>CHANGELOG.md<\/code><\/a>\u00a0\u0432 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0439 \u0440\u0435\u043f\u0435. \u0412 NuGet \u044d\u0442\u0438 \u0444\u0438\u043a\u0441\u044b \u0432\u044b\u0445\u043e\u0434\u044f\u0442 \u043f\u0430\u0447\u043a\u0430\u043c\u0438 \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u044b\u043c \u0440\u0435\u043b\u0438\u0437\u043e\u043c, \u0442\u0430\u043a \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432 production \u0443\u043f\u0435\u0440\u043b\u0438\u0441\u044c \u0432 \u0431\u0430\u0433 \u2014 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435\u00a0<code>main<\/code>\u00a0\u0438 \u0442\u0435\u0433 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e pre-release, \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c \u0436\u0434\u0438\u0442\u0435 \u043f\u0430\u043a\u0435\u0442 \u043d\u0430 <a href=\"http:\/\/nuget.org\" rel=\"noopener noreferrer nofollow\">nuget.org<\/a>. \u042d\u0442\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430, \u043d\u0435 \u0431\u0430\u0433 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<\/blockquote>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\n<\/th>\n<th>\n<p align=\"left\">\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">redb.Route \u043d\u0430 GitHub<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\" rel=\"noopener noreferrer nofollow\">github.com\/redbase-app\/redb-route<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">redb.Route.Llm \u043d\u0430 NuGet<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/www.nuget.org\/packages\/redb.Route.Llm\" rel=\"noopener noreferrer nofollow\">nuget.org\/packages\/redb.Route.Llm<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">redb.Route.Exec \u043d\u0430 NuGet<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/www.nuget.org\/packages\/redb.Route.Exec\" rel=\"noopener noreferrer nofollow\">nuget.org\/packages\/redb.Route.Exec<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041f\u043e\u043b\u043d\u044b\u0439 README \u043f\u0430\u043a\u0435\u0442\u0430<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/blob\/main\/redb.Route.Llm\/README.md\" rel=\"noopener noreferrer nofollow\">redb.Route.Llm\/<\/a><a href=\"http:\/\/README.md\" rel=\"noopener noreferrer nofollow\">README.md<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">USER-GUIDE (\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u0433\u0430\u0439\u0434 \u043f\u043e DSL, governance, conversation)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/blob\/main\/redb.Route.Llm\/doc\/USER-GUIDE.md\" rel=\"noopener noreferrer nofollow\">redb.Route.Llm\/doc\/<\/a><a href=\"http:\/\/USER-GUIDE.md\" rel=\"noopener noreferrer nofollow\">USER-GUIDE.md<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">STORAGE \u2014 REDB-\u0441\u0445\u0435\u043c\u044b \u043f\u043e\u0434 conversation \/ approval \/ audit \/ idempotency<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/blob\/main\/redb.Route.Llm\/doc\/STORAGE.md\" rel=\"noopener noreferrer nofollow\">redb.Route.Llm\/doc\/<\/a><a href=\"http:\/\/STORAGE.md\" rel=\"noopener noreferrer nofollow\">STORAGE.md<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">README Exec-\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u0430<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/blob\/main\/redb.Route.Exec\/README.md\" rel=\"noopener noreferrer nofollow\">redb.Route.Exec\/<\/a><a href=\"http:\/\/README.md\" rel=\"noopener noreferrer nofollow\">README.md<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Standalone-\u0434\u0435\u043c\u043e\u00a0<code>Llm.HttpShell<\/code>\u00a0(curl \u2192 Claude \u2192 shell \u2192 \u043e\u0442\u0432\u0435\u0442)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/blob\/main\/demo\/Llm.HttpShell\/Program.cs\" rel=\"noopener noreferrer nofollow\">demo\/Llm.HttpShell\/Program.cs<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041f\u043e\u043b\u043d\u0430\u044f \u0434\u0435\u043c\u043e-\u0440\u0435\u043f\u0430 (22+ \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/blob\/main\/demo\/redb.Route.Demo\/Routes\/LlmHttpRoutes.cs\" rel=\"noopener noreferrer nofollow\">demo\/redb.Route.Demo\/Routes\/LlmHttpRoutes.cs<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Discussions<\/p>\n<\/td>\n<td>\n<p align=\"left\"><a href=\"https:\/\/github.com\/redbase-app\/redb-route\/discussions\" rel=\"noopener noreferrer nofollow\">github.com\/redbase-app\/redb-route\/discussions<\/a><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0412\u0441\u0451 \u043f\u043e\u0434 Apache 2.0. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u0440\u0430\u0437\u0431\u043e\u0440 tool-loop, governance-\u0445\u0443\u043a\u043e\u0432 \u0438 REDB-\u0441\u0442\u043e\u0440\u0430\u0434\u0436\u0435\u0439 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439 \u0432 \u0441\u0435\u0440\u0438\u0438. \u0412\u043e\u043f\u0440\u043e\u0441\u044b \u043f\u0440\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u2014 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445; \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a \u043f\u0438\u0448\u0443\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1045356\/\">https:\/\/habr.com\/ru\/articles\/1045356\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>redb.route llm AI\u0421\u0435\u0440\u0438\u044f: redb ecosystem (\u0430\u043d\u043e\u043d\u0441, \u0440\u0430\u0437\u0431\u043e\u0440 \u043f\u043e\u0437\u0436\u0435)\u0412 3.1.0 \u0443\u00a0redb.Route\u00a0\u0432\u044b\u0448\u043b\u043e\u00a0\u0434\u0432\u0430 \u043d\u043e\u0432\u044b\u0445 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430:\u00a0redb.Route.Llm\u00a0(24-\u0439) \u0438\u00a0redb.Route.Exec\u00a0(25-\u0439). LLM \u0442\u0435\u043f\u0435\u0440\u044c \u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 endpoint \u043d\u0430\u0440\u0430\u0432\u043d\u0435 \u0441 Kafka, RabbitMQ \u0438 HTTP: \u0432\u044b\u0437\u043e\u0432 \u043c\u043e\u0434\u0435\u043b\u0438 \u2014 \u044d\u0442\u043e \u0448\u0430\u0433\u00a0.To(&#171;llm:\/\/claude&#187;), \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0430\u0433\u0435\u043d\u0442\u0430 \u2014 \u044d\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0441\u00a0.AsLlmTool(&#171;shell&#187;), \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0433\u0435\u043d\u0442 \u2014\u00a0From(&#171;llm:\/\/factory?schedule=5m&#187;). Exec \u2014 \u0441\u043f\u0430\u0432\u043d\u0435\u0440 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0441 allowlist, working-dir \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c; \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u043a\u0430\u043a backend shell-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0430\u0433\u0435\u043d\u0442\u0430, \u0438 \u043a\u0430\u043a \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0439 scheduled consumer (cron-less health-probes, \u0431\u044d\u043a\u0430\u043f\u044b \u0438 \u0442.\u043f.). \u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u00ab\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 AI-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0440\u044f\u0434\u043e\u043c \u0441 ESB\u00bb: \u0432\u0441\u0451 \u0432\u043d\u0443\u0442\u0440\u0438 \u0442\u043e\u0439 \u0436\u0435 DSL, \u0442\u0435\u0445 \u0436\u0435 retry\/throttle\/circuit-breaker\/audit, \u0442\u0435\u0445 \u0436\u0435 OpenTelemetry-\u0442\u0440\u0435\u0439\u0441\u043e\u0432.\u042d\u0442\u043e \u0430\u043d\u043e\u043d\u0441. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u0440\u0430\u0437\u0431\u043e\u0440 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u2014 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439 \u043f\u043e\u0437\u0436\u0435. \u0417\u0434\u0435\u0441\u044c \u2014 \u0447\u0442\u043e \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c, \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432 \u043a\u043e\u0434\u0435, \u0438 \u0447\u0442\u043e \u0447\u0435\u0441\u0442\u043d\u043e \u0435\u0449\u0451 \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043d\u043e.\u0415\u0441\u043b\u0438 \u0447\u0438\u0442\u0430\u0435\u0442\u0435 \u043f\u0440\u043e\u00a0redb.Route\u00a0\u0432\u043f\u0435\u0440\u0432\u044b\u0435\u00a0\u2014 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u0435\u0439 \u0441\u0435\u0440\u0438\u0438:redb.Route \u2014 Apache Camel \u0434\u043b\u044f .NET\u00a0\u2014 \u0437\u0430\u0447\u0435\u043c \u0432\u043e\u043e\u0431\u0449\u0435, \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u00abApache Camel \u043f\u043e\u0434 .NET\u00bbredb.Route \u0438\u0437\u043d\u0443\u0442\u0440\u0438: \u0447\u0435\u0442\u044b\u0440\u0435 in-memory \u043a\u0430\u043d\u0430\u043b\u0430 \u0438 Exchange\u00a0\u2014 \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d runtimeredb.Route 3.0.1 \u2014 \u043f\u043b\u043e\u0441\u043a\u0430\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u043f\u043e DSL, \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 CRTP \u0438 \u0442\u0438\u0445\u0438\u0439 null\u00a0\u2014 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043f\u0430\u0442\u0447 \u043f\u0435\u0440\u0435\u0434 3.1.0\u0421\u0430\u043c\u043e\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u043e\u0431\u044a\u044f\u0441\u043d\u0435\u043d\u0438\u0435From(&#171;kafka:\/\/orders&#187;)    .To(Llm.Factory(&#171;claude&#187;).Temperature(0.2).MaxTokens(1024).AsUri())    .To(&#171;kafka:\/\/orders.translated&#187;);\u042d\u0442\u0430 \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u2014 \u043f\u043e\u043b\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 LLM:\u0442\u0435\u043b\u043e \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0443\u0445\u043e\u0434\u0438\u0442 \u043a\u0430\u043a user-\u043f\u0440\u043e\u043c\u043f\u0442;\u0430\u0433\u0435\u043d\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043a\u0440\u0443\u0433 (\u043c\u043e\u0434\u0435\u043b\u044c \u2192 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e tool-use \u2192 \u043c\u043e\u0434\u0435\u043b\u044c \u2192 \u2026) \u0434\u043e\u00a0EndTurn\u00a0\u0438\u043b\u0438\u00a0MaxIterations;assistant-\u043e\u0442\u0432\u0435\u0442 \u043b\u043e\u0436\u0438\u0442\u0441\u044f \u0432\u00a0exchange.Out.Body;\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432, id \u043c\u043e\u0434\u0435\u043b\u0438, \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438, \u0447\u0438\u0441\u043b\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u2014 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438;OpenTelemetry-\u0442\u0440\u0435\u0439\u0441\u044b \u0438 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438;endpoint \u0432\u0438\u0434\u0435\u043d \u0432 dashboard&#8217;\u0435 tsak.web \u0441 messages\/sec, \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e, error rate \u0438 last-error \u2014 \u043a\u0430\u043a \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440.\u042d\u0442\u043e \u0432\u0435\u0441\u044c \u0441\u043c\u044b\u0441\u043b \u00abLLM \u043a\u0430\u043a \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440, \u0430 \u043d\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u00bb. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c Apache Camel-\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 ESB \u0441 retry, breaker, idempotent consumer \u0438 audit, \u0442\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0442\u044c LLM \u0432 \u0435\u0449\u0451 \u043e\u0434\u0438\u043d endpoint \u2014 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0447\u0435\u0441\u0442\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0442\u0440\u0430\u0438, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0442\u0430\u0449\u0438\u0442\u044c \u00abAI-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443\u00bb \u0440\u044f\u0434\u043e\u043c \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439.\u041e\u0434\u0438\u043d \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440-\u0430\u0434\u0430\u043f\u0442\u0435\u0440, 14 OpenAI-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0445 API\u0412 \u043f\u0430\u043a\u0435\u0442\u0435 \u0434\u0432\u0430 production-\u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430:OpenAiProvider\u00a0\u2014 \u043e\u0434\u0438\u043d \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 \u0434\u043b\u044f 14 OpenAI-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0445 API:\u00a0openai,\u00a0anthropic\/claude\u00a0(\u0447\u0435\u0440\u0435\u0437 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 OpenAI-compat endpoint Anthropic),\u00a0groq,\u00a0cerebras,\u00a0openrouter,\u00a0gemini\u00a0(OpenAI-compat),\u00a0github-models,\u00a0mistral,\u00a0together,\u00a0huggingface,\u00a0deepseek,\u00a0ollama,\u00a0lmstudio\u00a0+ \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439\u00a0custom\u00a0\u0434\u043b\u044f self-hosted \u0448\u043b\u044e\u0437\u043e\u0432.StubProvider\u00a0\u2014 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 echo \u0434\u043b\u044f unit-\u0442\u0435\u0441\u0442\u043e\u0432 \u0431\u0435\u0437 \u043a\u043b\u044e\u0447\u0435\u0439.\u041d\u0430\u0442\u0438\u0432\u043d\u044b\u0439\u00a0AnthropicProvider\u00a0(Messages API) \u2014 \u043d\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0434\u043b\u044f \u0444\u0438\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0435\u0442 \u0432 OpenAI-compat surface.\u0421\u043c\u0435\u043d\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u2014 \u044d\u0442\u043e \u0441\u043c\u0435\u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 DI:services.AddLlmConnectionFactory(&#171;groq&#187;, f =&gt;{    f.Provider = &#171;groq&#187;;    f.ModelId  = &#171;llama-3.3-70b-versatile&#187;;    f.ApiKey   = Environment.GetEnvironmentVariable(&#171;REDB_LLM_GROQ_KEY&#187;);});\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441\u00a0provider = &#171;anthropic&#187;\u00a0\u0438\u00a0modelId = &#171;claude-haiku-4-5&#187;\u00a0\u2014 \u0438 \u0442\u043e\u0442 \u0436\u0435\u00a0.To(&#171;llm:\/\/&#8230;&#187;)\u00a0\u0443\u0436\u0435 \u0437\u0432\u043e\u043d\u0438\u0442 \u0432 Claude. DSL \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0438 \u043d\u0430 \u0441\u0438\u043c\u0432\u043e\u043b.Tools \u2014 \u044d\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435:\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0430\u0433\u0435\u043d\u0442\u0430 \u2014 \u044d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u044b\u0439\u00a0RouteBuilder-\u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u043b\u044e\u0441 \u043e\u0434\u0438\u043d DSL-\u0430\u0441\u043f\u0435\u043a\u0442\u00a0.AsLlmTool(&#171;name&#187;). \u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u043f\u0430\u0434\u0430\u0435\u0442 \u0447\u0435\u0442\u044b\u0440\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0431\u044b \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e.From(&#171;direct:tool-shell&#187;)    .AsLlmTool(&#171;shell&#187;)        .Description(&#171;Run a small shell command on the host. Input: {\\&#187;command\\&#187;:\\&#187;&lt;name&gt;\\&#187;,\\&#187;args\\&#187;:[\\&#187;&#8230;\\&#187;]}.&#187;)        .Input(&#171;&#187;&#187;            {              &#171;type&#187;:&#187;object&#187;,              &#171;properties&#187;:{                &#171;command&#187;:{&#171;type&#187;:&#187;string&#187;},                &#171;args&#187;:{&#171;type&#187;:&#187;array&#187;,&#187;items&#187;:{&#171;type&#187;:&#187;string&#187;}}              },              &#171;required&#187;:[&#171;command&#187;]            }            &#171;&#187;&#187;)        .SideEffect(ToolSideEffect.ReadOnly)        .Cost(ToolCostClass.Cheap)    .Then()    .To(ExecDsl.Run()        .AllowedCommands(&#171;cmd&#187;, &#171;pwsh&#187;)        .WorkingDirectory(scratchDir)        .TimeoutMs(5_000)        .MaxStdoutBytes(8_192)        .MaxStderrBytes(8_192));\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e:Tools-as-routes\u00a0\u2014 \u0432\u043d\u0443\u0442\u0440\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0432\u0441\u0435 30+ EIP-\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432 (Splitter, Aggregator, CircuitBreaker, Throttle, Filter, TryCatch). Tool, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0431\u0430\u0437\u0443, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u00a0Transaction(), breaker \u0438 retry, \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u043d\u0438 \u0441\u0442\u0440\u043e\u043a\u0438 runtime-\u043a\u043e\u0434\u0430 \u0440\u0443\u043a\u0430\u043c\u0438.Tool \u0438\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u00a0\u2014 \u0431\u0435\u0440\u0451\u0448\u044c \u043b\u044e\u0431\u043e\u0439\u00a0From(&#171;http:\/\/&#8230;&#187;)\u00a0\u0438\u043b\u0438\u00a0From(&#171;sql:\/\/&#8230;&#187;)\u00a0\u0438 \u043d\u0430\u0432\u0435\u0448\u0438\u0432\u0430\u0435\u0448\u044c\u00a0.AsLlmTool(&#171;name&#187;). \u0423 \u043d\u0435\u0433\u043e \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u0430\u0443\u0434\u0438\u0442, \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u2014 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442.Inventory-as-data\u00a0\u2014\u00a0IToolDescriptorRegistry\u00a0\u0437\u043d\u0430\u0435\u0442 \u0432\u0441\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0441 JSON-schema. \u041c\u043e\u0436\u043d\u043e \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c\u00a0?tools=*\u00a0\u0438\u043b\u0438\u00a0?tools=lookup,shell, \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u00ab\u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0433\u0435\u043d\u0442\u0430 \u0438\u0437 \u0432\u0441\u0435\u0445 read-only-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\u00bb \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439.Zero bumps \u043d\u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u044b.\u00a0\u0410\u0441\u043f\u0435\u043a\u0442\u00a0AsLlmTool()\u00a0\u0436\u0438\u0432\u0451\u0442 \u0432\u00a0redb.Route.Llm.Abstractions. \u041b\u044e\u0431\u043e\u0439 \u0438\u0437 22 \u0434\u0440\u0443\u0433\u0438\u0445 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0435\u0433\u043e \u0431\u0435\u0437 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0432\u043e\u0438\u0445 NuGet-\u043f\u0430\u043a\u0435\u0442\u043e\u0432.\u0420\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440: standalone-\u0434\u0435\u043c\u043e \u00abcurl \u2192 Claude \u2192 shell \u2192 \u043e\u0442\u0432\u0435\u0442\u00bb\u042d\u0442\u043e \u043f\u043e\u043b\u043d\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430. \u0414\u0432\u0430 \u0444\u0430\u0439\u043b\u0430 \u2014\u00a0Llm.HttpShell.csproj\u00a0\u0438\u00a0Program.cs, top-level statements, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 host-\u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0439 \u0441\u0432\u0435\u0440\u0445\u0443. \u0412\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0448\u044c \u0441\u0432\u043e\u0439 Anthropic-\u043a\u043b\u044e\u0447 \u0432 \u043e\u0434\u043d\u043e \u043c\u0435\u0441\u0442\u043e \u0438\u00a0dotnet run\u00a0\u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u0442 HTTP-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043d\u0430 5088, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c Claude Haiku \u0443\u043c\u0435\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c shell \u043d\u0430 \u0445\u043e\u0441\u0442\u0435 \u0438 \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0440\u0435\u043f\u043b\u0438\u043a.csproj \u2014 \u043f\u044f\u0442\u044c NuGet-\u043f\u0430\u043a\u0435\u0442\u043e\u0432&lt;Project Sdk=&#187;Microsoft.NET.Sdk&#187;&gt;  &lt;PropertyGroup&gt;    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;    &lt;TargetFramework&gt;net9.0&lt;\/TargetFramework&gt;    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;    &lt;Nullable&gt;enable&lt;\/Nullable&gt;  &lt;\/PropertyGroup&gt;  &lt;ItemGroup&gt;    &lt;PackageReference Include=&#187;redb.Route&#187; Version=&#187;3.1.0&#8243; \/&gt;    &lt;PackageReference Include=&#187;redb.Route.Http&#187; Version=&#187;3.1.0&#8243; \/&gt;    &lt;PackageReference Include=&#187;redb.Route.Llm&#187; Version=&#187;3.1.0&#8243; \/&gt;    &lt;PackageReference Include=&#187;redb.Route.Llm.Abstractions&#187; Version=&#187;3.1.0&#8243; \/&gt;    &lt;PackageReference Include=&#187;redb.Route.Exec&#187; Version=&#187;3.1.0&#8243; \/&gt;    &lt;PackageReference Include=&#187;Microsoft.Extensions.Logging.Console&#187; Version=&#187;9.0.0&#8243; \/&gt;  &lt;\/ItemGroup&gt;&lt;\/Project&gt;redb.Route\u00a0\u2014 \u044f\u0434\u0440\u043e ESB,\u00a0redb.Route.Http\u00a0\u2014 HTTP-\u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442,\u00a0redb.Route.Llm[.Abstractions]\u00a0\u2014 LLM-\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440 \u0438 DSL-\u0430\u0441\u043f\u0435\u043a\u0442\u00a0.AsLlmTool(),\u00a0redb.Route.Exec\u00a0\u2014 \u0441\u043f\u0430\u0432\u043d\u0435\u0440 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0441 allowlist-\u043e\u043c \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e\u00a0redb.Core\/redb.Postgres\u00a0\u2014 \u0434\u0435\u043c\u043e \u0441\u0430\u043c\u043e\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u0435\u043d \u0438 \u0434\u0435\u0440\u0436\u0438\u0442 conversation memory \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.1. \u041a\u043b\u044e\u0447const string ApiKey = &#171;&lt;your-key&gt;&#187;;\u0422\u043e\u0442, \u0447\u0442\u043e \u0432\u0438\u0434\u0435\u043d \u0432\u00a0https:\/\/console.anthropic.com\/. \u041f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0430 Groq \u2014 \u044d\u0442\u043e\u00a0Provider = &#171;groq&#187;,\u00a0ModelId = &#171;llama-3.3-70b-versatile&#187;\u00a0\u0438 \u043a\u043b\u044e\u0447 \u0438\u0437\u00a0console.groq.com; DSL \u043d\u0438\u0436\u0435 \u043d\u0435 \u0434\u0432\u0438\u043d\u0435\u0442\u0441\u044f.2. DI \u0438\u00a0RouteContextRouteContext ctx = null!;var services = new ServiceCollection();services.AddLogging(b =&gt; b    .AddSimpleConsole(o =&gt; { o.SingleLine = true; o.TimestampFormat = &#171;HH:mm:ss &#171;; })    .SetMinimumLevel(LogLevel.Information));services.AddSingleton&lt;IRouteContext&gt;(_ =&gt; ctx);services.AddSingleton&lt;ILogger&gt;(sp =&gt; sp.GetRequiredService&lt;ILoggerFactory&gt;().CreateLogger(&#171;redb.Route&#187;));var sp = services.BuildServiceProvider();ctx = new RouteContext(sp, contextId: &#171;llm-http-shell&#187;);ctx.AddService(typeof(ILoggerFactory), sp.GetRequiredService&lt;ILoggerFactory&gt;());RouteContext\u00a0\u2014 runtime-\u0441\u043e\u0441\u0443\u0434: \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b, \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0441\u0435\u0440\u0432\u0438\u0441\u044b. \u0417\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435\u00a0_ =&gt; ctx\u00a0\u2014 \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00a0IRouteContext\u00a0\u043a\u0430\u043a DI-\u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0441\u0430\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u0437\u0434\u0430\u043d (\u043e\u043d \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u00a0IServiceProvider). \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u2014 \u043a\u043b\u0430\u0434\u0451\u043c\u00a0ILoggerFactory\u00a0\u0432 context \u0440\u0443\u043a\u0430\u043c\u0438; \u0431\u0435\u0437 \u044d\u0442\u043e\u0433\u043e\u00a0.Log(&#8230;)-\u0448\u0430\u0433\u0438 \u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u0445 \u043c\u043e\u043b\u0447\u0430 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 no-op (\u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u2014 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435).3. \u0422\u0440\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430ctx.AddComponent(new HttpComponent { ServerManager = new SharedHttpServerManager() });ctx.AddComponent(new LlmComponent());ctx.AddComponent(new ExecComponent());\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0432 redb.Route \u2014 \u043f\u043b\u0430\u0433\u0438\u043d \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043b\u0430\u0434\u0435\u0435\u0442 URI-\u0441\u0445\u0435\u043c\u043e\u0439:\u00a0http:\/\/,\u00a0llm:\/\/,\u00a0exec:\/\/. \u041f\u0440\u043e\u0441\u0442\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f; \u043d\u0438\u0447\u0435\u0433\u043e \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0442\u0443\u0442 \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442.4. Connection factory \u0434\u043b\u044f Claude Haikuctx.AddToRegistry(&#171;haiku&#187;, new LlmConnectionFactory{    Name        = &#171;haiku&#187;,    Provider    = &#171;anthropic&#187;,    ModelId     = &#171;claude-haiku-4-5&#8243;,    ApiKey      = ApiKey,    Temperature = 0.0,    MaxTokens   = 512});&#187;haiku&#187;\u00a0\u2014 \u043b\u0435\u0439\u0431\u043b, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u043e\u0442\u043e\u043c \u0441\u043a\u0430\u0436\u0435\u0442\u00a0Llm.Factory(&#171;haiku&#187;). \u0422\u043e\u0442 \u0436\u0435 \u043f\u0440\u0438\u0451\u043c, \u0447\u0442\u043e \u0443 redb.Route \u043d\u0430 all-connectors \u0443\u0440\u043e\u0432\u043d\u0435 \u0434\u043b\u044f \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 connection-factory.5. Agent engine + tool registryvar toolRegistry = new ToolDescriptorRegistry();ctx.AddService(typeof(IToolDescriptorRegistry), toolRegistry);var producerTemplate = new ProducerTemplate(ctx);ctx.AddService(typeof(IProducerTemplate), producerTemplate);var engine = new AgentEngine(    logger:           null,    producerTemplate: producerTemplate,    observer:         new NoopAgentObserver(),    budget:           new NoopBudgetEnforcer(),    approval:         new AutoApproveGate(),    redaction:        new NoopRedactionFilter(),    shadow:           new NoopShadowRunner(),    conversation:     new InMemoryConversationStore(),    idempotency:      null,    approvalStore:    null);ctx.AddService(typeof(IAgentEngine), engine);\u0412\u0441\u0435 state-\u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u0438 \u0430\u0433\u0435\u043d\u0442\u0430 \u2014 observability, budget, approval, redaction, shadow, conversation, idempotency \u2014 \u0437\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u043c\u0438. \u0412 \u0434\u0435\u043c\u043e \u0432\u0441\u0435 Noop\/InMemory: agent-loop \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043d\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0435\u0440\u0441\u0438\u0441\u0442\u0438\u0442\u0441\u044f. \u0411\u043e\u0435\u0432\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u2014\u00a0AddRedbLlmStorage()\u00a0(\u0441\u043c. \u0441\u0435\u043a\u0446\u0438\u044e \u043d\u0438\u0436\u0435): \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442\u00a0InMemoryConversationStore\u00a0\u043d\u0430\u00a0RedbConversationStore, \u043f\u0440\u0438\u0446\u0435\u043f\u043b\u044f\u0435\u0442\u00a0RedbAuditObserver\u00a0\u0438 \u0442.\u0434.6a. HTTP-\u043c\u0430\u0440\u0448\u0440\u0443\u0442var isWindows  = OperatingSystem.IsWindows();var allowed    = isWindows ? new[] { &#171;cmd&#187;, &#171;pwsh&#187;, &#171;powershell&#187; } : new[] { &#171;sh&#187;, &#171;bash&#187; };var scratchDir = Path.Combine(Path.GetTempPath(), &#171;redb-llm-shell&#187;);Directory.CreateDirectory(scratchDir);var systemPrompt =    &#171;You can run small shell commands through the &#8216;shell&#8217; tool. &#187; +    $&#187;The host is {(isWindows ? &#171;Windows (use cmd \/c)&#187; : &#171;Linux (use sh -c)&#187;)}. &#187; +    &#171;Use the tool when the user asks about the system, files, or&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-482945","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/482945","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=482945"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/482945\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=482945"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=482945"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=482945"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}