"""Gudao Laya lesson. Default: validate only. --run: download/load real model. Python 3.10+. Actual model runtime requires the official `laya` package. No synthetic model answers or fallback predictions are supplied. """ import argparse import importlib.metadata import json import math import os from pathlib import Path import sys import time MODEL = 'convaiinnovations/laya' LABELS = ('concept', 'debug', 'clarify') DEFAULT = { 'state': '我不懂向量,为什么一句话可以变成一组数字?', 'questions': {'help_route': { 'type': 'choice', 'instructions': '为这条学习求助选择一个最合适的入口。', 'criteria': { 'concept': '请求解释概念,没有具体程序错误。', 'debug': '请求排查代码或具体程序报错。', 'clarify': '信息不足、与学习无关,或无法明确归入前两类,需要先询问。', }, }}, 'expected': 'concept', } def validate_request(request): if not isinstance(request, dict): raise ValueError('题目必须是 JSON 对象。') state = request.get('state') if not isinstance(state, str) or not state.strip() or len(state) > 300: raise ValueError('state 必须是 1~300 字的求助。') if request.get('expected') not in LABELS: raise ValueError('expected 必须是 concept/debug/clarify。') questions = request.get('questions') if not isinstance(questions, dict) or set(questions) != {'help_route'}: raise ValueError('本课只接受 help_route 问题。') question = questions['help_route'] if not isinstance(question, dict) or question.get('type') != 'choice': raise ValueError('本课问题类型必须是 choice。') instruction = question.get('instructions') criteria = question.get('criteria') if not isinstance(instruction, str) or not instruction.strip() or len(instruction) > 300: raise ValueError('请填写不超过 300 字的 instructions。') if not isinstance(criteria, dict) or set(criteria) != set(LABELS): raise ValueError('criteria 必须含三个课程选项。') if any(not isinstance(v, str) or not v.strip() or len(v) > 300 for v in criteria.values()): raise ValueError('每个选项需有 1~300 字的说明。') return request def probability(value): return type(value) in (int, float) and math.isfinite(value) and 0 <= value <= 1 def validate_result(result): if not isinstance(result, dict) or not isinstance(result.get('answers'), dict): raise ValueError('SDK 未返回 answers 对象。') answer = result['answers'].get('help_route') if not isinstance(answer, dict) or answer.get('choice') not in LABELS: raise ValueError('模型返回了未知选项。') probs = answer.get('probabilities') if not isinstance(probs, dict) or set(probs) != set(LABELS): raise ValueError('模型返回的概率选项不完整。') if not all(probability(v) for v in probs.values()) or abs(sum(probs.values()) - 1) > .002: raise ValueError('模型返回的概率分布无效。') if probs[answer['choice']] + .0001 < max(probs.values()): raise ValueError('模型选项与最大概率不一致。') if not probability(answer.get('confidence')): raise ValueError('模型返回的 confidence 无效。') return answer def run_model(request, device): # This import and all model/network activity occur only after explicit --run. os.environ.setdefault('USE_TF', '0') import laya start = time.perf_counter() agent = laya.load(MODEL, subfolder='multilingual', device=device) loaded = time.perf_counter() result = agent.predict(request['state'], request['questions']) finished = time.perf_counter() answer = validate_result(result) return { 'status': 'real_model_result', 'model': MODEL, 'subfolder': 'multilingual', 'sdk_version': importlib.metadata.version('laya'), 'device_requested': device, 'device_actual': str(agent.device), 'load_seconds': round(loaded - start, 4), 'inference_seconds': round(finished - loaded, 4), 'request': request, 'result': result, 'matches_your_expected': answer['choice'] == request['expected'], 'note': '单题匹配仅核对你的预期,不代表总体准确率。模型未在本次推理中训练。', } def main(argv=None): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--input', type=Path, help='网页下载的 laya-request.json') parser.add_argument('--output', type=Path, help='保存报告;默认只打印') parser.add_argument('--run', action='store_true', help='下载/加载真实 Laya 并推理') parser.add_argument('--device', choices=['cpu', 'cuda'], default='cpu') args = parser.parse_args(argv) try: if args.output and args.output.exists(): raise ValueError('报告文件已存在,请改用新的 --output 文件名。') if args.input and args.input.stat().st_size > 20000: raise ValueError('本课题目文件不能超过 20 KB。') request = validate_request(json.loads(args.input.read_text(encoding='utf-8-sig')) if args.input else DEFAULT) report = run_model(request, args.device) if args.run else { 'status': 'prepared_only', 'request': request, 'note': '仅检查题目,没有加载模型、联网或产生预测。加 --run 才会运行真实模型。', } content = json.dumps(report, ensure_ascii=False, indent=2, allow_nan=False) if args.output: with args.output.open('x', encoding='utf-8') as stream: stream.write(content + '\n') print(content) return 0 except Exception as exc: print(f'实验停止:{exc}\n没有生成替代或模拟的模型答案。', file=sys.stderr) return 1 if __name__ == '__main__': if hasattr(sys.stdout, 'reconfigure'): sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') raise SystemExit(main())