当前位置:   article > 正文

AI论文生成系统JAVA代码简单实现

AI论文生成系统JAVA代码简单实现

       大学开学了,博主的堂弟现在正值大四,论文是分毫未动,想要用国内网上的AI辅助写作拟一篇文章进行完成,刚好聊天了解此事,我有点感兴趣,去百度了一下,各个AI生成网站价格不菲,临时起意想做一个AI代码生成程序,当然这种生成的论文问题还是特别多,无法和成熟的软件相比不过胜在成本低,可塑性强。

前置条件

        首先我们需要对接现有大模型的接口,国内的各个大模型都有自己的商场,GPT-4在理解和生成自然语言方面比国内的模型在具有更强的能力,因此作者嫌麻烦花了10米去搞了个国内的中转接口去调用GPT-4的api,否则你就需要魔法上网,去弄openAI的接口了。

代码实现

        首先简单讲解一下AI对话的原理,首先要了解tokens,引用一句AI的解释:

        在AI和机器学习的上下文中,"tokens" 通常是指文本数据被预处理后得到的基本单位。这些单位可以是单词、子词(subwords)、字符或更复杂的结构,取决于你选择的分词方法。Tokenization是自然语言处理中的一个重要步骤,它有助于将文本转换为机器学习模型可以理解和处理的形式。

        简单来说就是你发送的消息和AI返回给你的内容都与tokens有关,字符越多,tokens越大,一般来说400个汉字,相当于1000tokens

        而对话简单来讲就是你请求的时候会携带上文AI的回答或者你的塑造的上下文进行调用,如果不加限制就很容易消耗特别多的tokens,有些网站都是将次数限制到10条可能就是这个原因。

        论文这个东西文本实在太多,使用我直接不考虑带上文,这里是我的创建对话的代码实现:

  1. private ChatResponseWrapper createChat(String message, String model) {
  2. try {
  3. if (!chatConfig.isEnable()) {
  4. log.warn("createChat fail,ai is unable!");
  5. throw new AIExecuteException("check ai is enable!");
  6. }
  7. List<ChatMessagesDto> messages = new ArrayList<>();
  8. ChatMessagesDto systemMessage = new ChatMessagesDto(role, message);
  9. messages.add(systemMessage);
  10. //model 代表gpt当前的模型,我开发时使用的是3.5,role代表当前的角色
  11. ChatRequest chatCompletionRequest = ChatRequest.builder().model(model).messages(messages).user(role).maxTokens(4096).temperature(BigDecimal.ONE).build();
  12. ObjectMapper objectMapper = new ObjectMapper();
  13. //对接大模型的url
  14. HttpRequest request = HttpUtil.createPost(chatConfig.getUrl());
  15. request.setConnectionTimeout(TIMEOUT);
  16. request.body(objectMapper.writeValueAsBytes(chatCompletionRequest));
  17. // 设置请求头
  18. request.header("Content-Type", "application/json");
  19. //携带token
  20. request.header("Authorization", "Bearer " + chatConfig.getAccessKey());
  21. String body;
  22. try (HttpResponse response = request.execute()) {
  23. body = response.body();
  24. }
  25. if (log.isDebugEnabled()) {
  26. log.debug("get Ai response body:{}", body);
  27. }
  28. ChatResponseWrapper wrapper = new ChatResponseWrapper();
  29. wrapper.setResponse(objectMapper.readValue(body, ChatResponse.class));
  30. wrapper.setQuestion(message);
  31. return wrapper;
  32. } catch (JsonProcessingException e) {
  33. log.error("Json Parse error,message:{}", message, e);
  34. throw new AIExecuteException(e.getMessage());
  35. } catch (Exception e) {
  36. log.error("createChat error,message:{}", message, e);
  37. throw new AIExecuteException(e.getMessage());
  38. }
  39. }

        接下来创建生成大纲的接口,入参是标题,也就是前端传入的值

  1. public List<ThesisOutline> genThesisOutline(String message) {
  2. try {
  3. String template = "以《{}》为题目,给我一篇计算类论文的大纲,使用中文回答,一级目录使用编号1,二级目录使用编号1.1,以此类推";
  4. //对ai的问句
  5. String askQuestion = StringUtil.format(template, message);
  6. ChatResponseWrapper wrapper = aiClient.createChat35(askQuestion);
  7. ChatResponse response = wrapper.getResponse();
  8. if (response == null) {
  9. throw new AIExecuteException();
  10. }
  11. List<ThesisOutline> thesisOutlines = new ArrayList<>();
  12. for (ChatChoice choice : response.getChoices()) {
  13. ChatMessagesDto choiceMessage = choice.getMessage();
  14. String content = choiceMessage.getContent();
  15. if (StringUtil.isEmpty(content)) {
  16. continue;
  17. }
  18. //过滤为空的行和不为数字的行
  19. List<String> outlines = StringUtil.split(content, "\n").stream().filter(StringUtil::isNotBlank).filter(StringUtil::isStartsWithDigit).map(String::trim).collect(Collectors.toList());
  20. for (String outlineContent : outlines) {
  21. ThesisOutline outline = new ThesisOutline();
  22. outline.setContent(outlineContent);
  23. thesisOutlines.add(outline);
  24. }
  25. }
  26. return thesisOutlines;
  27. } catch (Exception e) {
  28. log.error("genThesisOutline error", e);
  29. return Collections.emptyList();
  30. }
  31. }
  32. /**
  33. * 论文标题实体
  34. * @author zwh
  35. */
  36. @Data
  37. public class ThesisOutline {
  38. /**
  39. * 层级
  40. */
  41. private Integer level;
  42. /**
  43. * 标题内容
  44. */
  45. private String content;
  46. }

