From 89543389f824837e127a293b823fd7b02536f02e Mon Sep 17 00:00:00 2001 From: aaron <> Date: Mon, 8 Jun 2026 09:34:34 +0800 Subject: [PATCH] 1 --- app/web/routes_recommendations.py | 7 ++++++- static/llm_insights.html | 12 ++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/web/routes_recommendations.py b/app/web/routes_recommendations.py index e567996..dc3014a 100644 --- a/app/web/routes_recommendations.py +++ b/app/web/routes_recommendations.py @@ -42,7 +42,12 @@ def _friendly_llm_item(item): "failed": "失败", "skipped": "跳过", }.get(status, status or "未知") - subject = payload.get("symbol") or payload.get("related_symbol") or payload.get("title") or payload.get("run_date") or payload.get("target_id") or item.get("target_id") + if target_type == "sentiment_batch": + source_count = payload.get("source_event_count") or payload.get("event_count") or 0 + event_count = payload.get("event_count") or 0 + subject = f"{payload.get('hours') or 24}h 批量舆情 · {event_count}/{source_count} 条事件" + else: + subject = payload.get("symbol") or payload.get("related_symbol") or payload.get("title") or payload.get("run_date") or payload.get("target_id") or item.get("target_id") summary = content.get("summary") or content.get("memo") or content.get("why_now_or_not") or content.get("raw") or item.get("error") or "" return { "id": item.get("id"), diff --git a/static/llm_insights.html b/static/llm_insights.html index 7e08388..d46dcb3 100644 --- a/static/llm_insights.html +++ b/static/llm_insights.html @@ -2,7 +2,7 @@ {% block title %}AlphaX Agent — AI 记录{% endblock %} {% block extra_head_css %} {% endblock %} {% block content %} @@ -59,7 +59,15 @@ async function selectItem(id,quiet){state.selected=id;renderList();$('detail').i function page(step){var next=state.offset+step*state.limit;if(next<0)return;state.offset=next;state.selected=null;load();} function reloadFirst(){state.offset=0;state.selected=null;load();} function kv(label,val){return '
'+val+'
';} -function renderDetail(d){$('detailTime').textContent=fmtTime(d.updated_at);var input=d.input||{},out=d.content||{};var rawIn=JSON.stringify(input,null,2);var rawOut=JSON.stringify(out,null,2);var evidence=out.key_evidence||out.key_tags||out.theme_tags||[];var risks=out.risk_flags||out.risk_types||[];var watch=out.watch_points||[];var invalid=out.invalid_if||[];var title=d.subject||input.symbol||input.title||input.run_date||'未命名对象';$('detail').innerHTML='
'+esc(title)+''+esc(d.status_label)+''+esc(d.type_label)+'
'+esc(d.summary||d.error||'暂无摘要')+'
模型'+esc(d.model||'未记录')+'
提示词版本'+esc(d.prompt_version||'--')+'
对象类型'+esc(d.target_type||'--')+'
对象 ID'+esc(d.target_id||'--')+'

AI 输出

'+kv('摘要',esc(out.summary||out.memo||out.why_now_or_not||'--'))+kv('为什么',esc(out.why_now_or_not||out.why_it_matters||'--'))+kv('关键证据',chipList(evidence))+kv('风险提示',chipList(risks))+kv('观察点',chipList(watch))+kv('失效条件',chipList(invalid))+kv('错误信息',esc(d.error||'--'))+'

这次喂给 AI 的结构化输入

'+kv('标的/主题',esc(input.symbol||input.related_symbol||input.title||input.run_date||'--'))+kv('状态/建议',esc(input.execution_label||input.action_status||input.decision||'--'))+kv('信号',chipList(input.signals||[]))+kv('价格上下文',esc([input.current_price?'现价 '+input.current_price:'',input.entry_price?'参考 '+input.entry_price:'',input.stop_loss?'止损 '+input.stop_loss:'',input.tp1?'TP1 '+input.tp1:''].filter(Boolean).join(' · ')||'--'))+'

原始输入 JSON

'+esc(rawIn)+'

原始输出 JSON

'+esc(rawOut)+'
';} +function directionLabel(v){return {positive:'利好',negative:'利空',risk:'风险',neutral:'中性',watch:'观察',avoid:'规避',verify:'验证'}[String(v||'')]||v||'--';} +function chipClass(v){v=String(v||'').toLowerCase();if(['positive','watch','verify','risk_on'].includes(v))return' good';if(['negative','avoid','risk_off','high'].includes(v))return' bad';if(['risk','medium','neutral'].includes(v))return' warn';return'';} +function table(headers,rows){if(!rows||!rows.length)return'
暂无结构化条目
';return'
'+headers.map(function(h){return'';}).join('')+''+rows.map(function(r){return''+headers.map(function(h){return'';}).join('')+'';}).join('')+'
'+esc(h[0])+'
'+h[1](r)+'
';} +function textOrDash(v){return esc(v||'--');} +function renderSentimentOutput(out,d){var themes=out.hot_themes||[],coins=out.coin_impacts||[],risks=out.risk_events||[],suggestions=out.suggestions||[],watch=out.watchlist||[];return '

AI 输出 · 舆情研判

'+kv('市场情绪',''+esc(out.market_mood||'--')+'')+kv('核心摘要',textOrDash(out.summary))+kv('专业判断',textOrDash(out.professional_view))+kv('错误信息',textOrDash(d.error))+'

主线叙事

'+(themes.length?themes.map(function(x){return'
'+esc(x.theme||'未命名主题')+' '+esc(directionLabel(x.impact))+'

'+esc(x.impact||x.reason||'')+'

置信度 '+esc(x.confidence||0)+' · '+esc((x.symbols||[]).join(', ')||'--')+'
';}).join(''):'
暂无主线
')+'

币种影响

'+table([['币种',function(x){return''+esc(x.symbol||'--')+''}],['方向',function(x){return''+esc(directionLabel(x.direction))+''}],['置信度',function(x){return esc(x.confidence||0)}],['窗口',function(x){return esc(x.impact_window||'--')}],['技术检查',function(x){return x.need_technical_check?'需要':'不需要'}],['理由',function(x){return esc(x.reason||'--')}]],coins)+'

建议与观察

'+table([['类型',function(x){return''+esc(directionLabel(x.type))+''}],['币种',function(x){return esc(x.symbol||'--')}],['原因',function(x){return esc(x.reason||x.why||'--')}],['下一步/触发',function(x){return esc(x.next_step||x.trigger||'--')}],['失效条件',function(x){return esc(x.invalid_if||'--')}]],suggestions.concat(watch.map(function(x){return Object.assign({type:'watch',reason:x.why,next_step:x.trigger},x)})))+'

风险事件

'+table([['事件',function(x){return esc(x.title||'--')}],['类型',function(x){return esc(x.risk_type||'--')}],['级别',function(x){return''+esc(x.severity||'--')+''}],['影响币种',function(x){return esc((x.symbols||[]).join(', ')||'--')}]],risks)+'
';} +function renderSentimentInput(input){var events=input.events||[];return '

这次喂给 AI 的结构化输入 · 新闻事件

'+kv('时间窗口',esc((input.hours||24)+' 小时'))+kv('事件数量',esc((input.event_count||events.length||0)+' / 原始 '+(input.source_event_count||input.event_count||events.length||0)))+kv('任务目标',esc(((input.instructions||{}).role)||'--'))+'
'+table([['#',function(x){return esc(x.rank||'--')}],['币种',function(x){return esc(x.symbol||x.base||'--')}],['重要性',function(x){return''+esc(x.importance||'--')+''}],['来源',function(x){return esc(x.source||'--')}],['标题',function(x){return esc(x.title||'--')}],['事件类型',function(x){return esc(x.event_type||'--')}],['技术分/趋势',function(x){return esc([x.tech_score!=null?'技术 '+x.tech_score:'',x.trend_rank?'趋势#'+x.trend_rank:''].filter(Boolean).join(' · ')||'--')}]],events)+'
';} +function renderGenericOutput(out,d){var evidence=out.key_evidence||out.key_tags||out.theme_tags||[];var risks=out.risk_flags||out.risk_types||[];var watch=out.watch_points||out.watchlist||[];var invalid=out.invalid_if||[];return '

AI 输出

'+kv('摘要',textOrDash(out.summary||out.memo||out.why_now_or_not||out.answer))+kv('专业判断',textOrDash(out.professional_view||out.why_now_or_not||out.why_it_matters))+kv('关键证据',chipList(evidence))+kv('风险提示',chipList(risks))+kv('观察点',Array.isArray(watch)?chipList(watch.map(function(x){return typeof x==='string'?x:(x.symbol||x.why||x.trigger||JSON.stringify(x));})):textOrDash(watch))+kv('失效条件',chipList(Array.isArray(invalid)?invalid:[invalid].filter(Boolean)))+kv('错误信息',textOrDash(d.error))+'
';} +function renderGenericInput(input){return '

这次喂给 AI 的结构化输入

'+kv('标的/主题',esc(input.symbol||input.related_symbol||input.title||input.run_date||input.target_id||'--'))+kv('状态/建议',esc(input.execution_label||input.action_status||input.decision||'--'))+kv('信号',chipList(input.signals||[]))+kv('价格上下文',esc([input.current_price?'现价 '+input.current_price:'',input.entry_price?'参考 '+input.entry_price:'',input.stop_loss?'止损 '+input.stop_loss:'',input.tp1?'TP1 '+input.tp1:''].filter(Boolean).join(' · ')||'--'))+'
';} +function renderDetail(d){$('detailTime').textContent=fmtTime(d.updated_at);var input=d.input||{},out=d.content||{};var rawIn=JSON.stringify(input,null,2);var rawOut=JSON.stringify(out,null,2);var title=d.subject||input.symbol||input.title||input.run_date||input.target_id||'未命名对象';var outputHtml=d.target_type==='sentiment_batch'?renderSentimentOutput(out,d):renderGenericOutput(out,d);var inputHtml=d.target_type==='sentiment_batch'?renderSentimentInput(input):renderGenericInput(input);$('detail').innerHTML='
'+esc(title)+''+esc(d.status_label)+''+esc(d.type_label)+'
'+esc(d.summary||d.error||'暂无摘要')+'
模型'+esc(d.model||'未记录')+'
提示词版本'+esc(d.prompt_version||'--')+'
对象类型'+esc(d.target_type||'--')+'
对象 ID'+esc(d.target_id||'--')+'
'+outputHtml+inputHtml+'

原始输入 JSON

'+esc(rawIn)+'

原始输出 JSON

'+esc(rawOut)+'
';} load(); {% endblock %}