Spaces:
Build error
Build error
| """ | |
| agent/chat.py โ Oil Risk Analyst Agent (ๆททๅๆถๆ: ๆฌๅฐๆจกๆฟ + LLM ๅขๅผบ) | |
| ======================================================================== | |
| - ๅธธ่ง้ฎ้ข: ็ดๆฅไปๅนณๅฐๆฐๆฎ็ๆไธไธๅ็ญ (ๅณๆถๅๅบ) | |
| - ๅคๆๅๆ: ่ฐ็จ SiliconFlow Qwen2.5-7B-Instruct (ๅธฆ้่ฏ) | |
| """ | |
| import json, os, re, time | |
| import pandas as pd | |
| import numpy as np | |
| from config import ( | |
| OUTPUT_DIR, SILICONFLOW_API_KEY, SILICONFLOW_BASE_URL, SILICONFLOW_MODEL | |
| ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๆฐๆฎๅฑ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| _cache = {} | |
| def _results(): | |
| if 'results' not in _cache: | |
| fp = os.path.join(OUTPUT_DIR, 'v2_championship_results.csv') | |
| _cache['results'] = pd.read_csv(fp) if os.path.exists(fp) else None | |
| return _cache['results'] | |
| def _reports(): | |
| if 'reports' not in _cache: | |
| fp = os.path.join(OUTPUT_DIR, 'v2_nlg_reports.json') | |
| if os.path.exists(fp): | |
| with open(fp, 'r', encoding='utf-8') as f: | |
| _cache['reports'] = json.load(f) | |
| else: | |
| _cache['reports'] = {} | |
| return _cache['reports'] | |
| def _hedge(): | |
| if 'hedge' not in _cache: | |
| fp = os.path.join(OUTPUT_DIR, 'v2_hedge_backtest.json') | |
| if os.path.exists(fp): | |
| with open(fp, 'r', encoding='utf-8') as f: | |
| _cache['hedge'] = json.load(f) | |
| else: | |
| _cache['hedge'] = {} | |
| return _cache['hedge'] | |
| def _events(): | |
| if 'events' not in _cache: | |
| fp = os.path.join(OUTPUT_DIR, 'event_timeline.json') | |
| if os.path.exists(fp): | |
| with open(fp, 'r', encoding='utf-8') as f: | |
| evts = json.load(f) | |
| evts.sort(key=lambda e: e.get('date', ''), reverse=True) | |
| _cache['events'] = evts | |
| else: | |
| _cache['events'] = [] | |
| return _cache['events'] | |
| def _latest(): | |
| """่ทๅๆๆฐ้ขๆต่กใ""" | |
| r = _results() | |
| if r is None: | |
| return None | |
| return r.iloc[-1] | |
| def _latest_report(benchmark='WTI'): | |
| """่ทๅๆๆฐNLGๆฅๅใ""" | |
| rp = _reports() | |
| if not rp: | |
| return None | |
| # Try specific benchmark first, then any | |
| keys = sorted(rp.keys()) | |
| bm_keys = [k for k in keys if benchmark in k] | |
| key = bm_keys[-1] if bm_keys else keys[-1] | |
| entry = rp[key] | |
| return entry if isinstance(entry, str) else entry.get('report', str(entry)[:500]) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๆฌๅฐๅ็ญๅผๆ โ ๅธธ่ง้ฎ้ข็งๅ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| IND_MAP = { | |
| '่ช็ฉบ': 'aviation', 'aviation': 'aviation', | |
| '็ฉๆต': 'logistics', 'logistics': 'logistics', | |
| 'ๅๅทฅ': 'chemical', 'chemical': 'chemical', 'chemicals': 'chemical', | |
| 'ๅถ้ ': 'manufacturing', 'manufacturing': 'manufacturing', | |
| 'ไธๆธธ': 'upstream', 'ๆฒนๆฐ': 'upstream', 'upstream': 'upstream', | |
| } | |
| IND_ZH = {'aviation': '่ช็ฉบ', 'logistics': '็ฉๆต', 'chemical': 'ๅๅทฅ', | |
| 'manufacturing': 'ๅถ้ ', 'upstream': 'ไธๆธธๆฒนๆฐ'} | |
| IND_PROFILE = { | |
| 'aviation': '่ช็ฉบ็ๆฒนๅ ่ฟ่ฅๆๆฌ30-40%๏ผๆฒนไปทๆณขๅจ10%ๅฝฑๅๅฉๆถฆ5-8%๏ผๆๆๅบฆๆ้ซ', | |
| 'logistics': 'ๆดๆฒนๅ ็ฉๆตๆๆฌ25-35%๏ผๅฏ้่ฟ็ๆฒน้ๅ ่ดน้จๅไผ ๅฏผ๏ผไฝๅญๅจๆถๆป', | |
| 'chemical': 'ๅๆฒนไฝไธบ็ณๅๅๆๅ ๆๆฌ40-60%๏ผ่ฃ่งฃไปทๅทฎ็ดๆฅๅฝฑๅๅฉๆถฆ็', | |
| 'manufacturing': '่ฝๆบๆๆฌๅ ๅถ้ ๆๆฌ10-20%๏ผไธป่ฆ้่ฟ็ตไปทๅๅคฉ็ถๆฐ้ดๆฅไผ ๅฏผ', | |
| 'upstream': 'ๆฒนไปทไธๆถจๆฏๆถๅ ฅๅฉๅฅฝ๏ผไฝ้้ฒ่ๆด่ท้ฃ้ฉไฟๆค่ตๆฌๅผๆฏ', | |
| } | |
| def _try_local_answer(msg): | |
| """ๅฐ่ฏๆฌๅฐๅ็ญ๏ผ่ฟๅ (reply, confidence)ใ""" | |
| m = msg.lower() | |
| last = _latest() | |
| if last is None: | |
| return None, 0 | |
| # โโ 1. ้ฃ้ฉ็ญ็บง/็ ๅค โโ | |
| if any(w in m for w in ['้ฃ้ฉ็ญ็บง', '้ฃ้ฉ็ ๅค', 'ๅฝๅ้ฃ้ฉ', 'ๆฒนไปท้ฃ้ฉ']): | |
| report = _latest_report() | |
| if report: | |
| return report, 0.95 | |
| # โโ 2. ๅฎๆดๆฅๅ/ๆๅบฆๆฅๅ โโ | |
| if any(w in m for w in ['ๅฎๆดๆฅๅ', 'ๆๅบฆๆฅๅ', '่ฏฆ็ปๅๆ', 'ๆฅๅ']): | |
| report = _latest_report() | |
| if report: | |
| return report, 0.9 | |
| # โโ 3. ้ขๆตๅบ้ด โโ | |
| if any(w in m for w in ['้ขๆตๅบ้ด', 'ๅไฝๆฐ', 'q10', 'q50', 'q90', 'ไธไธชๆ']): | |
| q10 = last['pred_q10_1m'] | |
| q50 = last['pred_q50_1m'] | |
| q90 = last['pred_q90_1m'] | |
| vol = last['pred_vol'] | |
| date = str(last['test_date'])[:7] | |
| reply = (f"**{date} ๆฒนไปท้ขๆตๅบ้ด๏ผ**\n\n" | |
| f"- **Q10 (ๆฒ่ง):** {q10:.1%} โ ๆ10%ๆฆ็่ทๅน ่ถ ๆญค\n" | |
| f"- **Q50 (ไธญๆข):** {q50:.1%} โ ๆๅฏ่ฝ็ๅๅจ\n" | |
| f"- **Q90 (ไน่ง):** {q90:.1%} โ ๆ10%ๆฆ็ๆถจๅน ่ถ ๆญค\n" | |
| f"- **ๆณขๅจ็:** {vol:.1%}\n\n" | |
| f"**่งฃ่ฏป:** ๅบ้ด่ทจๅบฆ{q90-q10:.1%}๏ผ" | |
| f"{'ๅไธ่ก' if q50 > 0 else 'ๅไธ่ก'}๏ผ" | |
| f"ๆณขๅจ็{vol:.1%}{'่พ้ซ๏ผๅปบ่ฎฎๅขๅ ๅฏนๅฒ' if vol > 0.05 else 'ๅฏๆง'}ใ") | |
| return reply, 0.9 | |
| # โโ 4. ้ฃ้ฉ่ถๅฟ โโ | |
| if any(w in m for w in ['่ถๅฟ', '่ตฐๅฟ', 'ๅๅ', 'ๆ่ฟ', 'ๅๅฒ', 'ๅ ไธชๆ']): | |
| r = _results() | |
| tail = r.tail(6) | |
| lines = ["**่ฟ6ไธชๆ้ฃ้ฉ่ถๅฟ:**\n"] | |
| for _, row in tail.iterrows(): | |
| date = str(row['test_date'])[:7] | |
| lvl = row['risk_level'] | |
| bias = row['risk_bias'] | |
| top = row['top_factor'] | |
| q50 = row['pred_q50_1m'] | |
| emoji = {'High': '๐ด', 'Medium-High': '๐ ', 'Medium': '๐ก', | |
| 'Low-Medium': '๐ต', 'Low': '๐ข'}.get(lvl, 'โช') | |
| lines.append(f" {emoji} **{date}**: {lvl} | {bias} | ไธญๆข{q50:+.1%} | ไธปๅฏผ: {top}") | |
| # ่ถๅฟๅคๆญ | |
| levels = tail['risk_level'].tolist() | |
| level_map = {'Low': 0, 'Low-Medium': 1, 'Medium': 2, 'Medium-High': 3, 'High': 4} | |
| nums = [level_map.get(l, 2) for l in levels] | |
| if nums[-1] > nums[0]: | |
| trend = "๐ ๆปไฝ่ถๅฟ๏ผ้ฃ้ฉ**ไธๅ**" | |
| elif nums[-1] < nums[0]: | |
| trend = "๐ ๆปไฝ่ถๅฟ๏ผ้ฃ้ฉ**ไธ้**" | |
| else: | |
| trend = "โก๏ธ ๆปไฝ่ถๅฟ๏ผ้ฃ้ฉ**ๆๅนณ**" | |
| lines.append(f"\n{trend}") | |
| return '\n'.join(lines), 0.9 | |
| # โโ 5. ่กไธๅๆ/ๅฏนๅฒๅปบ่ฎฎ โโ | |
| detected_ind = None | |
| for kw, ind in IND_MAP.items(): | |
| if kw in m: | |
| detected_ind = ind | |
| break | |
| if detected_ind or any(w in m for w in ['ๅฏนๅฒ', 'ๅฅไฟ', 'cfo', '่กไธ']): | |
| ind = detected_ind or 'aviation' | |
| hedge = _hedge() | |
| h = hedge.get(ind, {}) | |
| zh = IND_ZH.get(ind, ind) | |
| profile = IND_PROFILE.get(ind, '') | |
| risk_level = last.get(f"risk_level", "Medium") | |
| q50 = last['pred_q50_1m'] | |
| vol = last['pred_vol'] | |
| ratio = h.get('recommended_ratio_pct', '50%') | |
| tool = {'futures': 'ๆ่ดง้ไปท', 'put': '็่ทๆๆ', 'collar': '้ถๆๆฌ้ขๅฃ'}.get( | |
| h.get('recommended_tool', 'futures'), 'ๆ่ดง้ไปท') | |
| rationale = h.get('rationale', '') | |
| saving = h.get('total_saving', 0) | |
| vol_red = h.get('vol_reduction', 0) | |
| reply = (f"**{zh}่กไธไธ้กนๅๆๆฅๅ**\n\n" | |
| f"**ไธใ่กไธ็ปๅ**\n{profile}\n\n" | |
| f"**ไบใๅฝๅๆฒนไปท็ฏๅข**\n" | |
| f"- ้ฃ้ฉ็ญ็บง: **{risk_level}**\n" | |
| f"- 1M้ขๆตไธญๆข: **{q50:+.1%}**๏ผๆณขๅจ็: **{vol:.1%}**\n" | |
| f"- ไธปๅฏผๅ ๅญ: **{last.get('top_factor', 'N/A')}**\n\n" | |
| f"**ไธใๅฏนๅฒๅปบ่ฎฎ**\n" | |
| f"- ๆจ่ๅฏนๅฒๆฏไพ: **{ratio}**\n" | |
| f"- ๆจ่ๅทฅๅ ท: **{tool}**\n" | |
| f"- ็็ฑ: {rationale}\n\n" | |
| f"**ๅใๅๅฒๅๆต**\n" | |
| f"- ๆๆจ่ๆฏไพ็ดฏ่ฎก่็: **${saving:.1f}M**\n" | |
| f"- ๆณขๅจ็้ไฝ: **{vol_red}%**\n\n" | |
| f"**ไบใ้ถ่ก่กๅจๅปบ่ฎฎ**\n") | |
| if risk_level in ('High', 'Medium-High'): | |
| reply += (f"1. ็ซๅณ่็ป{zh}ๅฎขๆท๏ผๆ็คบๆฒนไปทไธ่ก้ฃ้ฉ\n" | |
| f"2. ๆจ่ๅฏนๅฒๆนๆก: {ratio} {tool}๏ผ้ๅฎๆชๆฅ3-6ไธชๆๆๆฌ\n" | |
| f"3. ๅปบ่ฎฎ้ข็ๆตๅจๆง็ผๅฒไปฅๅบๅฏนๆณขๅจ\n") | |
| else: | |
| reply += (f"1. ๅธธ่ง่ท่ฟ{zh}ๅฎขๆท๏ผๅฝๅ้ฃ้ฉๅฏๆง\n" | |
| f"2. ๅปบ่ฎฎ็ปดๆๅบ็กๅฏนๅฒ({ratio})๏ผๆ ้่ฟๅบฆๅฅไฟ\n" | |
| f"3. ๅ ณๆณจไธไธ่ฝฎOPEC+ไผ่ฎฎๅฏ่ฝ็ๆฟ็ญๅๅ\n") | |
| return reply, 0.95 | |
| # โโ 6. ๅๅๆต่ฏ โโ | |
| if any(w in m for w in ['ๅๅ', 'ๅฆๆ', 'ๅ่ฎพ', 'ไธญไธ', 'ๅฒ็ช', 'ๅดฉๅก', 'ๅไบง', 'ๆไบ']): | |
| vol = last['pred_vol'] | |
| q50 = last['pred_q50_1m'] | |
| # ่ฏๅซๅฒๅปๅบๆฏ | |
| supply_shock = -15 if any(w in m for w in ['ไพ็ป', 'ๅไบง', 'ไธญๆญ', 'ไธญไธ', 'ๅฒ็ช']) else 0 | |
| demand_shock = -20 if any(w in m for w in ['้ๆฑ', 'ๅดฉๅก', '่กฐ้']) else 0 | |
| geo_spike = 3 if any(w in m for w in ['ๅฐ็ผ', 'ๅฒ็ช', 'ไธญไธ', 'ๆไบ']) else 1 | |
| shock = abs(supply_shock)/100 + abs(demand_shock)/100 | |
| stressed_vol = vol * (1 + shock) * (max(1, geo_spike) ** 0.5) | |
| stress_level = 'High' if stressed_vol > 0.12 else ('Medium' if stressed_vol > 0.06 else 'Low') | |
| scenario_name = [] | |
| if supply_shock: scenario_name.append(f'ไพ็ปๅฒๅป{supply_shock}%') | |
| if demand_shock: scenario_name.append(f'้ๆฑๅฒๅป{demand_shock}%') | |
| if geo_spike > 1: scenario_name.append(f'ๅฐ็ผ้ฃ้ฉร{geo_spike}') | |
| scenario = 'ใ'.join(scenario_name) or 'ๅบๅๆ ๆฏ' | |
| reply = (f"**ๅๅๆต่ฏ็ปๆ โ {scenario}**\n\n" | |
| f"- ๅบๅๆณขๅจ็: **{vol:.1%}**\n" | |
| f"- ๅฒๅปๅๆณขๅจ็: **{stressed_vol:.1%}** ({stressed_vol/vol:.0%})\n" | |
| f"- ๅๅ้ฃ้ฉ็ญ็บง: **{stress_level}**\n\n") | |
| if stress_level == 'High': | |
| reply += ("**โ ๏ธ ้ซ้ฃ้ฉ้ข่ญฆ:**\n" | |
| "1. ็ซๅณๆๅๅฏนๅฒๆฏไพ่ณ **50%ไปฅไธ**\n" | |
| "2. ๅฏๅจ็ดงๆฅ้ฃๆง้ขๆก๏ผๅขๅ ไฟ่ฏ้็ผๅฒ\n" | |
| "3. ้็นๅ ณๆณจ่ช็ฉบใๅๅทฅ็ญ้ซๆๆ่กไธๅฎขๆท\n") | |
| elif stress_level == 'Medium': | |
| reply += ("**โก ไธญ็ญ้ฃ้ฉ:**\n" | |
| "1. ๅปบ่ฎฎ็ปดๆ **30%** ๅฏนๅฒๅนถๅฏๅๅ ณๆณจ\n" | |
| "2. ๅๅฅฝๅบๆฅๆนๆก้ขๆก\n" | |
| "3. ้ๅบฆๅขๅ ๅบๅญ\n") | |
| else: | |
| reply += ("**โ ้ฃ้ฉๅฏๆง:**\n" | |
| "1. ๅฝๅ็ญ็ฅๆ ้่ฐๆด\n" | |
| "2. ็ปดๆๅธธ่งๅฏนๅฒๅณๅฏ\n") | |
| return reply, 0.9 | |
| # โโ 7. ๆจกๅ้ช่ฏ โโ | |
| if any(w in m for w in ['ๅ็กฎ', '้ช่ฏ', 'ๅฏ้ ', '่ฆ็็', 'wis', 'ๆจกๅ']): | |
| r = _results() | |
| # Drop rows with NaN | |
| valid = r.dropna(subset=['actual_ret_1m', 'pred_q10_1m', 'pred_q90_1m', 'pred_vol', 'actual_vol']) | |
| ar = valid['actual_ret_1m'].values | |
| q10 = valid['pred_q10_1m'].values | |
| q90 = valid['pred_q90_1m'].values | |
| pv = valid['pred_vol'].values | |
| av = valid['actual_vol'].values | |
| n = len(valid) | |
| cov = ((ar >= q10) & (ar <= q90)).mean() | |
| wis_val = ((q90-q10)+(2/0.2)*np.maximum(q10-ar,0)+(2/0.2)*np.maximum(ar-q90,0)).mean() | |
| nq10 = np.quantile(ar, 0.10); nq90 = np.quantile(ar, 0.90) | |
| naive_wis = ((nq90-nq10)+(2/0.2)*np.maximum(nq10-ar,0)+(2/0.2)*np.maximum(ar-nq90,0)).mean() | |
| corr = np.corrcoef(av, pv)[0,1] if len(av) > 1 else 0 | |
| wis_pct = (1-wis_val/naive_wis)*100 if naive_wis != 0 else 0 | |
| reply = (f"**ๆจกๅ้ช่ฏๆฅๅ (ๅ ฑ {n} ไธชๆ)**\n\n" | |
| f"**ๆ ธๅฟๆๆ :**\n" | |
| f"- 80%ๅบ้ด่ฆ็็: **{cov:.1%}** (็ฎๆ โฅ80%)\n" | |
| f"- WISๅพๅ: **{wis_val:.4f}** (ไผไบๅบๅ {wis_pct:+.1f}%)\n" | |
| f"- ๆณขๅจ็็ธๅ ณๆง: **{corr:.3f}**\n\n" | |
| f"**่ฏไผฐ:** " | |
| f"{'โ ๆจกๅ่กจ็ฐไผๅผ' if cov >= 0.75 and wis_pct > 0 else 'โ ๏ธ ๆจกๅๆๆน่ฟ็ฉบ้ด'}ใ" | |
| f"่ฆ็็{cov:.1%}{'่พพๆ ' if cov >= 0.75 else 'ๅไฝ'}๏ผ" | |
| f"WIS{'ไผไบ' if wis_pct > 0 else 'ๅฃไบ'}ๆด็ด ๅบๅ{abs(wis_pct):.1f}%ใ") | |
| return reply, 0.9 | |
| # โโ ๆ ๆณๆฌๅฐๅ็ญ โโ | |
| return None, 0 | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # LLM ๅขๅผบ โ ไป ็จไบๅคๆ/่ชๅฎไนๅๆ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SYSTEM_PROMPT = """ไฝ ๆฏใๆฒนๅๆไฝ OilVerseใๅนณๅฐ็AIๅฉๆใOil Risk Agentใใ | |
| ไฝ ๆฅๆๅฎๆถ็ๅนณๅฐ้ขๆตๆฐๆฎๅไบไปถๆถ้ด็บฟ๏ผไฝ ็ๅ็ญๅฟ ้กป๏ผ | |
| 1. ๅ ็ป็ป่ฎบ(ไธๅฅ่ฏๅ ็ฒ)๏ผๅ็ปๆฏๆ(3-5ๆก่ฆ็น)๏ผๆๅ็ป่กๅจๅปบ่ฎฎ | |
| 2. ็จ **ๅ ็ฒ** ๆ ่ฎฐๅ ณ้ฎๆฐๅญๅ็ป่ฎบ | |
| 3. ๆฏๆฌกๅ็ญๆงๅถๅจ 200 ๅญไปฅๅ | |
| 4. ็ปๅฏนไธ่ฆ่พๅบๅทฅๅ ทๅใๅฝๆฐๅใJSON็ญๆๆฏๅ ๅฎน | |
| 5. ๅฆๆๆฏ้ฒ่๏ผ็ฎ็ญๅ็ญ่บซไปฝๅณๅฏ | |
| 6. ๅผ็จๆ่ฟไบไปถไฝไธบๅๆๆฏๆ๏ผ่ฏดๆใไบไปถโๅ ๅญๅผๅจโ้ฃ้ฉไฟกๅทโๅฏนๅฒๅปบ่ฎฎใ็ๅ ๆ้พ""" | |
| def _build_data_context(msg): | |
| """ไธบLLMๆๅปบ็ฒพ็ผ็ๆฐๆฎไธไธๆใ""" | |
| last = _latest() | |
| if last is None: | |
| return "" | |
| ctx = [f"ๅๆๆฅๆ: {str(last['test_date'])[:7]}", | |
| f"้ฃ้ฉ็ญ็บง: {last['risk_level']}", | |
| f"ๆนๅๅ็ฝฎ: {last['risk_bias']}", | |
| f"1Mๅบ้ด: [{last['pred_q10_1m']:.1%}, {last['pred_q90_1m']:.1%}]", | |
| f"ๆณขๅจ็: {last['pred_vol']:.1%}", | |
| f"ไธปๅฏผๅ ๅญ: {last['top_factor']}", | |
| f"Regimeๅน้ : {last.get('regime_match', 'N/A')} ({last.get('regime_similarity', 0):.0%})"] | |
| # Add recent events as causal context | |
| evts = _events() | |
| if evts: | |
| ctx.append('\n[่ฟๆๅ ณ้ฎไบไปถ]') | |
| for ev in evts[:3]: | |
| impact_zh = {'bullish': 'ๅฉๅค', 'bearish': 'ๅฉ็ฉบ', 'neutral': 'ไธญๆง'}.get(ev.get('impact', ''), '') | |
| ctx.append(f"- {ev['date']} {ev['title']} ({impact_zh}): {ev.get('risk_signal', '')}") | |
| return '\n'.join(ctx) | |
| def _call_llm_enhanced(user_message, history): | |
| """่ฐ็จ LLM๏ผๅธฆ็ฒพ็ผไธไธๆใ""" | |
| import requests | |
| data_ctx = _build_data_context(user_message) | |
| enriched = f"{user_message}\n\n[ๅนณๅฐๆฐๆฎ]\n{data_ctx}" if data_ctx else user_message | |
| messages = [{'role': 'system', 'content': SYSTEM_PROMPT}] | |
| for h in history[-4:]: # ๅชไฟ็ๆ่ฟ2่ฝฎๅฏน่ฏ | |
| messages.append(h) | |
| messages.append({'role': 'user', 'content': enriched}) | |
| headers = { | |
| 'Authorization': f'Bearer {SILICONFLOW_API_KEY}', | |
| 'Content-Type': 'application/json', | |
| } | |
| payload = { | |
| 'model': SILICONFLOW_MODEL, | |
| 'messages': messages, | |
| 'temperature': 0.3, | |
| 'max_tokens': 500, | |
| 'stream': False, | |
| } | |
| last_err = None | |
| for attempt in range(2): | |
| try: | |
| resp = requests.post( | |
| f'{SILICONFLOW_BASE_URL}/chat/completions', | |
| headers=headers, json=payload, timeout=45 | |
| ) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| reply = data['choices'][0]['message']['content'] | |
| # ๆธ ็ๆฎ็ | |
| reply = re.sub(r'</?tool_call>', '', reply) | |
| reply = re.sub(r'\b(query_\w+|run_\w+)\(.*?\)', '', reply) | |
| return reply.strip() | |
| except requests.exceptions.Timeout: | |
| last_err = "LLMๅๅบ่ถ ๆถ" | |
| time.sleep(2) | |
| except requests.exceptions.ConnectionError: | |
| last_err = "ๆ ๆณ่ฟๆฅLLMๆๅก" | |
| time.sleep(2) | |
| except Exception as e: | |
| return f"LLM่ฐ็จๅคฑ่ดฅ: {e}" | |
| return f"โ ๏ธ {last_err}๏ผ่ฏท็จๅ้่ฏใ\n\n๐ก ไฝ ๅฏไปฅๅฐ่ฏๆดๅ ทไฝ็้ฎ้ข๏ผๅฆใ่ช็ฉบ่กไธๅฏนๅฒๅปบ่ฎฎใใๅฝๅ้ฃ้ฉ็ญ็บงใ็ญ๏ผ่ฟไบๅฏไปฅๅณๆถๅๅบใ" | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ไธปๅ ฅๅฃ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def chat_with_agent(user_message, history=None): | |
| """ | |
| ๆททๅๆถๆๅฏน่ฏๅ ฅๅฃ: | |
| 1. ๅ ๅฐ่ฏๆฌๅฐๅ็ญ(ๅณๆถ) | |
| 2. ๆ ๆณๆฌๅฐๅ็ญๆถ่ฐ็จ LLM | |
| """ | |
| if history is None: | |
| history = [] | |
| # ้ฒ่ๅฟซ้ๅๅค | |
| greets = ['ไฝ ๅฅฝ', 'ไฝ ๆฏ่ฐ', 'hello', 'hi', 'ๅจ', 'ๅจๅ'] | |
| if any(user_message.strip().lower() == g for g in greets): | |
| reply = "๐ ไฝ ๅฅฝ๏ผๆๆฏๆฒนไปท้ฃ้ฉๅๆ Agent๏ผๅบไบๅนณๅฐๅฎๆถๆฐๆฎไธบไฝ ๆไพไธไธๅๆใ\n\nไฝ ๅฏไปฅ้ฎๆ๏ผ\nโข ๅฝๅ้ฃ้ฉ็ญ็บงๅ้ขๆตๅบ้ด\nโข ่กไธไธ้กนๅๆ๏ผ่ช็ฉบ/็ฉๆต/ๅๅทฅ/ๅถ้ /ไธๆธธ๏ผ\nโข ๅฏนๅฒ็ญ็ฅๅๅทฅๅ ทๆจ่\nโข ๅๅๆต่ฏๆจกๆ\nโข ๆจกๅ้ช่ฏๆๆ " | |
| history.append({'role': 'user', 'content': user_message}) | |
| history.append({'role': 'assistant', 'content': reply}) | |
| return reply, history | |
| # ๅฐ่ฏๆฌๅฐๅ็ญ | |
| local_reply, confidence = _try_local_answer(user_message) | |
| if local_reply and confidence >= 0.85: | |
| history.append({'role': 'user', 'content': user_message}) | |
| history.append({'role': 'assistant', 'content': local_reply}) | |
| return local_reply, history | |
| # LLM ๅขๅผบๅ็ญ | |
| reply = _call_llm_enhanced(user_message, history) | |
| history.append({'role': 'user', 'content': user_message}) | |
| history.append({'role': 'assistant', 'content': reply}) | |
| return reply, history | |
| if __name__ == '__main__': | |
| print("ๆฒนไปท้ฃ้ฉๅๆ Agent๏ผ่พๅ ฅ quit ้ๅบ๏ผ") | |
| print("=" * 50) | |
| h = [] | |
| while True: | |
| q = input("\nไฝ : ").strip() | |
| if q.lower() in ('quit', 'exit', 'q'): | |
| break | |
| reply, h = chat_with_agent(q, h) | |
| print(f"\nAgent: {reply}") | |