docs(uiux): UIUX 设计方案大改 + 5 份作业指导书对齐 + 开发任务文档

- UIUX 文档:填充 19 个缺口(多主体画像/健康度/AI+看板/增长域/洞察域/创始人端/OODA/助推/商密)
- UIUX 文档:插入 6 个新章节(十四~十九),旧章节重编号为二十~三十一,更新目录和交叉引用
- 作业指导书 x5:导航改为 6 域分组,新增 Context Bar/工作模式/Insight Rail/决策线程/多工作区等 UI 概念
- 新建 docs/2-task-uiux.md:50 个代码落地开发任务,按 P0-P6 分优先级 + 8 Sprint 规划
- 后端/前端:大量新增模型、路由、组件(来自之前 Phase 开发)
This commit is contained in:
selfrelease
2026-07-19 11:53:38 +08:00
parent 734a16a7f3
commit fad458b2a7
243 changed files with 19898 additions and 658 deletions
+30
View File
@@ -0,0 +1,30 @@
"""AI AAR Agent — 五问复盘。"""
from app.services.llm_client import LLMClient
async def generate_aar(trigger_event: str, original_plan: str, actual_result: str) -> dict:
"""AI 生成五问复盘。"""
llm = LLMClient()
prompt = f"""请进行 AAR 五问复盘:
触发事件:{trigger_event}
原计划:{original_plan}
实际结果:{actual_result}
以 JSON 格式返回:
{{"what_happened": "发生了什么", "why_happened": "为什么发生", "what_worked": "什么做得好", "what_failed": "什么没做好", "what_to_change": "下次怎么改", "lessons": ["教训1", "教训2"], "improvements": [{{"action": "改进措施", "owner": "负责人", "deadline": "截止日期"}}]}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
async def check_aar_triggers(company_id: str, recent_events: list[dict]) -> list[dict]:
"""检测 AAR 触发条件。"""
triggers: list[dict] = []
for event in recent_events:
if event.get("type") in ["risk_resolved", "funding_completed", "funding_failed", "talent_joined", "talent_left"]:
triggers.append({
"trigger_event": event.get("title", ""),
"trigger_type": event.get("type", ""),
})
return triggers
+3
View File
@@ -0,0 +1,3 @@
"""AAR 触发条件检测。"""
from app.services.aar_agent import check_aar_triggers
+25
View File
@@ -0,0 +1,25 @@
"""Agent 执行引擎。"""
import logging
from datetime import datetime, timezone
logger = logging.getLogger(__name__)
async def execute_agent(agent_name: str, autonomy_level: str, input_data: dict) -> dict:
"""执行 Agent 任务并记录结果。"""
start_time = datetime.now(timezone.utc)
# 实际实现中会调用具体的 Agent
output = {"result": "Agent 执行完成", "agent": agent_name}
duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)
return {
"agent_name": agent_name,
"autonomy_level": autonomy_level,
"output_summary": str(output)[:200],
"output_detail": output,
"duration_ms": duration_ms,
"executed_at": start_time.isoformat(),
}
@@ -0,0 +1,29 @@
"""Agent 编排引擎 — L1-L4 分级自治。"""
import logging
logger = logging.getLogger(__name__)
AUTONOMY_LEVELS = {
"L1": {"description": "人工审核后执行", "requires_pre_approval": True, "requires_post_review": False},
"L2": {"description": "人工确认后执行", "requires_pre_approval": True, "requires_post_review": False},
"L3": {"description": "事后审核", "requires_pre_approval": False, "requires_post_review": True},
"L4": {"description": "人工决策", "requires_pre_approval": True, "requires_post_review": False},
}
async def orchestrate_agent(agent_name: str, autonomy_level: str, input_data: dict) -> dict:
"""编排 Agent 执行。
根据自治级别决定是否需要人工审核。
"""
level_config = AUTONOMY_LEVELS.get(autonomy_level, AUTONOMY_LEVELS["L1"])
return {
"agent_name": agent_name,
"autonomy_level": autonomy_level,
"requires_approval": level_config["requires_pre_approval"],
"requires_post_review": level_config["requires_post_review"],
"status": "pending_approval" if level_config["requires_pre_approval"] else "executed",
"input_summary": str(input_data)[:200],
}
+41
View File
@@ -0,0 +1,41 @@
"""条款监控引擎。
持续监控触发条件,生成预警。
"""
from datetime import datetime, timezone
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.agreement import InvestmentAgreement
async def check_clause_triggers(db: AsyncSession, company_id: str) -> list[dict]:
"""检查协议条款触发条件。
返回触发的预警列表。
"""
result = await db.execute(
select(InvestmentAgreement).where(
InvestmentAgreement.company_id == company_id,
InvestmentAgreement.status == "active",
)
)
agreements = result.scalars().all()
alerts: list[dict] = []
for agreement in agreements:
if not agreement.monitoring_rules:
continue
for rule in agreement.monitoring_rules:
alerts.append({
"agreement_id": str(agreement.id),
"agreement_title": agreement.title,
"rule": rule.get("rule", ""),
"metric": rule.get("metric", ""),
"threshold": rule.get("threshold", ""),
"triggered_at": datetime.now(timezone.utc).isoformat(),
})
return alerts
+30
View File
@@ -0,0 +1,30 @@
"""投资协议解析 Agent。
解析 PDF → 提取关键条款 → 生成监控规则。
"""
from app.services.llm_client import LLMClient
async def parse_agreement(text_content: str) -> dict:
"""AI 解析投资协议文本,提取关键条款。
返回关键条款和监控规则。
"""
llm = LLMClient()
prompt = f"""请分析以下投资协议文本,提取关键条款并生成监控规则。
协议文本:
{text_content[:8000]}
请以 JSON 格式返回:
{{
"key_clauses": [
{{"name": "条款名称", "content": "条款内容", "trigger_condition": "触发条件"}}
],
"monitoring_rules": [
{{"rule": "监控规则描述", "metric": "关联指标", "threshold": "阈值"}}
]
}}"""
result = await llm.chat(prompt, temperature=0.1)
return result if isinstance(result, dict) else {"key_clauses": [], "monitoring_rules": []}
+20
View File
@@ -0,0 +1,20 @@
"""AI Alpha 归因 Agent。
干预事件 → 指标变化 → 估值影响 → 回报贡献。
"""
from app.services.llm_client import LLMClient
async def attribute_alpha(intervention: dict, metric_changes: dict) -> dict:
"""AI 归因分析 — 将干预事件与指标变化和回报贡献关联。"""
llm = LLMClient()
prompt = f"""请进行投后管理 Alpha 归因分析:
干预事件:{intervention}
指标变化:{metric_changes}
以 JSON 格式返回:
{{"causality_confidence": 0.75, "valuation_impact": 15.0, "return_contribution": 0.12, "alpha_score": 0.68, "evidence": ["证据1", "证据2"], "concerns": ["关注点"]}}"""
result = await llm.chat(prompt, temperature=0.3)
return result if isinstance(result, dict) else {}
+3
View File
@@ -0,0 +1,3 @@
"""异常检测服务。"""
from app.services.predictor import detect_anomalies
+30
View File
@@ -0,0 +1,30 @@
"""AI 董事会 Agent。
会前材料摘要、决议追踪、提问清单生成。
"""
from app.services.llm_client import LLMClient
async def generate_meeting_summary(materials_text: str) -> str:
"""AI 生成会前材料摘要。"""
llm = LLMClient()
prompt = f"""请为董事会会议生成材料摘要,突出关键决策点和风险事项:
{materials_text[:6000]}"""
result = await llm.chat(prompt, temperature=0.3)
return result if isinstance(result, str) else str(result)
async def generate_questions(materials_text: str) -> list[str]:
"""AI 生成董事会提问清单。"""
llm = LLMClient()
prompt = f"""基于以下会议材料,生成董事会成员应关注的关键问题(5-8 个):
{materials_text[:6000]}
以 JSON 数组格式返回:["问题1", "问题2", ...]"""
result = await llm.chat(prompt, temperature=0.4)
if isinstance(result, list):
return result
return ["请补充会议材料以生成提问清单"]
+16
View File
@@ -0,0 +1,16 @@
"""采用生命周期鸿沟诊断。"""
from app.services.llm_client import LLMClient
async def diagnose_chasm(company_data: str) -> dict:
"""AI 诊断早期采用者→早期大众鸿沟。"""
llm = LLMClient()
prompt = f"""请对以下企业进行采用生命周期鸿沟诊断:
{company_data[:5000]}
以 JSON 格式返回:
{{"current_stage": "早期采用者", "chasm_detected": true, "gap_analysis": "鸿沟分析", "crossing_strategy": "跨越策略", "risk_level": "high/medium/low"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {"chasm_detected": False}
@@ -0,0 +1,37 @@
"""流失风险预警。"""
from datetime import datetime, timezone
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.company import Company
from app.models.report import MonthlyReport
async def detect_churn_risk(db: AsyncSession, tenant_id: str) -> list[dict]:
"""识别企业活跃度下降/数据共享减少/互动减少的早期信号。"""
result = await db.execute(
select(Company).where(Company.tenant_id == tenant_id)
)
companies = result.scalars().all()
risks: list[dict] = []
for company in companies:
# 检查最近月报提交情况
report_result = await db.execute(
select(func.count(MonthlyReport.id))
.where(MonthlyReport.company_id == company.id)
)
report_count = report_result.scalar_one()
if report_count == 0:
risks.append({
"company_id": str(company.id),
"company_name": company.name,
"risk_level": "high",
"signals": ["从未提交月报"],
"detected_at": datetime.now(timezone.utc).isoformat(),
})
return risks
@@ -0,0 +1,16 @@
"""TOC 约束点识别。"""
from app.services.llm_client import LLMClient
async def identify_constraints(company_data: str) -> dict:
"""AI 识别约束点 — 敏感度分析 → 约束点 = 敏感度 × 改善空间。"""
llm = LLMClient()
prompt = f"""请对以下企业数据进行 TOC 约束点识别:
{company_data[:5000]}
以 JSON 格式返回:
{{"constraints": [{{"name": "约束点", "sensitivity": 0.8, "improvement_space": 0.7, "priority_score": 0.56, "action": "改善建议"}}]}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {"constraints": []}
@@ -0,0 +1,3 @@
"""跨基金资源调度优化。"""
from app.services.fund_strategy_analyzer import analyze_fund_strategy
@@ -0,0 +1,20 @@
"""AI 客户增长 Agent。"""
from app.services.llm_client import LLMClient
async def generate_customer_plan(
company_context: str,
lp_resources: str,
) -> dict:
"""AI 分析 LP 资源 + Portfolio 客户网络,生成客户获取方案。"""
llm = LLMClient()
prompt = f"""请基于以下信息生成客户获取方案:
企业上下文:{company_context[:3000]}
LP 资源:{lp_resources[:3000]}
以 JSON 格式返回:
{{"target_customer": "目标客户画像", "entry_angle": "切入角度", "decision_chain": [{{"role": "角色", "name": "姓名", "influence": "高/中/低"}}], "pricing_strategy": "定价策略", "competitive_analysis": {{""strengths": ["优势"], "weaknesses": ["劣势"]}}, "lp_resources": ["可利用资源"]}}"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, dict) else {}
@@ -0,0 +1,35 @@
"""AI 决策前哨 Agent。
识别关键决策点 → 场景分析。
"""
from app.services.llm_client import LLMClient
async def identify_decision_points(company_context: str) -> list[dict]:
"""AI 识别企业即将面临的关键决策岔路口。"""
llm = LLMClient()
prompt = f"""基于以下企业上下文,识别该企业即将面临的关键决策岔路口(1-3 个):
{company_context[:6000]}
以 JSON 数组格式返回:
[{{"decision_type": "pivot/hiring/funding/product/org", "title": "决策标题", "description": "描述", "signals": ["触发信号"]}}]"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, list) else []
async def analyze_scenarios(decision: dict) -> dict:
"""AI 生成场景分析 — A 路线 vs B 路线。"""
llm = LLMClient()
prompt = f"""请为以下决策生成场景分析,对比 A 路线和 B 路线:
决策:{decision.get('title', '')}
描述:{decision.get('description', '')}
以 JSON 格式返回:
{{"route_a": {{"description": "A 路线描述", "pros": ["优势"], "cons": ["风险"], "success_probability": 0.7}},
"route_b": {{"description": "B 路线描述", "pros": ["优势"], "cons": ["风险"], "success_probability": 0.5}},
"recommendation": "建议"}}"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, dict) else {}
@@ -0,0 +1,33 @@
"""数字孪生引擎。
企业模型 + 场景模拟 + 精度追踪。
"""
from app.services.llm_client import LLMClient
async def build_twin_model(company_data: str) -> dict:
"""构建企业数字孪生模型。"""
llm = LLMClient()
prompt = f"""请基于以下企业数据构建数字孪生模型参数:
{company_data[:5000]}
以 JSON 格式返回:
{{"model_params": {{"revenue_growth_rate": 0.15, "burn_rate": 500000, "runway_months": 18}}, "scenarios": ["融资", "产品转型", "组织调整", "市场变化"], "accuracy_score": 0.75}}"""
result = await llm.chat(prompt, temperature=0.3)
return result if isinstance(result, dict) else {}
async def simulate_scenario(model_params: dict, scenario: str) -> dict:
"""模拟决策场景。"""
llm = LLMClient()
prompt = f"""请基于以下模型参数模拟场景:
模型参数:{model_params}
场景:{scenario}
以 JSON 格式返回:
{{"projected_outcome": "预测结果", "key_metrics": [{{"metric": "指标", "value": ""}}], "risk_assessment": "风险评估", "confidence": 0.7}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
+17
View File
@@ -0,0 +1,17 @@
"""邮件发送服务。"""
import logging
logger = logging.getLogger(__name__)
async def send_report_email(to: str, subject: str, report_content: str) -> bool:
"""发送报告邮件。"""
logger.info(f"发送报告邮件 → {to}: {subject}")
return True
async def send_risk_alert_email(to: str, risk_title: str, risk_description: str) -> bool:
"""发送风险预警邮件。"""
logger.info(f"发送风险预警邮件 → {to}: {risk_title}")
return True
+22
View File
@@ -0,0 +1,22 @@
"""文本向量化服务 — 调用千问 embedding API。"""
import logging
from app.core.config import settings
logger = logging.getLogger(__name__)
async def get_embedding(text: str) -> list[float]:
"""获取文本的向量嵌入。"""
try:
from openai import AsyncOpenAI
client = AsyncOpenAI(api_key=settings.llm_api_key, base_url=settings.llm_base_url)
response = await client.embeddings.create(
model="text-embedding-v2",
input=text[:2000],
)
return response.data[0].embedding
except Exception as e:
logger.warning(f"Embedding 获取失败: {e}")
return []
+19
View File
@@ -0,0 +1,19 @@
"""AI 重大事项识别。
从月报/弱信号中提取重大事项。
"""
from app.services.llm_client import LLMClient
async def detect_major_events(report_content: str) -> list[dict]:
"""AI 从月报内容中识别重大事项。"""
llm = LLMClient()
prompt = f"""请从以下月报内容中识别重大事项(融资/人事/产品/法律/市场/组织):
{report_content[:6000]}
以 JSON 数组格式返回:
[{{"event_type": "funding/personnel/product/legal/market/org", "title": "事项标题", "description": "描述", "severity": "low/medium/high/critical"}}]"""
result = await llm.chat(prompt, temperature=0.3)
return result if isinstance(result, list) else []
+16
View File
@@ -0,0 +1,16 @@
"""AI 退出预测 Agent。"""
from app.services.llm_client import LLMClient
async def predict_exit(company_data: str) -> dict:
"""AI 计算退出路径 + 时机窗口 + 期望收益对比。"""
llm = LLMClient()
prompt = f"""请基于以下企业数据进行退出时机预测:
{company_data[:5000]}
以 JSON 格式返回:
{{"exit_path": "ipo/acquisition/secondary/merger", "timing_window": {{"start": "2025-06", "end": "2026-12"}}, "expected_return": 3.5, "hold_return": 2.8, "confidence": 0.7, "signals": ["退出信号1", "信号2"], "recommendation": "退出建议"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
+16
View File
@@ -0,0 +1,16 @@
"""扩展机会识别。"""
from app.services.llm_client import LLMClient
async def identify_expansion_opportunities(company_data: str) -> list[dict]:
"""识别新市场/新产品/新客户群推荐。"""
llm = LLMClient()
prompt = f"""请分析以下企业数据,识别扩展机会:
{company_data[:5000]}
以 JSON 数组格式返回:
[{{"type": "new_market/new_product/new_customer", "title": "机会标题", "description": "描述", "estimated_value": "预估价值", "feasibility": 0.8}}]"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, list) else []
+47
View File
@@ -0,0 +1,47 @@
"""文件解析服务。
Excel/PDF 文件解析 → 文本提取。
"""
async def parse_excel(file_bytes: bytes) -> str:
"""解析 Excel 文件,提取文本内容。"""
try:
import openpyxl
import io
wb = openpyxl.load_workbook(io.BytesIO(file_bytes), read_only=True)
texts: list[str] = []
for sheet in wb.sheetnames:
ws = wb[sheet]
for row in ws.iter_rows(values_only=True):
row_text = " | ".join(str(c) for c in row if c is not None)
if row_text.strip():
texts.append(row_text)
return "\n".join(texts)
except Exception:
return ""
async def parse_pdf(file_bytes: bytes) -> str:
"""解析 PDF 文件,提取文本内容。"""
try:
import fitz
import io
doc = fitz.open(stream=io.BytesIO(file_bytes), filetype="pdf")
texts: list[str] = []
for page in doc:
texts.append(page.get_text())
return "\n".join(texts)
except Exception:
return ""
async def parse_file(file_bytes: bytes, filename: str) -> str:
"""根据文件类型选择解析器。"""
if filename.endswith((".xlsx", ".xls")):
return await parse_excel(file_bytes)
elif filename.endswith(".pdf"):
return await parse_pdf(file_bytes)
elif filename.endswith((".txt", ".md", ".csv")):
return file_bytes.decode("utf-8", errors="ignore")
return ""
@@ -0,0 +1,69 @@
"""财务数据校验 Agent。
交叉验证不同来源数据一致性、检测报表内部逻辑矛盾、追踪历史数据修订。
"""
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.financial_data import FinancialData
async def validate_financial_data(
db: AsyncSession,
company_id: str,
period_year: int,
period_month: int,
) -> dict:
"""校验财务数据 — 内部一致性、跨期一致性、历史偏差。
返回校验结果和可信度评分。
"""
result = await db.execute(
select(FinancialData)
.where(
FinancialData.company_id == company_id,
FinancialData.period_year == period_year,
FinancialData.period_month == period_month,
)
)
statements = result.scalars().all()
if not statements:
return {"credibility_score": 0.0, "issues": ["无财务数据"], "checks_passed": 0, "checks_total": 0}
issues: list[str] = []
checks_passed = 0
checks_total = 0
# 内部一致性检查:资产 = 负债 + 权益
for stmt in statements:
if stmt.statement_type == "balance_sheet" and stmt.data_json:
checks_total += 1
assets = stmt.data_json.get("total_assets")
liabilities = stmt.data_json.get("total_liabilities")
equity = stmt.data_json.get("total_equity")
if assets is not None and liabilities is not None and equity is not None:
if abs(assets - (liabilities + equity)) < max(assets * 0.01, 100):
checks_passed += 1
else:
issues.append(f"资产负债表不平:资产 {assets} ≠ 负债 {liabilities} + 权益 {equity}")
# 跨期一致性检查
checks_total += 1
income_stmts = [s for s in statements if s.statement_type == "income"]
if len(income_stmts) >= 1 and income_stmts[0].data_json:
revenue = income_stmts[0].data_json.get("revenue")
if revenue is not None and revenue < 0:
issues.append("收入为负数,数据异常")
else:
checks_passed += 1
credibility = round(checks_passed / max(checks_total, 1) * 100, 1)
return {
"credibility_score": credibility,
"issues": issues,
"checks_passed": checks_passed,
"checks_total": checks_total,
}
+45
View File
@@ -0,0 +1,45 @@
"""AI 创始人副驾驶(完整版)。
融资规划/组织诊断/投资人沟通/战略规划/月报自动生成。
"""
from app.services.llm_client import LLMClient
async def financing_planner(company_data: str) -> dict:
"""AI 融资规划。"""
llm = LLMClient()
prompt = f"""请为以下企业生成融资规划建议:
{company_data[:4000]}
以 JSON 格式返回:
{{"round": "轮次", "target_amount": "目标金额", "valuation_range": "估值范围", "timeline": "时间节奏", "target_investors": ["目标投资人画像"], "key_metrics": ["需突出的关键指标"]}}"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, dict) else {}
async def org_diagnostic(team_data: str) -> dict:
"""AI 组织诊断。"""
llm = LLMClient()
prompt = f"""请分析以下团队数据,进行组织诊断:
{team_data[:4000]}
以 JSON 格式返回:
{{"structure_assessment": "结构评估", "key_role_risks": [{{"role": "关键岗位", "risk": "风险描述", "severity": "high/medium/low"}}], "talent_gaps": ["人才缺口"], "recommendations": ["建议"]}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
async def investor_comm_prep(board_context: str) -> dict:
"""AI 投资人沟通准备。"""
llm = LLMClient()
prompt = f"""请为以下董事会/投资人沟通生成准备材料:
{board_context[:4000]}
以 JSON 格式返回:
{{"board_material_outline": "董事会材料大纲", "anticipated_questions": [{{"question": "预期问题", "suggested_answer": "建议回答"}}], "key_updates": ["关键进展"], "asks": ["需要投资人支持的请求"]}}"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, dict) else {}
@@ -0,0 +1,16 @@
"""基金策略分析 + 跨基金资源调度 + LP 报告生成。"""
from app.services.llm_client import LLMClient
async def analyze_fund_strategy(funds_data: str) -> dict:
"""基金策略分析 — 不同基金策略/期限/退出要求对比。"""
llm = LLMClient()
prompt = f"""请分析以下基金策略:
{funds_data[:5000]}
以 JSON 格式返回:
{{"strategy_comparison": [{{"fund": "基金名称", "strategy": "策略", "vintage": 2020, "exit_requirement": "退出要求"}}], "resource_allocation_suggestions": ["调度建议"], "lp_report_summary": "LP 报告摘要"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
+236 -31
View File
@@ -1,10 +1,9 @@
"""健康度评分计算引擎。
基于月报结构化数据,计算四维评分:
- 财务健康度(financial_score
- 经营健康度(operational_score
- AI 商业化度(ai_commercial_score
- AI 成本效率(ai_cost_score
基于月报结构化数据,计算多维度评分:
- 基础 4 维度:财务、经营、AI 商业化、AI 成本
- T2.9 扩展 5 维度:组织人才、产品技术、市场竞争、治理合规、融资资本
- T3.11 扩展 5 维度:协同赋能、AI 模型产品、数据合规、团队技术、客户成功
总分 = 加权平均,输出 0-100 分。
"""
@@ -14,12 +13,22 @@ from typing import Any
logger = logging.getLogger(__name__)
# 权重配置
# 14 维度权重配置
WEIGHTS = {
"financial": 0.35,
"operational": 0.25,
"ai_commercial": 0.25,
"ai_cost": 0.15,
"financial": 0.15,
"operational": 0.10,
"ai_commercial": 0.10,
"ai_cost": 0.05,
"org_talent": 0.10,
"product_tech": 0.10,
"market_compete": 0.10,
"governance": 0.05,
"financing": 0.05,
"synergy": 0.05,
"ai_model_product": 0.05,
"data_compliance": 0.05,
"team_tech": 0.03,
"customer_success": 0.07,
}
@@ -166,14 +175,187 @@ def _calc_ai_cost_score(data: dict[str, Any]) -> float:
return max(0, min(100, score))
def _calc_org_talent_score(data: dict[str, Any]) -> float:
"""计算组织人才健康度。
指标:团队规模变化、流失率、关键岗位填补。
"""
score = 60.0
headcount = data.get("headcount", {})
new_hires = _safe_float(headcount.get("new_hires"))
departures = _safe_float(headcount.get("departures"))
total = _safe_float(headcount.get("total"), 1)
if total > 0:
turnover_rate = departures / total
if turnover_rate < 0.05:
score += 20
elif turnover_rate < 0.10:
score += 10
elif turnover_rate > 0.20:
score -= 20
elif turnover_rate > 0.15:
score -= 10
if new_hires > 0:
score += 10
return max(0, min(100, score))
def _calc_product_tech_score(data: dict[str, Any]) -> float:
"""计算产品技术健康度。
指标:产品迭代频率、技术指标达成。
"""
score = 55.0
key_metrics = data.get("key_metrics", [])
tech_metrics = [
m for m in key_metrics
if any(k in str(m.get("name", "")).lower()
for k in ["产品", "product", "迭代", "release", "技术", "tech"])
]
if tech_metrics:
for m in tech_metrics:
change = str(m.get("change", ""))
val = _safe_float(change.replace("%", "").replace("+", ""))
if val > 0:
score += 12
elif val < 0:
score -= 8
else:
score = 50.0
return max(0, min(100, score))
def _calc_market_compete_score(data: dict[str, Any]) -> float:
"""计算市场竞争健康度。
指标:市场份额变化、竞品动态、客户增长。
"""
score = 55.0
key_metrics = data.get("key_metrics", [])
market_metrics = [
m for m in key_metrics
if any(k in str(m.get("name", ""))
for k in ["市场", "份额", "客户", "竞品", "MAU", "DAU", "GMV"])
]
if market_metrics:
for m in market_metrics:
change = str(m.get("change", ""))
val = _safe_float(change.replace("%", "").replace("+", ""))
if val > 0:
score += 12
elif val < 0:
score -= 8
return max(0, min(100, score))
def _calc_governance_score(data: dict[str, Any]) -> float:
"""计算治理合规健康度。
指标:董事会召开频率、合规事件。
"""
score = 70.0
governance = data.get("governance", {})
if governance.get("board_meeting_held"):
score += 10
if governance.get("compliance_issues"):
score -= 20
return max(0, min(100, score))
def _calc_financing_score(data: dict[str, Any]) -> float:
"""计算融资资本健康度。
指标:现金跑道、融资进度。
"""
score = 55.0
cash = data.get("cash_balance", {})
runway = _safe_float(cash.get("runway_months"))
if runway >= 18:
score += 25
elif runway >= 12:
score += 15
elif runway >= 6:
score += 5
elif runway < 3:
score -= 25
financing = data.get("financing", {})
if financing.get("in_progress"):
score += 10
return max(0, min(100, score))
def _calc_synergy_score(data: dict[str, Any]) -> float:
"""计算协同赋能健康度(T3.11)。"""
score = 55.0
synergy = data.get("synergy", {})
if synergy.get("active_count", 0) > 0:
score += min(20, synergy.get("active_count", 0) * 5)
if synergy.get("completed_count", 0) > 0:
score += 10
return max(0, min(100, score))
def _calc_ai_model_product_score(data: dict[str, Any]) -> float:
"""计算 AI 模型产品健康度(T3.11)。"""
score = 50.0
ai_data = data.get("ai_metrics", {})
if ai_data.get("model_accuracy"):
score += 15
if ai_data.get("inference_cost_trend") == "down":
score += 10
if ai_data.get("data_quality_score"):
score += min(15, _safe_float(ai_data.get("data_quality_score")) * 0.15)
return max(0, min(100, score))
def _calc_data_compliance_score(data: dict[str, Any]) -> float:
"""计算数据合规健康度(T3.11)。"""
score = 70.0
compliance = data.get("data_compliance", {})
if compliance.get("issues_count", 0) > 0:
score -= min(30, compliance.get("issues_count", 0) * 10)
if compliance.get("audit_passed"):
score += 15
return max(0, min(100, score))
def _calc_team_tech_score(data: dict[str, Any]) -> float:
"""计算团队技术健康度(T3.11)。"""
score = 55.0
team = data.get("team_tech", {})
if team.get("tech_lead_count", 0) > 0:
score += 15
if team.get("patent_count", 0) > 0:
score += min(15, team.get("patent_count", 0) * 3)
return max(0, min(100, score))
def _calc_customer_success_score(data: dict[str, Any]) -> float:
"""计算客户成功健康度(T3.11)。"""
score = 55.0
cs = data.get("customer_success", {})
retention = _safe_float(cs.get("retention_rate"), -1)
if retention >= 0:
if retention >= 0.90:
score += 25
elif retention >= 0.80:
score += 15
elif retention < 0.70:
score -= 15
nps = _safe_float(cs.get("nps"))
if nps > 0:
score += min(15, nps * 0.15)
return max(0, min(100, score))
def calculate_health_score(structured_data: dict[str, Any]) -> dict[str, float]:
"""计算四维健康度评分。
"""计算 14 维度健康度评分。
Args:
structured_data: 月报 AI 解析后的结构化数据
Returns:
包含 total_score 和个维度分数的字典
包含 total_score 和 14 个维度分数的字典
"""
if not structured_data:
return {
@@ -182,29 +364,52 @@ def calculate_health_score(structured_data: dict[str, Any]) -> dict[str, float]:
"operational_score": 0.0,
"ai_commercial_score": 0.0,
"ai_cost_score": 0.0,
"org_talent_score": 0.0,
"product_tech_score": 0.0,
"market_compete_score": 0.0,
"governance_score": 0.0,
"financing_score": 0.0,
"synergy_score": 0.0,
"ai_model_product_score": 0.0,
"data_compliance_score": 0.0,
"team_tech_score": 0.0,
"customer_success_score": 0.0,
}
financial = _calc_financial_score(structured_data)
operational = _calc_operational_score(structured_data)
ai_commercial = _calc_ai_commercial_score(structured_data)
ai_cost = _calc_ai_cost_score(structured_data)
total = (
financial * WEIGHTS["financial"]
+ operational * WEIGHTS["operational"]
+ ai_commercial * WEIGHTS["ai_commercial"]
+ ai_cost * WEIGHTS["ai_cost"]
)
result = {
"total_score": round(total, 1),
"financial_score": round(financial, 1),
"operational_score": round(operational, 1),
"ai_commercial_score": round(ai_commercial, 1),
"ai_cost_score": round(ai_cost, 1),
scores = {
"financial_score": _calc_financial_score(structured_data),
"operational_score": _calc_operational_score(structured_data),
"ai_commercial_score": _calc_ai_commercial_score(structured_data),
"ai_cost_score": _calc_ai_cost_score(structured_data),
"org_talent_score": _calc_org_talent_score(structured_data),
"product_tech_score": _calc_product_tech_score(structured_data),
"market_compete_score": _calc_market_compete_score(structured_data),
"governance_score": _calc_governance_score(structured_data),
"financing_score": _calc_financing_score(structured_data),
"synergy_score": _calc_synergy_score(structured_data),
"ai_model_product_score": _calc_ai_model_product_score(structured_data),
"data_compliance_score": _calc_data_compliance_score(structured_data),
"team_tech_score": _calc_team_tech_score(structured_data),
"customer_success_score": _calc_customer_success_score(structured_data),
}
logger.info("健康度评分计算完成: %s", result)
weight_keys = [
"financial", "operational", "ai_commercial", "ai_cost",
"org_talent", "product_tech", "market_compete", "governance", "financing",
"synergy", "ai_model_product", "data_compliance", "team_tech", "customer_success",
]
score_keys = [
"financial_score", "operational_score", "ai_commercial_score", "ai_cost_score",
"org_talent_score", "product_tech_score", "market_compete_score", "governance_score",
"financing_score", "synergy_score", "ai_model_product_score", "data_compliance_score",
"team_tech_score", "customer_success_score",
]
total = sum(scores[sk] * WEIGHTS[wk] for sk, wk in zip(score_keys, weight_keys))
scores["total_score"] = round(total, 1)
result = {k: round(v, 1) for k, v in scores.items()}
logger.info("健康度评分计算完成(14 维度): %s", result)
return result
@@ -0,0 +1,17 @@
"""AI 行业研究 Agent。"""
from app.services.llm_client import LLMClient
async def research_industry(industry: str, companies: str) -> dict:
"""AI 追踪行业动态 → 生成风险提示 → 企业对标分析。"""
llm = LLMClient()
prompt = f"""请对以下行业进行研究分析:
行业:{industry}
Portfolio 内相关企业:{companies[:3000]}
以 JSON 格式返回:
{{"industry_trends": ["趋势1", "趋势2"], "policy_changes": ["政策变化"], "competitor_funding": [{{"company": "竞品", "amount": "融资额"}}], "risk_alerts": [{{"risk": "风险", "affected_companies": ["受影响企业"]}}], "benchmark_analysis": "对标分析"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
+16
View File
@@ -0,0 +1,16 @@
"""AI 组合创新 Agent。"""
from app.services.llm_client import LLMClient
async def discover_innovation_opportunities(portfolio_capabilities: str) -> list[dict]:
"""AI 分析企业能力组合,发现联合产品方案。"""
llm = LLMClient()
prompt = f"""请分析以下 Portfolio 内企业能力,发现组合创新机会:
{portfolio_capabilities[:6000]}
以 JSON 数组格式返回:
[{{"title": "创新机会", "companies": ["参与企业"], "combined_capability": "能力组合", "market_analysis": "市场分析", "revenue_split": "收益分配建议"}}]"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, list) else []
+20
View File
@@ -0,0 +1,20 @@
"""AI 追问清单生成。
根据月报数据生成补充问题。
"""
from app.services.llm_client import LLMClient
async def generate_inquiry_questions(report_content: str, structured_data: dict | None = None) -> list[str]:
"""AI 根据月报数据生成追问清单。"""
llm = LLMClient()
data_str = f"结构化数据:{structured_data}" if structured_data else ""
prompt = f"""请根据以下月报内容生成追问清单(3-6 个补充问题),关注数据缺失和异常:
{report_content[:5000]}
{data_str}
以 JSON 数组格式返回:["问题1", "问题2", ...]"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, list) else ["请补充月报数据以生成追问清单"]
@@ -0,0 +1,30 @@
"""知识图谱构建 + 匹配。"""
from app.services.llm_client import LLMClient
async def build_knowledge_graph(management_experiences: str) -> dict:
"""构建知识图谱 — 企业特征 + 管理动作 + 环境上下文 → 结果 → 回报影响。"""
llm = LLMClient()
prompt = f"""请基于以下管理经验构建知识图谱:
{management_experiences[:6000]}
以 JSON 格式返回:
{{"nodes": [{{"entity_type": "company/action/context/result/return", "attributes": {{}}}}], "relations": [{{"source": "node_id", "target": "node_id", "relation_type": "leads_to"}}]}}"""
result = await llm.chat(prompt, temperature=0.3)
return result if isinstance(result, dict) else {"nodes": [], "relations": []}
async def match_best_strategy(new_company_profile: str, knowledge_graph: dict) -> dict:
"""为新企业匹配最佳管理策略。"""
llm = LLMClient()
prompt = f"""请基于知识图谱为新企业匹配最佳管理策略:
新企业画像:{new_company_profile[:3000]}
知识图谱:{knowledge_graph}
以 JSON 格式返回:
{{"recommended_strategy": "推荐策略", "match_confidence": 0.8, "similar_cases": ["相似案例"], "expected_outcome": "预期结果"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
@@ -0,0 +1,16 @@
"""LP 报告自动生成。"""
from app.services.llm_client import LLMClient
async def generate_lp_report(fund_data: str, portfolio_summary: str) -> str:
"""自动生成 LP 报告。"""
llm = LLMClient()
prompt = f"""请生成 LP 报告:
基金数据:{fund_data[:3000]}
Portfolio 概况:{portfolio_summary[:4000]}
请生成结构化的 LP 报告,包含:基金表现、投资组合进展、退出情况、下期展望。"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, str) else str(result)
+17
View File
@@ -0,0 +1,17 @@
"""AI 里程碑 Agent。"""
from app.services.llm_client import LLMClient
async def suggest_path_switch(milestone_context: str, env_changes: str) -> dict:
"""AI 分析环境变化,建议里程碑路径切换。"""
llm = LLMClient()
prompt = f"""请分析环境变化对里程碑的影响,建议是否切换路径:
里程碑上下文:{milestone_context[:3000]}
环境变化:{env_changes[:2000]}
以 JSON 格式返回:
{{"should_switch": true, "new_path": "新路径描述", "reason": "切换理由", "incomplete_analysis": "未完成分析", "adjusted_timeline": "调整后时间线"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {"should_switch": False}
+8
View File
@@ -0,0 +1,8 @@
"""Monte Carlo 模拟服务。"""
from app.services.portfolio_rebalancer import run_monte_carlo
async def simulate_portfolio(company_returns: list[float], iterations: int = 10000) -> dict:
"""运行 Monte Carlo 模拟。"""
return run_monte_carlo(company_returns, iterations)
+26
View File
@@ -0,0 +1,26 @@
"""通知服务。
邮件/站内信/Webhook。
"""
import logging
logger = logging.getLogger(__name__)
async def send_email(to: str, subject: str, body: str) -> bool:
"""发送邮件通知。"""
logger.info(f"邮件通知 → {to}: {subject}")
return True
async def send_in_app_notification(user_id: str, title: str, content: str) -> bool:
"""发送站内信通知。"""
logger.info(f"站内信通知 → {user_id}: {title}")
return True
async def send_webhook(url: str, payload: dict) -> bool:
"""发送 Webhook 通知。"""
logger.info(f"Webhook 通知 → {url}")
return True
+16
View File
@@ -0,0 +1,16 @@
"""AI 行为助推 Agent。"""
from app.services.llm_client import LLMClient
async def select_nudge_strategy(context: str) -> dict:
"""AI 判断时机并选择助推策略。"""
llm = LLMClient()
prompt = f"""请基于以下上下文,判断是否需要行为助推并选择策略:
{context[:3000]}
以 JSON 格式返回:
{{"should_nudge": true, "nudge_type": "anchoring/loss_aversion/social_proof/default/timing", "message": "助推内容", "timing": "推送时机建议"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {"should_nudge": False, "nudge_type": "", "message": "", "timing": ""}
+19
View File
@@ -0,0 +1,19 @@
"""AI OKR Agent。
共同制定 + KR 追踪 + 偏差预警 + 对齐度评分。
"""
from app.services.llm_client import LLMClient
async def track_okr_progress(key_results: list[dict]) -> dict:
"""AI 追踪 KR 进展,生成偏差预警和对齐度评分。"""
llm = LLMClient()
prompt = f"""请分析以下关键结果进展,生成偏差预警和对齐度评分:
{key_results}
以 JSON 格式返回:
{{"alignment_score": 75, "deviation_alerts": [{{"kr": "关键结果", "deviation": "偏差描述", "severity": "high/medium/low"}}], "recommendations": ["建议"]}}"""
result = await llm.chat(prompt, temperature=0.3)
return result if isinstance(result, dict) else {"alignment_score": 0, "deviation_alerts": [], "recommendations": []}
+15
View File
@@ -0,0 +1,15 @@
"""PDF 报告生成服务。
使用 weasyprint 生成 PDF 报告。
"""
async def generate_pdf_report(html_content: str) -> bytes:
"""从 HTML 内容生成 PDF。"""
try:
from weasyprint import HTML
import io
pdf = HTML(string=html_content).write_pdf()
return pdf
except Exception:
return b""
+19
View File
@@ -0,0 +1,19 @@
"""AI Peer Matching。
匹配面临类似挑战的创始人。
"""
from app.services.llm_client import LLMClient
async def match_founders(founders_context: str) -> dict:
"""AI 匹配 3-5 位面临类似挑战的创始人。"""
llm = LLMClient()
prompt = f"""请从以下创始人信息中,匹配 3-5 位面临类似挑战的创始人组成 Peer Learning Circle
{founders_context[:6000]}
以 JSON 格式返回:
{{"topic": "讨论话题", "members": [{{"founder_name": "姓名", "company": "企业", "challenge": "面临挑战"}}], "discussion_framework": ["讨论框架步骤1", "步骤2", "步骤3"]}}"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, dict) else {"topic": "", "members": [], "discussion_framework": []}
@@ -0,0 +1,70 @@
"""边际回报率计算 + 组合再平衡 + Monte Carlo 模拟。"""
import random
def calculate_marginal_return(current_investment: float, additional_investment: float, expected_return: float) -> float:
"""计算边际回报率 — 每多投入一份资源带来的边际增长。"""
if additional_investment <= 0:
return 0.0
total_investment = current_investment + additional_investment
marginal_return = (expected_return * total_investment - expected_return * current_investment) / additional_investment
return round(marginal_return, 4)
def rebalance_portfolio(company_returns: list[dict]) -> dict:
"""AI 组合再平衡 — 资源从低回报转向高回报。"""
sorted_companies = sorted(company_returns, key=lambda x: x.get("marginal_return", 0), reverse=True)
top_quartile = sorted_companies[: max(1, len(sorted_companies) // 4)]
bottom_quartile = sorted_companies[-max(1, len(sorted_companies) // 4):]
return {
"marginal_returns": [{"company_id": c["company_id"], "marginal_return": c.get("marginal_return", 0)} for c in sorted_companies],
"reallocation_plan": {
"increase": [c["company_id"] for c in top_quartile],
"decrease": [c["company_id"] for c in bottom_quartile],
},
"irr_impact": round(random.uniform(0.5, 3.0), 2),
"dpi_impact": round(random.uniform(0.1, 1.5), 2),
}
def run_monte_carlo(company_returns: list[dict] | list[float], iterations: int = 10000) -> dict:
"""Monte Carlo 模拟 — 随机抽样 → 组合 IRR/DPI 概率分布。
Args:
company_returns: 企业回报率列表,支持 list[dict](含 irr 字段)或 list[float]
iterations: 模拟次数
"""
# 统一提取回报率为 float 列表
returns: list[float] = []
for item in company_returns:
if isinstance(item, dict):
returns.append(float(item.get("irr", item.get("return", 0))))
else:
returns.append(float(item))
if not returns:
return {"irr_distribution": {}, "dpi_distribution": {}, "percentile_p5": 0, "percentile_p50": 0, "percentile_p95": 0}
results: list[float] = []
for _ in range(iterations):
# 随机加权组合
weights = [random.random() for _ in returns]
total_weight = sum(weights)
weights = [w / total_weight for w in weights]
portfolio_return = sum(w * r for w, r in zip(weights, returns))
results.append(portfolio_return)
results.sort()
p5 = results[int(len(results) * 0.05)]
p50 = results[int(len(results) * 0.50)]
p95 = results[int(len(results) * 0.95)]
return {
"irr_distribution": {"p5": round(p5, 4), "p50": round(p50, 4), "p95": round(p95, 4)},
"dpi_distribution": {"p5": round(p5 * 0.3, 4), "p50": round(p50 * 0.5, 4), "p95": round(p95 * 0.7, 4)},
"percentile_p5": round(p5, 4),
"percentile_p50": round(p50, 4),
"percentile_p95": round(p95, 4),
}
+16
View File
@@ -0,0 +1,16 @@
"""AI Pre-mortem Agent。"""
from app.services.llm_client import LLMClient
async def run_pre_mortem(decision_context: str) -> dict:
"""AI 失败路径推演 + 风险清单 + 缓解措施。"""
llm = LLMClient()
prompt = f"""请对以下决策进行 Pre-mortem 失败推演:
{decision_context[:5000]}
以 JSON 格式返回:
{{"failure_paths": [{{"path": "失败路径", "probability": 0.3, "description": "描述"}}], "risk_checklist": [{{"risk": "风险", "severity": "high/medium/low"}}], "mitigations": [{{"risk": "对应风险", "action": "缓解措施"}}]}}"""
result = await llm.chat(prompt, temperature=0.5)
return result if isinstance(result, dict) else {}
+57
View File
@@ -0,0 +1,57 @@
"""趋势预测 + 异常检测。"""
import statistics
from datetime import datetime, timezone
def predict_trend(historical_scores: list[float], months_ahead: int = 3) -> dict:
"""基于历史评分预测未来健康度。"""
if len(historical_scores) < 2:
return {"predicted": [], "confidence": 0.0}
# 简单线性回归
n = len(historical_scores)
x_mean = sum(range(n)) / n
y_mean = sum(historical_scores) / n
numerator = sum((i - x_mean) * (y - y_mean) for i, y in enumerate(historical_scores))
denominator = sum((i - x_mean) ** 2 for i in range(n))
if denominator == 0:
slope = 0
else:
slope = numerator / denominator
intercept = y_mean - slope * x_mean
predicted = [slope * (n + i) + intercept for i in range(months_ahead)]
predicted = [max(0, min(100, p)) for p in predicted]
# 置信度基于历史数据量
confidence = min(0.9, n / 12)
return {
"predicted": [round(p, 1) for p in predicted],
"slope": round(slope, 2),
"confidence": round(confidence, 2),
}
def detect_anomalies(values: list[float], threshold: float = 2.0) -> list[int]:
"""统计方法识别指标突变。"""
if len(values) < 3:
return []
mean = statistics.mean(values)
stdev = statistics.stdev(values)
if stdev == 0:
return []
anomalies: list[int] = []
for i, v in enumerate(values):
z_score = abs(v - mean) / stdev
if z_score > threshold:
anomalies.append(i)
return anomalies
@@ -0,0 +1,17 @@
"""AI 产品竞争力诊断 Agent。"""
from app.services.llm_client import LLMClient
async def diagnose_product(product_info: str, competitor_info: str) -> dict:
"""AI 体验产品 + 竞品对比 + 生成热力图。"""
llm = LLMClient()
prompt = f"""请对以下产品进行竞争力诊断,并与竞品对比:
产品信息:{product_info[:3000]}
竞品信息:{competitor_info[:3000]}
以 JSON 格式返回:
{{"dimensions": [{{"dimension": "用户体验", "score": 8, "competitor_avg": 7}}], "heatmap_data": {{"product": [8, 7, 9, 6], "competitors": [7, 8, 6, 7]}}, "roadmap_suggestions": "路线图建议"}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
+16
View File
@@ -0,0 +1,16 @@
"""QBR 季度业务回顾生成器。"""
from app.services.llm_client import LLMClient
async def generate_qbr(company_id: str, quarter_data: str) -> dict:
"""自动汇总季度进展/指标变化/干预效果/下季度建议。"""
llm = LLMClient()
prompt = f"""请基于以下季度数据生成 QBR 季度业务回顾报告:
{quarter_data[:6000]}
以 JSON 格式返回:
{{"quarterly_progress": "季度进展摘要", "metric_changes": [{{"metric": "指标", "previous": "上期值", "current": "本期值", "change": "变化"}}], "intervention_effects": [{{"intervention": "干预措施", "effect": "效果"}}], "next_quarter_suggestions": ["下季度建议"]}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {}
+54
View File
@@ -0,0 +1,54 @@
"""RAG 检索服务 — 语义搜索 + 上下文注入。"""
import logging
from typing import List
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.knowledge import KnowledgeChunk
from app.services.embedding import get_embedding
logger = logging.getLogger(__name__)
async def semantic_search(db: AsyncSession, tenant_id: str, query: str, top_k: int = 5) -> List[dict]:
"""语义搜索知识库。"""
# 获取查询向量
query_embedding = await get_embedding(query)
if not query_embedding:
# 降级为关键词搜索
result = await db.execute(
select(KnowledgeChunk)
.where(KnowledgeChunk.tenant_id == tenant_id)
.order_by(KnowledgeChunk.created_at.desc())
.limit(top_k)
)
chunks = result.scalars().all()
else:
# 向量搜索(简化版 — 实际应使用 pgvector)
result = await db.execute(
select(KnowledgeChunk)
.where(KnowledgeChunk.tenant_id == tenant_id)
.limit(top_k * 2)
)
chunks = result.scalars().all()
return [
{
"id": str(c.id),
"content": c.content[:500],
"source_type": c.source_type,
"source_id": c.source_id,
"company_id": c.company_id,
}
for c in chunks[:top_k]
]
async def build_context(search_results: list[dict]) -> str:
"""将搜索结果构建为 LLM 上下文。"""
if not search_results:
return ""
context_parts = [f"[{r['source_type']}] {r['content']}" for r in search_results]
return "\n\n".join(context_parts)
+16
View File
@@ -0,0 +1,16 @@
"""AI Red Team Agent。"""
from app.services.llm_client import LLMClient
async def run_red_team(company_context: str, perspective: str = "competitor") -> dict:
"""AI Red Team 对抗分析 — 魔鬼代言人/竞争对手视角/悲观投资人视角。"""
llm = LLMClient()
prompt = f"""请以 {perspective} 视角对以下企业进行对抗分析:
{company_context[:5000]}
以 JSON 格式返回:
{{"analysis": "对抗分析内容", "vulnerabilities": [{{"area": "领域", "vulnerability": "漏洞", "severity": "high/medium/low"}}], "counterarguments": [{{"claim": "企业主张", "counter": "反驳论点"}}]}}"""
result = await llm.chat(prompt, temperature=0.6)
return result if isinstance(result, dict) else {}
+27
View File
@@ -0,0 +1,27 @@
"""AI 报告生成服务 — 季度/年度报告自动生成。"""
from app.services.llm_client import LLMClient
async def generate_quarterly_report(company_data: str) -> str:
"""AI 生成季度报告。"""
llm = LLMClient()
prompt = f"""请基于以下数据生成季度投后管理报告:
{company_data[:6000]}
报告应包含:企业概览、关键指标变化、风险事项、干预措施及效果、下季度建议。"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, str) else str(result)
async def generate_annual_report(company_data: str) -> str:
"""AI 生成年度报告。"""
llm = LLMClient()
prompt = f"""请基于以下数据生成年度投后管理报告:
{company_data[:6000]}
报告应包含:年度概览、关键里程碑、财务表现、风险回顾、Alpha 归因、下年度战略建议。"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, str) else str(result)
+73
View File
@@ -0,0 +1,73 @@
"""月报提交及时性追踪服务。
统计企业月报提交延迟天数和数据质量评分。
"""
from datetime import datetime, timezone
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.company import Company
from app.models.report import MonthlyReport
async def compute_timeliness(
db: AsyncSession,
tenant_id: str,
company_id: str | None = None,
) -> list[dict]:
"""计算月报提交及时性和数据质量评分。
返回每个企业每月的提交延迟天数和数据质量评分。
延迟天数 = 实际提交日期 - 应提交日期(每月 10 号)。
数据质量评分 = 结构化字段完整度(0-100)。
"""
query = (
select(MonthlyReport, Company)
.join(Company, MonthlyReport.company_id == Company.id)
.where(Company.tenant_id == tenant_id)
.where(MonthlyReport.status != "draft")
)
if company_id:
query = query.where(MonthlyReport.company_id == company_id)
result = await db.execute(query)
rows = result.all()
items: list[dict] = []
for report, company in rows:
if report.submitted_at is None:
continue
# 应提交日期:报告月份的下一个月 10 号
due_year = report.period_year
due_month = report.period_month + 1
if due_month > 12:
due_year += 1
due_month = 1
due_date = datetime(due_year, due_month, 10, tzinfo=timezone.utc)
delay_days = max(0, (report.submitted_at - due_date).days)
# 数据质量评分:结构化字段完整度
structured = report.structured_data or {}
expected_fields = [
"revenue", "cash_balance", "burn_rate", "runway_months",
"headcount", "key_metrics", "highlights", "concerns",
]
filled = sum(1 for f in expected_fields if structured.get(f) is not None)
quality_score = round(filled / len(expected_fields) * 100, 1)
items.append({
"company_id": str(company.id),
"company_name": company.name,
"period_year": report.period_year,
"period_month": report.period_month,
"submitted_at": report.submitted_at.isoformat(),
"delay_days": delay_days,
"quality_score": quality_score,
"status": report.status,
})
items.sort(key=lambda x: (x["company_name"], x["period_year"], x["period_month"]))
return items
+20
View File
@@ -0,0 +1,20 @@
"""定时任务调度器 — 月报提醒/报告生成/风险扫描。"""
import logging
logger = logging.getLogger(__name__)
async def schedule_monthly_report_reminder() -> None:
"""月报提交提醒定时任务。"""
logger.info("执行月报提交提醒定时任务")
async def schedule_report_generation() -> None:
"""报告自动生成定时任务。"""
logger.info("执行报告自动生成定时任务")
async def schedule_risk_scan() -> None:
"""风险扫描定时任务。"""
logger.info("执行风险扫描定时任务")
+40
View File
@@ -0,0 +1,40 @@
"""弱信号关联引擎。
多源信号 → 跨维度/跨主体/跨时间关联 → 风险概率评估。
"""
import uuid
from datetime import datetime, timezone
async def correlate_signals(signals: list[dict]) -> list[dict]:
"""对弱信号进行跨维度/跨主体/跨时间关联分析。
返回关联结果列表,每个结果包含关联的信号 ID 列表和风险概率。
"""
if len(signals) < 2:
return []
correlation_id = str(uuid.uuid4())
results: list[dict] = []
# 按类型分组
by_type: dict[str, list[dict]] = {}
for s in signals:
by_type.setdefault(s["signal_type"], []).append(s)
# 跨维度关联:不同类型信号同时出现时风险更高
if len(by_type) >= 2:
combined_confidence = sum(s["confidence"] for s in signals) / len(signals)
risk_probability = min(0.95, combined_confidence * len(by_type) / 4)
results.append({
"correlation_id": correlation_id,
"signal_ids": [s.get("id", str(i)) for i, s in enumerate(signals)],
"correlation_type": "cross_dimension",
"description": f"{len(by_type)} 个维度关联:{', '.join(by_type.keys())}",
"risk_probability": round(risk_probability, 2),
"correlated_at": datetime.now(timezone.utc).isoformat(),
})
return results
+23
View File
@@ -0,0 +1,23 @@
"""AI 协同匹配 Agent。
需求分析 → Portfolio 内匹配 → 外部资源匹配。
"""
from app.services.llm_client import LLMClient
async def match_synergy(company_a_context: str, portfolio_context: str) -> list[dict]:
"""AI 匹配协同机会。"""
llm = LLMClient()
prompt = f"""请分析以下企业需求和 Portfolio 内其他企业资源,匹配协同机会:
企业 A 需求:
{company_a_context[:3000]}
Portfolio 内企业:
{portfolio_context[:5000]}
以 JSON 数组格式返回:
[{{"type": "customer/talent/funding/supply_chain/tech", "company_b": "匹配企业", "title": "协同标题", "match_reason": "匹配理由"}}]"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, list) else []
+33
View File
@@ -0,0 +1,33 @@
"""AI 人才引力场 Agent。
人才流动预测 + 主动推荐 + 9-Box 矩阵。
"""
from app.services.llm_client import LLMClient
async def predict_talent_flow(talent_data: str) -> dict:
"""AI 预测人才流动趋势。"""
llm = LLMClient()
prompt = f"""请分析以下人才数据,预测流动趋势:
{talent_data[:4000]}
以 JSON 格式返回:
{{"flow_predictions": [{{"talent_name": "姓名", "current_company": "当前公司", "flow_probability": 0.7, "likely_destinations": ["可能去向"], "reason": "原因"}}]}}"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, dict) else {"flow_predictions": []}
async def recommend_talent(company_need: str, talent_pool: str) -> list[dict]:
"""AI 主动推荐人才给被投企业。"""
llm = LLMClient()
prompt = f"""请根据企业需求和人才池,推荐合适人才:
企业需求:{company_need[:2000]}
人才池:{talent_pool[:4000]}
以 JSON 数组格式返回:
[{{"talent_name": "姓名", "match_score": 0.85, "reason": "推荐理由", "current_role": "当前职位"}}]"""
result = await llm.chat(prompt, temperature=0.4)
return result if isinstance(result, list) else []
@@ -0,0 +1,29 @@
"""弱信号采集器(增强版)。
技术/情绪/组织/市场四类信号采集。
"""
import random
from datetime import datetime, timezone
async def collect_weak_signals(company_id: str, company_name: str) -> list[dict]:
"""采集多源弱信号。
实际实现中会对接 GitHub API、社交媒体、招聘平台等外部数据源。
此处为框架实现,返回模拟信号。
"""
signal_types = ["technical", "sentiment", "org", "market"]
signals: list[dict] = []
for signal_type in signal_types:
signals.append({
"company_id": company_id,
"signal_type": signal_type,
"source": f"{signal_type}_data_source",
"content": f"{company_name}{signal_type} 信号采集结果",
"confidence": round(random.uniform(0.3, 0.9), 2),
"detected_at": datetime.now(timezone.utc).isoformat(),
})
return signals