这个接口将生成的数据返回给前端

随手写的前端,样式什么的也懒的调了,能用就行,代码如下:

  1. <template>
  2. <div class="app-container home">
  3. <el-card style="height: 95vh">
  4. <el-button @click="genThesisOutline" :disabled="showText">生成论文大纲</el-button>
  5. <el-button @click="toText" v-if="!showText">变更为文本</el-button>
  6. <el-button @click="toList" v-if="showText">保存并展示为列表</el-button>
  7. <el-button @click="downloadByOutline" icon="el-icon-download" v-if="!showText">生成并下载论文</el-button>
  8. <div style="margin-top: 20px">
  9. <div v-show="!showText">
  10. <el-row style="margin-bottom: 10px">
  11. <el-col :span="4">标题</el-col>
  12. <el-col :span="20">{{ thesis.title }}</el-col>
  13. </el-row>
  14. <el-table ref="table" :data="outlineData" max-height="1100px">
  15. <el-table-column type="index" width="100"/>
  16. <el-table-column label="大纲内容" prop="content"/>
  17. </el-table>
  18. </div>
  19. <el-form v-show="showText">
  20. <el-form-item label="标题">
  21. <el-input v-model="thesis.title"/>
  22. </el-form-item>
  23. <el-form-item label="大纲">
  24. <el-input type="textarea" v-model="outlineText" rows="45"/>
  25. </el-form-item>
  26. </el-form>
  27. </div>
  28. </el-card>
  29. <el-dialog title="生成论文大纲" :visible.sync="outline.visible">
  30. <el-form>
  31. <el-form-item label="论文题目">
  32. <el-input v-model="outline.content"/>
  33. </el-form-item>
  34. </el-form>
  35. <div slot="footer" class="dialog-footer">
  36. <el-button @click="outline.visible = false">取 消</el-button>
  37. <el-button type="primary" @click="genOutline">确认生成大纲</el-button>
  38. </div>
  39. </el-dialog>
  40. </div>
  41. </template>
  42. <script>
  43. import {downloadWord, genOutline} from "@/api";
  44. export default {
  45. name: "Index",
  46. data() {
  47. return {
  48. outline: {
  49. visible: false,
  50. content: null,
  51. },
  52. outlineData: [],
  53. thesis: {
  54. title: null,
  55. messages: []
  56. },
  57. outlineText: null,
  58. showText: false,
  59. };
  60. },
  61. methods: {
  62. genThesisOutline() {
  63. this.outline.visible = true;
  64. },
  65. genOutline() {
  66. genOutline(this.outline.content).then(resp => {
  67. this.outlineData = resp.data
  68. this.thesis.title = this.outline.content
  69. this.outline.content = null
  70. this.outline.visible = false
  71. })
  72. },
  73. toText() {
  74. const fieldValues = this.outlineData.map(obj => obj.content);
  75. this.outlineText = fieldValues.join('\n');
  76. this.showText = true;
  77. },
  78. toList() {
  79. // 使用 split 方法按换行符分割字符串
  80. const lines = this.outlineText.split('\n');
  81. // 过滤掉空字符串,并将每个字符串转化回对象
  82. // 将每行转化为一个对象,该对象具有指定的字段名和内容
  83. this.outlineData = lines
  84. .filter(line => line.trim() !== '')
  85. .map(line => ({content: line}));
  86. this.showText = false;
  87. },
  88. downloadByOutline() {
  89. this.thesis.messages = this.outlineData.map(obj => obj.content);
  90. downloadWord(this.thesis).then(resp => {
  91. // 创建一个blob对象URL
  92. const url = window.URL.createObjectURL(new Blob([resp]));
  93. // 创建一个下载链接元素
  94. const link = document.createElement('a');
  95. link.href = url;
  96. link.setAttribute('download', this.thesis.title+'.docx'); // 设置下载文件名
  97. // 触发下载
  98. document.body.appendChild(link);
  99. link.click();
  100. // 清理
  101. window.URL.revokeObjectURL(url);
  102. })
  103. }
  104. },
  105. };
  106. </script>

