From 4f5d8cfbfe1b292a4b3169cde976227d9050970a Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Mon, 28 Jul 2025 10:32:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86poster=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=8F=90=E7=A4=BA=E5=AD=97=E6=9E=84=E7=AD=91=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/models/__pycache__/poster.cpython-312.pyc | Bin 4235 -> 5738 bytes api/models/poster.py | 20 ++- .../__pycache__/poster.cpython-312.pyc | Bin 6066 -> 7616 bytes api/routers/poster.py | 42 +++++- .../database_service.cpython-312.pyc | Bin 45313 -> 45319 bytes .../__pycache__/poster.cpython-312.pyc | Bin 15729 -> 24634 bytes api/services/database_service.py | 6 +- api/services/poster.py | 131 ++++++++++++++---- 8 files changed, 163 insertions(+), 36 deletions(-) diff --git a/api/models/__pycache__/poster.cpython-312.pyc b/api/models/__pycache__/poster.cpython-312.pyc index 19b994a01e37e9945c792d5234c696a7e89fef85..95239f1c94b97146cdcce51e48ecd71e294b78c8 100644 GIT binary patch delta 3130 zcmZ`*ZERE589vv(wy&>0lh}^=a1sb5F~m+FQAKH+j)B5bs0olxt*6Nt-xK1>U%U6Z zQ`l5{QwB*_X^w6+%v5P%sya5%U_#oYm37;%{n#HO=SOm-sc33wT4@7HCZzq^`yPM9 zc$pJF_nhZF=Y7vP_kG^uAMg5d!1*_aLnQFIbN|xxZO4Ms&;4VeZ=@hZ1&&CZWJ+1n zVpL4qqBf2g$Z;Z>XNhE?;teCtA|z^OtN^UFiFGhm1lHEXIvHyR*3rbe80!SqRcA-Q zv76Cupxf#+ux*TO2ezY$ZJ+T3y_$iOY@9Agah0mcG}S1#kDI+y;tp_8%1fQn_M7|- zLv7g}+CeSSj+dZoAYe61JDaRtYNh6FV1qG%>KNF17ewuDc<{}bgFU)e>oIxn{LAnj z$GNoc@!u-scfhm<UTjO`6*ZfV<+6%OF(Z@T_Y-_m_Hr2EN3@8T?Sp5Cive&^04dqLKACXml_o{D4dHnX_W#4G=)si-0`Dk+0A1Kp% z2O`Vq5C>TeI4gzyaE zy4qsrDs7aTQ{^M6dvngw(VliWC=Hpzt+5SCeTH!Z1+w!>E1sx}Z5lW{{(9W!Fk zquVEGI+p@@jAy2@d3c@LC84W;X|3C);~6PMWz6>=r<>xbWL(khH8z$5TDK-raYc#5 z(^MB(GnRpNM0Y4Nkjs$LayFe)bx&TQa;*~so<-DMGW||IDN`vnm82<2(Oub`n#^Y6 zg;bs4mE=XL3rXcjLQTF+b!$?I#aZ*2cUXHtiz6gdRr&G$3Fg zMww17GGQTA1t0M^;Y0u*`Srl}z(8?{pb?lGQ@H^oLZaW;_J>n@v?wqr}Y;xX*Z|t$@~T_Z4cQO4OAIGoT<5VubIL-2hj}_sLt$ zR^ae+qxv9cNsx;`nE{1or?PT7u0n@bFYawQ45|&<&Gj{e z>%+bYlN1r4NEW$Y+OF9?bg-O@1Wm1yMCs~|w-gXO%_OoCm6a{E{sxSjM7LNWXUToL zXUP|OWHf9O=N*M6&AVFzY^C|L;G?SS%^MV*Gx6)ZluW2H?qN%PpGE`1N%$%fFEN9H{WqtKEBUPcC;Kh5=G(xNIyP zn$J}D!%ul5UX%dUeVk@GT5zPI|5u!wVrr4lIarR2@FslOS>Y|-zv65!4bLZ+`Oa#H zb1X#8`3`qRd+6*ru~neWI?1$(LTviM(GnNaS4Q}@7Ih6cnRKxm33kXK?R{5&L1u4S z$jwOcjG9cyR-A!3QvKz@$_=Y3ED$^7b7*CrpN$6jn9fT16dje5Fa#4-$pJhvavb+q zFmU|81;Vv`N(L%q;O`{xDS2*VySa-K%i|ja9vffdTwJ*H!UloI#%}GT+i$ml_00_e OkB#kG&fUr57XAy7%Q6rS<^cz12@`ZsaXn8a@Lv%#TG38XYpQPL_jP#lCn2qR$@??MdzLAx%( zm!wi%3B<+KTyp4*3sQQh)FYyYNw`ujKP)I@ax_7&^KCRqPllZqAlXIJLL9QwV1tTAN4VgjB9C(#wx<<~My*zF7>#nB{NfirVmkfc6af$bL;z6i zaphX0NA_Vo9{+K@USYZI3Gil8roe*kF$ht^xkt`RKSciFH9`3cQRq4bNIo{?%Omop zoXc3eC78k@^=6jHERk}4u$q!ClcJLQT%{W?ux`}Mie9}kxJgbRS$CCGDTYzi;^JG+;8Dmp|?UC~iFT%C|6{%?NO;~|j{%zc)3iR`&Q&B$g z4N-#5L}D|Mb%o4^m#{{h@R=kG+KbwjbEVQ)ts0+N&a&ZHFVB%b!l})?9{QhwuoHTb z#;{ohhbPeE&2p(>ufUVOvX+4Zc$MA`ynE0@7*;rTZSB$Nxd%&?-P2X_XLKMndg}H& zyQAl>Ui#W>zqrM<&wf+e<9vjIui@ z`xuRP#9^##t#;5$bnTq=<5>HZ4x&pZ%HjAHwdvAPL_E@dzk}$~2@7~(i`sPQsAO$m IkcVRa1v3;nm;e9( diff --git a/api/models/poster.py b/api/models/poster.py index be9451c..cc19ceb 100644 --- a/api/models/poster.py +++ b/api/models/poster.py @@ -7,6 +7,7 @@ from re import T from typing import List, Dict, Any, Optional +from datetime import datetime from pydantic import BaseModel, Field @@ -47,9 +48,17 @@ class TemplateInfo(BaseModel): id: str name: str description: str - handlerPath: str - className: str - isActive: bool + handler_path: str = Field(alias="handlerPath") + class_name: str = Field(alias="className") + system_prompt: Optional[str] = None + user_prompt_template: Optional[str] = None + required_fields: Optional[List[str]] = None + optional_fields: Optional[List[str]] = None + size: Optional[List[int]] = None + is_active: bool = Field(alias="isActive") + + class Config: + allow_population_by_field_name = True class TemplateListResponse(BaseModel): """模板列表响应模型""" @@ -71,6 +80,11 @@ class PosterGenerateResponse(BaseModel): resultImagesBase64: List[Dict[str, Any]] = Field(description="生成的海报图像(base64编码)列表") psdFiles: Optional[List[Dict[str, Any]]] = Field(None, description="生成的PSD文件信息列表") metadata: Dict[str, Any] = Field(default_factory=dict) + + class Config: + json_encoders = { + datetime: lambda v: v.isoformat() + } class ImageUsageRequest(BaseModel): """图像使用查询请求模型""" diff --git a/api/routers/__pycache__/poster.cpython-312.pyc b/api/routers/__pycache__/poster.cpython-312.pyc index fa14f458498d6c1d5be6f84461b3783a6edab9c5..348b93415a786ad989a71aa690e338624e92c8c9 100644 GIT binary patch delta 2879 zcmai0eNa@_6~Fg=?Fah-%d#xri@PXqS+h{0L_nP^*qWLMGnlk~WsANSg+Z3Q_cp|t zMT{{CMyhshYn`Z#PGXA%MQW{?R&5IY;Y|NwyPd+kp&dKpE;yY@*1(MEq%-ZkZ&^Sz z(=+qVJNMr6JLlYc&pGe?#{YSp<^47@=?pZY9Pw zEk&ecp-9XTic{X!l!*3uq-zPX{9BNvVy>_vg)Ar$9rH-{5+r|+;yu^9s67;K^(&&R zV0NIZqZNx`8}^^LaeQX@XNiG}iSvJa_}-7FZ(rphGO;VyWZ=bj@iRLG0oVc{_&G3u z2xw2m`~>fTZJ>vWz;+sKVe@+%!EObBT@(O4bPp3@6gI6LVcK>XT)GrfPKAFCF zHU8m+ncKHDMb#`HlyTk!yQI*8E=9G6Wl@?R1k?GX0-lmq0MH90H}=3Ys&P-N+_pE^ z*7-(A&D!582cNX!Bva3{WJ&o6c8Hz(l>KtpiR~>-(ZnZ*6CYoSU+quyo#JU#cR-Zo z)_tOCA+c;H$sODq3U?~1Asw6?&L=jWO7{4tBQw9fRO{My8LE2m?NqQ2=WsrTEen9O z5*p!hOSkey)wr-}S!LU>4_RgQghCxEy-!ro4Cn3TBo~HD6_}} zIbgH*ag*NO>S6z7|KGfIQPax4EoX}E7gtP`@dK+T@Qki%LXNz$OAKIsHb*LS9hOE2(eJ16r>9_g4&V-he1tMETEk2iS)Hu&JtfICnQ zMpqGewA|3Z)1y_@4IX;T!(w@ir|~?BGPzvh#?8csm(UkXu}&(%=)DkavX!FEY!M2x z?hKeNr51pG#a6kg2&EMnJ;bC}aFLlYP$PjADD(}RXXetYxSn*Vl*`Gf!U|eh%$&Fx zKY2QF>~P}TF|>(uS#beqH*QoyN^3_0ig2Dx$%oY?Qgf0Yv#sbJ=kiNy$+(8F7YJKN z7-^GIEoMBum_5=4Jb_%gqJ~B+d>=pZLCogwU(fD@ljQzuqxRaEy}r*dWpz(FN~Uu2 z&#Xg-=2)5>Jxz{YkB=UW7#rX$I&LpVP+!AJq=8+3S+79oM}IPSxKyak+ z7!8n$Z9Rr+vB?aU7P40Q9+@X>*Y#J8I+#H^o%R{2q|i>=T68|+BL;s(Z0KS}POcp< zE~6nhXhe0Uyqetff?#X|5$aQT0=9q}&~L@F5eD4NOHqw^9aTUf$vjvr2XPw{*FNv{*1FuIz4^i9aL@Eg05Kldsn->-hE=HD3R{dB@0A7)lR2>`Z4ZP%`J`bzW3uJM-rz$ zc<_hceR-=descKX;bULkI;JHP!DCo36jr*zO0cU{*-uPhSPC$62gN`5{mjkb_?@Fq zfq5e()uZ*+t`gO>GzitY%p|&E&BvF0&suC=$ji%sx1m|`u%`6dh96NOdL!m4BraIT#N8BXJ4kYO|SadY_~b7j=(AK)+>*ZY$N zc>3RSg=lwHcY_1>4JzOSqZ|x4m{FaD$T_gl0Y@E>$WB9JHa%MUT!WP!vzo9xR#4q& zq3>E)EZ@zh8y(kkZEwM>g;PpZuhfEmk?qT0RvinKEBy#P$ab>=-qc6PWIv>n$bTa5 zMK|n^UniB0KL+@MMM)~c18EImq_0b)xV75XYDZL{2uNw%nKg2b+BSMgRZ+ delta 1515 zcmZuwT}+!*7(VCwDeYI<(jWTEPk}O8HV3TW3Oc0)O>h&9e+hblmG5I?c9xuewHh~2 zAPZ4qF^2(4j4lxa1H%|I^FoX;jfvifCW3Vr-n7)aW;B_*^PEC~s3$pZp7(v9_nh(@ul#q}R(t+0K$<|tH1R#Crbz@Nf$9EH_B(Y$C`U?>p)21uM# zu>g0qUMN~6muQp>y_{$p;TLwz;KgdmdHzYV`q~A~OR%W<+k0&wC+| zHG7ExQ6aLjF0Wl=@|B=5iv{_JAt=I2%m5REG4KZ41!C|EHU>S)=ur|WILSKv+U<`< zQeWOmy*rzlyNWl}T1%&gNvQU-OQ_|r91L~$;H=t(&nfkmc_yKb$U!+8>Fx=OQbHk0 zaeb;I8J zt39x33XH1m=&H8N<#()(>5ds~mS>BE3}974h6TlK@P1YRg~d3bYxmZZbB3Vb2S<#J zU<#h#rfrR2nHp}uMsQQ*Z(>)y{(5$;o+ogviS_$%K;tucAaasWlGI-Jy8FbHL5&Qd z8)vmHMKy7v8eFC;JM#dA$Em2Jg5K!ike6bbUqW+m#B04_)Ie+zjtE*`R6=P@dC{^m zpeuj^o%Ug}cQ_m`+Ta%9f)bDGZP19X=^YLe@#K~ZAnzqsHs|&8Hag09M{m$L@-~r^ zOR>RF<0#8pNR;nCQ6Akvgi2J9m@z{$^F72r7#7SV^+;`Pi!(X%O>#Ki(ViTgN?#?z zYdno#HK|H_!l6*`tV9{9>80fO=i3vXq(;Y6?|n@I@Gr(IoJWbgIBn{GyfdmL&q&6H zQdh43HSuA3X{p88H%@*jqD_y|%=UtFgO>bkIel}s#rYiCEjMw`15XiNEq-SZ6%j2V zybugUrNKM%Jo z)ti>44NKFOwS3cByMcc*`#l*wus*m0R94|0P#J_#Mb-?8j%?`a<`hKytgOpei1~ij zil4E>+5jB!9}6&GtsVvdx7MJg_)*vn;5vW=uQRH)Q|!9>c%YeGZ!RSGHt%iovA2CZ z#i!VIuu^D!9qRXsk*iuKYQ@-g%>J)2JgC8G{JX7+f12bI#n3(gBiNyFGae7VY`@n< z=Qg20;ixPjR7w^0tRy4)4|gj_XEVX|ha+;XHi({sTB)EDm7Cv$^5F|%F&dH{K`#>< tX(x!x;y)bM@M_7Vs}hpxX(}@;1%H|ti;@5U diff --git a/api/routers/poster.py b/api/routers/poster.py index 4e2c83b..0a67291 100644 --- a/api/routers/poster.py +++ b/api/routers/poster.py @@ -37,18 +37,52 @@ def get_poster_service( return PosterService(ai_agent, config_manager, output_manager) -@router.get("/templates", response_model=TemplateListResponse, summary="获取可用模板列表") +@router.get("/test/templates", summary="测试模板配置") +async def test_templates( + poster_service: PosterService = Depends(get_poster_service) +): + """ + 测试模板配置是否正确加载 + """ + try: + # 获取所有模板信息 + templates = poster_service._templates + + # 检查每个模板的配置 + result = {} + for template_id, template_info in templates.items(): + result[template_id] = { + "basic_info": template_info, + "has_system_prompt": bool(template_info.get('system_prompt')), + "has_user_prompt_template": bool(template_info.get('user_prompt_template')), + "prompt_lengths": { + "system_prompt": len(template_info.get('system_prompt', '')), + "user_prompt_template": len(template_info.get('user_prompt_template', '')) + } + } + + return { + "message": "模板配置检查完成", + "template_count": len(templates), + "templates": result + } + except Exception as e: + logger.error(f"测试模板配置失败: {e}", exc_info=True) + raise HTTPException(status_code=500, detail=f"测试失败: {str(e)}") + + +@router.get("/templates", response_model=TemplateListResponse, summary="获取海报模板列表") async def get_templates( poster_service: PosterService = Depends(get_poster_service) ): """ - 获取可用的海报模板列表 + 获取所有可用的海报模板列表 """ try: - templates = await poster_service.get_available_templates() + templates = poster_service.get_available_templates() return TemplateListResponse( templates=templates, - total_count=len(templates) + totalCount=len(templates) ) except Exception as e: logger.error(f"获取模板列表失败: {e}", exc_info=True) diff --git a/api/services/__pycache__/database_service.cpython-312.pyc b/api/services/__pycache__/database_service.cpython-312.pyc index 27bc0d2f98c1d2f0dd1509da4fa3c2e6dafd18ff..fa799f52f95ec171216e1e3118d3be162ffe4a5a 100644 GIT binary patch delta 250 zcmZpC#MJ(XiT5-wFBbz4{JGnfA+wS9`y^wdU{@bk=MV)g1-Br7UxkAF;*!*&_>$Dz zf}F&X)M5qyAQ#sl1*b@bhw*vXg@*KjQZJ!ptNp_JNH-UUjlUso3Vw zDeIWUb+8+_S#pLaBct17J9UH2lV`qTa(Bb74sLO{hij0lLS}J%Vsc4lS*n7qf+1GN ae3&CU`O%#F+zF_TnH)MLeY5Ah9YO%Ps#bmg delta 170 zcmZpF#MJnRiT5-wFBbz4oZHrtA-0kC`y?@gU{@bk=MV)g1-Br7UxkAF;*!*&kjc`M zC7B#cCTCBUog6ax5u?}S6LZ@)@0;SzET)ZB!)BQoo{WsHlOL{B+1xer7n6oFR%NNV n1v!Z&sl^K69 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 diff --git a/api/services/database_service.py b/api/services/database_service.py index 78fbd47..8d3a855 100644 --- a/api/services/database_service.py +++ b/api/services/database_service.py @@ -821,7 +821,7 @@ class DatabaseService: with self.db_pool.get_connection() as conn: with conn.cursor(dictionary=True) as cursor: cursor.execute( - "SELECT * FROM posterTemplates ORDER BY createdAt" + "SELECT * FROM poster_templates ORDER BY created_at" ) results = cursor.fetchall() logger.info(f"获取海报模板列表: 找到{len(results)}个模板") @@ -849,7 +849,7 @@ class DatabaseService: with self.db_pool.get_connection() as conn: with conn.cursor(dictionary=True) as cursor: cursor.execute( - "SELECT * FROM posterTemplates WHERE id = %s", + "SELECT * FROM poster_templates WHERE id = %s", (template_id,) ) result = cursor.fetchone() @@ -878,7 +878,7 @@ class DatabaseService: with self.db_pool.get_connection() as conn: with conn.cursor(dictionary=True) as cursor: cursor.execute( - "SELECT * FROM posterTemplates WHERE isActive = 1 ORDER BY createdAt" + "SELECT * FROM poster_templates WHERE is_active = 1 ORDER BY created_at" ) results = cursor.fetchall() logger.info(f"获取激活模板列表: 找到{len(results)}个模板") diff --git a/api/services/poster.py b/api/services/poster.py index afb709f..d924d49 100644 --- a/api/services/poster.py +++ b/api/services/poster.py @@ -63,18 +63,18 @@ class PosterService: 'vibrant': { 'id': 'vibrant', 'name': '活力风格', - 'handlerPath': 'poster.templates.vibrant_template', - 'className': 'VibrantTemplate', + 'handler_path': 'poster.templates.vibrant_template', + 'class_name': 'VibrantTemplate', 'description': '适合景点、活动等充满活力的场景', - 'isActive': True + 'is_active': True }, 'business': { 'id': 'business', 'name': '商务风格', - 'handlerPath': 'poster.templates.business_template', - 'className': 'BusinessTemplate', + 'handler_path': 'poster.templates.business_template', + 'class_name': 'BusinessTemplate', 'description': '适合酒店、房地产等商务场景', - 'isActive': True + 'is_active': True } } @@ -89,11 +89,11 @@ class PosterService: return self._template_instances[template_id] template_info = self._templates[template_id] - handler_path = template_info.get('handlerPath') - class_name = template_info.get('className') + handler_path = template_info.get('handler_path') + class_name = template_info.get('class_name') if not handler_path or not class_name: - logger.error(f"模板 {template_id} 缺少 handlerPath 或 className") + logger.error(f"模板 {template_id} 缺少 handler_path 或 class_name") return None try: @@ -132,10 +132,17 @@ class PosterService: """获取所有可用的模板信息""" result = [] for tid in self._templates: - if self._templates[tid].get('is_active'): - template_info = self.get_template_info(tid) - if template_info: - result.append(template_info) + template = self._templates[tid] + if template.get('is_active', True): # 默认为激活状态 + template_info = { + "id": template["id"], + "name": template["name"], + "description": template["description"], + "handlerPath": template.get("handler_path", ""), + "className": template.get("class_name", ""), + "isActive": template.get("is_active", True) + } + result.append(template_info) return result def get_template_info(self, template_id: str) -> Optional[Dict[str, Any]]: @@ -316,13 +323,17 @@ class PosterService: async def _generate_content_with_llm(self, template_id: str, content_id: Optional[int], product_id: Optional[int], scenic_spot_id: Optional[int]) -> Optional[Dict[str, Any]]: """使用LLM生成海报内容""" - # 获取提示词 + # 获取提示词 - 直接从数据库模板信息中获取 template_info = self._templates.get(template_id, {}) - system_prompt = template_info.get('systemPrompt', "") - user_prompt_template = template_info.get('userPromptTemplate', "") + system_prompt = template_info.get('system_prompt', "") + user_prompt_template = template_info.get('user_prompt_template', "") + if not system_prompt or not user_prompt_template: logger.error(f"模板 {template_id} 缺少提示词配置") + logger.debug(f"模板信息: {template_info}") return None + + logger.info(f"成功加载模板 {template_id} 的提示词配置") # 获取相关数据 data = {} @@ -332,23 +343,91 @@ class PosterService: data['product'] = self.db_service.get_product_by_id(product_id) if scenic_spot_id: data['scenic_spot'] = self.db_service.get_scenic_spot_by_id(scenic_spot_id) - logger.info(f"data: {data}") + + logger.info(f"获取到的数据: content={data.get('content') is not None}, product={data.get('product') is not None}, scenic_spot={data.get('scenic_spot') is not None}") - # 格式化提示词 + # 格式化数据为简洁的文本格式,参考其他模块的做法 try: - user_prompt = user_prompt_template.format(**data) - logger.info(f"user_prompt: {user_prompt}") - except KeyError as e: - logger.warning(f"格式化提示词时缺少键: {e}") - user_prompt = user_prompt_template + f"\n可用数据: {json.dumps(data, ensure_ascii=False)}" + logger.info("开始格式化数据...") + + # 景区信息格式化 + scenic_info = "无相关景区信息" + if data.get('scenic_spot'): + logger.info("正在格式化景区信息...") + spot = data['scenic_spot'] + scenic_info = f"""景区名称: {spot.get('name', '')} +地址: {spot.get('address', '')} +描述: {spot.get('description', '')} +优势: {spot.get('advantage', '')} +亮点: {spot.get('highlight', '')} +交通信息: {spot.get('trafficInfo', '')}""" + logger.info("景区信息格式化完成") + + # 产品信息格式化 + product_info = "无相关产品信息" + if data.get('product'): + logger.info("正在格式化产品信息...") + product = data['product'] + product_info = f"""产品名称: {product.get('productName', '')} +原价: {product.get('originPrice', '')} +实际价格: {product.get('realPrice', '')} +套餐信息: {product.get('packageInfo', '')} +核心优势: {product.get('keyAdvantages', '')} +亮点: {product.get('highlights', '')} +详细描述: {product.get('detailedDescription', '')}""" + logger.info("产品信息格式化完成") + + # 内容信息格式化 + tweet_info = "无相关内容信息" + if data.get('content'): + logger.info("正在格式化内容信息...") + content = data['content'] + tweet_info = f"""标题: {content.get('title', '')} +内容: {content.get('content', '')}""" + logger.info("内容信息格式化完成") + + logger.info("开始构建用户提示词...") + # 构建用户提示词 + user_prompt = user_prompt_template.format( + scenic_info=scenic_info, + product_info=product_info, + tweet_info=tweet_info + ) + + logger.info(f"用户提示词构建成功,长度: {len(user_prompt)}") + + except Exception as e: + logger.error(f"格式化提示词时发生错误: {e}", exc_info=True) + # 提供兜底方案 + user_prompt = f"""{user_prompt_template} + +当前可用数据: +- 景区信息: {'有' if data.get('scenic_spot') else '无'} +- 产品信息: {'有' if data.get('product') else '无'} +- 内容信息: {'有' if data.get('content') else '无'} + +请根据可用信息生成海报内容。""" try: - response, _, _, _ = await self.ai_agent.generate_text(system_prompt=system_prompt, user_prompt=user_prompt,use_stream=True) + response, _, _, _ = await self.ai_agent.generate_text( + system_prompt=system_prompt, + user_prompt=user_prompt, + use_stream=True + ) + + # 提取JSON响应 json_start = response.find('{') json_end = response.rfind('}') + 1 if json_start != -1 and json_end != -1: - return json.loads(response[json_start:json_end]) - logger.error(f"LLM响应中未找到JSON: {response}") + result = json.loads(response[json_start:json_end]) + logger.info(f"LLM生成内容成功: {list(result.keys())}") + return result + else: + logger.error(f"LLM响应中未找到JSON格式内容: {response[:200]}...") + return None + + except json.JSONDecodeError as e: + logger.error(f"解析LLM响应JSON失败: {e}") return None except Exception as e: logger.error(f"生成内容时发生错误: {e}", exc_info=True)