From 1c18f395cfa712e9393869afcc71ea38ed722d8c Mon Sep 17 00:00:00 2001 From: aaron <> Date: Mon, 8 Jun 2026 09:30:23 +0800 Subject: [PATCH] 1 --- .env.example | 2 +- app/services/llm_insights.py | 40 ++++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 2ac2959..249cc87 100644 --- a/.env.example +++ b/.env.example @@ -38,7 +38,7 @@ ALPHAX_LLM_MAX_TOKENS=900 ALPHAX_LLM_RECOMMENDATIONS_ENABLED=1 ALPHAX_LLM_SENTIMENT_ENABLED=1 ALPHAX_LLM_REVIEW_ENABLED=1 -ALPHAX_LLM_SENTIMENT_BATCH_MAX_EVENTS=12 +ALPHAX_LLM_SENTIMENT_BATCH_MAX_EVENTS=40 # 策略交易挂单门控。wait_pullback 只是候选,必须通过这些条件才会创建挂单。 ALPHAX_PAPER_TRADING_MODE=intraday_trading diff --git a/app/services/llm_insights.py b/app/services/llm_insights.py index 3925ea1..9e0dac5 100644 --- a/app/services/llm_insights.py +++ b/app/services/llm_insights.py @@ -214,7 +214,10 @@ def _call_llm_json(prompt_version, payload): }, {"role": "user", "content": user_prompt}, ] - resp = _chat_completion_request(base_url, api_key, model, retry_messages, max_tokens, timeout, response_format=False, params=params) + retry_max_tokens = max_tokens + if "finish_reason" in error_text and "length" in error_text: + retry_max_tokens = min(max(max_tokens * 2, max_tokens + 1200), 8192) + resp = _chat_completion_request(base_url, api_key, model, retry_messages, retry_max_tokens, timeout, response_format=False, params=params) result = _parse_llm_response(resp, model) if result.get("status") == "success": result["retry"] = "without_response_format" @@ -481,25 +484,42 @@ def _sentiment_event_rank(item): def _compact_sentiment_batch_payload(payload, max_events=None): """Shrink noisy news/event rows before sending to the LLM.""" - max_events = int(max_events or _env_int("ALPHAX_LLM_SENTIMENT_BATCH_MAX_EVENTS", 12) or 12) + max_events = int(max_events or _env_int("ALPHAX_LLM_SENTIMENT_BATCH_MAX_EVENTS", 40) or 40) events = list((payload or {}).get("events") or []) ranked = sorted(events, key=_sentiment_event_rank, reverse=True) + selected = [] + seen = set() + for item in ranked: + title_key = " ".join(str(item.get("title") or "").lower().split())[:96] + symbol_key = str(item.get("related_symbol") or item.get("symbol") or item.get("related_base") or "").upper() + key = (title_key, symbol_key) + if key in seen: + continue + seen.add(key) + selected.append(item) + if len(selected) >= max(max_events, 1): + break compact_events = [] - for idx, item in enumerate(ranked[:max(max_events, 1)], start=1): + for idx, item in enumerate(selected, start=1): compact_events.append({ "rank": idx, "event_id": item.get("event_id"), "source": item.get("source"), "symbol": item.get("related_symbol") or item.get("symbol"), "base": item.get("related_base"), - "title": str(item.get("title") or "")[:160], + "name": item.get("related_name"), + "title": str(item.get("title") or "")[:220], "published_at": item.get("published_at"), + "detected_at": item.get("detected_at"), "importance": item.get("importance"), "event_type": item.get("event_type"), "decision": item.get("decision"), "tech_score": item.get("tech_score"), + "rec_id": item.get("rec_id"), + "pushed": bool(item.get("pushed", False)), "trend_rank": item.get("trend_rank"), "trend_score": item.get("trend_score"), + "market_cap_rank": item.get("market_cap_rank"), "change_24h_pct": item.get("change_24h_pct"), }) return { @@ -513,18 +533,22 @@ def _compact_sentiment_batch_payload(payload, max_events=None): "instructions": { "role": "作为加密市场舆情分析师,基于精简事件判断短线市场影响。", "focus": [ - "只提炼真正可能影响 Binance 山寨币交易决策的主题", - "给出受影响币种、方向、理由和置信度", + "综合更多新闻和事件,提炼当前 24h 市场主线、叙事扩散和风险偏好", + "给出受影响币种、方向、理由、置信度、影响窗口和是否值得技术检查", + "区分已经反映在价格里的旧消息、可继续观察的催化、以及需要规避的风险", + "建议要专业、可执行,但不要直接下真实买卖指令", "标记是否需要进入技术检查", "如果没有有效事件,也必须返回空数组和 neutral 结论", ], "expected_schema": { "market_mood": "risk_on|neutral|risk_off", "summary": "中文摘要", + "professional_view": "更专业的市场解读,说明主线、资金偏好、潜在误判点", "hot_themes": [{"theme": "", "impact": "", "symbols": [], "confidence": 0}], - "coin_impacts": [{"symbol": "", "direction": "positive|negative|risk|neutral", "reason": "", "confidence": 0, "need_technical_check": False}], + "coin_impacts": [{"symbol": "", "direction": "positive|negative|risk|neutral", "reason": "", "confidence": 0, "impact_window": "intraday|1-3d|3-7d", "need_technical_check": False}], "risk_events": [{"title": "", "symbols": [], "risk_type": "", "severity": "low|medium|high"}], - "watchlist": [{"symbol": "", "why": "", "trigger": ""}], + "watchlist": [{"symbol": "", "why": "", "trigger": "", "invalid_if": ""}], + "suggestions": [{"type": "watch|avoid|verify", "symbol": "", "reason": "", "next_step": ""}], }, }, }