1
0

3 Commity 1922d13167 ... 593abaca66

Autor SHA1 Správa Dátum
  xie 593abaca66 更新 1 týždeň pred
  xie a1eb8512d6 Merge remote-tracking branch 'origin/master' 1 týždeň pred
  xie 9b15c27155 1. 支持高并发的文章生成请求; 1 týždeň pred
7 zmenil súbory, kde vykonal 454 pridanie a 204 odobranie
  1. 1 1
      .gitignore
  2. 9 16
      core/api_get_article2.py
  3. 131 59
      gpt/chatgpt.py
  4. 111 106
      gpt/get_article2.py
  5. 200 20
      mock/mock_request.py
  6. 1 1
      tools/audio.py
  7. 1 1
      tools/thread_pool_manager.py

+ 1 - 1
.gitignore

@@ -1,7 +1,7 @@
 .*
 .*
 /*.txt
 /*.txt
 test*.py
 test*.py
-/test
+/test/
 /log
 /log
 *.docx
 *.docx
 *.pdf
 *.pdf

+ 9 - 16
core/api_get_article2.py

@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 
 
-from fastapi import FastAPI, Form, HTTPException, Request,status,APIRouter,Query,Path, Depends
+from fastapi import FastAPI, Form, HTTPException, Request,status,APIRouter,Query,Path, Depends, BackgroundTasks
 from tools.loglog import logger,log_err_e
 from tools.loglog import logger,log_err_e
 
 
 from core.respone_format import *
 from core.respone_format import *
@@ -11,12 +11,8 @@ import asyncio
 
 
 router = APIRouter()
 router = APIRouter()
 
 
-async def get_article_dependency():
-    article_generator = GetArticle()
-    try:
-        yield article_generator
-    finally:
-        await article_generator.cleanup()
+get_article = GetArticle()
+
 
 
 class Word(BaseModel):
 class Word(BaseModel):
     meaning_id:int = Field(..., description="单词的词义id")
     meaning_id:int = Field(..., description="单词的词义id")
@@ -35,14 +31,12 @@ class ArticleRequest(BaseModel):
 
 
 
 
 @router.post("/article/reading-comprehension")
 @router.post("/article/reading-comprehension")
-async def post_article(
+def post_article(
     json_data:ArticleRequest,
     json_data:ArticleRequest,
     request:Request,
     request:Request,
-    get_article: GetArticle = Depends(get_article_dependency)
+    background_tasks: BackgroundTasks,
 ):
 ):
-    """
-    异步处理文章生成请求
-    """
+
     json_data = json_data.model_dump()
     json_data = json_data.model_dump()
     real_ip = request.headers.get("X-Real-IP","0.0.0.0")
     real_ip = request.headers.get("X-Real-IP","0.0.0.0")
 
 
@@ -54,16 +48,15 @@ async def post_article(
     exercise_id = json_data["exercise_id"]
     exercise_id = json_data["exercise_id"]
 
 
     try:
     try:
-       
-        r = await get_article.submit_task(
+        r = get_article.submit_task(
             real_ip=real_ip,
             real_ip=real_ip,
             core_words=core_words,
             core_words=core_words,
             take_count=take_count,
             take_count=take_count,
             demo_name=demo_name,
             demo_name=demo_name,
             reading_level=reading_level,
             reading_level=reading_level,
             article_length=article_length,
             article_length=article_length,
-            exercise_id=exercise_id
-
+            exercise_id=exercise_id,
+            background_tasks=background_tasks
         )
         )
         return r if not isinstance(r,str) else resp_500(message=r)
         return r if not isinstance(r,str) else resp_500(message=r)
 
 

+ 131 - 59
gpt/chatgpt.py

@@ -1,22 +1,22 @@
 # -*- coding:utf-8 -*-
 # -*- coding:utf-8 -*-
 if __name__ == '__main__':
 if __name__ == '__main__':
     import os
     import os
+
     os.chdir("..")
     os.chdir("..")
 
 
-import requests
-import random
-import json
 import time
 import time
-from tools.loglog import logger,simple_logger
-from tools.new_mysql import MySQLUploader
-from typing import Optional, Dict, Any,Union
+from typing import Dict, Any, Union
+
 import httpx
 import httpx
-import asyncio
+import requests
 
 
+from tools.loglog import logger, simple_logger, log_err_e
+from tools.new_mysql import MySQLUploader
 
 
 m = MySQLUploader()
 m = MySQLUploader()
 
 
-def get_openai_model(model_text:str):
+
+def get_openai_model(model_text: str):
     """模糊获得模型名"""
     """模糊获得模型名"""
     if "3.5" in model_text or "3.5-turbo" in model_text or "3.5turbo" in model_text:
     if "3.5" in model_text or "3.5-turbo" in model_text or "3.5turbo" in model_text:
         model = "gpt-3.5-turbo"
         model = "gpt-3.5-turbo"
@@ -29,16 +29,16 @@ def get_openai_model(model_text:str):
     return model
     return model
 
 
 
 
-def insert_ip_token(ip,demo_name,gpt_content,prompt_tokens,completion_tokens,total_tokens):
+def insert_ip_token(ip, demo_name, gpt_content, prompt_tokens, completion_tokens, total_tokens):
     sql = "insert into consumer_token (ip,demo_name,gpt_content,prompt_tokens,completion_tokens,total_tokens) values (%s,%s,%s,%s,%s,%s)"
     sql = "insert into consumer_token (ip,demo_name,gpt_content,prompt_tokens,completion_tokens,total_tokens) values (%s,%s,%s,%s,%s,%s)"
-    m.execute_(sql,(ip,demo_name,str(gpt_content),prompt_tokens,completion_tokens,total_tokens))
+    m.execute_(sql, (ip, demo_name, str(gpt_content), prompt_tokens, completion_tokens, total_tokens))
+
 
 
-def get_answer_from_gpt(question,real_ip="localhost",demo_name="无",model="gpt-4o",max_tokens=3500,temperature:float=0,
-                        json_resp:Union[Dict[Any, Any],bool]=False,n=1,check_fucn=None,sys_prompt=None):
+def get_answer_from_gpt(question, real_ip="localhost", demo_name="无", model="gpt-4o", max_tokens=3500, temperature: float = 0,
+                        json_resp: Union[Dict[Any, Any], bool] = False, n=1, check_fucn=None, sys_prompt=None):
     model = get_openai_model(model)
     model = get_openai_model(model)
 
 
-   
-    d2 = {"model": model,"messages": [],"max_tokens": max_tokens,"temperature": temperature,'n': n}
+    d2 = {"model": model, "messages": [], "max_tokens": max_tokens, "temperature": temperature, 'n': n}
     if sys_prompt:
     if sys_prompt:
         d2['messages'].append({"role": "system", "content": sys_prompt})
         d2['messages'].append({"role": "system", "content": sys_prompt})
     d2['messages'].append({"role": "user", "content": question})
     d2['messages'].append({"role": "user", "content": question})
@@ -52,36 +52,33 @@ def get_answer_from_gpt(question,real_ip="localhost",demo_name="无",model="gpt-
 
 
     for num_count in range(3):
     for num_count in range(3):
         try:
         try:
-           
+
             response = requests.post(f'http://170.106.108.95/v1/chat/completions', json=d2)
             response = requests.post(f'http://170.106.108.95/v1/chat/completions', json=d2)
             r_json = response.json()
             r_json = response.json()
-            if r2:= r_json.get("choices",None):
-                if n>1:
+            if r2 := r_json.get("choices", None):
+                if n > 1:
                     gpt_res = []
                     gpt_res = []
                     for i in r2:
                     for i in r2:
                         gpt_res.append(i["message"]["content"])
                         gpt_res.append(i["message"]["content"])
                 else:
                 else:
-                    gpt_res= r2[0]["message"]["content"]
+                    gpt_res = r2[0]["message"]["content"]
 
 
-               
                 gpt_content = str(gpt_res)
                 gpt_content = str(gpt_res)
                 prompt_tokens = r_json["usage"]["prompt_tokens"]
                 prompt_tokens = r_json["usage"]["prompt_tokens"]
                 completion_tokens = r_json["usage"]["completion_tokens"]
                 completion_tokens = r_json["usage"]["completion_tokens"]
                 total_tokens = r_json["usage"]["total_tokens"]
                 total_tokens = r_json["usage"]["total_tokens"]
-                insert_ip_token(real_ip,demo_name,gpt_content,prompt_tokens,completion_tokens,total_tokens)
+                insert_ip_token(real_ip, demo_name, gpt_content, prompt_tokens, completion_tokens, total_tokens)
 
 
                 simple_logger.info(f"问题日志:\n{question}\n回答日志:\n{gpt_res}")
                 simple_logger.info(f"问题日志:\n{question}\n回答日志:\n{gpt_res}")
 
 
-               
                 if not check_fucn:
                 if not check_fucn:
                     return gpt_res
                     return gpt_res
 
 
-               
                 check_result = check_fucn(str(gpt_res))
                 check_result = check_fucn(str(gpt_res))
-                if check_result: 
+                if check_result:
                     return gpt_res
                     return gpt_res
                 else:
                 else:
-                    raise Exception(f"第{num_count+1}次共3次,GPT的校验没有通过,校验函数:{check_fucn.__name__}")
+                    raise Exception(f"第{num_count + 1}次共3次,GPT的校验没有通过,校验函数:{check_fucn.__name__}")
 
 
             elif r_json.get("message") == "IP address blocked":
             elif r_json.get("message") == "IP address blocked":
                 print("IP address blocked")
                 print("IP address blocked")
@@ -95,8 +92,9 @@ def get_answer_from_gpt(question,real_ip="localhost",demo_name="无",model="gpt-
 
 
     logger.critical("get_answer_from_gpt 严重错误,3次后都失败了")
     logger.critical("get_answer_from_gpt 严重错误,3次后都失败了")
 
 
-async def get_article_gpt_pydantic(question, real_ip="localhost", demo_name="无", model="gpt-4o", max_tokens=3500, temperature:float=0, n=1,
-                        check_fucn=None, sys_prompt=None, client=None):
+
+def get_article_gpt_pydantic(question, real_ip="localhost", demo_name="无", model="gpt-4.1", max_tokens=3500, temperature: float = 0, n=1,
+                             check_fucn=None, sys_prompt=None):
     """
     """
     异步获取文章
     异步获取文章
     :param question: 问题
     :param question: 问题
@@ -108,50 +106,126 @@ async def get_article_gpt_pydantic(question, real_ip="localhost", demo_name="无
     :param n: 生成数量
     :param n: 生成数量
     :param check_fucn: 校验函数
     :param check_fucn: 校验函数
     :param sys_prompt: 系统提示
     :param sys_prompt: 系统提示
-    :param client: httpx.AsyncClient实例
     :return: 文章内容
     :return: 文章内容
     """
     """
-   
-    d2 = {"model": model, "messages": [], "max_tokens": max_tokens, "temperature": temperature, "response_format":"article"}
+
+    d2 = {"model": model, "messages": [], "max_tokens": max_tokens, "temperature": temperature, "n": n, "response_format": {'type': 'json_schema',
+                                                                                                                            'json_schema': {
+                                                                                                                                'name': 'Article',
+                                                                                                                                'schema': {'$defs': {
+                                                                                                                                    'Candidate': {
+                                                                                                                                        'properties': {
+                                                                                                                                            'label': {
+                                                                                                                                                'title': 'Label',
+                                                                                                                                                'type': 'string'},
+                                                                                                                                            'text': {
+                                                                                                                                                'title': 'Text',
+                                                                                                                                                'type': 'string'},
+                                                                                                                                            'isRight': {
+                                                                                                                                                'title': 'Isright',
+                                                                                                                                                'type': 'integer'}},
+                                                                                                                                        'required': [
+                                                                                                                                            'label',
+                                                                                                                                            'text',
+                                                                                                                                            'isRight'],
+                                                                                                                                        'title': 'Candidate',
+                                                                                                                                        'type': 'object'},
+                                                                                                                                    'DifficultSentence': {
+                                                                                                                                        'properties': {
+                                                                                                                                            'english': {
+                                                                                                                                                'title': 'English',
+                                                                                                                                                'type': 'string'},
+                                                                                                                                            'chinese': {
+                                                                                                                                                'title': 'Chinese',
+                                                                                                                                                'type': 'string'}},
+                                                                                                                                        'required': [
+                                                                                                                                            'english',
+                                                                                                                                            'chinese'],
+                                                                                                                                        'title': 'DifficultSentence',
+                                                                                                                                        'type': 'object'},
+                                                                                                                                    'Question': {
+                                                                                                                                        'properties': {
+                                                                                                                                            'trunk': {
+                                                                                                                                                'title': 'Trunk',
+                                                                                                                                                'type': 'string'},
+                                                                                                                                            'analysis': {
+                                                                                                                                                'title': 'Analysis',
+                                                                                                                                                'type': 'string'},
+                                                                                                                                            'candidates': {
+                                                                                                                                                'items': {
+                                                                                                                                                    '$ref': '#/$defs/Candidate'},
+                                                                                                                                                'title': 'Candidates',
+                                                                                                                                                'type': 'array'}},
+                                                                                                                                        'required': [
+                                                                                                                                            'trunk',
+                                                                                                                                            'analysis',
+                                                                                                                                            'candidates'],
+                                                                                                                                        'title': 'Question',
+                                                                                                                                        'type': 'object'}},
+                                                                                                                                           'properties': {
+                                                                                                                                               'difficultSentences': {
+                                                                                                                                                   'items': {
+                                                                                                                                                       '$ref': '#/$defs/DifficultSentence'},
+                                                                                                                                                   'title': 'Difficultsentences',
+                                                                                                                                                   'type': 'array'},
+                                                                                                                                               'usedMeanIds': {
+                                                                                                                                                   'items': {
+                                                                                                                                                       'type': 'integer'},
+                                                                                                                                                   'title': 'Usedmeanids',
+                                                                                                                                                   'type': 'array'},
+                                                                                                                                               'questions': {
+                                                                                                                                                   'items': {
+                                                                                                                                                       '$ref': '#/$defs/Question'},
+                                                                                                                                                   'title': 'Questions',
+                                                                                                                                                   'type': 'array'},
+                                                                                                                                               'englishArticle': {
+                                                                                                                                                   'title': 'Englisharticle',
+                                                                                                                                                   'type': 'string'},
+                                                                                                                                               'chineseArticle': {
+                                                                                                                                                   'title': 'Chinesearticle',
+                                                                                                                                                   'type': 'string'},
+                                                                                                                                               'allWordAmount': {
+                                                                                                                                                   'title': 'Allwordamount',
+                                                                                                                                                   'type': 'integer'}},
+                                                                                                                                           'required': [
+                                                                                                                                               'difficultSentences',
+                                                                                                                                               'usedMeanIds',
+                                                                                                                                               'questions',
+                                                                                                                                               'englishArticle',
+                                                                                                                                               'chineseArticle',
+                                                                                                                                               'allWordAmount'],
+                                                                                                                                           'title': 'Article',
+                                                                                                                                           'type': 'object'}}}}
     if sys_prompt:
     if sys_prompt:
         d2['messages'].append({"role": "system", "content": sys_prompt})
         d2['messages'].append({"role": "system", "content": sys_prompt})
     d2['messages'].append({"role": "user", "content": question})
     d2['messages'].append({"role": "user", "content": question})
 
 
     for num_count in range(3):
     for num_count in range(3):
         try:
         try:
-           
-            if client is None:
-                async with httpx.AsyncClient() as temp_client:
-                    response = await temp_client.post('http://170.106.108.95/get_article', json=d2)
-            else:
-                response = await client.post('http://170.106.108.95/get_article', json=d2)
-                
-            r_str = response.json() 
+            response = requests.post('http://170.106.108.95/v1/chat/completions', json=d2)
+            r_json = response.json()
+            simple_logger.info(f"问题日志:\n{question}\n回答日志:\n{r_json}")
+            return r_json
+
+            #
+
+            #
 
 
-            simple_logger.info(f"问题日志:\n{question}\n回答日志:\n{r_str}")
 
 
-           
-            if not check_fucn:
-                return r_str
 
 
-           
-            check_result = check_fucn(r_str)
 
 
-            if check_result: 
-                return r_str
-            else:
-                raise Exception(f"第{num_count + 1}次共3次,GPT的校验没有通过,校验函数:{check_fucn.__name__}")
 
 
         except httpx.HTTPError as e:
         except httpx.HTTPError as e:
             logger.error(f"HTTP请求错误: {str(e)}")
             logger.error(f"HTTP请求错误: {str(e)}")
-            if num_count < 2: 
-                await asyncio.sleep(10)
+            if num_count < 2:
+                time.sleep(10)
             else:
             else:
                 raise
                 raise
         except Exception as e:
         except Exception as e:
-            logger.error(f"其他错误: {str(e)}")
+            log_err_e(e, "其他错误")
+
             if num_count < 2:
             if num_count < 2:
-                await asyncio.sleep(10)
+                time.sleep(10)
             else:
             else:
                 raise
                 raise
 
 
@@ -159,25 +233,23 @@ async def get_article_gpt_pydantic(question, real_ip="localhost", demo_name="无
     raise Exception("获取文章失败,已达到最大重试次数")
     raise Exception("获取文章失败,已达到最大重试次数")
 
 
 
 
-def parse_gpt_phon_to_tuplelist(text:str) -> list:
+def parse_gpt_phon_to_tuplelist(text: str) -> list:
     """解析gpt返回的音标数据"""
     """解析gpt返回的音标数据"""
     result = []
     result = []
     if not text:
     if not text:
         return []
         return []
     for i in text.split("\n"):
     for i in text.split("\n"):
         ii = i.split("***")
         ii = i.split("***")
-        if len(ii)>=3:
-            result.append((ii[0].strip(),ii[1].strip(),ii[2].strip()))
+        if len(ii) >= 3:
+            result.append((ii[0].strip(), ii[1].strip(), ii[2].strip()))
     return result
     return result
 
 
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
-
     question = "hello"
     question = "hello"
-   
 
 
     sys_prompt = "你是一个专业的英语老师,擅长根据用户提供的词汇生成对应的英语文章和中文翻译和4个配套选择题。"
     sys_prompt = "你是一个专业的英语老师,擅长根据用户提供的词汇生成对应的英语文章和中文翻译和4个配套选择题。"
-    q= """下面我会为你提供两组数据,[单词组1]和[单词组2](里面包含词义id,英语单词,中文词义),优先使用[单词组1]内的单词,请根据这些单词的中文词义,生成一篇带中文翻译的考场英语文章,英语文章和中文翻译要有[标题]。注意这个单词有多个词义时,生成的英语文章一定要用提供的中文词义。并挑选一句复杂的句子和其中文翻译,放入difficultSentences。英语文章,放入"englishArticle"中。中文翻译,放入"chineseArticle"中。最终文中使用到的单词id放入"usedMeanIds"中。4个选择题,放入questions字段。questions结构下有4个选择题对象,其中trunk是[英语]问题文本,analysis是[中文]的问题分析,candidates是4个ABCD选项,内部有label是指选项序号A B C D ,text是[英语]选项文本,isRight是否正确答案1是正确0是错误。
+    q = """下面我会为你提供两组数据,[单词组1]和[单词组2](里面包含词义id,英语单词,中文词义),优先使用[单词组1]内的单词,请根据这些单词的中文词义,生成一篇带中文翻译的考场英语文章,英语文章和中文翻译要有[标题]。注意这个单词有多个词义时,生成的英语文章一定要用提供的中文词义。并挑选一句复杂的句子和其中文翻译,放入difficultSentences。英语文章,放入"englishArticle"中。中文翻译,放入"chineseArticle"中。最终文中使用到的单词id放入"usedMeanIds"中。4个选择题,放入questions字段。questions结构下有4个选择题对象,其中trunk是[英语]问题文本,analysis是[中文]的问题分析,candidates是4个ABCD选项,内部有label是指选项序号A B C D ,text是[英语]选项文本,isRight是否正确答案1是正确0是错误。
 
 
 要求:
 要求:
 1.必须用提供的这个词义的单词,其他单词使用常见、高中难度的的单词。文章整体难度适中,大约和中国的高中生,中国CET-6,雅思6分这样的难度标准。
 1.必须用提供的这个词义的单词,其他单词使用常见、高中难度的的单词。文章整体难度适中,大约和中国的高中生,中国CET-6,雅思6分这样的难度标准。
@@ -189,6 +261,6 @@ if __name__ == '__main__':
 提供[单词组1]:4238 penalty:惩罚, 刑罚;4591 bare:赤裸的, 无遮蔽的;4227 stable:畜舍, 马厩;4236 psychology:心理学;4245 offense:进攻, 攻势, 冒犯, 触怒, 过错;4237 innocent:清白的, 无辜的, 天真的;4228 refrigerator:冰箱, 冷库;4247 tissue:(动植物)组织;4250 awareness:察觉, 觉悟, 意识;4234 mode:方式, 模式;4224 neat:整洁, 利索;4225 statistics:统计;4251 random:任意的, 随机的;4201 laundry:洗衣房;4545 barrel:桶, 一桶之量;4249 recruit:招募, 新成员;4229 pregnant:怀孕的, 孕育的;4235 relevant:有关的, 相关联的;4252 incentive:刺激, 激励, 鼓励;4194 grave:坟墓, 墓穴;
 提供[单词组1]:4238 penalty:惩罚, 刑罚;4591 bare:赤裸的, 无遮蔽的;4227 stable:畜舍, 马厩;4236 psychology:心理学;4245 offense:进攻, 攻势, 冒犯, 触怒, 过错;4237 innocent:清白的, 无辜的, 天真的;4228 refrigerator:冰箱, 冷库;4247 tissue:(动植物)组织;4250 awareness:察觉, 觉悟, 意识;4234 mode:方式, 模式;4224 neat:整洁, 利索;4225 statistics:统计;4251 random:任意的, 随机的;4201 laundry:洗衣房;4545 barrel:桶, 一桶之量;4249 recruit:招募, 新成员;4229 pregnant:怀孕的, 孕育的;4235 relevant:有关的, 相关联的;4252 incentive:刺激, 激励, 鼓励;4194 grave:坟墓, 墓穴;
 提供[单词组2]:;
 提供[单词组2]:;
 """
 """
-    resp = get_article_gpt_pydantic(question=q,temperature=0.9,sys_prompt=sys_prompt,model="gpt-4.1")
+    resp = get_answer_from_gpt(question=question, temperature=0.9, sys_prompt=sys_prompt, model="gpt-4.1")
     print(type(resp))
     print(type(resp))
-    print(resp)
+    print(resp)

+ 111 - 106
gpt/get_article2.py

@@ -12,12 +12,16 @@ from pydantic import BaseModel
 from cachetools import TTLCache
 from cachetools import TTLCache
 from concurrent.futures import wait
 from concurrent.futures import wait
 from random import randint, shuffle, sample
 from random import randint, shuffle, sample
-import json
+import json,time
 import requests
 import requests
 from openpyxl import load_workbook
 from openpyxl import load_workbook
 from tenacity import retry, stop_after_attempt, wait_fixed
 from tenacity import retry, stop_after_attempt, wait_fixed
 import httpx
 import httpx
 import asyncio
 import asyncio
+from threading import Lock
+from collections import defaultdict
+from fastapi import BackgroundTasks
+
 
 
 
 
 def get_article_difficulty(article) -> int:
 def get_article_difficulty(article) -> int:
@@ -84,18 +88,12 @@ def merge_and_split(list1, list2):
 class GetArticle:
 class GetArticle:
     def __init__(self):
     def __init__(self):
         self.m = MySQLUploader() 
         self.m = MySQLUploader() 
-        self.client = httpx.AsyncClient(
-            timeout=httpx.Timeout(180.0), 
-            limits=httpx.Limits(
-                max_keepalive_connections=100, 
-                max_connections=1000, 
-                keepalive_expiry=90.0 
-            )
-        )
-
-        self.callback_url_dict = {}
-        self.real_ip_dict = {} 
-        self.demo_name = {}
+
+       
+        self.callback_url_dict = defaultdict(str)
+        self.real_ip_dict = defaultdict(str) 
+        self.demo_name = defaultdict(str)
+
 
 
         self.article_result = {} 
         self.article_result = {} 
 
 
@@ -106,6 +104,8 @@ class GetArticle:
        
        
         self.exchange_data: dict[str, list] = {} 
         self.exchange_data: dict[str, list] = {} 
         self.read_spring_bamboo_exchange_table()
         self.read_spring_bamboo_exchange_table()
+                    
+
 
 
    
    
     def read_spring_bamboo_exchange_table(self):
     def read_spring_bamboo_exchange_table(self):
@@ -122,7 +122,7 @@ class GetArticle:
         wb.close()
         wb.close()
 
 
    
    
-    async def parser_insert_to_mysql(self, resp_result):
+    def parser_insert_to_mysql(self, resp_result):
         try:
         try:
             for single_article in resp_result['articles']:
             for single_article in resp_result['articles']:
                 article = single_article['body']
                 article = single_article['body']
@@ -133,12 +133,13 @@ class GetArticle:
                 sql = "INSERT INTO spring_bamboo_article (article_json,difficult_level) VALUES (%s,%s)"
                 sql = "INSERT INTO spring_bamboo_article (article_json,difficult_level) VALUES (%s,%s)"
                 self.m.execute_(sql, (article_json, difficult_value))
                 self.m.execute_(sql, (article_json, difficult_value))
         except Exception as e:
         except Exception as e:
+           
             logger.error(f"插入数据库时发生错误: {str(e)}")
             logger.error(f"插入数据库时发生错误: {str(e)}")
-            raise
 
 
    
    
-    async def submit_task(self, real_ip: str, core_words: list, take_count: int,
-                          demo_name: str, reading_level: int, article_length: int, exercise_id: int):
+    def submit_task(self, real_ip: str, core_words: list, take_count: int,
+                          demo_name: str, reading_level: int, article_length: int, exercise_id: int,
+                          background_tasks: BackgroundTasks):
         """
         """
         core_words: 词义数据组
         core_words: 词义数据组
         take_count: 取文章数量 (int类型,正常是2篇,最大8篇)
         take_count: 取文章数量 (int类型,正常是2篇,最大8篇)
@@ -146,27 +147,89 @@ class GetArticle:
         reading_level:阅读等级
         reading_level:阅读等级
         article_length:文章长度
         article_length:文章长度
         exercise_id:学案id
         exercise_id:学案id
+        background_tasks: FastAPI的后台任务管理器
         """
         """
         task_id = randint(10000000, 99999999)
         task_id = randint(10000000, 99999999)
-       
         logger.info(f"reading-comprehension 生成文章id。学案id:{exercise_id},task_id:{task_id}")
         logger.info(f"reading-comprehension 生成文章id。学案id:{exercise_id},task_id:{task_id}")
 
 
         try:
         try:
             self.real_ip_dict[task_id] = real_ip
             self.real_ip_dict[task_id] = real_ip
             self.demo_name[task_id] = demo_name
             self.demo_name[task_id] = demo_name
 
 
-            resp_result = await self.run_task(core_words, task_id, take_count, reading_level, article_length)
-            await self.parser_insert_to_mysql(resp_result) 
-            logger.success(f"reading-comprehension 文章2任务完成。学案id:{exercise_id},taskid:{task_id}\n{resp_result}")
+            resp_result = self.run_task(core_words, task_id, take_count, reading_level, article_length)
+            
+           
+            background_tasks.add_task(self.parser_insert_to_mysql, resp_result)
+            
+            logger.success(f"reading-comprehension 文章2任务完成。学案id:{exercise_id},taskid:{task_id}")
             return resp_result
             return resp_result
         except Exception as e:
         except Exception as e:
             err_msg = f"GetArticle提交任务失败{type(e).__name__},{e}"
             err_msg = f"GetArticle提交任务失败{type(e).__name__},{e}"
             log_err_e(e, msg="GetArticle提交任务失败;")
             log_err_e(e, msg="GetArticle提交任务失败;")
             return err_msg
             return err_msg
+        finally:
+           
+            self.real_ip_dict.pop(task_id, None)
+            self.demo_name.pop(task_id, None)
+
+   
+    def __parse_gpt_resp(self,gpt_resp:dict,core_words:list):
+        return_json = {"articles": []} 
+        for choice in gpt_resp["choices"]:
+            single_article_dict = json.loads(choice["message"]["content"])
+
+            allWordAmount = 0 
+           
+            articleWordAmount = get_article_words_count(single_article_dict["englishArticle"])
+            allWordAmount += articleWordAmount
+
+            for i in single_article_dict["questions"]:
+                count_trunk = get_article_words_count(i["trunk"])
+                count_candidates = sum([get_article_words_count(ii["text"]) for ii in i["candidates"]])
+                allWordAmount += count_trunk
+                allWordAmount += count_candidates
+
+           
+            usedMeanIds: list = single_article_dict['usedMeanIds'] 
+           
+            article_words = split_text_to_word(single_article_dict['englishArticle'])
+           
+            for i in core_words:
+                meaning_id = i.get('meaning_id', 0)
+                if not meaning_id:
+                    continue
+                word = i["spell"]
+                if meaning_id not in usedMeanIds and word in self.exchange_data: 
+                    words_exchanges_list = self.exchange_data[word]
+                    for exchange_word in words_exchanges_list:
+                        if exchange_word in article_words:
+                            usedMeanIds.append(meaning_id)
+                            break
+
+           
+            single_article_dict["body"] = single_article_dict.pop("englishArticle")
+            single_article_dict["chinese"] = single_article_dict.pop("chineseArticle")
+
+           
+            for q in single_article_dict['questions']:
+                data = q['candidates']
+                shuffled_candidates = sample(data, len(data))
+
+                labels = ['A', 'B', 'C', 'D']
+                for index, candidate in enumerate(shuffled_candidates):
+                    candidate['label'] = labels[index]
+                q['candidates'] = shuffled_candidates
+
+           
+            return_json['articles'].append({**single_article_dict, "allWordAmount": allWordAmount, "articleWordAmount": articleWordAmount})
+
+        return return_json
+
+
 
 
    
    
     @retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
     @retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
-    async def get_article(self, core_words: list, task_id: int, reading_level, article_length) -> dict:
+    def get_article(self, core_words: list, task_id: int, reading_level, article_length,n) -> dict:
        
        
         if not article_length:
         if not article_length:
             if 0 < reading_level <= 10:
             if 0 < reading_level <= 10:
@@ -208,7 +271,7 @@ class GetArticle:
 
 
        
        
         shuffle(core_words)
         shuffle(core_words)
-        core_words_meaning_str = ";".join([str(i['meaning_id']) + ' ' + i["spell"] + ":" + i["meaning"] for i in core_words])
+        core_words_meaning_str = "; ".join([f"[{i['meaning_id']}  {i['spell']} {i['meaning']}]" for i in core_words])
 
 
         no_escape_code = r"\\n\\n"
         no_escape_code = r"\\n\\n"
 
 
@@ -232,53 +295,12 @@ class GetArticle:
             real_ip = self.real_ip_dict[task_id]
             real_ip = self.real_ip_dict[task_id]
             demo_name = self.demo_name[task_id]
             demo_name = self.demo_name[task_id]
 
 
-            r_json = json.loads(await get_article_gpt_pydantic(q, temperature=0.9, real_ip=real_ip, demo_name=demo_name, model='gpt-4.1',
-                                                               check_fucn=CheckArticleResult.get_article_1, max_tokens=4000,
-                                                               sys_prompt=sys_prompt, client=self.client))
-           
-            allWordAmount = 0 
-           
-            articleWordAmount = get_article_words_count(r_json["englishArticle"])
-            allWordAmount += articleWordAmount
+            gpt_resp = get_article_gpt_pydantic(q, temperature=1.2, real_ip=real_ip, demo_name=demo_name, model='gpt-4.1',
+                                                               check_fucn=CheckArticleResult.get_article_1, max_tokens=8000,
+                                                               sys_prompt=sys_prompt,n=n)
+            multi_articles_dict = self.__parse_gpt_resp(gpt_resp=gpt_resp,core_words=core_words)
+            return multi_articles_dict
 
 
-            for i in r_json["questions"]:
-                count_trunk = get_article_words_count(i["trunk"])
-                count_candidates = sum([get_article_words_count(ii["text"]) for ii in i["candidates"]])
-                allWordAmount += count_trunk
-                allWordAmount += count_candidates
-
-           
-            usedMeanIds: list = r_json['usedMeanIds'] 
-           
-            article_words = split_text_to_word(r_json['englishArticle'])
-           
-            for i in core_words:
-                meaning_id = i.get('meaning_id', 0)
-                if not meaning_id:
-                    continue
-                word = i["spell"]
-                if meaning_id not in usedMeanIds and word in self.exchange_data: 
-                    words_exchanges_list = self.exchange_data[word]
-                    for exchange_word in words_exchanges_list:
-                        if exchange_word in article_words:
-                            usedMeanIds.append(meaning_id)
-                            break
-
-           
-            r_json["body"] = r_json.pop("englishArticle")
-            r_json["chinese"] = r_json.pop("chineseArticle")
-
-           
-            for q in r_json['questions']:
-                data = q['candidates']
-                shuffled_candidates = sample(data, len(data))
-
-                labels = ['A', 'B', 'C', 'D']
-                for index, candidate in enumerate(shuffled_candidates):
-                    candidate['label'] = labels[index]
-                q['candidates'] = shuffled_candidates
-
-            return {**r_json, "allWordAmount": allWordAmount, "articleWordAmount": articleWordAmount}
         except httpx.HTTPError as e:
         except httpx.HTTPError as e:
             logger.error(f"HTTP请求错误: {str(e)}")
             logger.error(f"HTTP请求错误: {str(e)}")
             raise
             raise
@@ -289,8 +311,9 @@ class GetArticle:
             log_err_e(e, f"gpt生成文章回复其他错误.")
             log_err_e(e, f"gpt生成文章回复其他错误.")
             raise
             raise
 
 
+
    
    
-    async def run_get_article_task(self, core_words, task_id, take_count, reading_level, article_length) -> dict:
+    def run_get_article_task(self, core_words, task_id, take_count, reading_level, article_length) -> dict:
         """
         """
         :param core_words: 核心单词数据,优先级1;可能为空
         :param core_words: 核心单词数据,优先级1;可能为空
         :param task_id: 任务id
         :param task_id: 任务id
@@ -300,51 +323,33 @@ class GetArticle:
         :return:
         :return:
         """
         """
         try:
         try:
-            tasks = []
-            for i in range(take_count):
-                tasks.append(
-                    self.get_article(core_words, task_id, reading_level, article_length))
-
-            results = await asyncio.gather(*tasks, return_exceptions=True)
-
-           
-            for result in results:
-                if isinstance(result, Exception):
-                    continue
-
-            return {"articles": results}
+            return_json = self.get_article(core_words, task_id, reading_level, article_length,n=take_count)
+            return return_json
         except Exception as e:
         except Exception as e:
             logger.error(f"运行文章任务时发生错误: {str(e)}")
             logger.error(f"运行文章任务时发生错误: {str(e)}")
             raise
             raise
 
 
    
    
-    async def run_task(self, core_words, task_id, take_count, reading_level, article_length):
+    def run_task(self, core_words, task_id, take_count, reading_level, article_length):
         try:
         try:
-            outside_json = await self.run_get_article_task(core_words, task_id, take_count, reading_level, article_length)
+            outside_json = self.run_get_article_task(core_words, task_id, take_count, reading_level, article_length)
             return outside_json
             return outside_json
         except Exception as e:
         except Exception as e:
             log_err_e(e, msg="外层总任务捕获错误")
             log_err_e(e, msg="外层总任务捕获错误")
-        finally:
-            self.real_ip_dict.pop(task_id)
-            self.demo_name.pop(task_id)
 
 
-    async def cleanup(self):
+    def cleanup(self):
         """清理所有资源"""
         """清理所有资源"""
-        try:
-           
-            if hasattr(self, 'client'):
-                await self.client.aclose()
-           
-            self.real_ip_dict.clear()
-            self.demo_name.clear()
-            self.callback_url_dict.clear()
-            self.article_result.clear()
-
-        except Exception as e:
-            logger.error(f"清理资源时发生错误: {str(e)}")
-            raise
+        pass
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
 
 
-    def __del__(self):
-        """析构函数,确保资源被正确释放"""
-        if hasattr(self, 'client'):
-            asyncio.create_task(self.cleanup())

+ 200 - 20
mock/mock_request.py

@@ -1,9 +1,17 @@
 # -*- coding:utf-8 -*-
 # -*- coding:utf-8 -*-
 #
 #
-import requests
-import time
 import json 
 import json 
+import time
 from functools import wraps
 from functools import wraps
+from random import shuffle,sample,randint
+from threading import Thread
+from concurrent.futures import ThreadPoolExecutor,wait
+
+import httpx
+import requests
+from pydantic import BaseModel
+from typing import List
+
 
 
 product_adress = "http://111.231.167.191" 
 product_adress = "http://111.231.167.191" 
 test_address = "http://111.231.167.191:8004" 
 test_address = "http://111.231.167.191:8004" 
@@ -11,7 +19,32 @@ test_address2 = "http://111.231.167.191:8003"
 
 
 local_adress = "http://127.0.0.1:9000" 
 local_adress = "http://127.0.0.1:9000" 
 
 
-use_address = test_address 
+use_address = local_adress 
+
+class DifficultSentence(BaseModel):
+    english: str
+    chinese: str
+
+class Candidate(BaseModel):
+    label: str
+    text: str
+    isRight: int
+
+class Question(BaseModel):
+    trunk: str
+    analysis: str
+    candidates: List[Candidate]
+
+class Article(BaseModel):
+    difficultSentences: List[DifficultSentence]
+    usedMeanIds: List[int]
+    questions: List[Question]
+    englishArticle: str
+    chineseArticle: str
+    allWordAmount: int
+
+class ArticleData(BaseModel):
+    articles: List[Article]
 
 
 
 
 def time_use(fn):
 def time_use(fn):
@@ -25,7 +58,7 @@ def time_use(fn):
             print(f'函数:{fn.__name__} 一共用时', cha, '秒')
             print(f'函数:{fn.__name__} 一共用时', cha, '秒')
         return res 
         return res 
 
 
-    return cc 
+    return cc
 
 
 
 
 def test_connect():
 def test_connect():
@@ -100,29 +133,157 @@ def get_article2():
 @time_use
 @time_use
 def get_article2_1():
 def get_article2_1():
     """新的获取文章"""
     """新的获取文章"""
-    json_data = {'core_words': [{'spell': 'sudden', 'meaning': '突然的, 意外的', 'word_id': 1114468, 'meaning_id': 1734},
-                                {'spell': 'frighten', 'meaning': '惊吓, 惊恐', 'word_id': 899278, 'meaning_id': 1735},
-                                {'spell': 'relation', 'meaning': '关系, 联系, 亲戚, 亲属', 'word_id': 1061800, 'meaning_id': 1736},
-                                {'spell': 'agreement', 'meaning': '协议,协定', 'word_id': 753401, 'meaning_id': 1743},
-                                {'spell': 'pool', 'meaning': '游泳池, 池子', 'word_id': 1035634, 'meaning_id': 1747},
-                                {'spell': 'risk', 'meaning': '冒险, 风险', 'word_id': 1069002, 'meaning_id': 1748},
-                                {'spell': 'centre', 'meaning': '中心', 'word_id': 806629, 'meaning_id': 1749},
-                                {'spell': 'shut', 'meaning': '关上, 关闭', 'word_id': 1088662, 'meaning_id': 1751},
-                                {'spell': 'piano', 'meaning': '钢琴', 'word_id': 1027211, 'meaning_id': 1752},
-                                {'spell': 'trust', 'meaning': '信任, 信赖', 'word_id': 1142977, 'meaning_id': 1753},
-                                {'spell': 'camera', 'meaning': '照相机', 'word_id': 799656, 'meaning_id': 1754},
-                                {'spell': 'course', 'meaning': '课程', 'word_id': 834016, 'meaning_id': 399},
-                                {'spell': 'thought', 'meaning': '思想, 想法', 'word_id': 1130826, 'meaning_id': 685}],
-                 'take_count': 4, 'student_stage': 3, 'demo_name': '春笋英语',"exercise_id":999,"article_length":200}
+    core_words_list = [{'spell': 'sudden', 'meaning': '突然的, 意外的', 'word_id': 1114468, 'meaning_id': 1734},
+                       {'spell': 'frighten', 'meaning': '惊吓, 惊恐', 'word_id': 899278, 'meaning_id': 1735},
+                       {'spell': 'relation', 'meaning': '关系, 联系, 亲戚, 亲属', 'word_id': 1061800, 'meaning_id': 1736},
+                       {'spell': 'agreement', 'meaning': '协议,协定', 'word_id': 753401, 'meaning_id': 1743},
+                       {'spell': 'risk', 'meaning': '冒险, 风险', 'word_id': 1069002, 'meaning_id': 1748},
+                       {'spell': 'centre', 'meaning': '中心', 'word_id': 806629, 'meaning_id': 1749},
+                       {'spell': 'shut', 'meaning': '关上, 关闭', 'word_id': 1088662, 'meaning_id': 1751},
+                       {'spell': 'thought', 'meaning': '思想, 想法', 'word_id': 1130826, 'meaning_id': 685},
+                       {'spell': 'information', 'meaning': '消息, 信息', 'word_id': 940351, 'meaning_id': 487, 'serial': 330},
+                       {'spell': 'bright', 'meaning': '聪明的', 'word_id': 793695, 'meaning_id': 1451, 'serial': 1048},
+                       {'spell': 'international', 'meaning': '国际的', 'word_id': 945460, 'meaning_id': 1683, 'serial': 1232},
+                       {'spell': 'shelf', 'meaning': '架子, 搁板', 'word_id': 1086743, 'meaning_id': 1838, 'serial': 1366},
+                       {'spell': 'cave', 'meaning': '洞穴, 山洞', 'word_id': 805431, 'meaning_id': 2167, 'serial': 1639},
+                       {'spell': 'gym', 'meaning': '健身房, 体育馆', 'word_id': 915473, 'meaning_id': 2217, 'serial': 1683},
+                       {'spell': 'properly', 'meaning': '适当地, 正确地', 'word_id': 1045343, 'meaning_id': 2257, 'serial': 1720},
+                       {'spell': 'platform', 'meaning': '平台', 'word_id': 1031256, 'meaning_id': 2269, 'serial': 1730},
+                       {'spell': 'sweep', 'meaning': '打扫, 清扫', 'word_id': 1118098, 'meaning_id': 2321, 'serial': 1775},
+                       {'spell': 'clinic', 'meaning': '诊所, 门诊部', 'word_id': 815699, 'meaning_id': 2471, 'serial': 1898},
+                       {'spell': 'sauce', 'meaning': '酱油, 调味料', 'word_id': 1076452, 'meaning_id': 2501, 'serial': 1927},
+                       {'spell': 'retell', 'meaning': '重讲, 复述', 'word_id': 1065717, 'meaning_id': 2546, 'serial': 1970},
+                       {'spell': 'specific', 'meaning': '具体的, 明确的', 'word_id': 1099668, 'meaning_id': 3089, 'serial': 2421},
+                       {'spell': 'religion', 'meaning': '宗教', 'word_id': 1062490, 'meaning_id': 3358, 'serial': 2626},
+                       {'spell': 'collapse', 'meaning': '倒塌, 崩溃', 'word_id': 819500, 'meaning_id': 3667, 'serial': 2872},
+                       {'spell': 'bare', 'meaning': '光秃秃的', 'word_id': 777035, 'meaning_id': 4592, 'serial': 3650},
+                       {'spell': 'defendant', 'meaning': '被告的, 被告人', 'word_id': 1174797, 'meaning_id': 4975, 'serial': 3979},
+                       {'spell': 'interact', 'meaning': '互相作用, 互动', 'word_id': 943776, 'meaning_id': 5117, 'serial': 4103},
+                       {'spell': 'fact', 'meaning': '事实, 真相', 'word_id': 882302, 'meaning_id': 425, 'serial': 289},
+                       {'spell': 'except', 'meaning': '除了…之外', 'word_id': 878228, 'meaning_id': 814, 'serial': 561},
+                       {'spell': 'opposite', 'meaning': '相反, 对面', 'word_id': 1008508, 'meaning_id': 1650, 'serial': 1207},
+                       {'spell': 'clerk', 'meaning': '职员, 店员', 'word_id': 815428, 'meaning_id': 1826, 'serial': 1354},
+                       {'spell': 'chief', 'meaning': '主要的,首要的', 'word_id': 810493, 'meaning_id': 2067, 'serial': 1552},
+                       {'spell': 'congratulation', 'meaning': '祝贺, 贺辞', 'word_id': 826539, 'meaning_id': 2187, 'serial': 1657},
+                       {'spell': 'chest', 'meaning': '大箱子', 'word_id': 810293, 'meaning_id': 2223, 'serial': 1689},
+                       {'spell': 'monitor', 'meaning': '班长', 'word_id': 988984, 'meaning_id': 2262, 'serial': 1724},
+                       {'spell': 'accurate', 'meaning': '正确的, 精确的', 'word_id': 747138, 'meaning_id': 2278, 'serial': 1739},
+                       {'spell': 'investigate', 'meaning': '调查, 研究', 'word_id': 947316, 'meaning_id': 2359, 'serial': 1806},
+                       {'spell': 'forecast', 'meaning': '预报, 预测', 'word_id': 895859, 'meaning_id': 2495, 'serial': 1921},
+                       {'spell': 'sausage', 'meaning': '香肠, 腊肠', 'word_id': 1076506, 'meaning_id': 2536, 'serial': 1961},
+                       {'spell': 'insurance', 'meaning': '保险', 'word_id': 943100, 'meaning_id': 3044, 'serial': 2380},
+                       {'spell': 'reveal', 'meaning': '揭示, 暴露, 展现', 'word_id': 1066342, 'meaning_id': 3246, 'serial': 2544},
+                       {'spell': 'perception', 'meaning': '观念, 知觉, 觉察', 'word_id': 1174551, 'meaning_id': 3516, 'serial': 2749},
+                       {'spell': 'violation', 'meaning': '妨碍, 侵犯, 违犯', 'word_id': 1174695, 'meaning_id': 4452, 'serial': 3528},
+                       {'spell': 'convey', 'meaning': '表达', 'word_id': 830280, 'meaning_id': 4931, 'serial': 3938},
+                       {'spell': 'migration', 'meaning': '迁移, 移居', 'word_id': 1175117, 'meaning_id': 5069, 'serial': 4063}
+                       ]
+    shuffle(core_words_list)
+    core_words_chiose_list = sample(core_words_list,5)
+    json_data = {'core_words': core_words_chiose_list,
+                 'take_count': 8, 'student_stage': 2, 'demo_name': '春笋英语', "exercise_id": randint(100,999),
+                 "article_length": 120, "reading_level": 5}
 
 
     r = requests.post(f"{use_address}/article/reading-comprehension", json=json_data)
     r = requests.post(f"{use_address}/article/reading-comprehension", json=json_data)
     r_json = r.json()
     r_json = r.json()
+    print(r_json)
     try:
     try:
         return r_json
         return r_json
     except Exception as e:
     except Exception as e:
         print("春笋文章reading-comprehension错误", e)
         print("春笋文章reading-comprehension错误", e)
         print("错误数据", r_json)
         print("错误数据", r_json)
 
 
+@time_use
+def get_article2_2():
+    """测试通过requests来直接访问openai"""
+    core_words_list = [{'spell': 'sudden', 'meaning': '突然的, 意外的', 'word_id': 1114468, 'meaning_id': 1734},
+                       {'spell': 'frighten', 'meaning': '惊吓, 惊恐', 'word_id': 899278, 'meaning_id': 1735},
+                       {'spell': 'relation', 'meaning': '关系, 联系, 亲戚, 亲属', 'word_id': 1061800, 'meaning_id': 1736},
+                       {'spell': 'agreement', 'meaning': '协议,协定', 'word_id': 753401, 'meaning_id': 1743},
+                       {'spell': 'risk', 'meaning': '冒险, 风险', 'word_id': 1069002, 'meaning_id': 1748},
+                       {'spell': 'centre', 'meaning': '中心', 'word_id': 806629, 'meaning_id': 1749},
+                       {'spell': 'shut', 'meaning': '关上, 关闭', 'word_id': 1088662, 'meaning_id': 1751},
+                       {'spell': 'thought', 'meaning': '思想, 想法', 'word_id': 1130826, 'meaning_id': 685},
+                       {'spell': 'information', 'meaning': '消息, 信息', 'word_id': 940351, 'meaning_id': 487, 'serial': 330},
+                       {'spell': 'bright', 'meaning': '聪明的', 'word_id': 793695, 'meaning_id': 1451, 'serial': 1048},
+                       {'spell': 'international', 'meaning': '国际的', 'word_id': 945460, 'meaning_id': 1683, 'serial': 1232},
+                       {'spell': 'shelf', 'meaning': '架子, 搁板', 'word_id': 1086743, 'meaning_id': 1838, 'serial': 1366},
+                       {'spell': 'cave', 'meaning': '洞穴, 山洞', 'word_id': 805431, 'meaning_id': 2167, 'serial': 1639},
+                       {'spell': 'gym', 'meaning': '健身房, 体育馆', 'word_id': 915473, 'meaning_id': 2217, 'serial': 1683},
+                       {'spell': 'properly', 'meaning': '适当地, 正确地', 'word_id': 1045343, 'meaning_id': 2257, 'serial': 1720},
+                       {'spell': 'platform', 'meaning': '平台', 'word_id': 1031256, 'meaning_id': 2269, 'serial': 1730},
+                       {'spell': 'sweep', 'meaning': '打扫, 清扫', 'word_id': 1118098, 'meaning_id': 2321, 'serial': 1775},
+                       {'spell': 'clinic', 'meaning': '诊所, 门诊部', 'word_id': 815699, 'meaning_id': 2471, 'serial': 1898},
+                       {'spell': 'sauce', 'meaning': '酱油, 调味料', 'word_id': 1076452, 'meaning_id': 2501, 'serial': 1927},
+                       {'spell': 'retell', 'meaning': '重讲, 复述', 'word_id': 1065717, 'meaning_id': 2546, 'serial': 1970},
+                       {'spell': 'specific', 'meaning': '具体的, 明确的', 'word_id': 1099668, 'meaning_id': 3089, 'serial': 2421},
+                       {'spell': 'religion', 'meaning': '宗教', 'word_id': 1062490, 'meaning_id': 3358, 'serial': 2626},
+                       {'spell': 'collapse', 'meaning': '倒塌, 崩溃', 'word_id': 819500, 'meaning_id': 3667, 'serial': 2872},
+                       {'spell': 'bare', 'meaning': '光秃秃的', 'word_id': 777035, 'meaning_id': 4592, 'serial': 3650},
+                       {'spell': 'defendant', 'meaning': '被告的, 被告人', 'word_id': 1174797, 'meaning_id': 4975, 'serial': 3979},
+                       {'spell': 'interact', 'meaning': '互相作用, 互动', 'word_id': 943776, 'meaning_id': 5117, 'serial': 4103},
+                       {'spell': 'fact', 'meaning': '事实, 真相', 'word_id': 882302, 'meaning_id': 425, 'serial': 289},
+                       {'spell': 'except', 'meaning': '除了…之外', 'word_id': 878228, 'meaning_id': 814, 'serial': 561},
+                       {'spell': 'opposite', 'meaning': '相反, 对面', 'word_id': 1008508, 'meaning_id': 1650, 'serial': 1207},
+                       {'spell': 'clerk', 'meaning': '职员, 店员', 'word_id': 815428, 'meaning_id': 1826, 'serial': 1354},
+                       {'spell': 'chief', 'meaning': '主要的,首要的', 'word_id': 810493, 'meaning_id': 2067, 'serial': 1552},
+                       {'spell': 'congratulation', 'meaning': '祝贺, 贺辞', 'word_id': 826539, 'meaning_id': 2187, 'serial': 1657},
+                       {'spell': 'chest', 'meaning': '大箱子', 'word_id': 810293, 'meaning_id': 2223, 'serial': 1689},
+                       {'spell': 'monitor', 'meaning': '班长', 'word_id': 988984, 'meaning_id': 2262, 'serial': 1724},
+                       {'spell': 'accurate', 'meaning': '正确的, 精确的', 'word_id': 747138, 'meaning_id': 2278, 'serial': 1739},
+                       {'spell': 'investigate', 'meaning': '调查, 研究', 'word_id': 947316, 'meaning_id': 2359, 'serial': 1806},
+                       {'spell': 'forecast', 'meaning': '预报, 预测', 'word_id': 895859, 'meaning_id': 2495, 'serial': 1921},
+                       {'spell': 'sausage', 'meaning': '香肠, 腊肠', 'word_id': 1076506, 'meaning_id': 2536, 'serial': 1961},
+                       {'spell': 'insurance', 'meaning': '保险', 'word_id': 943100, 'meaning_id': 3044, 'serial': 2380},
+                       {'spell': 'reveal', 'meaning': '揭示, 暴露, 展现', 'word_id': 1066342, 'meaning_id': 3246, 'serial': 2544},
+                       {'spell': 'perception', 'meaning': '观念, 知觉, 觉察', 'word_id': 1174551, 'meaning_id': 3516, 'serial': 2749},
+                       {'spell': 'violation', 'meaning': '妨碍, 侵犯, 违犯', 'word_id': 1174695, 'meaning_id': 4452, 'serial': 3528},
+                       {'spell': 'convey', 'meaning': '表达', 'word_id': 830280, 'meaning_id': 4931, 'serial': 3938},
+                       {'spell': 'migration', 'meaning': '迁移, 移居', 'word_id': 1175117, 'meaning_id': 5069, 'serial': 4063}
+                       ]
+    shuffle(core_words_list)
+    core_words_chiose_list = sample(core_words_list,5)
+    core_words_meaning_str = "; ".join([f"[{i['meaning_id']}  {i['spell']} {i['meaning']}]" for i in core_words_chiose_list])
+
+    question = f"""下面我会为你提供一组数据,[单词组](里面包含词义id,英语单词,中文词义),请根据这些单词的中文词义,生成一篇带中文翻译的考场英语文章,英语文章和中文翻译要有[标题]。注意这个单词有多个词义时,生成的英语文章一定要用提供的中文词义。并挑选一句复杂的句子和其中文翻译,放入difficultSentences。英语文章,放入"englishArticle"中。中文翻译,放入"chineseArticle"中。最终文中使用到的单词id放入"usedMeanIds"中。4个选择题,放入questions字段。questions结构下有4个选择题对象,其中trunk是[英语]问题文本,analysis是[中文]的问题分析,candidates是4个ABCD选项,内部有label是指选项序号A B C D ,text是[英语]选项文本,isRight是否正确答案1是正确0是错误。
+
+要求:
+1.必须用提供的这个词义的单词,其他单词使用最简单最容易没有难度的单词。文章整体非常简洁,通俗易懂,适合初学者,刚入门,单词全是最常见的,语句通顺即可。选择题难度尽可能简单,参考中国小学生水平
+2.优先保证文章语句通顺,意思不要太生硬。不要为了使用特定的单词,造成文章语义前后不搭,允许不使用个别词义。
+3.文章中使用提供单词,一定要和提供单词的中文词义匹配,尤其是一词多义时,务必使用提供单词的词义。必须要用提供单词的词义。如果用到的词义与提供单词词义不一致,请不要使用这个单词。
+4.生成的文章要求120词左右,可以用\\n\\n字符分段,一般1-2个段落左右。第一段是文章标题。
+5.允许不使用[单词组]的个别单词,优先保证文章整体意思通顺连贯和故事完整。
+6.注意回复字段的中英文,englishArticle是英文,chineseArticle是中文,其中trunk是英文,analysis是中文,text是英文。
+
+提供[单词组]:{core_words_meaning_str}
+"""
+
+    url = "http://170.106.108.95/v1/chat/completions"
+
+   
+    headers = {
+        "Authorization": f"Bearer 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",
+        "Content-Type": "application/json"
+    }
+
+   
+    data = {
+        "model": "gpt-4.1", 
+        "messages": [
+           
+            {"role": "user", "content": question}
+        ],
+        "max_tokens": 4000, 
+        "temperature": 1.2, 
+        "n":8,
+        "response_format": {'type': 'json_schema', 'json_schema': {'name': 'Article', 'schema': {'$defs': {'Candidate': {'properties': {'label': {'title': 'Label', 'type': 'string'}, 'text': {'title': 'Text', 'type': 'string'}, 'isRight': {'title': 'Isright', 'type': 'integer'}}, 'required': ['label', 'text', 'isRight'], 'title': 'Candidate', 'type': 'object'}, 'DifficultSentence': {'properties': {'english': {'title': 'English', 'type': 'string'}, 'chinese': {'title': 'Chinese', 'type': 'string'}}, 'required': ['english', 'chinese'], 'title': 'DifficultSentence', 'type': 'object'}, 'Question': {'properties': {'trunk': {'title': 'Trunk', 'type': 'string'}, 'analysis': {'title': 'Analysis', 'type': 'string'}, 'candidates': {'items': {'$ref': '#/$defs/Candidate'}, 'title': 'Candidates', 'type': 'array'}}, 'required': ['trunk', 'analysis', 'candidates'], 'title': 'Question', 'type': 'object'}}, 'properties': {'difficultSentences': {'items': {'$ref': '#/$defs/DifficultSentence'}, 'title': 'Difficultsentences', 'type': 'array'}, 'usedMeanIds': {'items': {'type': 'integer'}, 'title': 'Usedmeanids', 'type': 'array'}, 'questions': {'items': {'$ref': '#/$defs/Question'}, 'title': 'Questions', 'type': 'array'}, 'englishArticle': {'title': 'Englisharticle', 'type': 'string'}, 'chineseArticle': {'title': 'Chinesearticle', 'type': 'string'}, 'allWordAmount': {'title': 'Allwordamount', 'type': 'integer'}}, 'required': ['difficultSentences', 'usedMeanIds', 'questions', 'englishArticle', 'chineseArticle', 'allWordAmount'], 'title': 'Article', 'type': 'object'}}}
+    }
+
+   
+    response = httpx.post(url, headers=headers, json=data,timeout=300)
+    print(response.json())
+    return response.json()
+
 
 
 def download_word():
 def download_word():
     from make_docx_demo.data import test_json2
     from make_docx_demo.data import test_json2
@@ -183,12 +344,31 @@ def run_all_test_cese():
    
    
 
 
 
 
+@time_use
+def multi_request():
+    with ThreadPoolExecutor(max_workers=50) as executor:
+       
+        futures = [executor.submit(get_article2_1) for _ in range(30)]
+       
+
+       
+        wait(futures)
+        print("完成等待")
+        for index,future in enumerate(futures,start=1):
+            future.result() 
+            print(f"完成循环{index}")
+
+
 if __name__ == '__main__':
 if __name__ == '__main__':
-    run_all_test_cese()
    
    
-
+    multi_request()
 
 
    
    
    
    
+
+   
+
    
    
    
    
+
+   

+ 1 - 1
tools/audio.py

@@ -82,7 +82,7 @@ class GetAudio:
         self.token = None
         self.token = None
         self.lock = Lock()
         self.lock = Lock()
 
 
-        self.re_compile = re.compile("[.!?;*]")
+        self.re_compile = re.compile("[.!?;*\"]")
 
 
    
    
     def upload_file_to_oss(self, word_or_hash_name, oss_file_name, local_file_path):
     def upload_file_to_oss(self, word_or_hash_name, oss_file_name, local_file_path):

+ 1 - 1
tools/thread_pool_manager.py

@@ -2,4 +2,4 @@
 from concurrent.futures import ThreadPoolExecutor, wait
 from concurrent.futures import ThreadPoolExecutor, wait
 
 
 
 
-pool_executor = ThreadPoolExecutor(max_workers=20)
+pool_executor = ThreadPoolExecutor(max_workers=200)