正文生成,因为是要对大纲的内容进行循环的请求,使用需要异步请求chatgpt,因为每一个论点请求花费的时间都十分之长,计算了一下一个论点大约30s左右,所以我们这里需要异步去请求论文返回的结果,我这里是使用CompletableFuture去异步请求,方法通过异步方式与AI交互,生成论文文本,并将结果导出为Word文档供用户下载。

  1. @Override
  2. public void genThesisText(List<String> messages, String title, HttpServletResponse resp) {
  3. try {
  4. String template = "请继续以{}为题生成关于:{}的内容,内容需要在500字以上";
  5. List<CompletableFuture<ThesisTextModel>> futures = new ArrayList<>(messages.size());
  6. // 每个标题异步创建会话
  7. for (String message : messages) {
  8. String question = StringUtil.format(template, title, message);
  9. CompletableFuture<ThesisTextModel> future = CompletableFuture.supplyAsync(() -> ThesisTextModel.builder().wrapper(aiClient.createChat35(question)).message(message).build(), executor).exceptionally(e -> {
  10. log.error("createChat sync execute error", e);
  11. return null;
  12. });
  13. futures.add(future);
  14. }
  15. // 获取所有消息内容 (key: 问题, value: 回答)
  16. Map<String, List<String>> allContent = new LinkedHashMap<>();
  17. for (CompletableFuture<ThesisTextModel> future : futures) {
  18. ThesisTextModel model = future.get();
  19. ChatResponse response = model.getWrapper().getResponse();
  20. List<ChatChoice> choices = response.getChoices();
  21. String outline = model.getMessage();
  22. ArrayList<String> perContent = new ArrayList<>();
  23. for (ChatChoice choice : choices) {
  24. ChatMessagesDto choiceMessage = choice.getMessage();
  25. // 获取某个标题的回答内容
  26. String content = choiceMessage.getContent().replaceAll("\n+", "\n");
  27. if (StringUtil.isEmpty(content)) {
  28. continue;
  29. }
  30. perContent.add(content);
  31. }
  32. allContent.put(outline, perContent);
  33. }
  34. // 生成 word 文档
  35. ThesisBuildHelper.exportWordDocument(resp, allContent);
  36. } catch (Exception e) {
  37. log.error("genThesisText error", e);
  38. }
  39. }

调用ThesisBuildHelper设置好相应的格式,这里贴上我大学时使用的论文格式(ps.还是从吃灰的老电脑里翻到的)

总结

经过两天的努力,到现在程序已经能够生成结构清晰、内容丰富的论文。同时,通过导出为Word文档的形式,使得我能够方便地查看和编辑生成的论文。

这样生成的论文成本也特别的低,我开发中使用的是gpt3.5,大概2w字的论文花费就2毛左右,可想而知百度到的论文生成网站里面利润之高,其实我还想了将gpt3.5生成的文章交给gpt4润色,加入参考文献,不过嫌麻烦,也没有好的思路,就懒得做了,说到底也是心血来潮。

最后,附以一张我毕业的论题生成的论文,27页字数23184,虽然不是标准的论文,但是再也不需要去冥思苦想论文该咋写了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/218701
推荐阅读
相关标签
  

闽ICP备14008679号