根据各学区教育部门的概述,法国教育部于 2025 年 6 月发布的教育领域 AI 使用框架允许将 AI 用于备课、评估和批改,同时要求教师履行两项义务:始终核查 AI 生成的内容,并公开说明其使用情况。该框架明确禁止在面向大众的 AI 产品中使用个人数据或机密数据,并建议优先采用自由软件方案。本地模型符合这三点:没有任何数据离开电脑,无需注册,而且开放权重模型属于通常意义上的自由软件方案。
为避免反复输入指令,请使用 Modelfile 文件创建自定义模型:其中的 SYSTEM 指令定义了每次对话都会应用的系统消息。下面的文件固定了生成练习单时使用的角色、难度级别和风格。
练习助手的 Modelfile
FROM qwen3.5:9b
PARAMETER temperature 0.4
PARAMETER num_ctx 8192
SYSTEM """Tu aides un professeur de collège à préparer des exercices conformes aux programmes
français du cycle 4. Vocabulaire adapté à l'âge des élèves. Tu ne donnes que des
corrigés dont tu es sûr ; sinon tu écris : à vérifier. Tu n'inventes jamais de citation,
de date ou de source."""
创建并启动模型
ollama create prof-exos -f Modelfile
ollama run prof-exos
#生成可实际使用的选择题,而不只是读得懂的题目
让模型以自由文本形式生成选择题,结果很难复用:需要在课堂使用的工具中重新录入。最好要求模型采用结构化格式,再由代码进行校验。Ollama 可以将回答限制为符合某个 JSON 模式:根据其文档,应将该模式传入 format 字段。模型负责编写题目;脚本检查是否有四个互不相同的选项且只有一个正确答案,然后写出可导入的文件。
生成并检查选择题测验
import json, requests, re
SCHEMA = {'type': 'object', 'properties': {'questions': {'type': 'array', 'items': {
'type': 'object', 'properties': {
'enonce': {'type': 'string'},
'choix': {'type': 'array', 'items': {'type': 'string'}},
'bonne_reponse': {'type': 'integer'},
'explication': {'type': 'string'}},
'required': ['enonce', 'choix', 'bonne_reponse', 'explication']}}},
'required': ['questions']}
def generer(sujet, niveau, n=10, modele='prof-exos'):
consigne = (f'Rédige {n} questions à choix multiple sur : {sujet}. Niveau : {niveau}. '
'Quatre choix par question, dont un seul correct ; bonne_reponse est l\'indice (0 à 3). '
'Les mauvais choix doivent être plausibles. Une phrase d\'explication. '
'Réponds par un JSON conforme à : ' + json.dumps(SCHEMA))
r = requests.post('http://localhost:11434/api/chat', json={
'model': modele, 'stream': False, 'format': SCHEMA,
'messages': [{'role': 'user', 'content': consigne}]})
return json.loads(r.json()['message']['content'])['questions']
def valides(questions):
ok, rejets, vus = [], [], set()
for q in questions:
c = q.get('choix', [])
if (len(c) != 4 or len({x.strip().lower() for x in c}) != 4
or q.get('bonne_reponse') not in (0, 1, 2, 3) or q['enonce'].strip().lower() in vus):
rejets.append(q); continue
vus.add(q['enonce'].strip().lower()); ok.append(q)
return ok, rejets
def esc(s):
return re.sub(r'([~=#{}:])', r'\\\1', s)
def vers_gift(questions):
blocs = []
for i, q in enumerate(questions, 1):
lignes = [('=' if j == q['bonne_reponse'] else '~') + esc(c) for j, c in enumerate(q['choix'])]
blocs.append(f"::Q{i}::{esc(q['enonce'])} {{\n" + '\n'.join(lignes) + '\n}\n')
return '\n'.join(blocs)
Moodle 使用的 GIFT 格式是一种简单的文本文件格式,可以导入题库。如果您的机构使用其他工具,请调整最后一个函数:原则仍然是根据经过检查的数据生成文件,而不是复制粘贴模型生成的文本。脚本并不检查题目内容是否真实正确,因此还需要阅读有关核查的章节。
import random
from decimal import Decimal
def fr(d):
return str(d.normalize() if d == d.to_integral() else d).replace('.', ',')
def exercices_decimaux(n=8, graine=None):
hasard = random.Random(graine)
sortie = []
for i in range(n):
nb = 2 if i < 2 else hasard.choice([3, 3, 4])
vals = [Decimal(hasard.randint(10, 9999)) / hasard.choice([10, 100]) for _ in range(nb)]
signes = ['+'] + [hasard.choice(['+', '-']) for _ in vals[1:]]
total = sum(v if s == '+' else -v for v, s in zip(vals, signes))
if total < 0:
continue
enonce = ' '.join((s + ' ' if k else '') + fr(v) for k, (v, s) in enumerate(zip(vals, signes)))
sortie.append((enonce, fr(total)))
return sortie
for e, r in exercices_decimaux(graine=42):
print(e, '=', r)
Prépare pour des élèves de 5e (niveau A2 du CECRL) une fiche sur le present simple
et le present continuous : 5 phrases à conjuguer, 3 phrases à transformer, un court
dialogue à trous. Vocabulaire courant uniquement. Donne le corrigé à la fin, séparé
par une ligne « CORRIGÉ », et signale entre crochets toute phrase où les deux temps
sont possibles.