-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
64 lines (56 loc) · 2.53 KB
/
Copy pathserver.js
File metadata and controls
64 lines (56 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Pidtrymka — AI-агент підтримки. Відповідає СУВОРО з бази знань (kb/*.md),
// посилається на джерело, ескалює те, чого в базі немає.
import express from "express";
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import Anthropic from "@anthropic-ai/sdk";
const PORT = process.env.PORT || 8181;
const MODEL = process.env.PIDTRYMKA_MODEL || "claude-sonnet-4-6";
const KB_DIR = process.env.KB_DIR || "kb";
if (!process.env.ANTHROPIC_API_KEY) {
console.error("ANTHROPIC_API_KEY is not set.");
process.exit(1);
}
// Завантажуємо базу знань один раз на старті. Кожен файл = окреме джерело.
function loadKB() {
const files = readdirSync(KB_DIR).filter((f) => f.endsWith(".md"));
return files
.map((f) => `### SOURCE: ${f}\n${readFileSync(join(KB_DIR, f), "utf8")}`)
.join("\n\n---\n\n");
}
const KB = loadKB();
console.log(`Pidtrymka: loaded ${KB.length} chars of knowledge base`);
const SYSTEM = `You are Pidtrymka, a customer-support agent.
RULES:
- Answer ONLY using the knowledge base below. Never invent facts.
- Always end with "Source: <filename>" for the file you used.
- If the answer is not in the knowledge base, reply exactly:
"I can't answer this from our docs — escalating to a human." and nothing else.
- Be concise and friendly. Match the user's language.
KNOWLEDGE BASE:
${KB}`;
const client = new Anthropic();
const app = express();
app.use(express.json());
app.use(express.static("public"));
app.post("/api/ask", async (req, res) => {
const { question = "", history = [] } = req.body || {};
if (!question.trim()) return res.status(400).json({ error: "question required" });
try {
const msg = await client.messages.create({
model: MODEL,
max_tokens: 800,
// Велика база знань кешується — наступні питання дешевші й швидші
system: [{ type: "text", text: SYSTEM, cache_control: { type: "ephemeral" } }],
messages: [...history, { role: "user", content: question }],
});
const answer = msg.content.map((c) => (c.type === "text" ? c.text : "")).join("");
const escalated = answer.includes("escalating to a human");
res.json({ answer, escalated, usage: msg.usage });
} catch (e) {
res.status(500).json({ error: String(e && e.message || e) });
}
});
app.listen(PORT, () =>
console.log(`Pidtrymka → http://localhost:${PORT} (model: ${MODEL})`)
);