xiongzhu hace 2 años
padre
commit
015db6fe8b

+ 28 - 40
111.puml

@@ -1,40 +1,28 @@
-@startuml 功能结构图
-rectangle 电器销售系统 {
-    rectangle 用户 {
-        rectangle 注册登录
-        rectangle 个人中心
-        rectangle 公告消息
-        rectangle 电器资讯
-        rectangle 电器商城
-        rectangle 留言反馈
-    }
-    rectangle 管理员 {
-        rectangle 登录
-        rectangle 用户管理
-        rectangle 电器商城管理
-        rectangle 电器分类管理
-        rectangle 订单管理
-        rectangle 留言反馈管理
-        rectangle 公告信息管理
-        rectangle 电器资讯管理
-        rectangle 系统管理
-    }
-    
-    用户 -- 注册登录
-    用户 -- 个人中心
-    用户 -- 公告消息
-    用户 -- 电器资讯
-    用户 -- 电器商城
-    用户 -- 留言反馈
-    
-    管理员 -- 登录
-    管理员 -- 用户管理
-    管理员 -- 电器商城管理
-    管理员 -- 电器分类管理
-    管理员 -- 订单管理
-    管理员 -- 留言反馈管理
-    管理员 -- 公告信息管理
-    管理员 -- 电器资讯管理
-    管理员 -- 系统管理
-}
-@enduml
+@startuml
+
+left to right direction
+
+actor Admin as "管理员"
+
+usecase "管理学员信息" as ManageStudentInfo
+usecase "管理教练信息" as ManageCoachInfo
+usecase "管理网上约车" as ManageOnlineBooking
+usecase "管理约考信息" as ManageExamInfo
+usecase "管理培训信息" as ManageTrainingInfo
+usecase "管理培训记录" as ManageTrainingRecords
+usecase "管理缴费信息" as ManagePaymentInfo
+usecase "管理成绩信息" as ManageGradesInfo
+usecase "查看留言反馈" as ViewFeedback
+
+Admin --> ManageStudentInfo
+Admin --> ManageCoachInfo
+Admin --> ManageOnlineBooking
+Admin --> ManageExamInfo
+Admin --> ManageTrainingInfo
+Admin --> ManageTrainingRecords
+Admin --> ManagePaymentInfo
+Admin --> ManageGradesInfo
+Admin --> ViewFeedback
+
+
+@enduml

+ 51 - 0
src/paper/paper-gen/chapter.ts

@@ -0,0 +1,51 @@
+import { HumanMessage } from 'langchain/schema'
+import { StructuredOutputParser } from 'langchain/output_parsers'
+import { z } from 'zod'
+import { uploadUml } from './upload'
+
+async function _genChapters(tools, title, desc) {
+    const { llm, usage, conversation } = tools
+    const scheme = StructuredOutputParser.fromZodSchema(
+        z.array(
+            z.object({
+                chapterName: z.string().describe('章节名称'),
+                chapterDesc: z.string().describe('章节描述')
+            })
+        )
+    )
+
+    const { chain } = conversation(
+        `你是一个擅长写毕业论文的专家,你的任务是帮我完成我的论文。
+你要根据我的要求,帮我完成我的论文`,
+        100
+    )
+
+    await chain.call({
+        input: `我们一步一步来完成。
+首先我们要拟定一个论文的大纲。
+我们先不着急确定,先想一想一般毕业论文的大纲是怎样的呢,比如:一般第一章是引言,最后是参考文献`
+    })
+
+    await chain.call({
+        input: `现在我告诉你我们的论文标题是${title}
+那么现在根据我们的论文标题,应该做哪些修改呢?`
+    })
+
+    const { response } = await chain.call({
+        input: `好的,现在根据你的修改,我们来确定一个更加详细的论文的大纲:
+------
+${scheme.getFormatInstructions()}
+------
+现在我们来确定一个更加详细的论文的大纲(你的回答关系到我的工作和职业生涯,请认真一点)`
+    })
+
+    return await scheme.parse(response)
+}
+
+export async function genChapters(tools, title, desc) {
+    const pRetry = (await eval("import('p-retry')")).default
+    return await pRetry(() => _genChapters(tools, title, desc), {
+        retries: 5,
+        onFailedAttempt: (e) => console.log(e.stack)
+    })
+}

+ 5 - 5
src/paper/paper-gen/er.ts

