GPT Academic 插件开发指南:从函数式插件到类式插件的完整实践
GPT Academic 的核心能力来源于其丰富的插件生态:从论文翻译到代码分析,从联网搜索到图片生成,每一个功能都以插件形式实现在 crazy_functions/ 目录下。本文基于仓库中的 插件开发文档 与真实源码,完整讲解两类插件(函数式与类式)的编写、注册、参数传递机制、文件上传与批量处理等实战技巧,并深入 toolbox.py、crazy_functions/crazy_utils.py 与插件模板类,帮助读者掌握从零开发一个可注册、可热更新、可交互的 GPT Academic 插件的完整能力。
插件系统概览
在开始编写代码之前,先了解 GPT Academic 插件系统的基本架构。
所有插件都位于 crazy_functions/ 目录下。当用户点击界面上的功能按钮或从下拉菜单选择插件时,系统会调用相应的插件函数或类来处理请求。插件可以访问用户的输入文本、对话历史、文件上传等信息,并通过大模型生成响应。
GPT Academic 支持两种插件形式:
| 类型 | 适用场景 | 特点 |
|---|---|---|
| 函数式插件 | 简单功能,无需用户额外输入 | 开发快速,代码简洁 |
| 类式插件 | 复杂功能,需要二级选项菜单 | 支持参数配置,交互更灵活 |
对于大多数场景,函数式插件已经足够。当需要在执行前让用户选择参数(如翻译语言、输出格式等)时,再考虑使用类式插件。这一区分在源码中也有直接体现:crazy_functional.py 的 get_crazy_functions() 函数集中导入并注册了所有插件,每个插件以字符串名为键,配置字典为值,"Function" 指向函数式入口,"Class"(可选)指向类式插件,例如"注释Python项目"同时注册了 HotReload(注释Python项目) 与 SourceCodeComment_Wrap 两个入口。
开发环境与热重载机制
插件开发无需特殊的环境配置,只需确保已经成功运行了 GPT Academic。开发过程中建议开启热重载功能,修改插件代码后无需重启程序即可生效。
热重载能力来自 toolbox.py 中的 HotReload 装饰器。从源码实现看,它依赖配置项 PLUGIN_HOT_RELOAD:
def HotReload(f):
if get_conf("PLUGIN_HOT_RELOAD"):
@wraps(f)
def decorated(*args, **kwargs):
fn_name = f.__name__
f_hot_reload = getattr(importlib.reload(inspect.getmodule(f)), fn_name)
yield from f_hot_reload(*args, **kwargs)
return decorated
else:
return f
当 PLUGIN_HOT_RELOAD 开启时,装饰器在每次调用插件前通过 importlib.reload 重新加载插件所在模块,并用 getattr 取出同名的最新函数执行。这意味着保存插件文件后,下次调用该插件时会自动加载最新代码,极大提升开发效率;若该配置关闭,HotReload 会原样返回原函数,不影响插件正常运行。
函数式插件开发
从最简单的函数式插件开始,下面这个示例会查询"历史上的今天"发生的事件:
from toolbox import CatchException, update_ui
from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
import datetime
@CatchException
def 历史上的今天(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
"""
插件入口函数
参数说明:
txt - 用户在输入框中输入的文本
llm_kwargs - 大模型参数(温度、top_p 等)
plugin_kwargs - 插件参数(来自二级菜单,函数式插件通常为空)
chatbot - 对话显示组件,用于更新界面
history - 对话历史记录
system_prompt - 系统提示词
user_request - 用户请求信息(包含 IP 等)
"""
# 清空历史,避免上下文过长
history = []
# 获取当前日期
today = datetime.date.today()
# 在界面上显示处理状态
chatbot.append(("正在查询历史上的今天...", "请稍候..."))
yield from update_ui(chatbot=chatbot, history=history)
# 构造提问
query = f"请列举历史上 {today.month} 月 {today.day} 日发生的 3 个重要事件,简要说明每个事件的背景和影响。"
# 调用大模型并流式输出
response = yield from request_gpt_model_in_new_thread_with_ui_alive(
inputs=query,
inputs_show_user=query,
llm_kwargs=llm_kwargs,
chatbot=chatbot,
history=history,
sys_prompt="你是一位历史学专家,擅长介绍历史事件。"
)
# 更新对话历史
history.extend([query, response])
yield from update_ui(chatbot=chatbot, history=history)
这个示例虽然简单,但包含了插件开发的所有核心要素,逐一解析如下:
装饰器 @CatchException 是一个错误处理包装器,确保插件运行时的异常不会导致整个程序崩溃。从 toolbox.py 的源码实现看,它会区分 FriendlyException 与一般 Exception 两类异常:前者渲染友好的错误 HTML,后者将精简后的 traceback 以 [Local Message] 插件调用出错 的形式追加到对话界面,并通过 update_ui 刷新界面。在开发阶段,它会把错误信息显示在对话界面中,方便调试。
函数签名定义了插件接收的参数。这七个参数是所有插件的标准接口,可以按需要使用其中的部分或全部。
yield from update_ui() 是更新界面的关键。由于插件函数是一个生成器,使用 yield from 可以实时将处理进度反馈给用户,而不是等待所有处理完成后才显示结果。
request_gpt_model_in_new_thread_with_ui_alive() 是调用大模型的核心函数,定义于 crazy_functions/crazy_utils.py。从源码实现看,它内部使用 ThreadPoolExecutor 在独立线程中发起请求,同时以约 0.2 秒的间隔向 chatbot 回写部分结果以保持界面响应;它还内置了看门狗机制(watch_dog_patience)检测程序终止,并可在 token 溢出时通过 input_clipping 自动截断重试。函数返回的 response 是模型的完整回复文本。其关键参数包括:
| 参数 | 含义 |
|---|---|
inputs |
真正发送给模型的输入文本 |
inputs_show_user |
展示给用户的输入,可借此在报告中隐藏啰嗦的真实输入 |
llm_kwargs |
大模型参数(模型名、温度、top_p 等) |
chatbot |
界面对话窗口句柄,用于数据流可视化 |
history |
对话历史列表 |
sys_prompt |
系统提示词 |
refresh_interval |
UI 刷新间隔(默认 0.2,建议低于 1,不可高于 3) |
handle_token_exceed |
是否自动处理 token 溢出(默认开启,溢出时暴力截断) |
retry_times_at_unknown_error |
未知错误时的重试次数(默认 2) |
注册插件
插件编写完成后,需要在 crazy_functional.py 的 function_plugins 字典中注册才能在界面上使用。以仓库中已注册的插件为例:
function_plugins = {
# ... 其他插件 ...
"历史上的今天": {
"Group": "对话", # 所属分组,用于插件分类
"Color": "secondary", # 按钮颜色:primary/secondary/stop
"AsButton": False, # True 显示为按钮,False 放入下拉菜单
"Info": "查询历史上今天发生的重要事件", # 插件说明
"Function": HotReload(历史上的今天), # 关联函数,HotReload 启用热重载
},
# ... 其他插件 ...
}
注册时需要指定的属性含义如下:
Group决定插件在界面上的分类位置。可选值包括"对话"、"编程"、"学术"、"智能体",也可以用|分隔同时归属多个分组,如 crazy_functional.py 中"虚空终端"注册的"Group": "对话|编程|学术|智能体"。Color控制按钮颜色,可选primary/secondary/stop。AsButton控制插件的显示形式。设为True时,插件以按钮形式显示在主界面;设为False时,插件出现在下拉菜单中。常用功能建议设为按钮,以便快速访问。Info是插件说明文本,展示在菜单中。Function必须用HotReload()包装,这样修改插件代码后无需重启程序。
保存文件后,刷新浏览器页面,新插件就会出现在界面上。
类式插件开发
当插件需要用户在执行前配置参数时,类式插件是更好的选择。它允许定义一个二级选项菜单,用户可以在其中输入文本、选择下拉选项等。
类式插件的基类是 crazy_functions/plugin_template/plugin_class_template.py 中的 GptAcademicPluginTemplate,参数描述模型 ArgProperty 也由该模块提供。下面是一个支持自定义天数的"历史上的今天"类式插件:
from crazy_functions.plugin_template.plugin_class_template import GptAcademicPluginTemplate, ArgProperty
from toolbox import update_ui
from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
import datetime
class HistoryToday_Wrap(GptAcademicPluginTemplate):
"""
类式插件必须继承 GptAcademicPluginTemplate
"""
def __init__(self):
"""
初始化函数。注意:execute 方法可能在不同线程中运行,
避免在此存储会被多线程访问的状态。
"""
pass
def define_arg_selection_menu(self):
"""
定义二级选项菜单
返回一个字典,每个键值对对应一个参数。
支持的参数类型:
- type="string": 文本输入框
- type="dropdown": 下拉选择菜单
"""
gui_definition = {
"main_input": ArgProperty(
title="查询日期",
description="留空则查询今天,或输入指定日期如 3-15",
default_value="",
type="string"
).model_dump_json(),
"event_count": ArgProperty(
title="事件数量",
description="选择要列举的历史事件数量",
options=["3个事件", "5个事件", "10个事件"],
default_value="3个事件",
type="dropdown"
).model_dump_json(),
"advanced_arg": ArgProperty(
title="额外要求",
description="如有特殊要求可在此输入,如:只列举中国历史事件",
default_value="",
type="string"
).model_dump_json(),
}
return gui_definition
def execute(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
"""
执行插件主逻辑
用户在二级菜单中的选择会通过 plugin_kwargs 字典传入。
字典的键与 define_arg_selection_menu 中定义的参数名对应。
"""
# 从 plugin_kwargs 获取用户选择
main_input = plugin_kwargs.get("main_input", "")
event_count = plugin_kwargs.get("event_count", "3个事件")
advanced_arg = plugin_kwargs.get("advanced_arg", "")
# 解析日期
if main_input:
try:
month, day = map(int, main_input.split("-"))
except Exception:
month, day = datetime.date.today().month, datetime.date.today().day
else:
month, day = datetime.date.today().month, datetime.date.today().day
# 解析事件数量
count = int(event_count.replace("个事件", ""))
# 构造提问
query = f"请列举历史上 {month} 月 {day} 日发生的 {count} 个重要事件。"
if advanced_arg:
query += f" 额外要求:{advanced_arg}"
# 清空历史并显示状态
history = []
chatbot.append((f"查询 {month}月{day}日 的历史事件", "正在查询..."))
yield from update_ui(chatbot=chatbot, history=history)
# 调用大模型
response = yield from request_gpt_model_in_new_thread_with_ui_alive(
inputs=query,
inputs_show_user=query,
llm_kwargs=llm_kwargs,
chatbot=chatbot,
history=history,
sys_prompt="你是一位历史学专家。"
)
history.extend([query, response])
yield from update_ui(chatbot=chatbot, history=history)
类式插件的核心在于 define_arg_selection_menu() 方法。它返回的字典定义了二级菜单的结构,每个参数都由 ArgProperty 对象描述。ArgProperty 是一个 Pydantic 模型,包含五个字段:
| 字段 | 含义 | 约束 |
|---|---|---|
title |
参数标题 | 字符串 |
description |
参数说明 | 字符串 |
default_value |
默认值 | 字符串 |
type |
控件类型 | 目前支持 string(文本输入框)与 dropdown(下拉选择) |
options |
下拉选项列表 | 仅当 type="dropdown" 时使用 |
每个 ArgProperty 通过 model_dump_json() 序列化为 JSON 字符串后放入返回字典。从模板类源码看,前端菜单由 get_js_code_for_generating_menu 方法生成:它把 define_arg_selection_menu() 的返回值序列化为 JSON 并做 base64 编码传给前端渲染,同时会执行硬约束校验——参数数量最多 8 个,超出会抛出 ValueError。
有两个特殊的参数名需要注意:
main_input会与界面主输入框自动同步,用户在输入框中的内容会预填到这个参数;advanced_arg会与界面的高级参数输入区自动同步。
除此之外,可以自由定义其他参数名,如示例中的 event_count。
另外,execute 方法可能在不同线程中运行(模板类注释明确提醒"不应在插件实例中存储可能被多线程访问的状态"),开发时不要把可变状态放在实例属性上。
注册类式插件
注册类式插件时,需要同时指定 Function 和 Class:
"历史上的今天(高级版)": {
"Group": "对话",
"Color": "stop",
"AsButton": False,
"Info": "可自定义日期和事件数量的历史查询插件",
"Function": HotReload(历史上的今天), # 兼容虚空终端调用
"Class": HistoryToday_Wrap, # 类式插件的类名
},
当插件同时注册了 Function 和 Class 时,界面按钮会触发类式插件(显示二级菜单),而虚空终端等自然语言调用场景会使用函数式入口。这一点在 crazy_functional.py 中也有官方注释佐证,例如"Arxiv论文翻译"同时注册了 Function: HotReload(Latex翻译中文并重新编译PDF)(注释说明"当注册 Class 后,Function 旧接口仅会在'虚空终端'中起作用")和 Class: Arxiv_Localize。仓库中 Conversation_To_File_Wrap、Document_Conversation_Wrap、ImageGen_Wrap、NetworkGPT_Wrap 等都是可以参考的类式插件实现。
实用开发技巧
处理文件上传
许多插件需要处理用户上传的文件。用户上传的文件路径会通过 txt 参数传入,可以使用以下模式解析:
import os, glob
@CatchException
def 处理上传文件(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
# txt 可能是单个文件路径,也可能是目录路径
if os.path.isfile(txt):
file_list = [txt]
elif os.path.isdir(txt):
# 获取目录下所有 PDF 文件
file_list = glob.glob(os.path.join(txt, "*.pdf"))
else:
chatbot.append(("错误", "请先上传文件或输入有效路径"))
yield from update_ui(chatbot=chatbot, history=history)
return
for file_path in file_list:
# 处理每个文件...
pass
多线程批量处理
当需要处理大量文件或执行耗时操作时,可以使用 crazy_functions/crazy_utils.py 中的多线程请求函数 request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency 提升效率:
from crazy_functions.crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
def 批量处理(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
# 准备多个任务
inputs_array = ["任务1", "任务2", "任务3"]
inputs_show_user_array = inputs_array.copy()
# 并行执行
results = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
inputs_array=inputs_array,
inputs_show_user_array=inputs_show_user_array,
llm_kwargs=llm_kwargs,
chatbot=chatbot,
history_array=[[] for _ in inputs_array],
sys_prompt_array=["" for _ in inputs_array],
)
# results 是所有任务结果的列表
从源码实现看,该函数以 _array 结尾的参数都是列表,列表长度为子任务数量,执行时会把列表拆解、放到每个子线程中分别执行,并实时在 UI 上反馈远程数据流。它还提供 max_workers 参数控制线程池大小(默认读取配置项 DEFAULT_WORKER_NUM,用于避免高频请求模型导致限流错误)、scroller_max_len 控制数据流滚动显示长度、show_user_at_complete 控制结束时是否把完整输入-输出结果展示在聊天框。如果某个子任务出错,results 中对应项会携带 traceback 报错信息,方便调试和定位问题。
生成下载文件
插件可以生成文件供用户下载。使用 toolbox.py 中的 on_report_generated() 函数将文件注册到下载区:
from toolbox import on_report_generated
def 生成报告(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
# ... 生成报告内容 ...
# 保存文件
report_path = "path/to/report.pdf"
with open(report_path, "wb") as f:
f.write(report_content)
# 注册到下载区
cookies = user_request.get("cookies", {})
cookies, report_files, chatbot = on_report_generated(
cookies=cookies,
files=[report_path],
chatbot=chatbot
)
yield from update_ui(chatbot=chatbot, history=history)
on_report_generated(cookies, files, chatbot) 接收 cookies 字典、文件路径列表和 chatbot 句柄,将生成的文件登记到会话的下载区并刷新界面,用户即可在界面上下载。
调试与测试
开发过程中,可以使用以下方法进行调试:
查看日志输出:在插件代码中使用 logger 输出调试信息:
from loguru import logger
logger.info(f"处理文件: {file_path}")
logger.warning("参数可能不正确")
logger.error("发生错误")
日志会输出到终端,帮助追踪插件的执行流程。
使用虚空终端测试:虚空终端支持通过自然语言调用插件,是快速测试插件功能的好方法。只需在虚空终端中描述想执行的任务,系统会自动匹配并调用相应插件。这也是为什么类式插件建议同时注册 Function 入口——自然语言调用走的正是函数式入口。
渐进式开发:建议先实现最基本的功能,验证可行后再逐步添加复杂特性。利用热重载特性,可以在不重启程序的情况下快速迭代。
小结
GPT Academic 的插件体系可以归纳为三条主线:
- 函数式插件:标准七参数生成器函数 +
@CatchException+HotReload注册,适合无需额外参数的快速功能; - 类式插件:继承
GptAcademicPluginTemplate,通过define_arg_selection_menu()声明二级菜单(最多 8 个ArgProperty参数,支持string/dropdown两类控件,main_input与advanced_arg为特殊同步参数),适合需要用户配置参数的复杂交互; - 核心工具链:
update_ui负责界面刷新,request_gpt_model_in_new_thread_with_ui_alive负责单请求流式输出,多线程版函数负责批量并行任务,on_report_generated负责产出文件下载。
掌握以上内容后,可以进一步阅读 crazy_functions/ 目录下的现有插件源码(如 PDF_Translate_Wrap.py、Internet_GPT_Wrap.py、Mermaid_Figure_Gen.py)学习更多实现技巧,也可以探索 自定义按钮 功能创建更轻量的快捷功能,或查阅 主题定制 为插件界面增添个性。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00