aria-code 4.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/__init__.py +32 -0
- agents/base.py +190 -0
- agents/deep/__init__.py +37 -0
- agents/deep/calibration_loop.py +144 -0
- agents/deep/critic.py +125 -0
- agents/deep/deepen.py +193 -0
- agents/deep/models.py +149 -0
- agents/deep/pipeline.py +164 -0
- agents/deep/quant_fusion.py +192 -0
- agents/deep/themes.py +95 -0
- agents/deep/tiers.py +106 -0
- agents/financial/__init__.py +10 -0
- agents/financial/catalyst.py +279 -0
- agents/financial/debate.py +145 -0
- agents/financial/earnings.py +303 -0
- agents/financial/fundamental.py +159 -0
- agents/financial/macro.py +99 -0
- agents/financial/news.py +207 -0
- agents/financial/risk.py +132 -0
- agents/financial/sector.py +279 -0
- agents/financial/synthesis.py +274 -0
- agents/financial/technical.py +258 -0
- agents/portfolio_agent.py +333 -0
- agents/realty/__init__.py +62 -0
- agents/realty/asset_diagnosis.py +150 -0
- agents/realty/business_match.py +165 -0
- agents/realty/cashflow_verify.py +208 -0
- agents/realty/contract_rules.py +209 -0
- agents/realty/energy_anomaly.py +188 -0
- agents/realty/exit_settlement.py +207 -0
- agents/realty/fulfillment_risk.py +205 -0
- agents/realty/ops_optimize.py +159 -0
- agents/realty/revenue_share.py +214 -0
- agents/registry.py +144 -0
- agents/sports/__init__.py +0 -0
- agents/sports/football_agent.py +169 -0
- agents/team.py +289 -0
- aliyun_data_client.py +660 -0
- apps/README.md +12 -0
- apps/__init__.py +2 -0
- apps/channels/README.md +15 -0
- apps/cli/README.md +13 -0
- apps/cli/__init__.py +2 -0
- apps/cli/bootstrap.py +99 -0
- apps/cli/codegen_paths.py +29 -0
- apps/cli/commands/__init__.py +16 -0
- apps/cli/commands/analysis_cmds.py +288 -0
- apps/cli/commands/backtest_cmds.py +1887 -0
- apps/cli/commands/broker_cmds.py +1154 -0
- apps/cli/commands/business_workflow_cmds.py +289 -0
- apps/cli/commands/catalog.py +84 -0
- apps/cli/commands/data_cmds.py +405 -0
- apps/cli/commands/diagnostic_cmds.py +179 -0
- apps/cli/commands/diagnostic_ops_cmds.py +696 -0
- apps/cli/commands/finance_render.py +12 -0
- apps/cli/commands/market.py +399 -0
- apps/cli/commands/market_cmds.py +1276 -0
- apps/cli/commands/market_context.py +425 -0
- apps/cli/commands/market_render.py +7 -0
- apps/cli/commands/model_cmds.py +1579 -0
- apps/cli/commands/ops_cmds.py +668 -0
- apps/cli/commands/portfolio_cmds.py +962 -0
- apps/cli/commands/report.py +377 -0
- apps/cli/commands/scaffold_templates.py +617 -0
- apps/cli/commands/session_cmds.py +179 -0
- apps/cli/commands/session_ux_cmds.py +280 -0
- apps/cli/commands/team.py +588 -0
- apps/cli/commands/team_render.py +8 -0
- apps/cli/commands/ui_cmds.py +358 -0
- apps/cli/commands/workflow_cmds.py +279 -0
- apps/cli/commands/workspace_cmds.py +1414 -0
- apps/cli/config_paths.py +70 -0
- apps/cli/config_store.py +61 -0
- apps/cli/deterministic.py +122 -0
- apps/cli/direct.py +48 -0
- apps/cli/github_app_auth.py +135 -0
- apps/cli/handlers/__init__.py +11 -0
- apps/cli/handlers/broker_handlers.py +122 -0
- apps/cli/handlers/chart_handlers.py +1309 -0
- apps/cli/handlers/market_handlers.py +2509 -0
- apps/cli/handlers/realty_handlers.py +114 -0
- apps/cli/handlers/strategy_advice.py +82 -0
- apps/cli/hooks.py +180 -0
- apps/cli/i18n.py +284 -0
- apps/cli/intent.py +136 -0
- apps/cli/intent_router.py +217 -0
- apps/cli/lifecycle_hooks.py +48 -0
- apps/cli/main.py +29 -0
- apps/cli/market_metadata.py +135 -0
- apps/cli/market_universe.py +265 -0
- apps/cli/message_processing.py +257 -0
- apps/cli/plan_mode.py +139 -0
- apps/cli/plotly_html.py +15 -0
- apps/cli/prediction_feedback.py +202 -0
- apps/cli/preflight.py +497 -0
- apps/cli/project_aria.py +60 -0
- apps/cli/prompts/__init__.py +0 -0
- apps/cli/prompts/coding.py +658 -0
- apps/cli/prompts/system_prompts.py +531 -0
- apps/cli/prompts/ui.py +434 -0
- apps/cli/providers/__init__.py +1 -0
- apps/cli/providers/base.py +271 -0
- apps/cli/providers/chat_routing.py +80 -0
- apps/cli/providers/llm/__init__.py +1 -0
- apps/cli/providers/llm/ollama_stream.py +1170 -0
- apps/cli/providers/llm/sse_stream.py +216 -0
- apps/cli/providers/runtime_bridge.py +185 -0
- apps/cli/runtime_consumer.py +489 -0
- apps/cli/session_export.py +87 -0
- apps/cli/session_jsonl.py +207 -0
- apps/cli/session_store.py +112 -0
- apps/cli/todo_tracker.py +190 -0
- apps/cli/tools/__init__.py +40 -0
- apps/cli/tools/context.py +46 -0
- apps/cli/tools/file_tools.py +112 -0
- apps/cli/tools/market_tools.py +549 -0
- apps/cli/tools/notebook_tools.py +111 -0
- apps/cli/tools/system_tools.py +669 -0
- apps/cli/tools/write_tools.py +715 -0
- apps/cli/tradingview_bridge.py +434 -0
- apps/cli/update_check.py +152 -0
- apps/cli/utils/__init__.py +0 -0
- apps/cli/utils/market_detect.py +1578 -0
- apps/daemon/README.md +14 -0
- apps/vscode/README.md +115 -0
- apps/vscode/package.json +70 -0
- aria_cli.py +11636 -0
- aria_code-4.1.3.dist-info/METADATA +952 -0
- aria_code-4.1.3.dist-info/RECORD +284 -0
- aria_code-4.1.3.dist-info/WHEEL +5 -0
- aria_code-4.1.3.dist-info/entry_points.txt +2 -0
- aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
- aria_code-4.1.3.dist-info/top_level.txt +50 -0
- aria_daemon.py +1295 -0
- aria_feishu_bot.py +1359 -0
- aria_relay_client.py +182 -0
- aria_relay_server.py +405 -0
- aria_telegram_bot.py +202 -0
- ariarc.py +328 -0
- artifacts.py +491 -0
- backtest_report.py +472 -0
- brokers/__init__.py +72 -0
- brokers/base.py +207 -0
- brokers/capabilities.py +264 -0
- brokers/cn/__init__.py +10 -0
- brokers/cn/easytrader_broker.py +193 -0
- brokers/cn/futu_broker.py +194 -0
- brokers/cn/longbridge_broker.py +190 -0
- brokers/cn/tiger_broker.py +196 -0
- brokers/cn/xtquant_broker.py +175 -0
- brokers/config.py +364 -0
- brokers/intl/__init__.py +5 -0
- brokers/intl/alpaca_broker.py +183 -0
- brokers/intl/ibkr_broker.py +215 -0
- brokers/intl/webull_broker.py +156 -0
- brokers/paper_broker.py +259 -0
- brokers/planning.py +296 -0
- brokers/registry.py +181 -0
- brokers/trading.py +237 -0
- change_store.py +127 -0
- command_safety.py +19 -0
- computer_use_tools.py +504 -0
- dashboard_generator.py +578 -0
- data_analysis_tools.py +808 -0
- data_cleaner.py +483 -0
- data_service.py +481 -0
- datasources/__init__.py +23 -0
- datasources/base.py +166 -0
- datasources/router.py +221 -0
- datasources/sources/__init__.py +15 -0
- datasources/sources/akshare_source.py +269 -0
- datasources/sources/alpha_vantage_source.py +202 -0
- datasources/sources/edgar_source.py +218 -0
- datasources/sources/finnhub_source.py +197 -0
- datasources/sources/fred_source.py +219 -0
- datasources/sources/tushare_source.py +141 -0
- datasources/sources/web_scraper_source.py +278 -0
- datasources/sources/world_bank_source.py +205 -0
- datasources/sources/yfinance_source.py +152 -0
- demo_player.py +204 -0
- doctor.py +508 -0
- file_analysis_tools.py +734 -0
- finance_formulas.py +389 -0
- football_data_client.py +1670 -0
- intent_classifier.py +358 -0
- local_finance_tools.py +3221 -0
- local_llm_provider.py +552 -0
- macro_tools.py +368 -0
- market_data_client.py +1899 -0
- mcp_client.py +506 -0
- memory_manager.py +245 -0
- model_capability.py +416 -0
- notification_tools.py +248 -0
- packages/__init__.py +23 -0
- packages/aria_agents/__init__.py +5 -0
- packages/aria_agents/manifest.py +69 -0
- packages/aria_core/__init__.py +34 -0
- packages/aria_core/architecture.py +192 -0
- packages/aria_core/export.py +124 -0
- packages/aria_core/manifest.py +65 -0
- packages/aria_infra/__init__.py +15 -0
- packages/aria_infra/arthera.py +52 -0
- packages/aria_infra/doctor.py +246 -0
- packages/aria_infra/product.py +37 -0
- packages/aria_mcp/__init__.py +25 -0
- packages/aria_mcp/bridge.py +38 -0
- packages/aria_mcp/config.py +97 -0
- packages/aria_mcp/tools.py +61 -0
- packages/aria_sdk/__init__.py +19 -0
- packages/aria_sdk/client.py +396 -0
- packages/aria_sdk/providers.py +70 -0
- packages/aria_sdk/streaming.py +73 -0
- packages/aria_sdk/types.py +86 -0
- packages/aria_services/__init__.py +55 -0
- packages/aria_services/context.py +258 -0
- packages/aria_services/data.py +11 -0
- packages/aria_services/provider_health.py +189 -0
- packages/aria_services/registry.py +213 -0
- packages/aria_services/usage.py +138 -0
- packages/aria_skills/__init__.py +5 -0
- packages/aria_skills/registry.py +59 -0
- packages/aria_tools/__init__.py +5 -0
- packages/aria_tools/registry.py +128 -0
- packages/quant_engine/__init__.py +6 -0
- packages/quant_engine/sports/__init__.py +72 -0
- packages/quant_engine/sports/calibrator.py +353 -0
- packages/quant_engine/sports/dixon_coles.py +234 -0
- packages/quant_engine/sports/elo.py +299 -0
- packages/quant_engine/sports/form.py +188 -0
- packages/quant_engine/sports/h2h.py +195 -0
- packages/quant_engine/sports/ml_model.py +354 -0
- packages/quant_engine/sports/predictor.py +311 -0
- packages/quant_engine/sports/tracker.py +664 -0
- packages/quant_engine/stochastic/__init__.py +27 -0
- packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- packages/quant_engine/stochastic/ito_calculus.py +477 -0
- packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- packages/quant_engine/stochastic/options_pricing.py +573 -0
- packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- plan_utils.py +194 -0
- plugin_loader.py +328 -0
- portfolio_ledger.py +262 -0
- privacy/__init__.py +5 -0
- privacy/feedback.py +123 -0
- project_tools.py +525 -0
- providers/__init__.py +30 -0
- providers/llm/__init__.py +19 -0
- providers/llm/anthropic.py +184 -0
- providers/llm/base.py +139 -0
- providers/llm/ollama.py +128 -0
- providers/llm/openai_compat.py +282 -0
- providers/llm/registry.py +358 -0
- realty_data_tools.py +659 -0
- report_generator.py +1314 -0
- runtime/__init__.py +103 -0
- runtime/agent_loop.py +1183 -0
- runtime/approval.py +51 -0
- runtime/events.py +102 -0
- runtime/gateway.py +128 -0
- runtime/lsp.py +346 -0
- runtime/subagent.py +258 -0
- runtime/tool_executor.py +104 -0
- runtime/tool_policy.py +106 -0
- safety/__init__.py +21 -0
- safety/permissions.py +275 -0
- setup_wizard.py +653 -0
- strategy_vault.py +420 -0
- ui/__init__.py +100 -0
- ui/banner.py +310 -0
- ui/completer.py +391 -0
- ui/console.py +271 -0
- ui/image_render.py +243 -0
- ui/input_box.py +376 -0
- ui/picker.py +195 -0
- ui/render/__init__.py +11 -0
- ui/render/finance.py +1480 -0
- ui/render/market.py +225 -0
- ui/render/output.py +681 -0
- ui/render/team.py +346 -0
- ui/robot.py +235 -0
- workspace/__init__.py +6 -0
- workspace/files.py +170 -0
- workspace/verify.py +113 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/realty/asset_diagnosis.py — 资产诊断 Agent
|
|
3
|
+
==================================================
|
|
4
|
+
判断资产适合出租、出售还是经营权共创。
|
|
5
|
+
|
|
6
|
+
输入数据(data dict keys):
|
|
7
|
+
asset_info — 资产基础信息 dict(面积、位置、产权状态、空置时间等)
|
|
8
|
+
market_context — 周边业态、租金行情(可选)
|
|
9
|
+
renovation_cost — 估算改造成本(可选)
|
|
10
|
+
|
|
11
|
+
输出:
|
|
12
|
+
analysis — 详细分析文本
|
|
13
|
+
signal — BUY=推荐共创 / HOLD=需观察 / SELL=建议出租或出售
|
|
14
|
+
key_points — 3-5 条关键结论
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any, Dict
|
|
19
|
+
from ..base import BaseAgent, AgentResult
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AssetDiagnosisAgent(BaseAgent):
|
|
23
|
+
name = "asset_diagnosis"
|
|
24
|
+
description = "资产诊断:判断处置方式(出租/出售/共创)并给出保底与分润结构建议"
|
|
25
|
+
|
|
26
|
+
_SYSTEM = (
|
|
27
|
+
"你是一名专业的不动产资产运营顾问,擅长评估商业空间的经营潜力。\n"
|
|
28
|
+
"你的任务是根据资产条件、空置状况、市场行情,判断该资产最适合哪种处置方式:\n"
|
|
29
|
+
" A) 传统出租(固定租金)\n"
|
|
30
|
+
" B) 经营权共创(保底+流水分润)\n"
|
|
31
|
+
" C) 出售(一次性变现)\n\n"
|
|
32
|
+
"输出格式要求:\n"
|
|
33
|
+
"1. 推荐方式及理由(2-3句话)\n"
|
|
34
|
+
"2. 如推荐共创,给出:保底收益建议(元/月)、分润比例建议(%)、适合业态Top3\n"
|
|
35
|
+
"3. 主要风险提示(1-2条)\n"
|
|
36
|
+
"4. 综合评分:[适合共创] / [建议观察] / [不建议共创]"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
40
|
+
asset = data.get("asset_info", {})
|
|
41
|
+
market = data.get("market_context", {})
|
|
42
|
+
reno = data.get("renovation_cost", 0)
|
|
43
|
+
|
|
44
|
+
# 构建资产摘要
|
|
45
|
+
area = asset.get("area", 0)
|
|
46
|
+
location = asset.get("location", "未知位置")
|
|
47
|
+
vacancy_days= asset.get("vacancy_days", 0)
|
|
48
|
+
expected_rent = asset.get("expected_rent", 0)
|
|
49
|
+
floor_height = asset.get("floor_height", 0)
|
|
50
|
+
business_types = asset.get("allowed_business", [])
|
|
51
|
+
prohibited = asset.get("prohibited_business", [])
|
|
52
|
+
property_state= asset.get("property_state", "正常")
|
|
53
|
+
|
|
54
|
+
user_prompt = (
|
|
55
|
+
f"资产信息:\n"
|
|
56
|
+
f" 位置: {location}\n"
|
|
57
|
+
f" 面积: {area} m² 层高: {floor_height}m\n"
|
|
58
|
+
f" 产权状态: {property_state} 空置天数: {vacancy_days}天\n"
|
|
59
|
+
f" 评估租金: {expected_rent}元/月 改造成本估算: {reno}元\n"
|
|
60
|
+
f" 适合业态: {', '.join(business_types) or '未指定'}\n"
|
|
61
|
+
f" 禁止业态: {', '.join(prohibited) or '无'}\n"
|
|
62
|
+
f" 市场背景: {market.get('summary', '暂无周边数据')}\n\n"
|
|
63
|
+
"请根据以上信息完成资产诊断。"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
analysis = await self._call_llm(self._SYSTEM, user_prompt, max_tokens=600)
|
|
67
|
+
if not analysis:
|
|
68
|
+
analysis = _template_diagnosis(asset, reno)
|
|
69
|
+
|
|
70
|
+
signal = _score_to_signal(asset, reno)
|
|
71
|
+
confidence = _calc_confidence(asset)
|
|
72
|
+
key_points = _extract_key_points(asset, reno, market)
|
|
73
|
+
|
|
74
|
+
return AgentResult(
|
|
75
|
+
agent = self.name,
|
|
76
|
+
symbol = symbol,
|
|
77
|
+
analysis = analysis,
|
|
78
|
+
confidence = confidence,
|
|
79
|
+
signal = signal,
|
|
80
|
+
key_points = key_points,
|
|
81
|
+
data_used = {"asset_info": asset, "renovation_cost": reno},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ── 内部辅助函数 ──────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
def _score_to_signal(asset: Dict, reno: float) -> str:
|
|
88
|
+
"""根据资产条件估算共创适合度"""
|
|
89
|
+
area = asset.get("area", 0)
|
|
90
|
+
vacancy_days = asset.get("vacancy_days", 0)
|
|
91
|
+
rent = asset.get("expected_rent", 0)
|
|
92
|
+
allowed = asset.get("allowed_business", [])
|
|
93
|
+
state = asset.get("property_state", "正常")
|
|
94
|
+
|
|
95
|
+
score = 0
|
|
96
|
+
if area >= 50: score += 1
|
|
97
|
+
if area >= 200: score += 1
|
|
98
|
+
if vacancy_days >= 90: score += 1 # 空置越久越需要共创
|
|
99
|
+
if rent > 0: score += 1
|
|
100
|
+
if len(allowed) >= 2: score += 1
|
|
101
|
+
if state != "正常": score -= 2
|
|
102
|
+
if reno > rent * 24: score -= 1 # 改造成本超过2年租金则不划算
|
|
103
|
+
|
|
104
|
+
if score >= 4: return "BUY" # 非常适合共创
|
|
105
|
+
if score >= 2: return "HOLD" # 需进一步评估
|
|
106
|
+
return "SELL" # 建议传统出租或出售
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _calc_confidence(asset: Dict) -> float:
|
|
110
|
+
"""数据越完整,置信度越高"""
|
|
111
|
+
fields = ["area", "location", "expected_rent", "allowed_business", "property_state"]
|
|
112
|
+
filled = sum(1 for f in fields if asset.get(f))
|
|
113
|
+
return round(0.5 + 0.1 * filled, 2)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _extract_key_points(asset: Dict, reno: float, market: Dict) -> list:
|
|
117
|
+
points = []
|
|
118
|
+
area = asset.get("area", 0)
|
|
119
|
+
vacancy = asset.get("vacancy_days", 0)
|
|
120
|
+
rent = asset.get("expected_rent", 0)
|
|
121
|
+
allowed = asset.get("allowed_business", [])
|
|
122
|
+
|
|
123
|
+
if area >= 200:
|
|
124
|
+
points.append(f"面积 {area}m² 具备多业态组合潜力")
|
|
125
|
+
if vacancy >= 90:
|
|
126
|
+
points.append(f"已空置 {vacancy} 天,引入经营方具有紧迫性")
|
|
127
|
+
if rent > 0:
|
|
128
|
+
points.append(f"评估租金 {rent:,.0f} 元/月,保底基准参考值")
|
|
129
|
+
if allowed:
|
|
130
|
+
points.append(f"适合业态:{', '.join(allowed[:3])}")
|
|
131
|
+
if reno > 0:
|
|
132
|
+
points.append(f"改造成本约 {reno:,.0f} 元,需纳入分润谈判")
|
|
133
|
+
return points[:5]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _template_diagnosis(asset: Dict, reno: float) -> str:
|
|
137
|
+
area = asset.get("area", 0)
|
|
138
|
+
vacancy = asset.get("vacancy_days", 0)
|
|
139
|
+
rent = asset.get("expected_rent", 0)
|
|
140
|
+
allowed = asset.get("allowed_business", [])
|
|
141
|
+
|
|
142
|
+
suitability = "建议经营权共创" if area >= 100 and vacancy >= 30 else "建议先评估市场需求"
|
|
143
|
+
return (
|
|
144
|
+
f"资产诊断(模板):\n"
|
|
145
|
+
f" 面积: {area}m² 空置: {vacancy}天 评估租金: {rent:,.0f}元/月\n"
|
|
146
|
+
f" 适合业态: {', '.join(allowed[:3]) or '待评估'}\n"
|
|
147
|
+
f" 改造成本估算: {reno:,.0f}元\n"
|
|
148
|
+
f" 诊断结论: {suitability}\n"
|
|
149
|
+
f" 建议保底: {int(rent * 0.7):,}元/月 建议分润比例: 8-12%"
|
|
150
|
+
)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/realty/business_match.py — 业态匹配 Agent
|
|
3
|
+
=================================================
|
|
4
|
+
匹配资产条件与经营方能力,给出业态组合建议。
|
|
5
|
+
|
|
6
|
+
输入数据(data dict keys):
|
|
7
|
+
asset_info — 资产基础信息(面积、位置、层高、客流等)
|
|
8
|
+
operators — 候选经营方列表(可选)
|
|
9
|
+
market_context — 周边业态、人口、消费力数据(可选)
|
|
10
|
+
|
|
11
|
+
输出:
|
|
12
|
+
analysis — 业态匹配建议
|
|
13
|
+
signal — BUY=高度匹配 / HOLD=一般匹配 / SELL=低匹配
|
|
14
|
+
key_points — 推荐业态 Top3 + 理由
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any, Dict, List
|
|
19
|
+
from ..base import BaseAgent, AgentResult
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BusinessMatchAgent(BaseAgent):
|
|
23
|
+
name = "business_match"
|
|
24
|
+
description = "业态匹配:根据空间条件和市场数据推荐适合的经营业态组合"
|
|
25
|
+
|
|
26
|
+
_SYSTEM = (
|
|
27
|
+
"你是一名商业空间招商运营专家,擅长根据空间条件匹配最优业态。\n"
|
|
28
|
+
"分析维度:\n"
|
|
29
|
+
" 1. 空间适配度(面积、层高、水电、消防条件)\n"
|
|
30
|
+
" 2. 市场需求(周边客群、竞争格局、消费力)\n"
|
|
31
|
+
" 3. 经营方能力(品牌力、资金实力、运营经验)\n"
|
|
32
|
+
" 4. 收益预期(流水规模、分润可行性)\n\n"
|
|
33
|
+
"输出格式:\n"
|
|
34
|
+
"1. 推荐业态 Top3(每个包含:业态名称、预期月流水范围、分润比例建议、空间需求匹配度)\n"
|
|
35
|
+
"2. 经营方匹配建议(如有候选方)\n"
|
|
36
|
+
"3. 不建议的业态及原因\n"
|
|
37
|
+
"4. 综合匹配评级:[高度匹配] / [一般匹配] / [谨慎入场]"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
41
|
+
asset = data.get("asset_info", {})
|
|
42
|
+
operators = data.get("operators", [])
|
|
43
|
+
market = data.get("market_context", {})
|
|
44
|
+
|
|
45
|
+
area = asset.get("area", 0)
|
|
46
|
+
location = asset.get("location", "")
|
|
47
|
+
floor_ht = asset.get("floor_height", 0)
|
|
48
|
+
power_cap = asset.get("power_capacity", "未知")
|
|
49
|
+
allowed = asset.get("allowed_business", [])
|
|
50
|
+
prohibited = asset.get("prohibited_business", [])
|
|
51
|
+
foot_traffic= asset.get("foot_traffic", "未知")
|
|
52
|
+
can_fire = asset.get("open_fire_allowed", False)
|
|
53
|
+
can_renovate= asset.get("renovation_allowed", True)
|
|
54
|
+
|
|
55
|
+
op_summary = ""
|
|
56
|
+
if operators:
|
|
57
|
+
op_lines = []
|
|
58
|
+
for op in operators[:3]:
|
|
59
|
+
op_lines.append(
|
|
60
|
+
f" - {op.get('name','未知')}: 行业={op.get('industry','未知')}, "
|
|
61
|
+
f"预算={op.get('budget',0):,}元, 品牌={op.get('brand_level','未知')}"
|
|
62
|
+
)
|
|
63
|
+
op_summary = "候选经营方:\n" + "\n".join(op_lines)
|
|
64
|
+
|
|
65
|
+
user_prompt = (
|
|
66
|
+
f"资产条件:\n"
|
|
67
|
+
f" 位置: {location} 面积: {area}m² 层高: {floor_ht}m\n"
|
|
68
|
+
f" 电容量: {power_cap} 明火许可: {'是' if can_fire else '否'}\n"
|
|
69
|
+
f" 可改造: {'是' if can_renovate else '否'} 客流量: {foot_traffic}\n"
|
|
70
|
+
f" 允许业态: {', '.join(allowed) or '无限制'}\n"
|
|
71
|
+
f" 禁止业态: {', '.join(prohibited) or '无'}\n"
|
|
72
|
+
f" 市场背景: {market.get('summary','暂无')}\n"
|
|
73
|
+
f"{op_summary}\n\n"
|
|
74
|
+
"请给出业态匹配分析。"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
analysis = await self._call_llm(self._SYSTEM, user_prompt, max_tokens=700)
|
|
78
|
+
if not analysis:
|
|
79
|
+
analysis = _template_match(asset, operators)
|
|
80
|
+
|
|
81
|
+
signal = _match_signal(asset, market)
|
|
82
|
+
confidence = _match_confidence(asset, market)
|
|
83
|
+
key_points = _match_key_points(asset, operators, market)
|
|
84
|
+
|
|
85
|
+
return AgentResult(
|
|
86
|
+
agent = self.name,
|
|
87
|
+
symbol = symbol,
|
|
88
|
+
analysis = analysis,
|
|
89
|
+
confidence = confidence,
|
|
90
|
+
signal = signal,
|
|
91
|
+
key_points = key_points,
|
|
92
|
+
data_used = {"asset_info": asset, "operators_count": len(operators)},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ── 辅助函数 ──────────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
def _match_signal(asset: Dict, market: Dict) -> str:
|
|
99
|
+
score = 0
|
|
100
|
+
area = asset.get("area", 0)
|
|
101
|
+
allowed = asset.get("allowed_business", [])
|
|
102
|
+
foot_traffic = str(asset.get("foot_traffic", "")).lower()
|
|
103
|
+
ft_score = asset.get("foot_traffic_score", 0) # numeric 0-10
|
|
104
|
+
|
|
105
|
+
# market_score: direct int, or derive from market_maturity string
|
|
106
|
+
market_score = market.get("score", 0)
|
|
107
|
+
if not market_score:
|
|
108
|
+
maturity = str(market.get("market_maturity", "")).lower()
|
|
109
|
+
market_score = {"high": 8, "medium": 5, "low": 2, "成熟": 8, "一般": 5, "新兴": 4}.get(maturity, 0)
|
|
110
|
+
|
|
111
|
+
if area >= 100: score += 1
|
|
112
|
+
if area >= 300: score += 1
|
|
113
|
+
if len(allowed) >= 3: score += 1
|
|
114
|
+
if "高" in foot_traffic or "large" in foot_traffic or ft_score >= 7: score += 1
|
|
115
|
+
if market_score >= 7: score += 1
|
|
116
|
+
elif 0 < market_score <= 3: score -= 1
|
|
117
|
+
|
|
118
|
+
if score >= 4: return "BUY"
|
|
119
|
+
if score >= 2: return "HOLD"
|
|
120
|
+
return "SELL"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _match_confidence(asset: Dict, market: Dict) -> float:
|
|
124
|
+
fields = ["area", "location", "foot_traffic", "allowed_business", "floor_height"]
|
|
125
|
+
filled = sum(1 for f in fields if asset.get(f))
|
|
126
|
+
market_bonus = 0.1 if market.get("summary") else 0
|
|
127
|
+
return round(min(0.9, 0.4 + 0.1 * filled + market_bonus), 2)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _match_key_points(asset: Dict, operators: List, market: Dict) -> List[str]:
|
|
131
|
+
points = []
|
|
132
|
+
area = asset.get("area", 0)
|
|
133
|
+
allowed = asset.get("allowed_business", [])
|
|
134
|
+
can_fire= asset.get("open_fire_allowed", False)
|
|
135
|
+
|
|
136
|
+
if allowed:
|
|
137
|
+
points.append(f"允许业态:{', '.join(allowed[:3])} 等 {len(allowed)} 类")
|
|
138
|
+
if area:
|
|
139
|
+
points.append(f"面积 {area}m²,适合{'大型品牌' if area >= 300 else '中小型经营方'}")
|
|
140
|
+
if not can_fire:
|
|
141
|
+
points.append("不允许明火,餐饮业态受限(建议西餐/轻食/烘焙)")
|
|
142
|
+
if operators:
|
|
143
|
+
points.append(f"共 {len(operators)} 个候选经营方,需进一步资质评估")
|
|
144
|
+
if market.get("competitors"):
|
|
145
|
+
points.append(f"周边竞争: {market['competitors']}")
|
|
146
|
+
return points[:5]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _template_match(asset: Dict, operators: List) -> str:
|
|
150
|
+
area = asset.get("area", 0)
|
|
151
|
+
allowed = asset.get("allowed_business", [])
|
|
152
|
+
can_fire= asset.get("open_fire_allowed", False)
|
|
153
|
+
|
|
154
|
+
top3 = allowed[:3] if allowed else ["零售", "轻餐饮", "生活服务"]
|
|
155
|
+
if not can_fire and "餐饮" in top3:
|
|
156
|
+
top3 = [b for b in top3 if "餐饮" not in b] + ["轻食/咖啡"]
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
f"业态匹配分析(模板):\n"
|
|
160
|
+
f" 面积: {area}m² 适合业态: {', '.join(allowed[:5]) or '无限制'}\n"
|
|
161
|
+
f" 推荐 Top3:\n"
|
|
162
|
+
+ "\n".join(f" {i+1}. {b}(预期流水 {(area*200*(i+1)):,.0f}-{area*400*(i+1):,.0f}元/月)"
|
|
163
|
+
for i, b in enumerate(top3[:3]))
|
|
164
|
+
+ f"\n 候选经营方数: {len(operators)}"
|
|
165
|
+
)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/realty/cashflow_verify.py — 流水核验 Agent
|
|
3
|
+
=================================================
|
|
4
|
+
判断经营方申报流水是否真实,识别私账/逃费/低报行为。
|
|
5
|
+
|
|
6
|
+
输入数据(data dict keys):
|
|
7
|
+
pos_transactions — POS/扫码流水列表(可选)
|
|
8
|
+
bank_statements — 银行流水(可选)
|
|
9
|
+
delivery_revenue — 外卖/团购收入(可选)
|
|
10
|
+
inventory_data — 库存进货数据(可选)
|
|
11
|
+
energy_data — 水电用量(可选)
|
|
12
|
+
declared_revenue — 经营方自报流水
|
|
13
|
+
expected_revenue — 系统预估流水(基于历史/业态/客流)
|
|
14
|
+
|
|
15
|
+
输出:
|
|
16
|
+
analysis — 核验报告
|
|
17
|
+
signal — BUY=流水真实 / HOLD=轻度异常需复核 / SELL=疑似造假/私账
|
|
18
|
+
key_points — 异常项目清单
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any, Dict, List
|
|
23
|
+
from ..base import BaseAgent, AgentResult
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CashFlowVerifyAgent(BaseAgent):
|
|
27
|
+
name = "cashflow_verify"
|
|
28
|
+
description = "流水核验:多源数据交叉比对,识别私账/逃费/低报流水行为"
|
|
29
|
+
|
|
30
|
+
_SYSTEM = (
|
|
31
|
+
"你是一名专业的财务审计和风控专家,擅长识别经营流水中的异常。\n"
|
|
32
|
+
"请对流水核验结果进行分析:\n"
|
|
33
|
+
" 1. 各数据源的一致性分析(POS/银行/外卖/库存/能耗)\n"
|
|
34
|
+
" 2. 申报流水与预估流水的差异分析\n"
|
|
35
|
+
" 3. 识别的异常模式(如:只用现金、收款码变更、能耗与流水不匹配)\n"
|
|
36
|
+
" 4. 风险等级判断:[正常] / [需复核] / [疑似私账] / [建议稽查]\n"
|
|
37
|
+
" 5. 具体建议:要求补充哪些凭证,或启动哪种核查程序"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
41
|
+
# 支持两种输入格式:
|
|
42
|
+
# 标量简化模式: pos_revenue=55000, bank_revenue=48000, cash_ratio=0.35
|
|
43
|
+
# 列表精细模式: pos_transactions=[{amount,payment_type,...}], bank_statements=[{amount,type,...}]
|
|
44
|
+
pos = data.get("pos_transactions", [])
|
|
45
|
+
bank = data.get("bank_statements", [])
|
|
46
|
+
delivery = data.get("delivery_revenue", 0)
|
|
47
|
+
inventory= data.get("inventory_data", {})
|
|
48
|
+
energy = data.get("energy_data", {})
|
|
49
|
+
declared = data.get("declared_revenue", 0)
|
|
50
|
+
expected = data.get("expected_revenue", 0)
|
|
51
|
+
|
|
52
|
+
# 标量快捷键:如果没有列表数据但有聚合数字,转换为单条虚拟记录
|
|
53
|
+
if not pos and data.get("pos_revenue", 0):
|
|
54
|
+
pos_total_val = data["pos_revenue"]
|
|
55
|
+
cash_r = data.get("cash_ratio", 0.0) # 0.0~1.0
|
|
56
|
+
if cash_r > 0:
|
|
57
|
+
pos = [
|
|
58
|
+
{"amount": pos_total_val * (1 - cash_r), "payment_type": "digital"},
|
|
59
|
+
{"amount": pos_total_val * cash_r, "payment_type": "cash"},
|
|
60
|
+
]
|
|
61
|
+
else:
|
|
62
|
+
pos = [{"amount": pos_total_val, "payment_type": "digital"}]
|
|
63
|
+
if not bank and data.get("bank_revenue", 0):
|
|
64
|
+
bank = [{"amount": data["bank_revenue"], "type": "IN"}]
|
|
65
|
+
if not delivery and data.get("wechat_revenue", 0):
|
|
66
|
+
delivery = data.get("wechat_revenue", 0) + data.get("alipay_revenue", 0)
|
|
67
|
+
|
|
68
|
+
# 交叉核验
|
|
69
|
+
check = _cross_verify(pos, bank, delivery, inventory, energy, declared, expected)
|
|
70
|
+
|
|
71
|
+
user_prompt = (
|
|
72
|
+
f"流水核验数据:\n"
|
|
73
|
+
f" 经营方申报流水: {declared:,.2f}元\n"
|
|
74
|
+
f" 系统预估流水: {expected:,.2f}元\n"
|
|
75
|
+
f" 差异率: {check['gap_pct']:.1f}%({'偏低' if check['gap_pct'] < 0 else '偏高'})\n\n"
|
|
76
|
+
f"数据源汇总:\n"
|
|
77
|
+
f" POS/扫码流水: {check['pos_total']:,.2f}元({len(pos)}笔)\n"
|
|
78
|
+
f" 银行流水: {check['bank_total']:,.2f}元\n"
|
|
79
|
+
f" 外卖/团购收入: {delivery:,.2f}元\n"
|
|
80
|
+
f" 能耗指数(用电): {energy.get('electricity_kwh',0)} kWh\n"
|
|
81
|
+
f" 库存周转估算: {check['inventory_implied_revenue']:,.2f}元\n\n"
|
|
82
|
+
f"发现异常:\n"
|
|
83
|
+
+ "\n".join(f" - {a}" for a in check["anomalies"])
|
|
84
|
+
+ "\n\n请完成流水核验报告。"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
analysis = await self._call_llm(self._SYSTEM, user_prompt, max_tokens=700)
|
|
88
|
+
if not analysis:
|
|
89
|
+
analysis = _template_verify(check, declared, expected)
|
|
90
|
+
|
|
91
|
+
signal = _verify_signal(check)
|
|
92
|
+
confidence = _verify_confidence(check)
|
|
93
|
+
key_points = _verify_key_points(check, declared, expected)
|
|
94
|
+
|
|
95
|
+
return AgentResult(
|
|
96
|
+
agent = self.name,
|
|
97
|
+
symbol = symbol,
|
|
98
|
+
analysis = analysis,
|
|
99
|
+
confidence = confidence,
|
|
100
|
+
signal = signal,
|
|
101
|
+
key_points = key_points,
|
|
102
|
+
data_used = {"verify_result": check},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# ── 核验逻辑 ──────────────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
def _cross_verify(
|
|
109
|
+
pos: List[Dict], bank: List[Dict], delivery: float,
|
|
110
|
+
inventory: Dict, energy: Dict, declared: float, expected: float,
|
|
111
|
+
) -> Dict:
|
|
112
|
+
pos_total = sum(t.get("amount", 0) for t in pos if not t.get("is_refund"))
|
|
113
|
+
bank_total = sum(t.get("amount", 0) for t in bank if t.get("type") in ("IN", "in", None))
|
|
114
|
+
|
|
115
|
+
# 库存推算流水(进货金额 * 毛利率倒推)
|
|
116
|
+
purchase_cost = inventory.get("purchase_cost", 0)
|
|
117
|
+
gross_margin = inventory.get("expected_margin_pct", 40) / 100
|
|
118
|
+
inv_implied = purchase_cost / (1 - gross_margin) if gross_margin < 1 else 0
|
|
119
|
+
|
|
120
|
+
# 综合核验流水(取最大可信来源)
|
|
121
|
+
verified_sources = [s for s in [pos_total, bank_total, delivery + pos_total] if s > 0]
|
|
122
|
+
verified_max = max(verified_sources) if verified_sources else declared
|
|
123
|
+
|
|
124
|
+
gap_pct = ((declared - expected) / expected * 100) if expected else 0
|
|
125
|
+
source_gap = ((declared - verified_max) / declared * 100) if declared else 0
|
|
126
|
+
|
|
127
|
+
anomalies = []
|
|
128
|
+
|
|
129
|
+
# 申报 vs 预期 差距
|
|
130
|
+
if gap_pct < -30:
|
|
131
|
+
anomalies.append(f"申报流水比预估低{abs(gap_pct):.1f}%,偏差较大")
|
|
132
|
+
if gap_pct < -50:
|
|
133
|
+
anomalies.append("申报流水不足预估50%,建议启动稽查")
|
|
134
|
+
|
|
135
|
+
# POS vs 银行 不一致
|
|
136
|
+
if pos_total > 0 and bank_total > 0:
|
|
137
|
+
consistency = abs(pos_total - bank_total) / max(pos_total, bank_total)
|
|
138
|
+
if consistency > 0.2:
|
|
139
|
+
anomalies.append(f"POS流水({pos_total:,.0f})与银行流水({bank_total:,.0f})差异{consistency*100:.1f}%")
|
|
140
|
+
|
|
141
|
+
# 申报 vs 核验源 差距
|
|
142
|
+
if declared > 0 and verified_max > 0 and source_gap > 25:
|
|
143
|
+
anomalies.append(f"申报流水比可核验来源高{source_gap:.1f}%,可能虚报")
|
|
144
|
+
elif declared < verified_max * 0.7:
|
|
145
|
+
anomalies.append(f"申报流水({declared:,.0f})低于可核验来源({verified_max:,.0f}),疑似漏报")
|
|
146
|
+
|
|
147
|
+
# 能耗异常(有水电无流水)
|
|
148
|
+
elec = energy.get("electricity_kwh", 0)
|
|
149
|
+
if elec > 0 and declared == 0:
|
|
150
|
+
anomalies.append("有水电消耗记录但申报流水为零,疑似有经营未申报")
|
|
151
|
+
|
|
152
|
+
# 库存推算差距
|
|
153
|
+
if inv_implied > 0 and declared < inv_implied * 0.6:
|
|
154
|
+
anomalies.append(f"库存进货推算流水{inv_implied:,.0f}元,申报仅{declared:,.0f}元,差距较大")
|
|
155
|
+
|
|
156
|
+
# 现金比例过高
|
|
157
|
+
cash_txns = [t for t in pos if t.get("payment_type") in ("cash", "现金")]
|
|
158
|
+
if pos and len(cash_txns) / len(pos) > 0.3:
|
|
159
|
+
anomalies.append(f"现金交易占比{len(cash_txns)/len(pos)*100:.1f}%偏高,难以核验")
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
"pos_total": pos_total,
|
|
163
|
+
"bank_total": bank_total,
|
|
164
|
+
"delivery": delivery,
|
|
165
|
+
"inventory_implied_revenue": inv_implied,
|
|
166
|
+
"verified_max": verified_max,
|
|
167
|
+
"gap_pct": gap_pct,
|
|
168
|
+
"source_gap_pct": source_gap,
|
|
169
|
+
"anomalies": anomalies,
|
|
170
|
+
"anomaly_count": len(anomalies),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _verify_signal(check: Dict) -> str:
|
|
175
|
+
n = check.get("anomaly_count", 0)
|
|
176
|
+
gap = abs(check.get("gap_pct", 0))
|
|
177
|
+
if n == 0 and gap < 15: return "BUY"
|
|
178
|
+
if n <= 1 and gap < 30: return "HOLD"
|
|
179
|
+
if n <= 2 and gap < 50: return "SELL"
|
|
180
|
+
return "STRONG_SELL"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _verify_confidence(check: Dict) -> float:
|
|
184
|
+
sources = sum(1 for k in ["pos_total", "bank_total", "delivery"] if check.get(k, 0) > 0)
|
|
185
|
+
return round(min(0.9, 0.5 + 0.15 * sources), 2)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _verify_key_points(check: Dict, declared: float, expected: float) -> List[str]:
|
|
189
|
+
pts = [f"申报流水: {declared:,.2f}元 预估流水: {expected:,.2f}元 "
|
|
190
|
+
f"差异: {check['gap_pct']:+.1f}%"]
|
|
191
|
+
if check["pos_total"]:
|
|
192
|
+
pts.append(f"POS核验流水: {check['pos_total']:,.2f}元")
|
|
193
|
+
for a in check["anomalies"][:3]:
|
|
194
|
+
pts.append(f"异常: {a}")
|
|
195
|
+
return pts[:5]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _template_verify(check: Dict, declared: float, expected: float) -> str:
|
|
199
|
+
status = "正常" if check["anomaly_count"] == 0 else "发现异常"
|
|
200
|
+
return (
|
|
201
|
+
f"流水核验报告(模板):\n"
|
|
202
|
+
f" 申报流水: {declared:,.2f}元 预估流水: {expected:,.2f}元 "
|
|
203
|
+
f"差异: {check['gap_pct']:+.1f}%\n"
|
|
204
|
+
f" POS流水: {check['pos_total']:,.2f}元 银行流水: {check['bank_total']:,.2f}元\n"
|
|
205
|
+
f" 核验状态: {status}\n"
|
|
206
|
+
+ (" 发现异常:\n" + "\n".join(f" • {a}" for a in check["anomalies"])
|
|
207
|
+
if check["anomalies"] else " 无异常项目")
|
|
208
|
+
)
|