AGUI: удалён полностью
Убрана зависимость pw.binom.agui:server из проекта — AGUI больше не нужен (см. Memory #3675; agentik переходит на свой протокол :proto и HTTP-фасад :server). Чистка: - standalone/build.gradle.kts: implementation(libs.agui.server) → удалено - gradle/libs.versions.toml: [versions] agui + agui-api/agui-client/ agui-server entries → удалены - settings.gradle.kts: убран 'AG-UI' из комментария к Nexus-репо - proto/build.gradle.kts: комментарий 'Зеркалит набор AG-UI api' → 'Полный набор KMP-целей' - proto/src/.../Agent.kt: KDoc '(замена AG-UI)' → удалено - settings.gradle.kts: то же - docs/ARCHITECTURE.md: переписан (описывал старую AGUI-centric архитектуру с AbstractAgent/SessionStore/ AgentEngine — ничего этого в коде уже нет; теперь отражает текущее состояние: :proto + :server + :standalone + LiteLlm + MCP + SqliteStores) - NATIVE-COMPATIBILITY.md: пункт 10 'agui-server KMP-готовность' → отменён (см. -); убран из 'Жёсткие блокеры' В коде не осталось ни одного обращения к AGUI. После чистки в репо больше нет ни одной зависимости от pw.binom.agui.*. Проверка: ./gradlew :server:assemble (9/9 KMP-целей) + :standalone:jvmTest 44/44 (ChatAgent 15, LlmConfig 6, McpConfig 8, McpRegistry 4, Persistence 11) — зелёные.
This commit is contained in:
@@ -85,10 +85,13 @@
|
|||||||
JDBC `:memory:`). Объём: один `expect fun openInMemory()` в commonMain +
|
JDBC `:memory:`). Объём: один `expect fun openInMemory()` в commonMain +
|
||||||
два `actual` (JDBC `jdbc:sqlite::memory:` / native-driver нативный API).
|
два `actual` (JDBC `jdbc:sqlite::memory:` / native-driver нативный API).
|
||||||
|
|
||||||
## Блок 6. Внешние либы (`a2a-server`, `agui-server`) — наши
|
## Блок 6. Внешние либы (`a2a-server`) — наши
|
||||||
|
|
||||||
- 10. [ ] **`agui-server`: проверить KMP-готовность.** Per Memory #3561 — KMP
|
- 10. [x] **AGUI убран из проекта.** catalog-entries `agui-api`/`agui-client`/`agui-server`,
|
||||||
jvm+native. *Если по факту уже KMP — только подключить `linuxX64`-артефакт в `:standalone`; если JVM-only — пункт 11.*
|
`[versions] agui` и `implementation(libs.agui.server)` в `:standalone`
|
||||||
|
удалены — AGUI больше не нужен (см. Memory #3675 и переписанный
|
||||||
|
`docs/ARCHITECTURE.md`). AGUI-фасад был stateless request/response,
|
||||||
|
а agentik переходит на свой stateful протокол `:proto` / `:server`.
|
||||||
- 11. [ ] **`a2a-server`: JVM-only → KMP.** Per Memory #3561 — JVM-only
|
- 11. [ ] **`a2a-server`: JVM-only → KMP.** Per Memory #3561 — JVM-only
|
||||||
(client/server; shared уже KMP). Без native-таргета весь стек A2A
|
(client/server; shared уже KMP). Без native-таргета весь стек A2A
|
||||||
недоступен на linuxX64. Объём: переписать на `multiplatform`,
|
недоступен на linuxX64. Объём: переписать на `multiplatform`,
|
||||||
@@ -150,4 +153,3 @@
|
|||||||
|
|
||||||
- `litertlm-jvm` → нужен `litertlm-native` от Google (см. п.5)
|
- `litertlm-jvm` → нужен `litertlm-native` от Google (см. п.5)
|
||||||
- `a2a-server` JVM-only (см. п.11) — нужно переписать или явно не подключать
|
- `a2a-server` JVM-only (см. п.11) — нужно переписать или явно не подключать
|
||||||
- `agui-server` KMP-готовность под вопросом (см. п.10)
|
|
||||||
|
|||||||
+154
-102
@@ -1,113 +1,165 @@
|
|||||||
# Agentik — архитектура (на основе ограничений AGUI)
|
# Agentik — архитектура
|
||||||
|
|
||||||
|
> Полевые заметки о структуре проекта на текущий момент.
|
||||||
|
> Подробности запуска — `docs/STANDALONE.md`, чек-лист native-сборки —
|
||||||
|
> `NATIVE-COMPATIBILITY.md`, открытые вопросы — `IRC-QUESTIONS.md`.
|
||||||
|
|
||||||
## 1. Контекст
|
## 1. Контекст
|
||||||
|
|
||||||
agentik — runtime агента. Внешне он exposes два фасада:
|
agentik — runtime агента. Ядро на собственном протоколе (`:proto`),
|
||||||
- **AG-UI** — SSE-поток событий для UI-клиентов (`POST /agui`), транспорт Netty.
|
HTTP/SSE-фасад в `:server`, runtime-контейнер в `:standalone`. Раньше
|
||||||
- **A2A** — JSON-RPC для agent↔agent (`POST /`, `message/send`), транспорт CIO.
|
фасадов было два (AG-UI + A2A); AG-UI убран как устаревший,
|
||||||
|
`a2a-server` остаётся заготовкой в `libs.versions.toml` и подключён в
|
||||||
|
`:standalone` `build.gradle.kts`, но в `Main.kt` пока не монтируется.
|
||||||
|
|
||||||
Ядро (сессии, чат-цикл, тулы, LLM, MCP) **не зависит от транспорта**: фасады лишь сериализуют/десериализуют его. Библиотеки из каталога `caffeine`: `agui` 0.1.0, `a2a` 1.0.0-SNAPSHOT; LLM — `pw.binom.openai` (ktor-impl).
|
LLM — за интерфейсом `pw.binom.litert.LiteLlm`. Реализации:
|
||||||
|
`pw.binom.litert.openai` (HTTP/JSON поверх OpenAI-API, любая
|
||||||
|
совместимая endpoint) и `pw.binom.litert.google` (встроенный
|
||||||
|
LiteRT-LM движок для `.litertlm`/`.task` моделей).
|
||||||
|
|
||||||
## 2. Ограничения AGUI (что диктует протокол)
|
MCP — `io.modelcontextprotocol:kotlin-sdk-client` (KMP). Подключаются
|
||||||
|
stdio + streamable-HTTP серверы, тулы оборачиваются в `LiteTool`.
|
||||||
|
|
||||||
AGUI — push-протокол. Агент = `Agent { RunAgentInput -> Flow<BaseEvent> }`.
|
## 2. Модули
|
||||||
|
|
||||||
Ключевые типы (из `pw.binom.agui.api`):
|
```
|
||||||
|
agentik
|
||||||
|
├─ :proto KMP jvm + native. Интерфейсы Agent/Conversation, Content,
|
||||||
|
│ Message, Event, AgentEvent. @SerialName
|
||||||
|
│ дискриминаторы snake_case на проводе.
|
||||||
|
├─ :server KMP jvm + native. HTTP/SSE фасад `Route.agentikAgent(agent,
|
||||||
|
│ path = "/agentik")`. Json DTO — свой
|
||||||
|
│ Snapshot-тип, чтобы протокол оставался
|
||||||
|
│ сериализационно-чистым.
|
||||||
|
├─ :client JVM-only (пока). Ktor-клиент к фасаду `:server`,
|
||||||
|
│ подключающий удалённый агент как
|
||||||
|
│ локальный `Agent`.
|
||||||
|
└─ :standalone KMP jvm (linuxX64 — в работе). Runnable-контейнер:
|
||||||
|
SqliteStores + ChatAgent + MCP + LiteLlm,
|
||||||
|
поднимает Ktor (CIO) на AGENTIK_PORT.
|
||||||
|
```
|
||||||
|
|
||||||
| Тип | Смысл |
|
Каталог версий — `gradle/libs.versions.toml`. Из внешних — только
|
||||||
|
`pw.binom.litert.*`, `pw.binom.a2a.*`, `app.cash.sqldelight`,
|
||||||
|
`io.ktor:*`, `io.modelcontextprotocol:kotlin-sdk-client`,
|
||||||
|
`org.jetbrains.kotlinx:*`.
|
||||||
|
|
||||||
|
## 3. `:proto` — интерфейсы
|
||||||
|
|
||||||
|
`Agent` (см. `proto/src/commonMain/.../Agent.kt`):
|
||||||
|
- `id: String`
|
||||||
|
- `createConversation(temp: Boolean): Conversation`
|
||||||
|
- `suspend getConversation(id): Conversation?`
|
||||||
|
- `suspend getConversations(offset, limit): List<Conversation>`
|
||||||
|
- `getConversations(offset = 0): Flow<Conversation>` — cold-flow paging через suspend-версию, `PAGE_SIZE = 100`.
|
||||||
|
- `events(after: Instant): Flow<AgentEvent>` — replay-free, бэкфилл через snapshot.
|
||||||
|
- `deleteConversation(id): Boolean`
|
||||||
|
|
||||||
|
`Conversation`:
|
||||||
|
- `isSupportImageInput / Output / isTemporal: Boolean`
|
||||||
|
- `updatedAt: Instant`
|
||||||
|
- `send(content: List<Content>)` — write-only, ничего не возвращает.
|
||||||
|
- `interrupt()` — отмена активного хода.
|
||||||
|
- `events(after): Flow<Event>` — live, replay-free.
|
||||||
|
- `getMessages(after, offset, limit)` + `getMessages(after): Flow<Message>` — paging.
|
||||||
|
- `rename(title)` — мутация, бампит `updatedAt`.
|
||||||
|
- `AutoCloseable` — `close()` идемпотентен.
|
||||||
|
|
||||||
|
`Content = Text(body) | Image(data, mime)`.
|
||||||
|
`Message = UserMessage | AssistantMessage | ToolCall | ToolResult | Summary | System`.
|
||||||
|
`Event = StartReasoning | StartResponse | End | AppendText | AppendImage | ToolCall | ToolResult | Error`.
|
||||||
|
`AgentEvent = Created(conversationId) | Deleted(id) | Renamed(id, title)`.
|
||||||
|
|
||||||
|
Принцип: **агент — источник истины** для транскрипта и сессий. Клиент
|
||||||
|
лишь рендерит Event-stream и кэширует историю.
|
||||||
|
|
||||||
|
## 4. `:standalone` — runtime-контейнер
|
||||||
|
|
||||||
|
Точка входа `pw.binom.agentik.standalone.MainKt`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Main.kt
|
||||||
|
├─ LlmConfig.fromEnv() → LlmConfig(backend, openai, google, systemPrompt)
|
||||||
|
├─ llmConfig.createLlm() → LiteLlm (рефлексия для google)
|
||||||
|
├─ SqliteStores.open(dbPath) → ConversationStore + MessageStore + WorkingMemoryStore
|
||||||
|
├─ McpRegistry.fromConfig(...) → список NamedTool из всех MCP-серверов
|
||||||
|
├─ ChatAgent(stores, llm, llmConfig, tools)
|
||||||
|
└─ embeddedServer(CIO, port) { agentikAgent(agent, "/agentik") }
|
||||||
|
```
|
||||||
|
|
||||||
|
**`ChatAgent`** — реализация `Agent`:
|
||||||
|
- `live: Map<String, ChatConversation>` — in-memory кэш активных сессий.
|
||||||
|
- `createConversation(temp)`: для temp — только кэш; для persistent —
|
||||||
|
`upsert(conversation)` + `workingMemory.append(System(systemPrompt))`
|
||||||
|
атомарно, потом `live[id] = ChatConversation(...)`.
|
||||||
|
- `getConversation(id)`: кэш → store (cold-load). Переоткрытие восстанавливает
|
||||||
|
`LiteConversation` из `workingMemory.list(id)` (см. ниже).
|
||||||
|
- `send`/`events`/`interrupt`/`delete`/`rename` проксируются в `ChatConversation`.
|
||||||
|
|
||||||
|
**`ChatConversation`** — реализация `Conversation`:
|
||||||
|
- На каждый ход: `workingMemory.append(User)` → `liteConv.sendStreamContents(...)`
|
||||||
|
→ стрим `LiteDelta` → клиенту (AppendText / ToolCall / ToolResult).
|
||||||
|
- На завершение стрима: `messageStore.append(Assistant)` + `workingMemory.append(Assistant)`
|
||||||
|
+ `conversationStore.touch(id)`.
|
||||||
|
- Tool-loop: на `delta.toolCalls` → emit `Event.ToolCall` → execute
|
||||||
|
(`tool.invoke(argsJson)`) → emit `Event.ToolResult` →
|
||||||
|
`liteConv.addToolResult(callId, name, result)` → продолжение стрима.
|
||||||
|
- `LiteConversation` живёт **один на весь `ChatConversation`** для обоих
|
||||||
|
бэкендов: KV-cache движка сохраняется между ходами. Это контракт
|
||||||
|
litert-api, не только Google-specific.
|
||||||
|
- На `interrupt()` отменяется текущий `Job` и `liteConv.interrupt()`.
|
||||||
|
|
||||||
|
**`WorkingMemory`** — read-write контекст, который видит LLM:
|
||||||
|
- `System(text, sourceMessageId=null)` — системный промпт.
|
||||||
|
- `User/Assistant(sourceMessageId, content)` — снимки реальных сообщений.
|
||||||
|
- `compact(dropFromOrderIdx, conversationId)` — v1: DELETE rows ≥ order_idx,
|
||||||
|
summarization-вставка отложена (нужен дизайн-проработка).
|
||||||
|
|
||||||
|
**`MessageStore`** — append-only аудит. На каждый ход дописываются
|
||||||
|
`UserMessage`, `AssistantMessage`, `ToolCall`, `ToolResult`. Никаких
|
||||||
|
update/delete кроме каскада из `ConversationStore.delete`.
|
||||||
|
|
||||||
|
## 5. Слои персистентности
|
||||||
|
|
||||||
|
`SqliteStores` (jvmMain) — три интерфейса из commonMain, одна
|
||||||
|
SQLite-БД. Схема (`jvmMain/sqldelight/`):
|
||||||
|
|
||||||
|
| таблица | поля | роль |
|
||||||
|
|---|---|---|
|
||||||
|
| `conversation` | id, title, is_temporal, created_at, updated_at | карточки диалогов |
|
||||||
|
| `message` | id, conversation_id, role, payload_json, created_at | аудит-хвост |
|
||||||
|
| `working_memory` | id, conversation_id, order_idx, source_message_id, payload_json, created_at | контекст LLM |
|
||||||
|
|
||||||
|
`Content` (text/image) и per-kind payload сериализуются в
|
||||||
|
`payload_json` через `kotlinx.serialization`. Maps в каскадное удаление
|
||||||
|
(`ConversationStore.delete` — одна транзакция).
|
||||||
|
|
||||||
|
## 6. Фасады
|
||||||
|
|
||||||
|
- **`:server` (HTTP+SSE)** — основной, KMP, Ktor Route extension.
|
||||||
|
`Route.agentikAgent(agent, path = "/agentik")` монтирует весь CRUD +
|
||||||
|
live event-stream. Подробнее — `docs/STANDALONE.md`.
|
||||||
|
- **A2A (`pw.binom.a2a:server`)** — заготовка в `libs.versions.toml`,
|
||||||
|
подключён в `:standalone` для совместимости с зависимостями через
|
||||||
|
`:client`, но **не монтируется в `Main.kt`**. Если/когда понадобится —
|
||||||
|
`Route.a2aAgent(...)` (как у agui в старом дизайне).
|
||||||
|
|
||||||
|
## 7. Конфигурация (env)
|
||||||
|
|
||||||
|
| env | назначение |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `RunAgentInput { threadId, runId, state, messages, tools, context, forwardedProps }` | весь ввод хода. Клиент шлёт всё, сервер по умолчанию stateless. |
|
| `AGENTIK_PORT` | порт Ktor (default `8080`) |
|
||||||
| `threadId` | **сессия/конверсация**. |
|
| `AGENTIK_DB_PATH` | путь к SQLite (default `./agentik.db`) |
|
||||||
| `runId` | **один ход** в рамках сессии. |
|
| `AGENTIK_SYSTEM_PROMPT` | текст системного промпта |
|
||||||
| `Message { id, role: developer\|system\|assistant\|user\|tool, content, toolCalls?, toolCallId? }` | сообщение. У assistant — `toolCalls`; у tool — `toolCallId`. |
|
| `AGENTIK_LLM_BACKEND` | `openai` (default) или `google` |
|
||||||
| `Tool { name, description, parameters: JSONSchema }` | тул (JSON Schema в `parameters`). |
|
| `OPENAI_BASE_URL` / `OPENAI_API_KEY` / `OPENAI_MODEL` | для backend=openai |
|
||||||
| `State = Map<String, JsonElement>` | нестрогий state хода (snapshot/delta). |
|
| `AGENTIK_GOOGLE_MODEL_PATH` / `AGENTIK_GOOGLE_THREADS` | для backend=google |
|
||||||
| `Context { description, value }` | кусок контекста. |
|
| `AGENTIK_MCP_CONFIG` | путь к `mcp.json` в формате Claude Desktop |
|
||||||
|
|
||||||
События (`BaseEvent`, дискриминатор `type`):
|
## 8. Что осталось за рамками v1
|
||||||
- lifecycle: `RUN_STARTED{threadId,runId}` → … → `RUN_FINISHED` / `RUN_ERROR{message,code}`
|
|
||||||
- steps: `STEP_STARTED/FINISHED{stepName}`
|
|
||||||
- text: `TEXT_MESSAGE_START{messageId,role}` → `TEXT_MESSAGE_CONTENT{messageId,delta}` → `TEXT_MESSAGE_END{messageId}`
|
|
||||||
- tool: `TOOL_CALL_START{toolCallId,toolCallName,parentMessageId?}` → `TOOL_CALL_ARGS{toolCallId,delta}` → `TOOL_CALL_END{toolCallId}` → `TOOL_CALL_RESULT{toolCallId,content}`
|
|
||||||
- state: `STATE_SNAPSHOT{snapshot}` / `STATE_DELTA{delta}` / `MESSAGES_SNAPSHOT{messages}`
|
|
||||||
- прочее: `RAW{event}` / `CUSTOM{name,value}`
|
|
||||||
|
|
||||||
Свободные варианты на стороне либы:
|
- Суммаризация `WorkingMemory.compact()` (пункт `compact(dropFromOrderIdx)` пуст).
|
||||||
- stateless: `Agent { input -> flow }` (использует `input.messages/state/tools`).
|
- Image input (Content.Image принимается, но `LiteContentPart.Image`
|
||||||
- stateful: `AbstractAgent(agentId, threadId)` — держит `state` + `messages`, переопределяешь `runAgent(RunAgentParameters)`.
|
сейчас дропается в Chat-цикле с warn-логом — модель видит только текст).
|
||||||
|
- Auth на фасаде.
|
||||||
Сервер (`aguiAgent(agent, path="/agui")`) не хранит ничего между запросами: декодирует `RunAgentInput`, вызывает `agent.run`, стримит события, оборачивает сбой в `RUN_ERROR`.
|
- A2A transport facade в `Main.kt`.
|
||||||
|
|
||||||
## 3. Как вписываемся (принципы)
|
|
||||||
|
|
||||||
1. **`threadId` = наша сессия.** Держим `SessionStore[threadId]` на сервере даже при stateless-сервере AGUI: на каждом ходе реконсилируем `input.messages` с сохранёнными и дописываем новые. Это даёт «настоящие сессии» с историей.
|
|
||||||
2. **`runId` = ход.** Один `RunAgentInput` → один цикл ядра → один поток событий.
|
|
||||||
3. **Ядро говорит на AGUI-событиях** — это и есть наш протокол вывода; фасады не переопределяют его.
|
|
||||||
4. **LLM — за интерфейсом.** Ядро не знает, какая LLM. Точка входа `LlmClient.complete(...)`.
|
|
||||||
5. **Тулы: внутренний реестр + MCP.** `ToolRegistry` объединяет «родные» тулы и тулы MCP-серверов (через bridge). LLM вызывает их; ядро исполняет и возвращает `TOOL_CALL_RESULT`.
|
|
||||||
6. **А2A = request/response.** Переиспользуем то же ядро, но собираем финальный текст и отдаём одним A2A `Message`.
|
|
||||||
|
|
||||||
## 4. Пакеты / модули
|
|
||||||
|
|
||||||
```
|
|
||||||
pw.binom.agentik
|
|
||||||
├─ core
|
|
||||||
│ ├─ session Session(threadId, messages, state) + SessionStore (in-memory, by threadId)
|
|
||||||
│ ├─ tool ToolSpec, Tool, ToolRegistry (InMemoryToolRegistry, builtin tools)
|
|
||||||
│ ├─ llm LlmClient (interface) + LlmMessage/LlmToolCall/LlmResponse + EchoLlmClient (stub)
|
|
||||||
│ ├─ mcp McpClient (interface: listTools/callTool) + McpTool (bridge) + ToolRegistry.registerMcpClient
|
|
||||||
│ └─ engine AgentEngine: prompt -> LLM -> tool-loop -> AGUI events (RunStarted..RunFinished)
|
|
||||||
└─ fronts
|
|
||||||
├─ agui AgentikAgent : Agent { input -> engine.run(input) } [вместо EchoAgent]
|
|
||||||
└─ a2a EngineA2aHandler : A2A handle(msg, contextId) -> Message [собирает финальный текст]
|
|
||||||
```
|
|
||||||
Wiring (в `standalone`): `Main` собирает `AgentEngine(llm, tools+MCP, sessions)`, отдаёт его `AgentikAgent` (AGUI) и `EngineA2aHandler` (A2A). Конфигурация (LLM, MCP-серверы, тулы) — в одном месте (см. §8).
|
|
||||||
|
|
||||||
## 5. Поток данных (AGUI)
|
|
||||||
|
|
||||||
```
|
|
||||||
client ──POST /agui {RunAgentInput}──> AgentikAgent
|
|
||||||
│ engine.run(input)
|
|
||||||
▼
|
|
||||||
SessionStore.getOrCreate(threadId)
|
|
||||||
merge input.messages, input.tools
|
|
||||||
│
|
|
||||||
┌─> llm.complete(system, history, tools, state)
|
|
||||||
│ └─ toolCalls? -> TOOL_CALL_* + ToolRegistry.call + TOOL_CALL_RESULT -> llm.complete (loop)
|
|
||||||
└─> text? -> TEXT_MESSAGE_* (последнее)
|
|
||||||
│
|
|
||||||
persist final assistant msg in session
|
|
||||||
│
|
|
||||||
client <──SSE [BaseEvent]──────┘ (RunStarted .. RunFinished / RunError)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. A2A-фасад (request/response)
|
|
||||||
|
|
||||||
`A2A handle(request: Message, contextId): Message`:
|
|
||||||
1. `text = request.parts.filterIsInstance<TextPart>().join("")`.
|
|
||||||
2. `RunAgentInput(threadId = contextId ?: new, runId = randomRunId(), messages = [Message(USER, text)])`.
|
|
||||||
3. `events = engine.run(input)` (синхронно, `runBlocking { ...toList() }`).
|
|
||||||
4. `answer = events.filterIsInstance<TextMessageContentEvent>().join { it.delta }`.
|
|
||||||
5. вернуть `Message(role=AGENT, parts=[TextPart(answer)])`.
|
|
||||||
|
|
||||||
`contextId` ↔ `threadId` — общая сессия. A2A не стримит, поэтому события ядра сворачиваются в финальный ответ.
|
|
||||||
|
|
||||||
## 7. Зоны ответственности
|
|
||||||
|
|
||||||
**Ядро (скелет уже закладываем):**
|
|
||||||
`Session/SessionStore`, `Tool/ToolRegistry`, `LlmClient`(iface)+stub, `McpClient`(iface)+bridge, `AgentEngine`, `AgentikAgent`, `EngineA2aHandler`, `Main`.
|
|
||||||
|
|
||||||
**Ты (поверх ядра):**
|
|
||||||
- реализация `LlmClient` (через `pw.binom.openai:ktor-impl`),
|
|
||||||
- конкретные «родные» тулы,
|
|
||||||
- транспорт `McpClient` (подключение к MCP-серверам).
|
|
||||||
|
|
||||||
## 8. Открытые решения (нужны решения)
|
|
||||||
|
|
||||||
1. **Персист сессий** — in-memory или на диск/БД (сейчас in-memory).
|
|
||||||
2. **Конфигурация** — MCP-серверы, LLM endpoint/ключ, системный промпт: env / yaml / файл (сейчас env-заготовки: `AGENTIK_A2A_AGENTS`).
|
|
||||||
3. **Ограничения** — `maxToolRounds`, таймауты LLM/тулов, лимиты размера.
|
|
||||||
4. **Аутентификация** — Bearer на обоих фасадах (а2a/agui принимают `token`), пока не включена.
|
|
||||||
5. **Структура модулей** — держать всё в `standalone` (по пакетам) или вынести `core`/`mcp` в отдельные подмодули (для переиспользования).
|
|
||||||
6. **Поведение сессии** — «клиент источник правды» (replace) vs «сервер источник правды» (append) при реконсилии `messages`.
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ kotlin = "2.4.20"
|
|||||||
kotlinx-serialization = "1.11.0"
|
kotlinx-serialization = "1.11.0"
|
||||||
kotlinx-coroutines = "1.11.0"
|
kotlinx-coroutines = "1.11.0"
|
||||||
ktor = "3.1.3"
|
ktor = "3.1.3"
|
||||||
agui = "0.1.0"
|
|
||||||
a2a = "1.0.0-SNAPSHOT"
|
a2a = "1.0.0-SNAPSHOT"
|
||||||
litert = "7"
|
litert = "7"
|
||||||
sqldelight = "2.3.2"
|
sqldelight = "2.3.2"
|
||||||
@@ -15,11 +14,6 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
|
|||||||
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# --- AG-UI (pw.binom.agui) — KMP: jvm + native ---
|
|
||||||
agui-api = { module = "pw.binom.agui:api", version.ref = "agui" }
|
|
||||||
agui-client = { module = "pw.binom.agui:client", version.ref = "agui" }
|
|
||||||
agui-server = { module = "pw.binom.agui:server", version.ref = "agui" }
|
|
||||||
|
|
||||||
# --- A2A (pw.binom.a2a) — shared: KMP (jvm + linuxX64); client/server: JVM-only ---
|
# --- A2A (pw.binom.a2a) — shared: KMP (jvm + linuxX64); client/server: JVM-only ---
|
||||||
a2a-shared = { module = "pw.binom.a2a:shared", version.ref = "a2a" }
|
a2a-shared = { module = "pw.binom.a2a:shared", version.ref = "a2a" }
|
||||||
a2a-client = { module = "pw.binom.a2a:client", version.ref = "a2a" }
|
a2a-client = { module = "pw.binom.a2a:client", version.ref = "a2a" }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ plugins {
|
|||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(21)
|
||||||
|
|
||||||
// "Все возможные цели сборки": jvm + весь натив. Зеркалит набор AG-UI api.
|
// "Все возможные цели сборки": jvm + весь натив. Полный набор KMP-целей.
|
||||||
jvm()
|
jvm()
|
||||||
macosX64()
|
macosX64()
|
||||||
macosArm64()
|
macosArm64()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.flow
|
|||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ядро собственного протокола agentik (замена AG-UI). Явно stateful.
|
* Ядро собственного протокола agentik. Явно stateful.
|
||||||
*
|
*
|
||||||
* Транспортно-агностично. [Agent] — фабрика stateful-диалогов:
|
* Транспортно-агностично. [Agent] — фабрика stateful-диалогов:
|
||||||
* [createConversation] возвращает [Conversation], который сам хранит историю
|
* [createConversation] возвращает [Conversation], который сам хранит историю
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@ dependencyResolutionManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
// Home Nexus, репо "caffeine": pw.binom.* (AG-UI, A2A, ...)
|
// Home Nexus, репо "caffeine": pw.binom.* (A2A, ...)
|
||||||
maven {
|
maven {
|
||||||
name = "caffeine"
|
name = "caffeine"
|
||||||
url = uri(caffeineRepo)
|
url = uri(caffeineRepo)
|
||||||
@@ -24,7 +24,7 @@ dependencyResolutionManagement {
|
|||||||
rootProject.name = "agentik"
|
rootProject.name = "agentik"
|
||||||
|
|
||||||
include(":standalone")
|
include(":standalone")
|
||||||
// Собственный протокол agentik (замена AG-UI). Пока в нём пилим, потом вынесем.
|
// Собственный протокол agentik. Пока в нём пилим, потом вынесем.
|
||||||
include(":proto")
|
include(":proto")
|
||||||
// Ktor-сервер, экспонирующий Agent по HTTP (SSE + JSON).
|
// Ktor-сервер, экспонирующий Agent по HTTP (SSE + JSON).
|
||||||
include(":server")
|
include(":server")
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ kotlin {
|
|||||||
implementation(libs.ktor.server.content.negotiation)
|
implementation(libs.ktor.server.content.negotiation)
|
||||||
implementation(libs.ktor.serialization.kotlinx.json)
|
implementation(libs.ktor.serialization.kotlinx.json)
|
||||||
|
|
||||||
// Транспортные фасады
|
// Транспортные фасады (A2A остаётся заготовкой; на v1 не подключается в Main.kt —
|
||||||
implementation(libs.agui.server)
|
// см. docs/ARCHITECTURE.md §3).
|
||||||
implementation(libs.a2a.server)
|
implementation(libs.a2a.server)
|
||||||
|
|
||||||
// MCP (Model Context Protocol) клиент — подключение внешних/внутренних MCP-серверов
|
// MCP (Model Context Protocol) клиент — подключение внешних/внутренних MCP-серверов
|
||||||
|
|||||||
Reference in New Issue
Block a user