chore: organize documentation structure for monorepo
- Create /docs structure (microdao, daarion, agents) - Organize 61 cursor technical docs - Add README files for each category - Copy key documents to public categories - Add GitHub setup instructions and scripts
This commit is contained in:
646
docs/cursor/11_llm_integration.md
Normal file
646
docs/cursor/11_llm_integration.md
Normal file
@@ -0,0 +1,646 @@
|
||||
# 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 (або інша модель)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 3. Структура директорій для LLM
|
||||
|
||||
Додайте на бекенд:
|
||||
|
||||
```
|
||||
src/
|
||||
llm/
|
||||
openaiClient.ts
|
||||
modelRouter.ts
|
||||
prompts/
|
||||
system_agent.txt
|
||||
system_onboarding.txt
|
||||
system_evolution.txt
|
||||
```
|
||||
|
||||
- `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 ?? "";
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
У майбутньому це місце для:
|
||||
|
||||
* локальних моделей (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 });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 7. Інтеграція з Agent Chat у фронтенді
|
||||
|
||||
**Файл: `api/agents.ts`**
|
||||
|
||||
```ts
|
||||
export async function agentChat(agentId: string, messages: ChatMessage[]) {
|
||||
return api.post(`/agents/${agentId}/chat`, { messages });
|
||||
}
|
||||
```
|
||||
|
||||
**У `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 });
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 8. Agent-First Onboarding Integration
|
||||
|
||||
Використовує той самий LLM-клієнт, але з іншим системним промптом:
|
||||
|
||||
**`prompts/system_onboarding.txt`:**
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
У онбордингу:
|
||||
|
||||
```ts
|
||||
const reply = await callLLM([
|
||||
{ role: "system", content: onboardingSystemPrompt },
|
||||
...conversation
|
||||
]);
|
||||
```
|
||||
|
||||
Але state-machine керує реальними діями (API), LLM — тільки текстом.
|
||||
|
||||
---
|
||||
|
||||
# 9. Integration with Evolutionary Agent (09_evolutionary_agent.md)
|
||||
|
||||
Meta-Agent (self-review) використовує **ще один промпт**:
|
||||
|
||||
`prompts/system_evolution.txt`:
|
||||
|
||||
```
|
||||
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"]`.
|
||||
```
|
||||
|
||||
Self-review:
|
||||
|
||||
```ts
|
||||
const improvements = await callLLM([
|
||||
{ role: "system", content: evolutionPrompt },
|
||||
{ role: "user", content: JSON.stringify(conversationLog) }
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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 }
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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;
|
||||
```
|
||||
|
||||
## 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 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,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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;
|
||||
}
|
||||
```
|
||||
|
||||
## 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;
|
||||
}
|
||||
```
|
||||
|
||||
## 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();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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");
|
||||
}
|
||||
```
|
||||
|
||||
## 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");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 15.2. Метрики
|
||||
|
||||
Відстежувати:
|
||||
|
||||
- кількість викликів на агента
|
||||
- середній час відповіді
|
||||
- витрати на токени
|
||||
- частота помилок
|
||||
- популярні моделі
|
||||
|
||||
---
|
||||
|
||||
# 16. Завдання для Cursor
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 17. Типи та інтерфейси
|
||||
|
||||
## 17.1. ChatMessage
|
||||
|
||||
```ts
|
||||
interface ChatMessage {
|
||||
role: "system" | "user" | "assistant";
|
||||
content: string;
|
||||
timestamp?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 17.2. LLMResponse
|
||||
|
||||
```ts
|
||||
interface LLMResponse {
|
||||
content: string;
|
||||
model: string;
|
||||
tokens?: {
|
||||
prompt: number;
|
||||
completion: number;
|
||||
total: number;
|
||||
};
|
||||
finishReason?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 17.3. AgentChatRequest
|
||||
|
||||
```ts
|
||||
interface AgentChatRequest {
|
||||
messages: ChatMessage[];
|
||||
context?: {
|
||||
channelId?: string;
|
||||
threadId?: string;
|
||||
userId?: string;
|
||||
};
|
||||
options?: {
|
||||
temperature?: number;
|
||||
maxTokens?: number;
|
||||
stream?: boolean;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 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();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 19. Результат
|
||||
|
||||
Після інтеграції:
|
||||
|
||||
* будь-який агент microDAO працює на GPT/LLM,
|
||||
* онбординг веде агент-гіда,
|
||||
* Team Assistant відповідає у чаті,
|
||||
* Meta-Agent генерує покращення,
|
||||
* вся система стає справжньою OS на базі ШІ.
|
||||
|
||||
---
|
||||
|
||||
# 20. Наступні кроки
|
||||
|
||||
Після базової інтеграції можна додати:
|
||||
|
||||
- Streaming відповідей для кращого UX
|
||||
- Кешування для оптимізації витрат
|
||||
- Підтримку альтернативних провайдерів
|
||||
- Fine-tuning моделей для конкретних спільнот
|
||||
- Інтеграцію з DAGI для колективного навчання
|
||||
|
||||
---
|
||||
|
||||
**Готово.**
|
||||
Це **повна специфікація інтеграції LLM**, готова до використання в Cursor.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user