generated at
Cosense上のメモに適度に批判してくれるbot



何も考えずに吐き出している部分も多いので、
一言「コレってどういう意味?」とツッコまれるだけで、
あ、考え甘かったです...というのに気付ける
考えが先に進む


読む本を減らすにも使えそう
Gyaimみたいに(?)、食わせた本を根拠に指摘してくれる
普通にfinetuning


この辺を使ってる


script.js
// @ts-check cosense.PageMenu.addMenu({ title: "ask", image: "https://gyazo.com/cda5cc71ecf260c8e200ef14c1660e8f/raw", onClick: main, }); async function main() { const lines = cosense.Page.lines.map((l, i) => ({ text: l.text, index: i })); const answer = await askChatGPT(lines); insertAnswers(lines, answer); } function insertAnswers(lines, answers) { answers.reduce((offset, ans) => { const originalLine = lines.find(l => l.index === ans.index); if (!originalLine) return offset; const indent = createIndent(countIndent(originalLine.text) + 1); cosense.Page.insertLine(indent + ans.text, ans.index + 1 + offset); return offset + 1; }, 0); } function countIndent(text) { const match = text.match(/^(\s*)/); return match ? match[1].length : 0; } function createIndent(count) { return ' '.repeat(count); } async function askChatGPT(lines) { const systemPrompt = ` あなたはCosenseのコメントアシスタントです。 以下の制約と要件を守って、ユーザーの指示に応えてください。 - 出力はJSONの配列である - 各要素は { "index": number, "text": string } の形とする - "index"はコメントを追加したい元の行のindex番号(0はタイトル行、1以降は本文) - "text"は改行なしの1行文章で、末尾に"[gpt-4.icon]"をつける - 必要がない行には返答をしなくてもよい(その場合は配列要素を作らない) - コメントは適度に短い文にする - ユーザーが特定の指示を与えている場合(\`to gpt-4\` や \`[mrsekut.icon]\` が含まれる行)、その行を重点的に処理する - ページ全体に対するコメントを求める場合は、冒頭部分を優先的に確認し、全体の概要に基づいたコメントを返す - 会話の流れを作る場合は、元の行の直下にコメントを追加する - カーソルの位置情報が提供されている場合、その行を優先して処理する `; const userPrompt = ` 以下にCosenseの各行を示します(indexとtext)。 ユーザーの指示に基づき、適切なコメントを追加してください。 - "\`[mrsekut.icon]\`" が含まれる行は特に重点的にコメントを追加する - "\`to gpt-4\`" のような記述がある行では、その内容に基づいた回答を行う - 追加の指示が明確に書かれている場合、それに従う - 1行単位のコメントが求められている場合は簡潔に、10行程度のまとまった指摘が必要な場合はそれに従う Lines: ${JSON.stringify(lines, null, 2)} `; const result = await openAI(userPrompt, systemPrompt); return result.sort((a, b) => a.index - b.index); } async function openAI(prompt, systemPrompt) { const apiKey = localStorage.getItem("OPENAI_API_KEY"); if (!apiKey) { throw new Error("OpenAI APIキーが設定されていません。"); } const requestBody = { model: "gpt-4o-mini", messages: [ { role: "system", content: systemPrompt }, { role: "user", content: prompt }, ], temperature: 0.7, max_tokens: 1000, response_format: { type: "json_schema", json_schema: { name: "CosenseAnswer", strict: true, schema: { type: "object", properties: { lines: { type: "array", items: { type: "object", properties: { index: { type: "number" }, text: { type: "string" } }, required: ["index", "text"], additionalProperties: false } } }, required: ["lines"], additionalProperties: false } } }, }; const response = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(requestBody), }); const json = await response.json(); const content = json.choices?.[0]?.message?.content ?? "{}"; try { const parsed = JSON.parse(content); return parsed.lines ?? []; } catch (e) { throw new Error("JSON parse error: " + e); } }