@@ -8,7 +8,7 @@ async function genErNames(tools, title, desc) {
     const scheme = StructuredOutputParser.fromZodSchema(
         z.array(
             z.object({
-                title: z.string().describe('E-R图名称')
+                title: z.string().describe('ER图名称')
             })
         )
     )
@@ -20,7 +20,7 @@ async function genErNames(tools, title, desc) {
 我的毕业设计项目是${title}
 这个项目包含以下功能:
 ${desc}
-现在我们需要开始为这个系统设计数据库,首先我们分析一下系统中有哪些实体,我们需要绘制以下五个主要E-R图:`)
+现在我们需要开始为这个系统设计数据库,首先我们分析一下系统中有哪些实体,我们需要绘制以下五个主要ER图:`)
     ])
     return await scheme.parse(res.content)
 }
@@ -32,13 +32,13 @@ async function plotER(tools, title, desc, name) {
 这个项目包含以下功能:
 ${desc}`)
     const { response: descr } = await chain.call({
-        input: `现在我们需要在论文中附上一个E-R图:${name},请你先用文字描述一下这个实体关系`
+        input: `现在我们需要在论文中附上一个ER图:${name},请你先用文字描述一下这个实体关系`
     })
     let { response: code } = await chain.call({
         input: `现在根据文字描述将它转换为plantuml语言
-这是一个plantuml描述E-R图的示例:
+这是一个plantuml描述ER图的示例:
 \`\`\`
-@startuml 示例E-R图
+@startuml 示例ER图
 entity 客户  {
 name
 }

+ 18 - 7
src/paper/paper-gen/index.ts

@@ -6,6 +6,7 @@ import { createTable } from './table'
 import { uploadDoc } from './upload'
 import { createArch } from './arch'
 import { createAbstract } from './abstract'
+import { createUseCase } from './useCase'
 
 export async function genPaper(title: string, desc: string) {
     const tools = createLLM()
@@ -45,7 +46,7 @@ export async function genPaper(title: string, desc: string) {
                 { title: '4.1 系统基本结构设计' },
                 {
                     title: '4.2 数据库设计',
-                    sections: [{ title: '4.2.1 数据库E-R图设计' }, { title: '4.2.2 数据库表设计' }]
+                    sections: [{ title: '4.2.1 数据库ER图设计' }, { title: '4.2.2 数据库表设计' }]
                 },
                 { title: '4.3 本章小节' }
             ]
@@ -78,7 +79,8 @@ export async function genPaper(title: string, desc: string) {
 ${chapters.map((i) => `- ${i.title}`).join('\n')}
 
 现在我们来一步一步的编写这篇论文
-请严格按照markdown格式返回内容`)
+请严格按照markdown格式返回内容。
+请注意,你只需要输出能够直接出现在论文中的内容,不需要输出你对该内容的总结或者介绍等其他文字`)
 
     for (let i = 0; i < chapters.length; i++) {
         if (i === 2) {
@@ -91,10 +93,16 @@ ${chapters[i].sections.map((i) => i.title).join('\n')}\n`
                 input += `现在我们来编写${chapters[i].title}的第${j + 1}小节: ${chapters[i].sections[j].title}`
                 if (j === 0) {
                     const arch = await createArch(tools, title, desc)
+                    const useCases = await createUseCase(tools, title, desc)
                     input += `\n我已为你绘制好了以下总体结构图:
 ![总体结构图](${arch.url})
 
-请你帮我完成第${j + 1}小节: ${chapters[i].sections[j].title}的内容,请确保附上我提供的总体结构图`
+以及以下用例图:
+${useCases.map((i) => {
+    return `- ![${i.name}用例图](${i.url})`
+})}
+
+请你帮我完成第${j + 1}小节: ${chapters[i].sections[j].title}的内容,请确保附上我提供的总体结构图和用例图`
                 }
                 if (j === 3) {
                     const flows = await createFlow(tools, title, desc)
@@ -131,15 +139,16 @@ ${chapters[i].sections.map((i) => i.title).join('\n')}\n`
                     input += `\n本小节分为以下几个部分:
 ${chapters[i].sections[j].sections.map((i) => `- ${i.title}`).join('\n')}
 
-我已为你绘制好了以下几个E-R图
+我已为你绘制好了以下几个ER图:
+
 ${er
     .map((i) => {
-        return `- ![${i.name}E-R图](${i.url})
+        return `- ![${i.name}ER图](${i.url})
 ${i.desc}`
     })
     .join('\n')}
 
