chatChain.js 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { config } from "../src/config/index.js";
  2. import { chatCompletion } from "../src/services/ollamaClient.js";
  3. import { searchDocs } from "../chat/searchChat.js";
  4. import fs from "node:fs";
  5. function buildContextBlock(hits) {
  6. const lines = [];
  7. hits.forEach((h, i) => {
  8. const header = `# Fonte ${i + 1}${h.source ? ` (${h.source})` : ""}`;
  9. lines.push(header);
  10. lines.push(h.text);
  11. lines.push("");
  12. });
  13. return lines.join("\n").trim();
  14. }
  15. export async function answerWithContext({ message, sessionId }) {
  16. const hits = await searchDocs({ query: message, topK: config.rag.topK });
  17. const context = buildContextBlock(hits);
  18. // #region debug-point D:image-rag-context
  19. (() => {
  20. let u = "http://127.0.0.1:7777/event";
  21. let s = "image-rag-miss";
  22. try {
  23. const e = fs.readFileSync(".dbg/image-rag-miss.env", "utf8");
  24. u = e.match(/DEBUG_SERVER_URL=(.+)/)?.[1] || u;
  25. s = e.match(/DEBUG_SESSION_ID=(.+)/)?.[1] || s;
  26. } catch {}
  27. fetch(u, {
  28. method: "POST",
  29. headers: { "content-type": "application/json" },
  30. body: JSON.stringify({
  31. sessionId: s,
  32. runId: "post",
  33. hypothesisId: "D",
  34. location: "api/chat/chatChain.js",
  35. msg: "[DEBUG] answerWithContext context built",
  36. data: {
  37. questionHead: String(message ?? "").slice(0, 140),
  38. hits: Array.isArray(hits) ? hits.length : null,
  39. contextChars: context.length,
  40. topScore: hits?.[0]?.score ?? null,
  41. topSource: hits?.[0]?.source ?? null
  42. },
  43. ts: Date.now()
  44. })
  45. }).catch(() => {});
  46. })();
  47. // #endregion
  48. const system = [
  49. "Você é um assistente de atendimento interno de uma empresa.",
  50. "Responda apenas com base no CONTEXTO quando ele estiver disponível.",
  51. "Se o CONTEXTO não tiver a informação, diga que não encontrou na base da empresa e sugira o que solicitar ao time responsável.",
  52. "Seja direto e em português."
  53. ].join("\n");
  54. const messages = [
  55. { role: "system", content: system },
  56. ...(context ? [{ role: "system", content: `CONTEXTO:\n${context}` }] : []),
  57. ...(sessionId ? [{ role: "system", content: `session_id:${sessionId}` }] : []),
  58. { role: "user", content: message }
  59. ];
  60. const completion = await chatCompletion({ messages });
  61. return {
  62. answer: completion.content,
  63. sources: hits.map((h) => ({
  64. id: h.id,
  65. source: h.source,
  66. score: h.score,
  67. chunkIndex: h.chunkIndex,
  68. metadata: h.metadata
  69. }))
  70. };
  71. }