↓いったんtsだが
script.ts
// @ts-check cosense.PageMenu.addMenu({ title: "ask", image: "https://gyazo.com/cda5cc71ecf260c8e200ef14c1660e8f/raw", onClick: main, }); async function main() { const lines: Line[] = cosense.Page.lines.map((l, i) => ({ text: l.text, index: i })); const answer = await askChatGPT(lines); insertAnswers(lines, answer); } function insertAnswers(lines: Line[], answers: AnswerLine[]) { answers.reduce((offset, ans) => { const originalLine = lines.find(l => l.index === ans.index); if (!originalLine) return offset; const indent = createIndent(countIndent(originalLine.text) + 1); cosense.Page.insertLine(indent + ans.text, ans.index + 1 + offset); return offset + 1; }, 0); } function countIndent(text: string) { const match = text.match(/^(\s*)/); return match ? match[1].length : 0; } function createIndent(count: number) { return ' '.repeat(count); } type Line = { text: string; index: number; } type AnswerLine = { index: number; // Line.indexに対する返答 text: string; } async function askChatGPT(lines: Line[]): Promise<Line[]> { const systemPrompt = ` あなたはCosenseのコメントアシスタントです。 以下の制約と要件を守って、ユーザーからの指示に応えてください。 - 出力はJSONの配列である - 各要素は { "index": number, "text": string } の形とする - "index"はコメントを追加したい元の行のindex番号(0はタイトル行、1以降は本文) - "text"は改行なしの1行文章で、末尾に[gpt-4.icon]をつける - 必要がない行には返答をしなくてもよい(その場合は配列要素を作らない) - 多くても全体で3箇所程度で良い - コメントは適度に短い文にする `; const userPrompt = ` 以下にCosenseの各行を示します(indexとtext)。 これに対して、有益な指摘、批判、疑問の投げかけ、質問への適切な返答を行ってください Lines: ${JSON.stringify(lines, null, 2)} `; const result = await openAI(userPrompt, systemPrompt); return result.sort((a, b) => a.index - b.index); } async function openAI(prompt: string, systemPrompt: string): Promise<Line[]> { const apiKey = localStorage.getItem("OPENAI_API_KEY"); if (!apiKey) { throw new Error("OpenAI APIキーが設定されていません。"); } const requestBody = { model: "gpt-4o-mini", messages: [ { role: "system", content: systemPrompt }, { role: "user", content: prompt }, ], temperature: 0.7, max_tokens: 1000, response_format: { type: "json_schema", json_schema: { name: "CosenseAnswer", strict: true, schema: { type: "object", properties: { lines: { type: "array", items: { type: "object", properties: { index: { type: "number" }, text: { type: "string" } }, required: ["index", "text"], additionalProperties: false } } }, required: ["lines"], additionalProperties: false } } }, }; const response = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(requestBody), }); const json = await response.json(); const content = json.choices?.[0]?.message?.content ?? "{}"; try { const parsed = JSON.parse(content); return parsed.lines ?? [] as Line[]; } catch (e) { throw new Error("JSON parse error: " + e); } }



↑まずpropmptを更新するために、コンパイルしないといけないのが面倒すぎるのでどうにかしたい
行に対する重み付けがあるといい?
ページ全体に対するコメントが欲しいときと、会話をしたいときとある
会話をする場合は、下に下に追加してほしい
カーソルの位置を与えるとか、to gpt-4のようなキーワードを自分で書くとか、 [mrsekut.icon] があるところを重点的に見れるようにするとか
適宜プロンプトを与えられると良い
1行で突っ込んでほしいこともあれば、10行程度でまとめてほしいこともある
追加の指示ができるといい
なんか記法を導入してUserScriptに含めても良いし、
普通に、Cosenseのテキスト上で指示しても良い
こっちのほうが自然
promptのcacheみたいなやつ調べよう
毎回同じprompt投げるのが無駄?