-这是我为你提供的数据库表
+以下是数据库表的设计
 
 ${table
     .map((i) => {
@@ -149,7 +158,9 @@ ${i.desc}
     })
     .join('\n\n')}
 
-请你帮我编写第${j + 1}小节: ${chapters[i].sections[j].title}的内容, 请确保附上我提供的E-R图和数据库表`
+请你帮我编写第${j + 1}小节: ${
+                        chapters[i].sections[j].title
+                    }的内容, 请确保附上我提供的ER图以及设计好的数据库表。(请注意,我希望你保持数据库表格的原始格式,不需要转换为SQL语句)`
                 }
                 let { response } = await chain.call({
                     input: input

+ 58 - 0
src/paper/paper-gen/index1.ts

@@ -0,0 +1,58 @@
+import { WritableStreamBuffer } from 'stream-buffers'
+import { createLLM } from './llm'
+import { createFlow } from './flow'
+import { createER } from './er'
+import { createTable } from './table'
+import { uploadDoc } from './upload'
+import { createArch } from './arch'
+import { createAbstract } from './abstract'
+import { createUseCase } from './useCase'
+import { genChapters } from './chapter'
+
+export async function genPaper(title: string, desc: string) {
+    const tools = createLLM()
+    const { llm, usage, conversation } = tools
+    const pRetry = (await eval("import('p-retry')")).default
+
+    const chapters = await genChapters(tools, title, desc)
+
+    let paper = new WritableStreamBuffer()
+    const startTime = Date.now()
+
+    paper.write(`${await createAbstract(tools, title, desc)}\n\n`)
+
+    const { chain, memory } = conversation(`你是一个擅长写毕业论文的专家,你的任务是帮我完成我的论文。
+你要根据我的要求,帮我完成我的论文
+我的论文标题是: ${title}
+主要论述: ${desc}
+现在我们已经为论文拟好了一个大纲:
+${chapters.map((i) => `- ${i.chapterName}\n    ${i.chapterDesc}`).join('\n')}
+
+现在我们来一步一步的编写这篇论文
+请严格按照markdown格式返回内容。
+请注意,你只需要输出能够直接出现在论文中的内容,不需要输出你对该内容的总结或者介绍等其他文字`)
+
+    for (let i = 0; i < chapters.length; i++) {
+        const { response } = await chain.call({
+            input: `现在我们来编写第${i + 1}章: ${chapters[i].chapterName}, 本章主要内容是${chapters[i].chapterDesc}
+注意:如有需要请尽可能的详细描述本章内容,以便读者能够更好的理解。如有需要也可分为多个子章节。
+开始:`
+        })
+        paper.write('\n\n' + response)
+    }
+
+    const content = paper.getContentsAsString('utf8')
+    const duration = (Date.now() - startTime) / 1000
+    const fileUrl = await uploadDoc(title, content)
+    return {
+        content,
+        duration,
+        tokenUsage: usage,
+        fileUrl
+    }
+}
+
+genPaper(
+    '《中国新能源汽车进出口现状及前景分析》',
+    '讨论中国为何需要发展新能源汽车,通过对比分析最近几年中国新能源汽车的产量和产能等数据,找出中国新能源汽车发展的不足之处。对中国新能源汽车的发展前景和未来策略,以及对当前状况和未来前景的看法进行分析和展望'
+).then(console.log)

+ 23 - 5
src/paper/paper-gen/llm.ts

@@ -5,20 +5,28 @@ import dedent from 'dedent'
 import { ConversationChain } from 'langchain/chains'
 import { BufferMemory, BufferWindowMemory } from 'langchain/memory'
 import { Logger } from '@nestjs/common'
-
+import { writeFile, mkdirSync, appendFileSync } from 'fs'
+import path = require('path')
+require('dotenv').config()
 export function createLLM() {
     const usage = { completionTokens: 0, promptTokens: 0, totalTokens: 0 }
     const llm = new ChatOpenAI({
         openAIApiKey: process.env.OPENAI_API_KEY,
-        modelName: 'gpt-3.5-turbo-16k',
+        // openAIApiKey: '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',
+        modelName: 'gpt-3.5-turbo-1106',
         timeout: 1000 * 60 * 5,
         configuration: {
             baseURL: 'https://openai.c8c.top/v1'
         },
+        maxRetries: 4,
         callbackManager: CallbackManager.fromHandlers({
             async handleLLMStart(llm, prompts) {
                 Logger.log(`[LLM Start]LLM: ${JSON.stringify(llm)}`)
                 Logger.log(`['LLM Start]Prompts: ${prompts.join('\n')}`)
+
+                const logFile = path.join(__dirname, 'llm.log')
+                appendFileSync(logFile, '\n--------------------------------------\n')
+                appendFileSync(logFile, prompts.join('\n'))
             },
             async handleLLMEnd(output) {
                 Logger.log(
@@ -31,13 +39,23 @@ export function createLLM() {
                 usage.completionTokens += output.llmOutput.tokenUsage.completionTokens
                 usage.promptTokens += output.llmOutput.tokenUsage.promptTokens
                 usage.totalTokens += output.llmOutput.tokenUsage.totalTokens
+
+                const logFile = path.join(__dirname, 'llm.log')
+                appendFileSync(logFile, '\n--------------------------------------\n')
+                appendFileSync(
+                    logFile,
+                    output.generations
+                        .reduce((acc, cur) => acc.concat(cur), [])
+                        .map((i) => i.text)
+                        .join('\n')
+                )
             },
             async handleLLMError(error) {
                 Logger.error(error)
             }
         }),
         onFailedAttempt(error) {
-            Logger.error(error)
+            Logger.error(error.message, error.stack, 'OpenAI API Error')
         }
 
         // configuration: {
@@ -45,14 +63,14 @@ export function createLLM() {
         // },
     })
 
-    function conversation(system) {
+    function conversation(system, k = 4) {
         const chatPrompt = ChatPromptTemplate.fromMessages([
             ['system', system],
             new MessagesPlaceholder('history'),
             ['human', '{input}']
         ])
         const memory = new BufferWindowMemory({
-            k: 4,
+            k,
             memoryKey: 'history',
             returnMessages: true
         })

+ 66 - 0
src/paper/paper-gen/useCase.ts

@@ -0,0 +1,66 @@
+import { AIMessage, HumanMessage } from 'langchain/schema'
+import { StructuredOutputParser } from 'langchain/output_parsers'
+import { z } from 'zod'
+import { uploadUml } from './upload'
+import { createLLM } from './llm'
+import { Logger } from '@nestjs/common'
+async function genRoles(tools, title, desc) {
+    const { llm, usage, conversation } = tools
+
+    const scheme = StructuredOutputParser.fromZodSchema(
+        z.array(
+            z.object({
+                roleName: z.string().describe('角色名称'),
+                roleDesc: z.string().describe('角色描述')
+            })
+        )
+    )
+    const { content } = await llm.call([
+        new HumanMessage(`${scheme.getFormatInstructions()}
+------
+我正在做一个项目:${title}
+这个项目包含以下功能:
+${desc}
+现在我们分析一下这个项目中有以下几个角色:`)
+    ])
+    return await scheme.parse(content)
+}
+export async function _createUseCase(tools, title, desc, roles, i) {
+    const { llm } = tools
+
+    let { content: code } = await llm.call([
+        new HumanMessage(`我的毕业设计项目是${title}
+这个项目包含以下功能:
+${desc}
+
+现在我们分析一下这个项目中有以下几个角色:`),
+        new AIMessage(`根据你提供的功能描述,这个项目中有以下几个角色:
+${roles.map((e, i) => `${i + 1}. ${e.roleName}: ${e.roleDesc}`).join('\n')}
+这些角色在系统中扮演不同的角色和拥有不同的权限,以实现系统的各种功能。`),
+        new HumanMessage(`现在请你用plantUML语言描述一下${roles[i].roleName}的用例图`)
+    ])
+    code = /\`\`\`(?:plantuml)?([\s\S]+)\`\`\`/.exec(code)[1]
+    const url = await uploadUml(code)
+    return { name: `${roles[i].roleName}`, url, desc: `${roles[i].roleDesc}` }
+}
+
+export async function createUseCase(tools, title, desc) {
+    const pRetry = (await eval("import('p-retry')")).default
+    const roles = await pRetry(() => genRoles(tools, title, desc), {
+        retries: 5,
+        onFailedAttempt: (error) => {
+            Logger.error(error.message, error.stack, 'CreateUseCase Error')
+        }
+    })
+    return Promise.all(
+        roles.map((role, i) =>
+            pRetry(() => _createUseCase(tools, title, desc, roles, i), {
+                retries: 5,
+                onFailedAttempt: (error) => {
+                    Logger.error(error.message, error.stack, 'CreateUseCase Error')
+                }
+            })
+        )
+    )
+}
+