From f492e2974950fb5340278cea4e36448c17673f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E8=BE=85=E5=85=83?= <2401926342@qq.com> Date: Thu, 16 Apr 2026 16:26:58 +0800 Subject: [PATCH] Refactor `_chat_stream_events` in `api_server.py` to standardize SSE response format by removing additional status codes and consolidating output into a unified JSON structure. Update logging to enhance clarity on streaming events and improve error handling. Adjust documentation to reflect changes in event data structure. --- __pycache__/api_server.cpython-312.pyc | Bin 53257 -> 52966 bytes api_server.py | 17 +- .../length.bin | Bin 4000 -> 4000 bytes logs/text2sql_api.log | 211 +++++------------- 4 files changed, 59 insertions(+), 169 deletions(-) diff --git a/__pycache__/api_server.cpython-312.pyc b/__pycache__/api_server.cpython-312.pyc index b19096017a740fe726a4d67c01cee1dcb2e3661e..b4a19b4492e83e7264cd42dc27f3876897e51dbf 100644 GIT binary patch delta 3111 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 M@^G&5%Q>z1Uw6-X%>V!Z delta 3370 zcma)8eNmZs~veeB@$1spDxq-gI9fjWe? zBr`&0Jc*vvq*e_V744QnDye%Q6LrP$0Fv9sW>o8*va_`v``EMZb7>Ge{jv8Pe)so$ zywCf5yzga3w)4SO-q5GlM{?}3ulsn3cgI14;In)+f#VvY8;lL626Ka@!P*ehV1o}I zj)%6#bD-Ln3kx?{1c{SW(CeGS$N2Q{(k8P$3ZvlT=YlbiS#=lTVMW!8#WC$ zqx$wb&A2qw;WYeeR*ZIhG5#@|r2TaMr z^D+3@r*HO@q{WNBB8t`29q-ZWM^))WutYqX+KvaV+{t(i)SJN5Gcf=)sMlDFd~Q@4*e5WEV2J+j&%Ci*8-JOHI-W`NbRQ*WM;AzahVGlHL1-y=HMxg({j_~mD9{Dx!+M(EZ!mZ?Txs5{8zZEP{(5RpQ z$19VfnaNtid$?-ClzdYSTzNJlSIJ@go|5tZ*%Q+0Qq4cJII5;?3f3z4M|iw0l`Mw( zy4<`3zkVyXT_xG~-V#1;Hyv%QZ{b)K`mvVd7vtNyTkmOP@krAh$((Oq*eivj0^vNU zpIOa?D`~p6kFO@qwBLWZk$pq<@bq=&Pj@2hf+yB=!HQR-;I_4pMW007K?cJ@;at~4 z^bE?ZZQL-wbm=ns8d@T}Rb^%6UN60$>E$zP8&%}*u&DlN(~C@|xPwzmryzd`KB%{o zX85kYf($^(^G9@S@zYC-pD3@Sn;>D`C)(kN6{Xa-q;x6#cioR<6a3Kd0EvYKf!SmW zGzX?+DLKA`v04zS5jG<0^H_iE;~_(FNK-tdG048)lVny@Z0q#n_VYzIOqcDM2Vy#{7wva_&k=3R#^fQB zwduZZ4V=!hrLxm={=wpYOGzlQG$fS5erl5=8>>{e4cQVl`ERw!1qY>W?i5 z845$1LYTi{YMp@-@BG2Lf%Ra0fzFddPFjguBb0 zLEUqwz<93;7xe0h+bHxJ3LH#0HC=E|Q=Q5p?y2fi**x+Y1?kMtmmIyw#Pwy9qD*z) zZ9MW^m!l}Zjz4Q7McL}Jv6G7O`LlVXXomXi?TL(^v+J1GxfD{AC!9+akUI)~K?s>$CMH}=B_4-xG0liP*M)fvsE8+97~s{&bp+8sSk?nZgL%}>nldoPB6)~hD zi^K^-d<61H4f7l_X+3fL(8L^%mA@1%vIv)~BGX)o!)9JeAS}~M4ibm2WUVKYzm%>R zG6?ca!ILBFHo8>om@jBf6Fb;^9CA&wo{$b$2VaO?Oi>_vU!JWcf08@5PaxCRGmUcR zXd|+E22K@SiTo;F%q<8l2wRyB)H{0A+tK(c^zTT}{Tb<_2s`2Wj`=y873K)V>ayihdAehn_lb0eRO&0OE%|iKb4rv|Mqhc24?f#nP4>cLZ={Jm zEWH*v@P?kKNBaa@+uY(Arfq}&v|S+Y!TWpM;@_COUrzbU9Ad&o?orVr?4^9{Xd~3N zyTyx4?vjVvml4tr_wLJApGJp12<&r;gN(i>AKKSK$OTx?Vbfs@8bWA<*xY}+!=dQ6?|7lf>QBXOk@p%Ve-^4d1hE#QR*vu zk{*VIT~6_579v)z?fM;wyTB;@9${QFP&^EuzLOGt9qHO|ET*&_NfSBtRZfF~Bd5t1 zpg+1m zj~X?t!?jfi!|3-2veWRzi44;}P?ph4MHOxa=gIlbFHv2MZpz*Y`{$VMYM40%aGkUy z;-0AL#5fwNG)vh{(VF4>$vAQyuAX!lY*-rm!=zLy*l{9W&iU&(l@9;OQT&R~{b1;| z*L5*EEUZ}0E*V}}ir0eTEueVxDV}cn69S%PiidPqEtM}V_mxvTniP*C#gj+zNKrgT z6b}sDiGXvbI6aCZqc|Ljvlx!3dS*%4a#{(ydrj&nR2!hXH;d%T-}GKlspqN4o!1_g JKX_j!{ty0mpOgRq diff --git a/api_server.py b/api_server.py index bb1311b..39b62af 100644 --- a/api_server.py +++ b/api_server.py @@ -659,7 +659,9 @@ async def _run_generate( async def _chat_stream_events(request: NLChatRequest) -> AsyncIterator[bytes]: if not request.message or not request.message.strip(): lang = _normalize_lang_code(request.lang_code) - yield _sse_data({"code": 400, "msg": _localized_empty_input_reply(lang), "data": None}) + yield _sse_data( + {"stage": "sql_gen", "stream_kind": "content", "content": _localized_empty_input_reply(lang)} + ) return text = request.message.strip() orch = get_orchestrator() @@ -696,16 +698,13 @@ async def _chat_stream_events(request: NLChatRequest) -> AsyncIterator[bytes]: len(reply), reply[:500] + ("…" if len(reply) > 500 else ""), ) - yield _sse_data({"stage": "orchestrator", "stream_kind": "content", "content": "CHAT"}) - async for pkt in _sse_stream_text_chunks("chat", reply): + async for pkt in _sse_stream_text_chunks("sql_gen", reply): yield pkt data_dict = _conversation_nl_dict(reply) data_dict["llm"] = _effective_llm_route_from_request(request) - yield _sse_data({"code": 200, "msg": "success", "data": data_dict}) await _append_session_if_needed(request, text, data_dict) return - yield _sse_data({"stage": "orchestrator", "stream_kind": "content", "content": "DATA_QUERY"}) dialect = resolve_sql_dialect(os.getenv("TEXT2SQL_DIALECT", "sqlserver")) dc_ctx = (dialog_block or "").strip() or None top_k = 20 @@ -759,7 +758,7 @@ async def _chat_stream_events(request: NLChatRequest) -> AsyncIterator[bytes]: result = holder["result"] except Exception as e: logger.error(f"[API/stream] 生成异常: {e}", exc_info=True) - yield _sse_data({"code": 500, "msg": str(e), "data": None}) + yield _sse_data({"stage": "sql_gen", "stream_kind": "content", "content": str(e)}) return sql_out = (result.sql or "").strip() @@ -792,8 +791,6 @@ async def _chat_stream_events(request: NLChatRequest) -> AsyncIterator[bytes]: data_dict = _nl_dict_from_generation(result) data_dict["llm"] = _effective_llm_route_from_request(request) - msg = "success" if result.valid else "partial" - yield _sse_data({"code": 200, "msg": msg, "data": data_dict}) await _append_session_if_needed(request, text, data_dict) @@ -919,8 +916,8 @@ async def nl_chat(request: NLChatRequest): async def nl_chat_stream(request: NLChatRequest): """ 自然语言对话流式接口(SSE)。 - 事件体为 JSON:分片 `data: {"stage","stream_kind","content"}`(例:sql_gen 时 - `stream_kind` 为 `content`)或结束包 `{code, msg, data}`。 + 事件体为 JSON:仅发送分片 `data: {"stage":"sql_gen","stream_kind":"content","content":"..."}`, + 流结束表示完成;不再发送额外的结束包。 """ return StreamingResponse( _chat_stream_events(request), diff --git a/data/embeddings/chroma_fewshot/9756e6ea-6d51-4e24-a490-4a58f0a585b9/length.bin b/data/embeddings/chroma_fewshot/9756e6ea-6d51-4e24-a490-4a58f0a585b9/length.bin index 35d3e3f2d1dd4c04ee0c6f4a11e8ce5956420f64..d1fa5fe9b7b0ce3292b49c25b0aa11a055477be1 100644 GIT binary patch literal 4000 zcmXRowdChCMh1q4oEiT?Gy{VK2Z%huIUOt>!_dOO1ms7sfkYV?ws=G6h!YUH@MSlc z!3^PIu#tqR!sLgV;=|y`5Wo<^;148q8O#_=8JvKmH-kBY1yGcMfd#6L5yD^<1@hBp zurp8k)VeD4++|s?SHWsPzGGm3@nJL<0}oi70qlDy6G%aW0WQD_Wf?J;Fc>fxGFSrn zFf-taMj4|aFd71*Aut*OqaiRF0;3@?8UmvsFvvncQb{9*fsdhKkU4AAIin#kqC)@x DbKM(I literal 4000 zcmb7`TW}o36^46uXLe_#z3aBSl2&W$W?8-p6JsvEV_6s57~8Vk#x{c4wJlGFgb=WC zf>ntz3(3P&K~+@2Z@l3bic1w$yZ}j6s={rJ9=rurDJOXXUfNner1F&0=ZqjTTB1sq zT7ABA`ah?8EJ zjGOFbFaBFE{-qZueUyFT#YrDwfA!)^`UNk(r1#g4ve$k3<@H|+=>7Fg_M%T8VP6BQ z{ZV}@|CfN?Uw`HO|1O~S*N?LA0`>j-xAJcU^#1x$_K{D&eE)9;^#1xLd&#F?zW?C* z{qLYUYZ^;H@6bZ* zIt#)45X=w3+z@C&Fu%Xn(>pg>IkTYm)>aS9jl0POeV|tCo7-P`YC-R-^$*P*aCa=| zL$%()xe2#tK_8U*DJ>VNna!0c8CK?PGgc`}LzyzdtVHEm8AULem2@N0LTF-E9HuoR z#F&+G+hrUfL7F5&N=-W}(+C+Ac}}(0th75QI}ozWidBxu970|tD9KKQ zE~PPKH$s6~ZEi{SAQYL^?p~Iw5LPoQ<34bw7okrTy)63?22{2Sau8vS(!3yt5Y{Tq zb~%i&PAzAHT#vAUS=sr5+=#G=>TE_Bq15*vY@yWmBK$;cVM=aAxKBmOavQ?^YSm@= z0K$WGsUAYuPSbWE?9`am;*QB(8scui?0in{A>0d?n;%+ym~bCpta3t*67B~~R4&Oe z!g0W)Ysm@1NkEnP0O3K@PRT=rj{vI09wvO$!y|-80o9E@MmPni9*^UM(}1zcv^+t` zQF~6FBzz1fUzDc^PXlJ$`{WtI#{rYBDQ5_u092`-B(yzkiLgxC#j}LxJnebHS-^Nj z$P0uHM&nWt{uD6fcFU&-e+HOwACf;Od>XLDJs^KU_>70YBzzXI)jcJjBfJRM<_dX< z@Oi+F`MA7H_$xrQj9(MJ0GM>+@(SV35G`X@i0&_-s=J49FQ6*)FyTHxwa8Jz{WN)T zjBp%K)tw-m1dLZ4d4TXBVA74rLxhh2X50tmVZui}JVJOBu*IE}#|Wo9JWelqKRahA+yq zgy#U&^KqVV77(7EI~NF@5KDE{;IFkriABT0Ru6+I(UkCr zC_8TCYq@PQ4C8I9chP{+o29Hm!mzb^F=X`C&5%*7n`^;2tu+}Vce6dlq;_|=)(oC} z+-R*AG-J0WBVl7&tMv@aFd8HGl5yJ_t{cj8w@EYhY4wP1G}U98v85i-j9tK*F^>PY zljE+Mz`k#9GVR9J!@JET$6Gzm10F;ZxOA$71?syL_2RsyUFPgS)LU+o)dYV7oEIQbn!0H&AUZ`7jRCq(<6$!C|2>+ z$T6o^^zlBl<4(UA-~(ci4~jK>4f+z!kXXytieWx1*70@dOFHYt2EGC9l(SK6;+xP; zJDbG_AJJIJn1!P-*h=wPI1z&lyl^lE8+hS-3^wq>F&S*&h0`+F%0>w;z&wlSeg8tRu?=2m@5%y*uLvP() | [OK] 已加载配置文件: C:\Users\24019\Desktop\backman-camel\.env -2026-04-16 13:35:29 INFO [__main__] api_server.py:1120 () | 启动服务: http://0.0.0.0:8041 -2026-04-16 13:35:29 INFO [__main__] api_server.py:1121 () | API文档: http://0.0.0.0:8041/docs -2026-04-16 13:35:29 INFO [uvicorn.error] server.py:92 _serve() | Started server process [37328] -2026-04-16 13:35:29 INFO [uvicorn.error] on.py:48 startup() | Waiting for application startup. -2026-04-16 13:35:29 INFO [__main__] api_server.py:811 lifespan() | ============================================================ -2026-04-16 13:35:29 INFO [__main__] api_server.py:812 lifespan() | Text2SQL API Server 启动中... -2026-04-16 13:35:29 INFO [__main__] api_server.py:813 lifespan() | ============================================================ -2026-04-16 13:35:29 INFO [main] main.py:126 setup_environment() | [OK] 环境检查通过 -2026-04-16 13:35:29 INFO [main] main.py:127 setup_environment() | - Schema: data\schemas\G3SB_MCDataDictionary_table_structure.json -2026-04-16 13:35:29 INFO [main] main.py:128 setup_environment() | - LLM: openai -2026-04-16 13:35:29 INFO [main] main.py:154 load_schema() | 加载Schema: ./data/schemas/G3SB_MCDataDictionary_table_structure.json -2026-04-16 13:35:29 INFO [main] main.py:156 load_schema() | 表注释(meta): ./data/schemas/G3SB_MCDataDictionary_table_meta.json -2026-04-16 13:35:30 INFO [schema.loader] loader.py:107 load_from_json() | [OK] 加载Schema完成(G3SB schemas): G3SB_MCDataDictionary_table_structure, 共2516张表 -2026-04-16 13:35:30 INFO [main] main.py:163 load_schema() | [OK] Schema加载完成: G3SB_MCDataDictionary_table_structure, 共2516张表, 59196个字段 -2026-04-16 13:35:33 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-5.4, base_url=http://113.192.49.54:9080/v1 -2026-04-16 13:35:35 INFO [utils.embedding] embedding.py:144 __init__() | 使用 OpenAI Embedding API:model=text-embedding-ada-002,base_url=http://113.192.49.54:9080/v1,max_batch=100 -2026-04-16 13:35:37 INFO [utils.fewshot_chroma_store] fewshot_chroma_store.py:86 __init__() | [OK] FewShotChromaStore: 磁盘 data\embeddings\chroma_fewshot collection=fewshot_samples count=50 -2026-04-16 13:35:37 INFO [utils.fewshot_selector] fewshot_selector.py:128 _init_chroma_mode() | [Few-shot] 已从向量库加载(Chroma 50 条,data\embeddings\chroma_fewshot) -2026-04-16 13:35:37 INFO [utils.fewshot_selector] fewshot_selector.py:159 _init_chroma_mode() | [OK] Few-shot 使用 Chroma(50 条) -2026-04-16 13:35:37 INFO [agents.orchestrator] orchestrator.py:116 __init__() | Few-shot已启用: top_k=3, min_rating=7 -2026-04-16 13:35:37 INFO [agents.orchestrator] orchestrator.py:127 __init__() | [OK] Text2SQLOrchestrator初始化完成: max_retry=2, use_vector_search=True, fewshot=on, nl→zh_norm=on -2026-04-16 13:35:37 INFO [__main__] api_server.py:132 get_orchestrator() | [OK] Orchestrator 初始化完成 -2026-04-16 13:35:37 INFO [__main__] api_server.py:817 lifespan() | [OK] 服务已就绪 -2026-04-16 13:35:37 INFO [uvicorn.error] on.py:62 startup() | Application startup complete. -2026-04-16 13:35:37 INFO [uvicorn.error] server.py:224 _log_started_message() | Uvicorn running on http://0.0.0.0:8041 (Press CTRL+C to quit) -2026-04-16 13:35:52 INFO [uvicorn.access] httptools_impl.py:483 send() | 127.0.0.1:23615 - "POST /g3sb/api/nl/chat/stream HTTP/1.1" 200 -2026-04-16 13:35:52 INFO [__main__] api_server.py:668 _chat_stream_events() | [API/stream] 开始: user_id='anonymous' visitor_biz_id=None session_id=None service_code=None model='gpt-4o-mini' lang_code='auto' msg_chars=15 preview='所有客户账户之间的股票转移记录' dialog_context_chars=0 last_turn_was_data_query=False -2026-04-16 13:35:55 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 -2026-04-16 13:35:55 INFO [utils.dialog_classifier] dialog_classifier.py:261 classify_dialog() | [dialog] intent=text2sql (hybrid fast: query hint) preview='所有客户账户之间的股票转移记录' -2026-04-16 13:35:55 INFO [__main__] api_server.py:715 _chat_stream_events() | [GEN/API/stream] dialect=tsql top_k=20 dialog_context_chars=0 question_len=15 preview='所有客户账户之间的股票转移记录' -2026-04-16 13:35:57 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 -2026-04-16 13:36:00 INFO [agents.orchestrator] orchestrator.py:803 generate() | [GEN] 问句已归一中文:所有客户账户的股票转移记录 -2026-04-16 13:36:00 INFO [agents.orchestrator] orchestrator.py:816 generate() | [GEN] 开始生成SQL: question_chars=13 preview='所有客户账户的股票转移记录' dialog_context_chars=0 -2026-04-16 13:36:00 INFO [agents.orchestrator] orchestrator.py:831 generate() | 尝试 #1 -2026-04-16 13:36:00 INFO [schema.indexer] indexer.py:82 __init__() | [OK] 初始化SchemaIndexer(Chroma磁盘 path=data\embeddings\chroma): collection=schema_tables, count=2516 -2026-04-16 13:36:00 INFO [schema.indexer] indexer.py:106 ensure_index_for_schema() | Schema 向量索引已就绪(2516 张表),跳过向量化 -2026-04-16 13:36:00 INFO [agents.orchestrator] orchestrator.py:188 _coarse_filter() | [Orchestrator] 开始向量检索: query_chars=13 query_preview='所有客户账户的股票转移记录' -2026-04-16 13:36:02 INFO [utils.embedding] embedding.py:163 _set_dim_from_vector() | [OK] Embedding 向量维度:1536 -2026-04-16 13:36:03 INFO [schema.indexer] indexer.py:244 search() | Schema 向量检索: query_chars=13 命中=20(阈值=0.1)top=[('VSBHKRpt0430', 0.8264), ('VSBHKRpt0431', 0.8188), ('TSBTransferInstruction', 0.8147), ('VSBTransferInstruction', 0.8134), ('VSBHKRpt0397', 0.8126), ('VSBHKRpt0672', 0.8117), ('VSBHKRpt0090C', 0.81), ('VSBHKRpt0570', 0.8084), ('VSBHKRpt1030A', 0.8077), ('VSBRpt0999E', 0.8065), ('VSBRpt0999C', 0.8064), ('TSBAccountEntitlementRelease', 0.8063), ('VSBRpt0999A', 0.8059), ('TSBAccountInstrumentMovement', 0.8038), ('VSBTransferInstructionGenerationByAccountContract', 0.8033)] -2026-04-16 13:36:03 INFO [agents.orchestrator] orchestrator.py:204 _coarse_filter() | [Orchestrator] 向量粗筛: 命中=20 张(阈值内),表名+分: [('VSBHKRpt0430', 0.8264), ('VSBHKRpt0431', 0.8188), ('TSBTransferInstruction', 0.8147), ('VSBTransferInstruction', 0.8134), ('VSBHKRpt0397', 0.8126), ('VSBHKRpt0672', 0.8117), ('VSBHKRpt0090C', 0.81), ('VSBHKRpt0570', 0.8084), ('VSBHKRpt1030A', 0.8077), ('VSBRpt0999E', 0.8065), ('VSBRpt0999C', 0.8064), ('TSBAccountEntitlementRelease', 0.8063), ('VSBRpt0999A', 0.8059), ('TSBAccountInstrumentMovement', 0.8038), ('VSBTransferInstructionGenerationByAccountContract', 0.8033), ('VSBRpt0060', 0.8021), ('XCGatewayStockReconciliationReport', 0.802), ('TSBAccountEntitlementHold', 0.8013), ('WSBBatchLocationTransferDetail', 0.8012), ('VCAccountCashMovement', 0.8012)] -2026-04-16 13:36:07 INFO [agents.orchestrator] orchestrator.py:258 _llm_select_tables() | LLM精筛选中表:['VSBHKRpt0430', 'VSBHKRpt0431', 'TSBTransferInstruction', 'VSBTransferInstruction', 'TSBAccountInstrumentMovement'] | reasoning_chars=181 reasoning_preview='问题涉及客户账户的股票转移记录,VSBHKRpt0430和VSBHKRpt0431提供了客户股票的日常转移报告和账户工具移动报告,TSBTransferInstruction和VSBTransferInstruction则记录了账户转移指令,TSBAccountInstrumentMovement包含账户工具的移动交易信息,这些表共同覆盖了股票转移的各个方面。' -2026-04-16 13:36:07 INFO [agents.orchestrator] orchestrator.py:859 generate() | 选中表:['VSBHKRpt0430', 'VSBHKRpt0431', 'TSBTransferInstruction', 'VSBTransferInstruction', 'TSBAccountInstrumentMovement'],扩展后:['TSBTransferInstruction', 'VSBTransferInstruction', 'VSBHKRpt0431', 'VSBHKRpt0430', 'TSBAccountInstrumentMovement'] -2026-04-16 13:36:08 INFO [utils.fewshot_selector] fewshot_selector.py:209 select_best_with_score() | [Few-shot] best_with_score: qid=Q2 score=0.9575 preview='列出今日所有客户账户之间的股票转移记录。' -2026-04-16 13:36:08 INFO [agents.orchestrator] orchestrator.py:432 _generate_sql_golden_adapt() | [GEN] 黄金 few-shot 条件适配: qid=Q2 score=0.9575 -2026-04-16 13:36:12 INFO [agents.orchestrator] orchestrator.py:450 _generate_sql_golden_adapt() | 生成的SQL(黄金适配,chars=494): +2026-04-16 16:22:05 INFO [utils.repo_logging] repo_logging.py:79 configure_text2sql_api_logging() | 日志文件: C:\Users\24019\Desktop\backman-camel\logs\text2sql_api.log +2026-04-16 16:22:10 INFO [__main__] api_server.py:85 () | [OK] 已加载配置文件: C:\Users\24019\Desktop\backman-camel\.env +2026-04-16 16:22:10 INFO [__main__] api_server.py:1108 () | 启动服务: http://0.0.0.0:8041 +2026-04-16 16:22:10 INFO [__main__] api_server.py:1109 () | API文档: http://0.0.0.0:8041/docs +2026-04-16 16:22:10 INFO [uvicorn.error] server.py:92 _serve() | Started server process [33080] +2026-04-16 16:22:10 INFO [uvicorn.error] on.py:48 startup() | Waiting for application startup. +2026-04-16 16:22:10 INFO [__main__] api_server.py:799 lifespan() | ============================================================ +2026-04-16 16:22:10 INFO [__main__] api_server.py:800 lifespan() | Text2SQL API Server 启动中... +2026-04-16 16:22:10 INFO [__main__] api_server.py:801 lifespan() | ============================================================ +2026-04-16 16:22:10 INFO [main] main.py:126 setup_environment() | [OK] 环境检查通过 +2026-04-16 16:22:10 INFO [main] main.py:127 setup_environment() | - Schema: data\schemas\G3SB_MCDataDictionary_table_structure.json +2026-04-16 16:22:10 INFO [main] main.py:128 setup_environment() | - LLM: openai +2026-04-16 16:22:10 INFO [main] main.py:154 load_schema() | 加载Schema: ./data/schemas/G3SB_MCDataDictionary_table_structure.json +2026-04-16 16:22:10 INFO [main] main.py:156 load_schema() | 表注释(meta): ./data/schemas/G3SB_MCDataDictionary_table_meta.json +2026-04-16 16:22:10 INFO [schema.loader] loader.py:107 load_from_json() | [OK] 加载Schema完成(G3SB schemas): G3SB_MCDataDictionary_table_structure, 共2516张表 +2026-04-16 16:22:10 INFO [main] main.py:163 load_schema() | [OK] Schema加载完成: G3SB_MCDataDictionary_table_structure, 共2516张表, 59196个字段 +2026-04-16 16:22:14 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 +2026-04-16 16:22:17 INFO [utils.embedding] embedding.py:144 __init__() | 使用 OpenAI Embedding API:model=text-embedding-ada-002,base_url=http://113.192.49.54:9080/v1,max_batch=100 +2026-04-16 16:22:19 INFO [utils.fewshot_chroma_store] fewshot_chroma_store.py:86 __init__() | [OK] FewShotChromaStore: 磁盘 data\embeddings\chroma_fewshot collection=fewshot_samples count=50 +2026-04-16 16:22:19 INFO [utils.fewshot_selector] fewshot_selector.py:128 _init_chroma_mode() | [Few-shot] 已从向量库加载(Chroma 50 条,data\embeddings\chroma_fewshot) +2026-04-16 16:22:19 INFO [utils.fewshot_selector] fewshot_selector.py:159 _init_chroma_mode() | [OK] Few-shot 使用 Chroma(50 条) +2026-04-16 16:22:19 INFO [agents.orchestrator] orchestrator.py:116 __init__() | Few-shot已启用: top_k=3, min_rating=7 +2026-04-16 16:22:19 INFO [agents.orchestrator] orchestrator.py:127 __init__() | [OK] Text2SQLOrchestrator初始化完成: max_retry=2, use_vector_search=True, fewshot=on, nl→zh_norm=on +2026-04-16 16:22:19 INFO [__main__] api_server.py:132 get_orchestrator() | [OK] Orchestrator 初始化完成 +2026-04-16 16:22:19 INFO [__main__] api_server.py:805 lifespan() | [OK] 服务已就绪 +2026-04-16 16:22:19 INFO [uvicorn.error] on.py:62 startup() | Application startup complete. +2026-04-16 16:22:19 INFO [uvicorn.error] server.py:224 _log_started_message() | Uvicorn running on http://0.0.0.0:8041 (Press CTRL+C to quit) +2026-04-16 16:22:20 INFO [uvicorn.access] httptools_impl.py:483 send() | 127.0.0.1:14651 - "POST /g3sb/api/nl/chat/stream HTTP/1.1" 200 +2026-04-16 16:22:20 INFO [__main__] api_server.py:669 _chat_stream_events() | [API/stream] 开始: user_id='anonymous' visitor_biz_id=None session_id=None service_code=None model='gpt-4o-mini' lang_code='auto' msg_chars=15 preview='所有客户账户之间的股票转移记录' dialog_context_chars=0 last_turn_was_data_query=False +2026-04-16 16:22:23 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 +2026-04-16 16:22:23 INFO [utils.dialog_classifier] dialog_classifier.py:261 classify_dialog() | [dialog] intent=text2sql (hybrid fast: query hint) preview='所有客户账户之间的股票转移记录' +2026-04-16 16:22:23 INFO [__main__] api_server.py:712 _chat_stream_events() | [GEN/API/stream] dialect=tsql top_k=20 dialog_context_chars=0 question_len=15 preview='所有客户账户之间的股票转移记录' +2026-04-16 16:22:26 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 +2026-04-16 16:22:29 INFO [agents.orchestrator] orchestrator.py:803 generate() | [GEN] 问句已归一中文:所有客户账户的股票转移记录 +2026-04-16 16:22:29 INFO [agents.orchestrator] orchestrator.py:816 generate() | [GEN] 开始生成SQL: question_chars=13 preview='所有客户账户的股票转移记录' dialog_context_chars=0 +2026-04-16 16:22:29 INFO [agents.orchestrator] orchestrator.py:831 generate() | 尝试 #1 +2026-04-16 16:22:29 INFO [schema.indexer] indexer.py:82 __init__() | [OK] 初始化SchemaIndexer(Chroma磁盘 path=data\embeddings\chroma): collection=schema_tables, count=2516 +2026-04-16 16:22:29 INFO [schema.indexer] indexer.py:106 ensure_index_for_schema() | Schema 向量索引已就绪(2516 张表),跳过向量化 +2026-04-16 16:22:29 INFO [agents.orchestrator] orchestrator.py:188 _coarse_filter() | [Orchestrator] 开始向量检索: query_chars=13 query_preview='所有客户账户的股票转移记录' +2026-04-16 16:22:30 INFO [utils.embedding] embedding.py:163 _set_dim_from_vector() | [OK] Embedding 向量维度:1536 +2026-04-16 16:22:31 INFO [schema.indexer] indexer.py:244 search() | Schema 向量检索: query_chars=13 命中=20(阈值=0.1)top=[('VSBHKRpt0430', 0.8264), ('VSBHKRpt0431', 0.8188), ('TSBTransferInstruction', 0.8147), ('VSBTransferInstruction', 0.8134), ('VSBHKRpt0397', 0.8126), ('VSBHKRpt0672', 0.8117), ('VSBHKRpt0090C', 0.81), ('VSBHKRpt0570', 0.8084), ('VSBHKRpt1030A', 0.8077), ('VSBRpt0999E', 0.8065), ('VSBRpt0999C', 0.8064), ('TSBAccountEntitlementRelease', 0.8063), ('VSBRpt0999A', 0.8059), ('TSBAccountInstrumentMovement', 0.8038), ('VSBTransferInstructionGenerationByAccountContract', 0.8033)] +2026-04-16 16:22:31 INFO [agents.orchestrator] orchestrator.py:204 _coarse_filter() | [Orchestrator] 向量粗筛: 命中=20 张(阈值内),表名+分: [('VSBHKRpt0430', 0.8264), ('VSBHKRpt0431', 0.8188), ('TSBTransferInstruction', 0.8147), ('VSBTransferInstruction', 0.8134), ('VSBHKRpt0397', 0.8126), ('VSBHKRpt0672', 0.8117), ('VSBHKRpt0090C', 0.81), ('VSBHKRpt0570', 0.8084), ('VSBHKRpt1030A', 0.8077), ('VSBRpt0999E', 0.8065), ('VSBRpt0999C', 0.8064), ('TSBAccountEntitlementRelease', 0.8063), ('VSBRpt0999A', 0.8059), ('TSBAccountInstrumentMovement', 0.8038), ('VSBTransferInstructionGenerationByAccountContract', 0.8033), ('VSBRpt0060', 0.8021), ('XCGatewayStockReconciliationReport', 0.802), ('TSBAccountEntitlementHold', 0.8013), ('WSBBatchLocationTransferDetail', 0.8012), ('VCAccountCashMovement', 0.8012)] +2026-04-16 16:22:35 INFO [agents.orchestrator] orchestrator.py:258 _llm_select_tables() | LLM精筛选中表:['VSBHKRpt0430', 'VSBHKRpt0431', 'TSBTransferInstruction', 'VSBTransferInstruction', 'TSBAccountInstrumentMovement'] | reasoning_chars=192 reasoning_preview='问题涉及客户账户的股票转移记录,VSBHKRpt0430和VSBHKRpt0431提供了客户股票的日常转移报告和账户工具移动报告,TSBTransferInstruction和VSBTransferInstruction包含账户转移指令的详细信息,TSBAccountInstrumentMovement记录账户工具的移动交易。这些表共同涵盖了股票转移的各个方面,确保了信息的完整性。' +2026-04-16 16:22:35 INFO [agents.orchestrator] orchestrator.py:859 generate() | 选中表:['VSBHKRpt0430', 'VSBHKRpt0431', 'TSBTransferInstruction', 'VSBTransferInstruction', 'TSBAccountInstrumentMovement'],扩展后:['TSBAccountInstrumentMovement', 'TSBTransferInstruction', 'VSBHKRpt0431', 'VSBHKRpt0430', 'VSBTransferInstruction'] +2026-04-16 16:22:37 INFO [utils.fewshot_selector] fewshot_selector.py:209 select_best_with_score() | [Few-shot] best_with_score: qid=Q2 score=0.9575 preview='列出今日所有客户账户之间的股票转移记录。' +2026-04-16 16:22:37 INFO [agents.orchestrator] orchestrator.py:432 _generate_sql_golden_adapt() | [GEN] 黄金 few-shot 条件适配: qid=Q2 score=0.9575 +2026-04-16 16:22:41 INFO [agents.orchestrator] orchestrator.py:450 _generate_sql_golden_adapt() | 生成的SQL(黄金适配,chars=494): -- 查询所有客户账户间股票转移记录 -- 使用 TSBAccountInstrumentMovement,MovementType='T' 表示账户间转移 SELECT @@ -61,115 +61,8 @@ LEFT JOIN MCInstrument i ON m.InstrumentID = i.InstrumentID WHERE m.MovementType = 'T' AND m.ValueDate = CAST(GETDATE() AS DATE) ORDER BY m.ValueDate; -2026-04-16 13:36:12 INFO [db.engine] engine.py:45 get_engine() | SQLAlchemy engine initialized from database_url -2026-04-16 13:36:12 INFO [db.dbhub_tools] dbhub_tools.py:627 _execute_sql() | _execute_sql 执行语句数=1 readonly=True max_rows=1 -2026-04-16 13:36:15 INFO [agents.orchestrator] orchestrator.py:754 _validate_sql() | [validate] 程序+探针+LLM 汇总: valid=True err_count=0 warn_count=0 db_execution_status=0 sql_chars=494 -2026-04-16 13:36:15 INFO [agents.orchestrator] orchestrator.py:921 generate() | [OK] SQL生成与验证通过(1次尝试) -2026-04-16 13:36:15 INFO [__main__] api_server.py:776 _chat_stream_events() | [API/stream] Text2SQL 完成: valid=True attempts=1 tables_used=['TSBTransferInstruction', 'VSBTransferInstruction', 'VSBHKRpt0431', 'VSBHKRpt0430', 'TSBAccountInstrumentMovement'] sql_chars=494 sql_head="-- 查询所有客户账户间股票转移记录\n-- 使用 TSBAccountInstrumentMovement,MovementType='T' 表示账户间转移\nSELECT \n m.MovementID,\n m.AccountID AS FromAccountID,\n m.TransferToAccountID,\n m.InstrumentID,\n i.Name AS InstrumentSymbol,\n m.MovementType,\n m.Quantity AS TransferQuantity,\n m.ValueDate AS TransferDate\nFROM TSBAccountInstrumentMovement m\nLEFT JOIN MCInstrument i ON m.InstrumentID = i.InstrumentID\nWHERE m.MovementType = 'T'\n AND m.ValueDate = CAST(GETDATE() AS DATE)\nORDER BY m.ValueDate;" -2026-04-16 13:47:46 INFO [uvicorn.access] httptools_impl.py:483 send() | 127.0.0.1:27791 - "POST /g3sb/api/nl/chat/stream HTTP/1.1" 200 -2026-04-16 13:47:46 INFO [__main__] api_server.py:668 _chat_stream_events() | [API/stream] 开始: user_id='anonymous' visitor_biz_id=None session_id=None service_code=None model='gpt-4o-mini' lang_code='auto' msg_chars=15 preview='所有客户账户之间的股票转移记录' dialog_context_chars=0 last_turn_was_data_query=False -2026-04-16 13:47:49 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 -2026-04-16 13:47:49 INFO [utils.dialog_classifier] dialog_classifier.py:261 classify_dialog() | [dialog] intent=text2sql (hybrid fast: query hint) preview='所有客户账户之间的股票转移记录' -2026-04-16 13:47:49 INFO [__main__] api_server.py:715 _chat_stream_events() | [GEN/API/stream] dialect=tsql top_k=20 dialog_context_chars=0 question_len=15 preview='所有客户账户之间的股票转移记录' -2026-04-16 13:47:52 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-4o-mini, base_url=http://113.192.49.54:9080/v1 -2026-04-16 13:47:54 INFO [agents.orchestrator] orchestrator.py:803 generate() | [GEN] 问句已归一中文:所有客户账户的股票转移记录 -2026-04-16 13:47:54 INFO [agents.orchestrator] orchestrator.py:816 generate() | [GEN] 开始生成SQL: question_chars=13 preview='所有客户账户的股票转移记录' dialog_context_chars=0 -2026-04-16 13:47:54 INFO [agents.orchestrator] orchestrator.py:831 generate() | 尝试 #1 -2026-04-16 13:47:54 INFO [schema.indexer] indexer.py:106 ensure_index_for_schema() | Schema 向量索引已就绪(2516 张表),跳过向量化 -2026-04-16 13:47:54 INFO [agents.orchestrator] orchestrator.py:188 _coarse_filter() | [Orchestrator] 开始向量检索: query_chars=13 query_preview='所有客户账户的股票转移记录' -2026-04-16 13:47:56 INFO [schema.indexer] indexer.py:244 search() | Schema 向量检索: query_chars=13 命中=20(阈值=0.1)top=[('VSBHKRpt0430', 0.8264), ('VSBHKRpt0431', 0.8188), ('TSBTransferInstruction', 0.8147), ('VSBTransferInstruction', 0.8134), ('VSBHKRpt0397', 0.8126), ('VSBHKRpt0672', 0.8117), ('VSBHKRpt0090C', 0.81), ('VSBHKRpt0570', 0.8084), ('VSBHKRpt1030A', 0.8077), ('VSBRpt0999E', 0.8065), ('VSBRpt0999C', 0.8064), ('TSBAccountEntitlementRelease', 0.8063), ('VSBRpt0999A', 0.8059), ('TSBAccountInstrumentMovement', 0.8038), ('VSBTransferInstructionGenerationByAccountContract', 0.8033)] -2026-04-16 13:47:56 INFO [agents.orchestrator] orchestrator.py:204 _coarse_filter() | [Orchestrator] 向量粗筛: 命中=20 张(阈值内),表名+分: [('VSBHKRpt0430', 0.8264), ('VSBHKRpt0431', 0.8188), ('TSBTransferInstruction', 0.8147), ('VSBTransferInstruction', 0.8134), ('VSBHKRpt0397', 0.8126), ('VSBHKRpt0672', 0.8117), ('VSBHKRpt0090C', 0.81), ('VSBHKRpt0570', 0.8084), ('VSBHKRpt1030A', 0.8077), ('VSBRpt0999E', 0.8065), ('VSBRpt0999C', 0.8064), ('TSBAccountEntitlementRelease', 0.8063), ('VSBRpt0999A', 0.8059), ('TSBAccountInstrumentMovement', 0.8038), ('VSBTransferInstructionGenerationByAccountContract', 0.8033), ('VSBRpt0060', 0.8021), ('XCGatewayStockReconciliationReport', 0.802), ('TSBAccountEntitlementHold', 0.8013), ('WSBBatchLocationTransferDetail', 0.8012), ('VCAccountCashMovement', 0.8012)] -2026-04-16 13:48:00 INFO [agents.orchestrator] orchestrator.py:258 _llm_select_tables() | LLM精筛选中表:['VSBHKRpt0430', 'VSBHKRpt0431', 'TSBTransferInstruction', 'VSBTransferInstruction', 'TSBAccountInstrumentMovement'] | reasoning_chars=182 reasoning_preview='问题涉及客户账户的股票转移记录,VSBHKRpt0430和VSBHKRpt0431提供了客户股票的日常转移报告和账户工具移动报告,TSBTransferInstruction和VSBTransferInstruction包含账户转移指令的详细信息,TSBAccountInstrumentMovement记录账户工具的移动交易。这些表共同涵盖了股票转移的相关信息。' -2026-04-16 13:48:00 INFO [agents.orchestrator] orchestrator.py:859 generate() | 选中表:['VSBHKRpt0430', 'VSBHKRpt0431', 'TSBTransferInstruction', 'VSBTransferInstruction', 'TSBAccountInstrumentMovement'],扩展后:['TSBTransferInstruction', 'VSBTransferInstruction', 'VSBHKRpt0431', 'VSBHKRpt0430', 'TSBAccountInstrumentMovement'] -2026-04-16 13:48:01 INFO [utils.fewshot_selector] fewshot_selector.py:209 select_best_with_score() | [Few-shot] best_with_score: qid=Q2 score=0.9575 preview='列出今日所有客户账户之间的股票转移记录。' -2026-04-16 13:48:01 INFO [agents.orchestrator] orchestrator.py:432 _generate_sql_golden_adapt() | [GEN] 黄金 few-shot 条件适配: qid=Q2 score=0.9575 -2026-04-16 13:48:06 INFO [agents.orchestrator] orchestrator.py:450 _generate_sql_golden_adapt() | 生成的SQL(黄金适配,chars=494): --- 查询所有客户账户间股票转移记录 --- 使用 TSBAccountInstrumentMovement,MovementType='T' 表示账户间转移 -SELECT - m.MovementID, - m.AccountID AS FromAccountID, - m.TransferToAccountID, - m.InstrumentID, - i.Name AS InstrumentSymbol, - m.MovementType, - m.Quantity AS TransferQuantity, - m.ValueDate AS TransferDate -FROM TSBAccountInstrumentMovement m -LEFT JOIN MCInstrument i ON m.InstrumentID = i.InstrumentID -WHERE m.MovementType = 'T' - AND m.ValueDate = CAST(GETDATE() AS DATE) -ORDER BY m.ValueDate; -2026-04-16 13:48:06 INFO [db.dbhub_tools] dbhub_tools.py:627 _execute_sql() | _execute_sql 执行语句数=1 readonly=True max_rows=1 -2026-04-16 13:48:08 INFO [openai._base_client] _base_client.py:1111 _sleep_for_retry() | Retrying request to /chat/completions in 0.481978 seconds -2026-04-16 13:48:13 INFO [agents.orchestrator] orchestrator.py:754 _validate_sql() | [validate] 程序+探针+LLM 汇总: valid=True err_count=0 warn_count=0 db_execution_status=0 sql_chars=494 -2026-04-16 13:48:13 INFO [agents.orchestrator] orchestrator.py:921 generate() | [OK] SQL生成与验证通过(1次尝试) -2026-04-16 13:48:13 INFO [__main__] api_server.py:776 _chat_stream_events() | [API/stream] Text2SQL 完成: valid=True attempts=1 tables_used=['TSBTransferInstruction', 'VSBTransferInstruction', 'VSBHKRpt0431', 'VSBHKRpt0430', 'TSBAccountInstrumentMovement'] sql_chars=494 sql_head="-- 查询所有客户账户间股票转移记录\n-- 使用 TSBAccountInstrumentMovement,MovementType='T' 表示账户间转移\nSELECT \n m.MovementID,\n m.AccountID AS FromAccountID,\n m.TransferToAccountID,\n m.InstrumentID,\n i.Name AS InstrumentSymbol,\n m.MovementType,\n m.Quantity AS TransferQuantity,\n m.ValueDate AS TransferDate\nFROM TSBAccountInstrumentMovement m\nLEFT JOIN MCInstrument i ON m.InstrumentID = i.InstrumentID\nWHERE m.MovementType = 'T'\n AND m.ValueDate = CAST(GETDATE() AS DATE)\nORDER BY m.ValueDate;" -2026-04-16 14:40:07 INFO [uvicorn.access] httptools_impl.py:483 send() | 192.168.3.210:53090 - "POST /g3sb/api/nl/chat/stream HTTP/1.1" 200 -2026-04-16 14:50:35 INFO [__main__] api_server.py:668 _chat_stream_events() | [API/stream] 开始: user_id='ACCOUNT' visitor_biz_id=None session_id='a68b69c0-d945-4553-869f-3538d42900f9' service_code='openai' model=None lang_code='zh' msg_chars=3 preview='123' dialog_context_chars=0 last_turn_was_data_query=False -2026-04-16 14:50:38 INFO [llm.openai_client] openai_client.py:55 __init__() | [OK] OpenAIClient初始化: model=gpt-5.4, base_url=http://113.192.49.54:9080/v1 -2026-04-16 14:50:38 INFO [uvicorn.error] server.py:272 shutdown() | Shutting down -2026-04-16 14:50:38 ERROR [asyncio] base_events.py:1820 default_exception_handler() | Exception in callback BaseProactorEventLoop._start_serving..loop(<_OverlappedF...210', 55004))>) at D:\conda\Lib\asyncio\proactor_events.py:843 -handle: .loop(<_OverlappedF...210', 55004))>) at D:\conda\Lib\asyncio\proactor_events.py:843> -Traceback (most recent call last): - File "D:\conda\Lib\asyncio\events.py", line 88, in _run - self._context.run(self._callback, *self._args) - File "D:\conda\Lib\asyncio\proactor_events.py", line 858, in loop - self._make_socket_transport( - File "D:\conda\Lib\asyncio\proactor_events.py", line 647, in _make_socket_transport - return _ProactorSocketTransport(self, sock, protocol, waiter, - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "D:\conda\Lib\asyncio\proactor_events.py", line 613, in __init__ - super().__init__(loop, sock, protocol, waiter, extra, server) - File "D:\conda\Lib\asyncio\proactor_events.py", line 189, in __init__ - super().__init__(loop, sock, protocol, waiter, extra, server) - File "D:\conda\Lib\asyncio\proactor_events.py", line 335, in __init__ - super().__init__(*args, **kw) - File "D:\conda\Lib\asyncio\proactor_events.py", line 66, in __init__ - self._server._attach() - File "D:\conda\Lib\asyncio\base_events.py", line 296, in _attach - assert self._sockets is not None - ^^^^^^^^^^^^^^^^^^^^^^^^^ -AssertionError -2026-04-16 14:50:38 INFO [uvicorn.error] server.py:102 _serve() | Finished server process [37328] -2026-04-16 14:50:38 ERROR [uvicorn.error] on.py:134 send() | Traceback (most recent call last): - File "D:\conda\Lib\asyncio\runners.py", line 194, in run - return runner.run(main) - ^^^^^^^^^^^^^^^^ - File "D:\conda\Lib\asyncio\runners.py", line 118, in run - return self._loop.run_until_complete(task) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "D:\conda\Lib\asyncio\base_events.py", line 674, in run_until_complete - self.run_forever() - File "D:\conda\Lib\asyncio\windows_events.py", line 322, in run_forever - super().run_forever() - File "D:\conda\Lib\asyncio\base_events.py", line 641, in run_forever - self._run_once() - File "D:\conda\Lib\asyncio\base_events.py", line 1986, in _run_once - handle._run() - File "D:\conda\Lib\asyncio\events.py", line 88, in _run - self._context.run(self._callback, *self._args) - File "C:\Users\24019\AppData\Roaming\Python\Python312\site-packages\uvicorn\server.py", line 78, in serve - with self.capture_signals(): - ^^^^^^^^^^^^^^^^^^^^^^ - File "D:\conda\Lib\contextlib.py", line 144, in __exit__ - next(self.gen) - File "C:\Users\24019\AppData\Roaming\Python\Python312\site-packages\uvicorn\server.py", line 339, in capture_signals - signal.raise_signal(captured_signal) - File "D:\conda\Lib\asyncio\runners.py", line 157, in _on_sigint - raise KeyboardInterrupt() -KeyboardInterrupt - -During handling of the above exception, another exception occurred: - -Traceback (most recent call last): - File "C:\Users\24019\AppData\Roaming\Python\Python312\site-packages\starlette\routing.py", line 645, in lifespan - await receive() - File "C:\Users\24019\AppData\Roaming\Python\Python312\site-packages\uvicorn\lifespan\on.py", line 137, in receive - return await self.receive_queue.get() - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "D:\conda\Lib\asyncio\queues.py", line 158, in get - await getter -asyncio.exceptions.CancelledError - -2026-04-16 14:50:43 INFO [utils.dialog_classifier] dialog_classifier.py:211 _classify_dialog_llm() | [dialog] intent=conversation (LLM) reply_chars=51 reply_preview='请具体说明你想查询什么数据,例如时间范围、品种、账户或指标。仅输入“123”我还无法判断你的查询需求。' +2026-04-16 16:22:41 INFO [db.engine] engine.py:45 get_engine() | SQLAlchemy engine initialized from database_url +2026-04-16 16:22:41 INFO [db.dbhub_tools] dbhub_tools.py:627 _execute_sql() | _execute_sql 执行语句数=1 readonly=True max_rows=1 +2026-04-16 16:22:44 INFO [agents.orchestrator] orchestrator.py:754 _validate_sql() | [validate] 程序+探针+LLM 汇总: valid=True err_count=0 warn_count=0 db_execution_status=0 sql_chars=494 +2026-04-16 16:22:44 INFO [agents.orchestrator] orchestrator.py:921 generate() | [OK] SQL生成与验证通过(1次尝试) +2026-04-16 16:22:44 INFO [__main__] api_server.py:765 _chat_stream_events() | [API/stream] Text2SQL 完成: valid=True attempts=1 tables_used=['TSBAccountInstrumentMovement', 'TSBTransferInstruction', 'VSBHKRpt0431', 'VSBHKRpt0430', 'VSBTransferInstruction'] sql_chars=494 sql_head="-- 查询所有客户账户间股票转移记录\n-- 使用 TSBAccountInstrumentMovement,MovementType='T' 表示账户间转移\nSELECT \n m.MovementID,\n m.AccountID AS FromAccountID,\n m.TransferToAccountID,\n m.InstrumentID,\n i.Name AS InstrumentSymbol,\n m.MovementType,\n m.Quantity AS TransferQuantity,\n m.ValueDate AS TransferDate\nFROM TSBAccountInstrumentMovement m\nLEFT JOIN MCInstrument i ON m.InstrumentID = i.InstrumentID\nWHERE m.MovementType = 'T'\n AND m.ValueDate = CAST(GETDATE() AS DATE)\nORDER BY m.ValueDate;"