feat(lesson-preparation): add AI evaluation, analytics, attachments, calendar, comments, review, substitutes, formative, and version diff
- Add actions-ai-evaluation, actions-analytics, actions-attachments, actions-calendar, actions-comments, actions-formative, actions-questions, actions-review, actions-substitutes - Add corresponding data-access layers for each new action module - Add calendar-view, curriculum-map-view, version-diff-viewer components - Add editor-slice, selection-slice, version-slice hooks for state management - Add document-diff and scope-check lib utilities - Add default-question-service and external-questions-bridge services
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import "server-only";
|
||||
|
||||
import { z } from "zod";
|
||||
import { env } from "@/env.mjs";
|
||||
import { createAiChatCompletion } from "@/shared/lib/ai";
|
||||
import { isRecord } from "@/shared/lib/type-guards";
|
||||
import {
|
||||
getKnowledgePointsByTextbookId,
|
||||
getKnowledgePointsByChapterId,
|
||||
@@ -18,18 +18,23 @@ const SuggestedKpListSchema = z.array(SuggestedKpSchema);
|
||||
|
||||
/** 从 unknown 节点安全提取文本(类型守卫从 unknown 收窄) */
|
||||
const extractNodeText = (node: unknown): string => {
|
||||
if (!node || typeof node !== "object") return ""
|
||||
// 从 unknown 收窄为 Record<string, unknown> 以进行字段检查
|
||||
const record = node as Record<string, unknown>
|
||||
const data = record.data
|
||||
if (!data || typeof data !== "object") return ""
|
||||
// 从 unknown 收窄为 Record<string, unknown> 以进行字段检查
|
||||
const dataRecord = data as Record<string, unknown>
|
||||
const html = typeof dataRecord.html === "string" ? dataRecord.html : ""
|
||||
const sourceText = typeof dataRecord.sourceText === "string" ? dataRecord.sourceText : ""
|
||||
if (!isRecord(node)) return ""
|
||||
const data = node.data
|
||||
if (!isRecord(data)) return ""
|
||||
const html = typeof data.html === "string" ? data.html : ""
|
||||
const sourceText = typeof data.sourceText === "string" ? data.sourceText : ""
|
||||
return html || sourceText || ""
|
||||
}
|
||||
|
||||
// P2 修复:AI prompt 提取为模块常量,便于维护和未来国际化
|
||||
// 注:AI prompt 属于系统级提示词,非用户可见文本,暂不纳入 next-intl i18n 体系
|
||||
const AI_SUGGEST_PROMPT_TEMPLATE = `你是教学设计助手。以下是教师备课内容:
|
||||
---
|
||||
{text}
|
||||
---
|
||||
请从下列知识点中推荐最相关的 3-8 个,并说明理由。返回 JSON 数组,每项含 id/name/reason。
|
||||
候选知识点:{kpList}`;
|
||||
|
||||
export async function suggestKnowledgePoints(
|
||||
doc: { nodes: unknown[] },
|
||||
textbookId?: string,
|
||||
@@ -52,13 +57,10 @@ export async function suggestKnowledgePoints(
|
||||
|
||||
const kpList = allKps.map((kp) => ({ id: kp.id, name: kp.name })).slice(0, 100);
|
||||
|
||||
// 3. 调用 AI
|
||||
const prompt = `你是教学设计助手。以下是教师备课内容:
|
||||
---
|
||||
${text}
|
||||
---
|
||||
请从下列知识点中推荐最相关的 3-8 个,并说明理由。返回 JSON 数组,每项含 id/name/reason。
|
||||
候选知识点:${JSON.stringify(kpList)}`;
|
||||
// 3. 调用 AI(使用模板构建 prompt)
|
||||
const prompt = AI_SUGGEST_PROMPT_TEMPLATE
|
||||
.replace("{text}", text)
|
||||
.replace("{kpList}", JSON.stringify(kpList));
|
||||
|
||||
const { content } = await createAiChatCompletion({
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
|
||||
Reference in New Issue
Block a user