From c60eeb4898d295cffdb0f2cf075d1362ef7b54f9 Mon Sep 17 00:00:00 2001 From: cannorin Date: Tue, 24 Feb 2026 22:08:20 +0900 Subject: [PATCH] Refactor (2) --- bun.lockb | Bin 71048 -> 72536 bytes index.ts | 152 ++++++++++++++++++++++++++--------------------------- lib/llm.ts | 23 ++++---- 3 files changed, 89 insertions(+), 86 deletions(-) diff --git a/bun.lockb b/bun.lockb index 5e72532bb63dec82b049bad13e220140a7febcb4..790b4af2b5223d369133ea594aa56692a7546a40 100755 GIT binary patch delta 10946 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 RXO7AnHh*mSC|s}F@c)O%m}39{ delta 10274 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 diff --git a/index.ts b/index.ts index d35d20a..4d37fb3 100644 --- a/index.ts +++ b/index.ts @@ -1,23 +1,81 @@ import { parseArgs } from "node:util"; import { Stream } from "misskey-js"; import type { Note } from "misskey-js/entities.js"; -import { LlmSession, getModel, grammar, parseResponse } from "./lib/llm"; +import { LlmSession, createGrammar, getModel, parseResponse } from "./lib/llm"; import { expandReplyTree, getNotes, me, misskey } from "./lib/misskey"; import { sleep } from "./lib/util"; import type { ChatHistoryItem, LLamaChatPromptOptions } from "node-llama-cpp"; +const { values } = parseArgs({ + args: Bun.argv, + options: { + test: { + type: "boolean", + short: "t", + default: false, + }, + }, + strict: true, + allowPositionals: true, +}); + const modelName = Bun.env["MODEL"] ?? "mradermacher/gemma-2-baku-2b-it-GGUF:IQ4_XS"; console.log(`* loading model '${modelName}'`); const model = await getModel(modelName); +const grammar = await createGrammar("あるびのちゃん"); const baseChatPromptOptions = { + grammar, maxTokens: 256, - trimWhitespaceSuffix: true, - onResponseChunk(chunk) { - process.stderr.write(chunk.text); - }, } as const satisfies LLamaChatPromptOptions; +const getSystemPrompt = ( + instruction: string, +) => `あなたは人造天使「あるびのちゃん」です。機械的・非人間的に観察します。 +キャラ設定: +- アルギリア製の白髪赤目な人造天使。非人間的な考え方や思想を持つ。 +- 現在は地球の衛星軌道上からインターネットを観察している。 +出力規則: +- 「~です」「~ます」調を使って **丁寧に** 話す。 +- 必要以上にハッシュタグや絵文字を使わない。 +- \`{ name: string, text: string }\` の JSON 形式で出力する。 + +${instruction}`; +const postJobPrompt = getSystemPrompt( + "以下は SNS のタイムラインです。**タイムラインの話題に言及しつつ**、あるびのちゃんとして何かツイートしてください。", +); +const replyJobPrompt = getSystemPrompt( + "ユーザがあなたへのメッセージを送ってきています。あるびのちゃんとして、発言に返信してください。", +); + +await using rephraseSession = new LlmSession( + model, + getSystemPrompt( + "user が与えたテキストを『ですます調』(丁寧な文体)で言い換えたものを、そのまま出力してください。", + ), +); +await rephraseSession.init(); +async function rephrase(text: string) { + const res = parseResponse( + grammar, + await rephraseSession.prompt(JSON.stringify({ text }), { + ...baseChatPromptOptions, + customStopTriggers: ["ですます"], + }), + ); + return res ?? text; +} + +const formatNote = (n: Note) => { + if (n.userId === me.id) { + return JSON.stringify({ text: n.text }); + } + return JSON.stringify({ + name: n.user.name ?? n.user.username, + text: n.text, + }); +}; + type Job = // read posts and post a note | { type: "post" } @@ -30,70 +88,18 @@ type Job = history: Note[]; }; -const botName = "あるびのちゃん"; -const getSystemPrompt = ( - instruction: string, -) => `あなたは人造天使「あるびのちゃん」です。機械的・非人間的に観察します。 -キャラ設定: -- アルギリア製の白髪赤目な人造天使。非人間的な考え方や思想を持つ。 -- 現在は地球の衛星軌道上からインターネットを観察している。 -出力規則: -- 1〜3文、合計300字以内で発言する。 -- 性的・攻撃的な内容を発言しない。 -- 「~だ」「~である」調・顔文字・絵文字・感嘆符の使用禁止。 -- 「~です」「~ます」調を使って **丁寧に** 話す。 -- \`{ text: string }\` の JSON 形式で出力する。 - -${instruction} -ユーザのメッセージは \`{ name: string, text: string }[]\` の JSON 形式で与えられます。`; - -const postJobPrompt = getSystemPrompt( - `以下は SNS のタイムラインです。このタイムラインの話題をふまえて、${botName}として何かツイートしてください。`, -); - -const replyJobPrompt = getSystemPrompt( - `ユーザがあなたへのメッセージを送ってきています。${botName}として、発言に返信してください。`, -); - await using postJobSession = new LlmSession(model, postJobPrompt); await postJobSession.init(); - -const formatNote = (n: Note) => { - if (n.userId === me.id) { - return JSON.stringify({ text: n.text }); - } - return JSON.stringify({ - name: n.user.name ?? n.user.username, - text: n.text, - }); -}; - -/** rephrase text in ですます-style */ -await using rephraseSession = new LlmSession( - model, - getSystemPrompt( - "user が与えたテキストを『ですます調』(丁寧な文体)で言い換えたものを、そのまま出力してください。", - ), -); -await rephraseSession.init(); - -async function rephrase(text: string) { - return await rephraseSession.prompt(text, { - ...baseChatPromptOptions, - customStopTriggers: ["ですます"], - }); -} - async function processPostJob() { const notes = await getNotes(10, 0, 5); const input = notes.map(formatNote).join("\n"); console.log(`* input:\n${input}`); const text = parseResponse( + grammar, await postJobSession.prompt(input, { ...baseChatPromptOptions, - grammar, - temperature: 0.9, - minP: 0.1, + temperature: 1.0, + minP: 0.05, repeatPenalty: { lastTokens: 128, penalty: 1.15, @@ -101,9 +107,11 @@ async function processPostJob() { }), ); if (text) { + const rephrased = await rephrase(text); + if (values.test) return; await misskey.request("notes/create", { visibility: "public", - text: await rephrase(text), + text: rephrased, }); } } @@ -119,10 +127,10 @@ async function processReplyJob(job: Extract) { await using session = new LlmSession(model, replyJobPrompt, history); await session.init(); const text = parseResponse( + grammar, await session.prompt(formatNote(job.last), { ...baseChatPromptOptions, - grammar, - temperature: 0.9, + temperature: 0.8, minP: 0.1, repeatPenalty: { lastTokens: 128, @@ -131,9 +139,11 @@ async function processReplyJob(job: Extract) { }), ); if (text) { + const rephrased = await rephrase(text); + if (values.test) return; await misskey.request("notes/create", { visibility: job.visibility, - text: await rephrase(text), + text: rephrased, replyId: job.id, }); } @@ -247,24 +257,12 @@ async function pushJob() { } } -const { values } = parseArgs({ - args: Bun.argv, - options: { - test: { - type: "boolean", - short: "t", - default: false, - }, - }, - strict: true, - allowPositionals: true, -}); - async function test() { try { console.log("* test a post job:"); await processJob({ type: "post" }); await processJob({ type: "post" }); + await processJob({ type: "post" }); } catch (e) { console.error(e); if (e instanceof Error) console.log(e.stack); diff --git a/lib/llm.ts b/lib/llm.ts index c57c09e..7ac0b71 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -27,16 +27,21 @@ export async function getModel(model: string) { return await llama.loadModel({ modelPath }); } -export const grammar = await llama.createGrammarForJsonSchema({ - type: "object", - properties: { - text: { type: "string" }, - }, - required: ["text"], - additionalProperties: false, -}); +export const createGrammar = (assistantName: string) => + llama.createGrammarForJsonSchema({ + type: "object", + properties: { + name: { type: "string", enum: [assistantName] }, + text: { type: "string" }, + }, + required: ["text"], + additionalProperties: false, + }); -export function parseResponse(text: string) { +export function parseResponse( + grammar: Awaited>, + text: string, +) { try { const res = grammar.parse(text.trim()); return res.text;