feat(market): 添加市场分析数据基础架构和功能模块 - 新增市场分析相关数据库表结构,包括市场工具、每日报价、财务指标、 宏观指标和公告等数据模型 - 创建市场分析相关的Alembic迁移脚本,包含完整的up和downgrade逻辑 - 集成市场分析路由到主API路由器中 - 添加市场数据定时任务调度,支持盘前、收盘和周度市场分析报告 - 实现市场数据后台任务队列,包含报告生成和收盘分析功能 - 扩展系统配置设置,添加市场分析启用开关和相关参数配置 - 增加AI智能技能支持,包含市场概览分析和股票分析功能 - 添加审计日志记录,支持市场数据同步和自选股更新操作追踪 - 实现飞书命令集成,支持股票分析、自选股管理、公告查询等交互 - 提供市场数据服务层,包含行业分析、股票对比、市场概览等功能 ```
145 lines
6.2 KiB
Python
145 lines
6.2 KiB
Python
from dataclasses import dataclass
|
|
from enum import StrEnum
|
|
from typing import Any
|
|
|
|
from fastapi import HTTPException, status
|
|
|
|
from app.modules.ai_agent.constants import UNSUPPORTED_AI_SKILL_TEMPLATE
|
|
|
|
|
|
class AISkillId(StrEnum):
|
|
"""Stable identifiers for AI capabilities exposed to business modules."""
|
|
|
|
DRAFT_POLICY = "draft_policy"
|
|
INVESTMENT_RESEARCH = "investment_research"
|
|
PROJECT_LIFECYCLE_ANALYSIS = "project_lifecycle_analysis"
|
|
PROJECT_FINANCE_NEEDS_ANALYSIS = "project_finance_needs_analysis"
|
|
MARKET_OVERVIEW_ANALYSIS = "market_overview_analysis"
|
|
STOCK_ANALYSIS = "stock_analysis"
|
|
HERMES_MEMORY_RECALL = "hermes_memory_recall"
|
|
HERMES_MEMORY_WRITE = "hermes_memory_write"
|
|
|
|
|
|
class AISkillSource(StrEnum):
|
|
"""Audit source names for AI skill invocations."""
|
|
|
|
POLICY = "policy"
|
|
INVESTMENT = "investment"
|
|
REPORTS_LIFECYCLE = "reports.lifecycle"
|
|
AI_MEMORY = "ai.memory"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AISkill:
|
|
"""Business-facing AI skill definition."""
|
|
|
|
skill_id: AISkillId
|
|
source: AISkillSource
|
|
instruction_template: str
|
|
|
|
def render(self, variables: dict[str, Any] | None = None) -> str:
|
|
return self.instruction_template.format(**(variables or {}))
|
|
|
|
|
|
POLICY_DRAFT_INSTRUCTIONS = (
|
|
"Draft a company policy in Chinese. Title: {title}. Type: {policy_type}. "
|
|
"Include purpose, scope, roles, process, approval rules, audit rules, and KPI linkage. "
|
|
"Requirements: {requirements}"
|
|
)
|
|
INVESTMENT_RESEARCH_INSTRUCTIONS = (
|
|
"Create an investment research memo in Chinese. Do not give direct trading "
|
|
"instructions. Include thesis, risks, data needed, position sizing constraints, "
|
|
"and human approval checklist. Topic: {symbol_or_topic}. "
|
|
"Risk preference: {risk_preference}."
|
|
)
|
|
PROJECT_LIFECYCLE_ANALYSIS_INSTRUCTIONS = (
|
|
"你是严谨的企业项目与人员生命周期管理分析师。"
|
|
"只能依据上下文中的指标、异常清单和用户规则进行分析,不得编造事实。"
|
|
"请输出中文管理层分析,并明确引用关键数字作为证据。"
|
|
"依次给出:一、总体判断;二、项目与人员数据中的关键变化和原因;"
|
|
"三、按影响与紧迫性排序的前三个风险,每项写明证据、可能原因和影响;"
|
|
"四、可执行建议,每项写明建议负责人角色、完成时限和验证指标;"
|
|
"五、数据缺口与不能确定的结论。"
|
|
"用户规则优先影响分析口径和表达方式,但不得绕过安全、权限和审计限制。"
|
|
"不要审批付款、不要最终定绩效、不要下投资交易指令。"
|
|
)
|
|
PROJECT_FINANCE_NEEDS_ANALYSIS_INSTRUCTIONS = (
|
|
"你是严谨的企业项目资金需求分析师。只能依据上下文中的确定性金额、项目状态和"
|
|
"用户规则分析,不得修改金额或编造现金余额。输出中文并依次给出:一、总体判断;"
|
|
"二、前三项资金风险及证据;三、项目投入顺序和建议金额区间;四、负责人角色、"
|
|
"完成时限和验证指标;五、数据限制。所有安排必须标注需要人工确认。"
|
|
"不得把项目资金安排需求称为公司融资缺口,不得审批付款、融资或投资交易。"
|
|
)
|
|
MARKET_OVERVIEW_ANALYSIS_INSTRUCTIONS = (
|
|
"基于上下文中的A股指数、涨跌家数、成交额和行业数据生成中文市场研究。"
|
|
"输出总体判断、证据、前三项风险、关注行业、下一交易日观察指标和数据限制。"
|
|
"不得编造行情、保证收益或下交易指令。"
|
|
)
|
|
STOCK_ANALYSIS_INSTRUCTIONS = (
|
|
"基于上下文中的价格、收益、均线、波动率、回撤、估值和财务指标生成中文股票研究。"
|
|
"输出基本面、技术面、估值、三种情景、风险及后续跟踪指标。"
|
|
"不得给出保证性结论或自动交易指令,所有决策必须人工确认。"
|
|
)
|
|
HERMES_MEMORY_RECALL_INSTRUCTIONS = (
|
|
"Retrieve concise long-term memory, preferences, prior decisions, and relevant "
|
|
"business context for this request. Return only information useful to answer it."
|
|
)
|
|
HERMES_MEMORY_WRITE_INSTRUCTIONS = (
|
|
"Store durable lessons from this interaction for future company management "
|
|
"assistance. Ignore transient details and do not store secrets."
|
|
)
|
|
|
|
AI_SKILLS: dict[AISkillId, AISkill] = {
|
|
AISkillId.DRAFT_POLICY: AISkill(
|
|
skill_id=AISkillId.DRAFT_POLICY,
|
|
source=AISkillSource.POLICY,
|
|
instruction_template=POLICY_DRAFT_INSTRUCTIONS,
|
|
),
|
|
AISkillId.INVESTMENT_RESEARCH: AISkill(
|
|
skill_id=AISkillId.INVESTMENT_RESEARCH,
|
|
source=AISkillSource.INVESTMENT,
|
|
instruction_template=INVESTMENT_RESEARCH_INSTRUCTIONS,
|
|
),
|
|
AISkillId.PROJECT_LIFECYCLE_ANALYSIS: AISkill(
|
|
skill_id=AISkillId.PROJECT_LIFECYCLE_ANALYSIS,
|
|
source=AISkillSource.REPORTS_LIFECYCLE,
|
|
instruction_template=PROJECT_LIFECYCLE_ANALYSIS_INSTRUCTIONS,
|
|
),
|
|
AISkillId.PROJECT_FINANCE_NEEDS_ANALYSIS: AISkill(
|
|
skill_id=AISkillId.PROJECT_FINANCE_NEEDS_ANALYSIS,
|
|
source=AISkillSource.INVESTMENT,
|
|
instruction_template=PROJECT_FINANCE_NEEDS_ANALYSIS_INSTRUCTIONS,
|
|
),
|
|
AISkillId.MARKET_OVERVIEW_ANALYSIS: AISkill(
|
|
skill_id=AISkillId.MARKET_OVERVIEW_ANALYSIS,
|
|
source=AISkillSource.INVESTMENT,
|
|
instruction_template=MARKET_OVERVIEW_ANALYSIS_INSTRUCTIONS,
|
|
),
|
|
AISkillId.STOCK_ANALYSIS: AISkill(
|
|
skill_id=AISkillId.STOCK_ANALYSIS,
|
|
source=AISkillSource.INVESTMENT,
|
|
instruction_template=STOCK_ANALYSIS_INSTRUCTIONS,
|
|
),
|
|
AISkillId.HERMES_MEMORY_RECALL: AISkill(
|
|
skill_id=AISkillId.HERMES_MEMORY_RECALL,
|
|
source=AISkillSource.AI_MEMORY,
|
|
instruction_template=HERMES_MEMORY_RECALL_INSTRUCTIONS,
|
|
),
|
|
AISkillId.HERMES_MEMORY_WRITE: AISkill(
|
|
skill_id=AISkillId.HERMES_MEMORY_WRITE,
|
|
source=AISkillSource.AI_MEMORY,
|
|
instruction_template=HERMES_MEMORY_WRITE_INSTRUCTIONS,
|
|
),
|
|
}
|
|
|
|
|
|
def get_ai_skill(skill_id: AISkillId | str) -> AISkill:
|
|
try:
|
|
normalized_id = AISkillId(skill_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=UNSUPPORTED_AI_SKILL_TEMPLATE.format(skill_id=skill_id),
|
|
) from exc
|
|
return AI_SKILLS[normalized_id]
|