Files
microdao-daarion/docs/cursor/11_llm_integration.md
2026-02-16 07:44:56 -08:00

14 KiB
Raw Blame History

11 — LLM Integration Guide (MicroDAO)

Інтеграція ChatGPT / OpenAI / інших моделей у агентську систему MicroDAO

Цей документ описує:

  • де і як підключити LLM,

  • як організувати backend-виклики,

  • як звʼязати агента з моделлю,

  • як працює agent-first онбординг,

  • як працює агентський чат,

  • як працює еволюційна модель на основі LLM.

Документ орієнтований на Cursor + Node/TS backend.


1. Принцип інтеграції

Усі виклики до LLM здійснюються на бекенді, не з фронтенду.

Причини:

  • безпека (ключ не світиться),

  • стабільність,

  • контроль ціни,

  • можливість додавати кэшинг, rate-limits,

  • можливість підміняти провайдерів (OpenAI → Anthropic → локальні моделі).


2. Високорівнева архітектура

Frontend (React SPA)
|
| POST /agents/{id}/chat
↓
Backend
├── agentsController.ts
├── llm/
│      ├── openaiClient.ts
│      ├── modelRouter.ts
│      └── prompts/
│             ├── system_agent.txt
│             └── system_onboarding.txt
|
↓
OpenAI API (або інша модель)
```text

---

## 3. Структура директорій для LLM

Додайте на бекенд:

```text
src/
llm/
openaiClient.ts
modelRouter.ts
prompts/
system_agent.txt
system_onboarding.txt
system_evolution.txt
```text

- `openaiClient.ts` — клієнт OpenAI / GPT.

- `modelRouter.ts` — місце, де ти можеш вирішити, яку модель використовувати (gpt-4.1-mini, o3, claude тощо).

- `prompts/*.txt` — системні промпти для:

  - Agent Chat

  - Onboarding Guide Agent

  - Evolution Meta-Agent

---

## 4. Реалізація базового клієнта OpenAI

**Файл: `src/llm/openaiClient.ts`**

```ts
import OpenAI from "openai";

export const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
});

export async function callLLM(messages: any[], model = "gpt-4.1-mini") {
  const res = await openai.chat.completions.create({
    model,
    messages,
    temperature: 0.2,
  });

  return res.choices[0]?.message?.content ?? "";
}
```text

---

## 5. Model Router

**Файл: `src/llm/modelRouter.ts`**

```ts
export function pickModel(agentProfile: string) {
  switch (agentProfile) {
    case "technical":
      return "gpt-4.1";
    case "business":
      return "gpt-4.1-mini";
    case "creative":
      return "gpt-4o-mini";
    default:
      return "gpt-4.1-mini";
  }
}
```text

У майбутньому це місце для:

- локальних моделей (Ollama, vLLM),
- кластеру DAGI,
- автоматичного підбору моделі.

---

## 6. Запит до LLM для агентського чату

**Файл: `src/controllers/agentsController.ts`**

```ts
import { callLLM } from "../llm/openaiClient";
import { pickModel } from "../llm/modelRouter";
import systemAgent from "../llm/prompts/system_agent.txt";

export async function chatWithAgent(req, res) {
  const { agentId } = req.params;
  const { messages } = req.body;

  const agent = await db.agent.find(agentId);

  const model = pickModel(agent.role);

  const llmMessages = [
    { role: "system", content: systemAgent },
    ...messages
  ];

  const reply = await callLLM(llmMessages, model);

  // зберегти останнє повідомлення як agent message
  await db.agentMessages.insert({
    agent_id: agentId,
    role: "assistant",
    body: reply
  });

  res.json({ reply });
}
```text

---

## 7. Інтеграція з Agent Chat у фронтенді

**Файл: `api/agents.ts`**

```ts
export async function agentChat(agentId: string, messages: ChatMessage[]) {
  return api.post(`/agents/${agentId}/chat`, { messages });
}
```text

**У `AgentChatWindow.tsx`:**

```ts
const onSend = async (text: string) => {
  addMessage({ role: "user", content: text });

  const response = await agentChat(agentId, [
    ...history,
    { role: "user", content: text }
  ]);

  addMessage({ role: "assistant", content: response.reply });
};
```text

---

## 8. Agent-First Onboarding Integration

Використовує той самий LLM-клієнт, але з іншим системним промптом:

**`prompts/system_onboarding.txt`:**

```text
You are MicroDAO Guide Agent.

Your job is to ask the user questions one-by-one to configure their microDAO.

NEVER skip steps. NEVER jump too far.

Be friendly, minimalistic and precise.
```text

У онбордингу:

```ts
const reply = await callLLM([
  { role: "system", content: onboardingSystemPrompt },
  ...conversation
]);
```text

Але state-machine керує реальними діями (API), LLM — тільки текстом.

---

## 9. Integration with Evolutionary Agent (09_evolutionary_agent.md)

Meta-Agent (self-review) використовує **ще один промпт**:

`prompts/system_evolution.txt`:

```text
You are Meta-Agent responsible for analyzing logs of conversations.

Find mistakes, weak answers, missing rules, and propose improvements.

Always output JSON with `["type", "value", "explanation"]`.
```text

Self-review:

```ts
const improvements = await callLLM([
  { role: "system", content: evolutionPrompt },
  { role: "user", content: JSON.stringify(conversationLog) }
]);
```text

---

## 10. Як передавати пам'ять агента в LLM

У LLM-запит можна додати:

- `short-term memory` (останні X повідомлень)

- `long-term memory` (витяг з Co-Memory)

- `agent profile`

- інструкції агента (структура з DB)

Приклад у messages:

```ts
const llmMessages = [
  { role: "system", content: systemPrompt },
  { role: "assistant", content: "AGENT_PROFILE:" + JSON.stringify(agentProfile) },
  { role: "assistant", content: "MEMORY:" + JSON.stringify(memories) },
  ...history,
  { role: "user", content: question }
];
```text

---

## 11. Безпека

- API key зберігати у `.env` на сервері.

- Ніколи не відправляти ключ у фронтенд.

- Додавати rate limit.

- Додавати аудит використання агента.

---

## 12. Кешування та оптимізація

## 12.1. Кешування відповідей

Для однакових запитів можна кешувати відповіді:

```ts
const cacheKey = hash(messages);
const cached = await cache.get(cacheKey);
if (cached) return cached;

const reply = await callLLM(messages);
await cache.set(cacheKey, reply, { ttl: 3600 });
return reply;
```text

## 12.2. Streaming відповідей

Для кращого UX можна використовувати streaming:

```ts
const stream = await openai.chat.completions.create({
  model,
  messages,
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) {
    res.write(content);
  }
}
```text

## 12.3. Rate Limiting

Обмеження кількості запитів:

```ts
import rateLimit from "express-rate-limit";

const agentLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 хвилина
  max: 10, // 10 запитів на хвилину
  keyGenerator: (req) => req.user.id,
});
```text

---

## 13. Альтернативні провайдери

## 13.1. Anthropic Claude

```ts
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY!,
});

export async function callClaude(messages: any[]) {
  const response = await anthropic.messages.create({
    model: "claude-3-5-sonnet-20241022",
    max_tokens: 1024,
    messages,
  });
  
  return response.content[0].text;
}
```text

## 13.2. Локальні моделі (Ollama)

```ts
export async function callOllama(messages: any[], model = "llama2") {
  const response = await fetch("http://localhost:11434/api/chat", {
    method: "POST",
    body: JSON.stringify({
      model,
      messages,
    }),
  });
  
  const data = await response.json();
  return data.message.content;
}
```text

## 13.3. Уніфікований інтерфейс

```ts
interface LLMProvider {
  call(messages: any[], options?: any): Promise<string>;
}

class OpenAIProvider implements LLMProvider {
  async call(messages: any[], options?: any) {
    return callLLM(messages, options?.model);
  }
}

class AnthropicProvider implements LLMProvider {
  async call(messages: any[], options?: any) {
    return callClaude(messages);
  }
}

export function getLLMProvider(provider: string): LLMProvider {
  switch (provider) {
    case "openai":
      return new OpenAIProvider();
    case "anthropic":
      return new AnthropicProvider();
    case "ollama":
      return new OllamaProvider();
    default:
      return new OpenAIProvider();
  }
}
```text

---

## 14. Обробка помилок

## 14.1. Retry Logic

```ts
async function callLLMWithRetry(
  messages: any[],
  model: string,
  maxRetries = 3
): Promise<string> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await callLLM(messages, model);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await sleep(1000 * (i + 1)); // exponential backoff
    }
  }
  throw new Error("LLM call failed after retries");
}
```text

## 14.2. Fallback моделі

```ts
async function callLLMWithFallback(messages: any[], primaryModel: string) {
  try {
    return await callLLM(messages, primaryModel);
  } catch (error) {
    console.warn(`Primary model failed, using fallback`);
    return await callLLM(messages, "gpt-3.5-turbo");
  }
}
```text

---

## 15. Моніторинг та логування

## 15.1. Логування викликів

```ts
async function callLLM(messages: any[], model: string) {
  const startTime = Date.now();
  
  try {
    const reply = await openai.chat.completions.create({
      model,
      messages,
      temperature: 0.2,
    });
    
    const duration = Date.now() - startTime;
    const tokens = reply.usage?.total_tokens || 0;
    
    logger.info("LLM call", {
      model,
      duration,
      tokens,
      cost: calculateCost(model, tokens),
    });
    
    return reply.choices[0]?.message?.content ?? "";
  } catch (error) {
    logger.error("LLM call failed", { model, error });
    throw error;
  }
}
```text

## 15.2. Метрики

Відстежувати:

- кількість викликів на агента
- середній час відповіді
- витрати на токени
- частота помилок
- популярні моделі

---

## 16. Завдання для Cursor

```text
You are a senior backend + frontend engineer.

Integrate OpenAI LLM into the MicroDAO Agents system using:

- 11_llm_integration.md
- 03_api_core_snapshot.md
- 05_coding_standards.md

Tasks:

1. Create openaiClient.ts
2. Create modelRouter.ts
3. Add AgentChat endpoint
4. Connect AgentChatWindow to backend
5. Add LLM to AgentOnboardingChat
6. Add LLM to EvolutionMetaAgent (stub)

Output:

- list of modified files
- diff
- summary
```text

---

## 17. Типи та інтерфейси

## 17.1. ChatMessage

```ts
interface ChatMessage {
  role: "system" | "user" | "assistant";
  content: string;
  timestamp?: string;
}
```text

## 17.2. LLMResponse

```ts
interface LLMResponse {
  content: string;
  model: string;
  tokens?: {
    prompt: number;
    completion: number;
    total: number;
  };
  finishReason?: string;
}
```text

## 17.3. AgentChatRequest

```ts
interface AgentChatRequest {
  messages: ChatMessage[];
  context?: {
    channelId?: string;
    threadId?: string;
    userId?: string;
  };
  options?: {
    temperature?: number;
    maxTokens?: number;
    stream?: boolean;
  };
}
```text

---

## 18. Тестування

## 18.1. Unit Tests

```ts
describe("openaiClient", () => {
  it("should call LLM with correct messages", async () => {
    const messages = [
      { role: "system", content: "You are a helpful assistant" },
      { role: "user", content: "Hello" },
    ];
    
    const response = await callLLM(messages);
    expect(response).toBeDefined();
    expect(typeof response).toBe("string");
  });
});
```text

## 18.2. Integration Tests

```ts
describe("Agent Chat Integration", () => {
  it("should handle full chat flow", async () => {
    const agentId = "test-agent";
    const messages = [
      { role: "user", content: "Hello" },
    ];
    
    const response = await agentChat(agentId, messages);
    expect(response.reply).toBeDefined();
  });
});
```text

---

## 19. Результат

Після інтеграції:

- будь-який агент microDAO працює на GPT/LLM,
- онбординг веде агент-гіда,
- Team Assistant відповідає у чаті,
- Meta-Agent генерує покращення,
- вся система стає справжньою OS на базі ШІ.

---

## 20. Наступні кроки

Після базової інтеграції можна додати:

- Streaming відповідей для кращого UX
- Кешування для оптимізації витрат
- Підтримку альтернативних провайдерів
- Fine-tuning моделей для конкретних спільнот
- Інтеграцію з DAGI для колективного навчання

---

**Готово.**  
Це **повна специфікація інтеграції LLM**, готова до використання в Cursor.