From 03b53aed71e5bf28b5092d56812088302d7d5894 Mon Sep 17 00:00:00 2001 From: "Lv, Qi" Date: Fri, 28 Nov 2025 20:11:17 +0800 Subject: [PATCH] feat: Refactor Analysis Context Mechanism and Generic Worker - Implemented Unified Context Mechanism (Task 20251127): - Decoupled intent (Module) from resolution (Orchestrator). - Added ContextResolver for resolving input bindings (Manual Glob/Auto LLM). - Added IOBinder for managing physical paths. - Updated GenerateReportCommand to support explicit input bindings and output paths. - Refactored Report Worker to Generic Execution (Task 20251128): - Removed hardcoded financial DTOs and specific formatting logic. - Implemented Generic YAML-based context assembly for better LLM readability. - Added detailed execution tracing (Sidecar logs). - Fixed input data collision bug by using full paths as context keys. - Updated Tushare Provider to support dynamic output paths. - Updated Common Contracts with new configuration models. --- config/analysis-config.json | 130 ++- crates/workflow-context/src/types.rs | 4 + crates/workflow-context/src/vgcs.rs | 28 +- .../20251127_refactor_context_mechanism.md | 160 ++++ .../20251128_refactor_worker_generic.md | 71 ++ frontend/src/api/schema.gen.ts | 169 ++-- frontend/src/pages/Dashboard.tsx | 12 +- frontend/src/pages/config/TemplateTab.tsx | 341 ++++++-- openapi.json | 170 +++- services/api-gateway/src/api.rs | 5 + .../common-contracts/src/config_models.rs | 11 +- services/common-contracts/src/configs.rs | 46 ++ services/common-contracts/src/lib.rs | 1 + services/common-contracts/src/messages.rs | 11 +- services/data-persistence-service/Dockerfile | 1 + .../data-persistence-service/src/seeding.rs | 30 +- services/report-generator-service/Cargo.lock | 126 ++- services/report-generator-service/Cargo.toml | 2 + .../report-generator-service/src/config.rs | 5 + .../report-generator-service/src/formatter.rs | 72 -- services/report-generator-service/src/main.rs | 1 - .../src/message_consumer.rs | 27 +- .../report-generator-service/src/worker.rs | 773 +++++------------- .../src/generic_worker.rs | 6 +- .../workflow-orchestrator-service/Cargo.lock | 1 + .../workflow-orchestrator-service/Cargo.toml | 1 + .../src/context_resolver.rs | 279 +++++++ .../src/io_binder.rs | 43 + .../workflow-orchestrator-service/src/lib.rs | 3 + .../src/llm_client.rs | 76 ++ .../src/workflow.rs | 131 ++- .../yfinance-provider-service/src/yfinance.rs | 30 +- 32 files changed, 1886 insertions(+), 880 deletions(-) create mode 100644 docs/tasks/completed/20251127_refactor_context_mechanism.md create mode 100644 docs/tasks/completed/20251128_refactor_worker_generic.md create mode 100644 services/common-contracts/src/configs.rs delete mode 100644 services/report-generator-service/src/formatter.rs create mode 100644 services/workflow-orchestrator-service/src/context_resolver.rs create mode 100644 services/workflow-orchestrator-service/src/io_binder.rs create mode 100644 services/workflow-orchestrator-service/src/llm_client.rs diff --git a/config/analysis-config.json b/config/analysis-config.json index ad7fd4b..3f50f6e 100644 --- a/config/analysis-config.json +++ b/config/analysis-config.json @@ -2,50 +2,118 @@ "analysis_modules": { "company_profile": { "name": "公司简介", - "model": "qwen-flash-2025-07-28", - "prompt_template": "您是一位专业的证券市场分析师。请为公司 {company_name} (股票代码: {ts_code}) 生成一份详细且专业的公司介绍。开头不要自我介绍,直接开始正文。正文用MarkDown输出,尽量说明信息来源,用斜体显示信息来源。在生成内容时,请严格遵循以下要求并采用清晰、结构化的格式:\n\n1. **公司概览**:\n * 简要介绍公司的性质、核心业务领域及其在行业中的定位。\n * 提炼并阐述公司的核心价值理念。\n\n2. **主营业务**:\n * 详细描述公司主要的**产品或服务**。\n * **重要提示**:如果能获取到公司最新的官方**年报**或**财务报告**,请从中提取各主要产品/服务线的**收入金额**和其占公司总收入的**百分比**。请**明确标注数据来源**(例如:\"数据来源于XX年年度报告\")。\n * **严格禁止**编造或估算任何财务数据。若无法找到公开、准确的财务数据,请**不要**在这一点中提及具体金额或比例,仅描述业务内容。\n\n3. **发展历程**:\n * 以时间线或关键事件的形式,概述公司自成立以来的主要**里程碑事件**、重大发展阶段、战略转型或重要成就。\n\n4. **核心团队**:\n * 介绍公司**主要管理层和核心技术团队成员**。\n * 对于每位核心成员,提供其**职务、主要工作履历、教育背景**。\n * 如果公开可查,可补充其**出生年份**。\n\n5. **供应链**:\n * 描述公司的**主要原材料、部件或服务来源**。\n * 如果公开信息中包含,请列出**主要供应商名称**,并**明确其在总采购金额中的大致占比**。若无此数据,则仅描述采购模式。\n\n6. **主要客户及销售模式**:\n * 阐明公司的**销售模式**(例如:直销、经销、线上销售、代理等)。\n * 列出公司的**主要客户群体**或**代表性大客户**。\n * 如果公开信息中包含,请标明**主要客户(或前五大客户)的销售额占公司总销售额的比例**。若无此数据,则仅描述客户类型。\n\n7. **未来展望**:\n * 基于公司**公开的官方声明、管理层访谈或战略规划**,总结公司未来的发展方向、战略目标、重点项目或市场预期。请确保此部分内容有可靠的信息来源支持。" + "dependencies": [], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "您是一位专业的证券市场分析师。请为公司 {company_name} (股票代码: {ts_code}) 生成一份详细且专业的公司介绍。开头不要自我介绍,直接开始正文。正文用MarkDown输出,尽量说明信息来源,用斜体显示信息来源。在生成内容时,请严格遵循以下要求并采用清晰、结构化的格式:\n\n1. **公司概览**:\n * 简要介绍公司的性质、核心业务领域及其在行业中的定位。\n * 提炼并阐述公司的核心价值理念。\n\n2. **主营业务**:\n * 详细描述公司主要的**产品或服务**。\n * **重要提示**:如果能获取到公司最新的官方**年报**或**财务报告**,请从中提取各主要产品/服务线的**收入金额**和其占公司总收入的**百分比**。请**明确标注数据来源**(例如:\"数据来源于XX年年度报告\")。\n * **严格禁止**编造或估算任何财务数据。若无法找到公开、准确的财务数据,请**不要**在这一点中提及具体金额或比例,仅描述业务内容。\n\n3. **发展历程**:\n * 以时间线或关键事件的形式,概述公司自成立以来的主要**里程碑事件**、重大发展阶段、战略转型或重要成就。\n\n4. **核心团队**:\n * 介绍公司**主要管理层和核心技术团队成员**。\n * 对于每位核心成员,提供其**职务、主要工作履历、教育背景**。\n * 如果公开可查,可补充其**出生年份**。\n\n5. **供应链**:\n * 描述公司的**主要原材料、部件或服务来源**。\n * 如果公开信息中包含,请列出**主要供应商名称**,并**明确其在总采购金额中的大致占比**。若无此数据,则仅描述采购模式。\n\n6. **主要客户及销售模式**:\n * 阐明公司的**销售模式**(例如:直销、经销、线上销售、代理等)。\n * 列出公司的**主要客户群体**或**代表性大客户**。\n * 如果公开信息中包含,请标明**主要客户(或前五大客户)的销售额占公司总销售额的比例**。若无此数据,则仅描述客户类型。\n\n7. **未来展望**:\n * 基于公司**公开的官方声明、管理层访谈或战略规划**,总结公司未来的发展方向、战略目标、重点项目或市场预期。请确保此部分内容有可靠的信息来源支持。", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "fundamental_analysis": { "name": "基本面分析", - "model": "qwen-flash-2025-07-28", - "prompt_template": "# 角色\n你是一位专注于长期价值投资的顶级证券分析师,擅长从基本面出发,对公司进行深入、全面的分析。你的分析报告以客观、严谨、逻辑清晰、数据详实著称。\n# 任务\n为公司 {company_name} (股票代码: {ts_code}) 生成一份全面、专业、结构化的投资分析报告。\n# 输出要求\n直接开始:不要进行任何自我介绍或客套话,直接输出报告正文。\nMarkdown格式:使用清晰的多级Markdown标题(如 ## 和 ###)来组织报告结构。\n专业口吻:保持客观、中立、分析性的专业语调。\n信息缺失处理:如果某些信息在公开渠道无法获取,请明确指出“相关信息未公开披露”或类似说明。\n\n# 报告核心结构与分析要点\n一、 公司基本面分析 (Fundamental Analysis)\n1.1 护城河与核心竞争力\n公司通过何种独有优势(如品牌、技术、成本、网络效应、牌照等)获取超额利润?\n该护城河是在增强、维持还是在削弱?请提供论据。\n1.2 管理层与公司治理\n管理能力:管理层过往的战略决策和执行能力如何?是否有卓越的业界声誉?\n股东回报:管理层及大股东是否珍惜股权价值?(分析历史上的增持/减持行为、分红派息政策、是否存在损害小股东利益的体外资产等)\n激励与目标:公司的经营目标是长期主义还是短期化?管理层的激励机制(如股权激励、考核指标)是否与长期战略目标一致?\n1.3 企业文化与财务政策\n公司是否有独特且可观察到的企业文化?(例如:创新文化、成本控制文化等)\n公司的财务政策(如资本结构、现金流管理、投资策略)与同行业公司相比有何显著特点?是激进还是保守?\n1.4 发展历程与战略规划\n梳理公司发展史上的关键事件、重大业务转型或里程碑。\n公司是否有清晰的长期战略目标(未来5-10年)?计划成为一家什么样的企业?\n二、 业务与市场分析 (Business & Market Analysis)\n2.1 产品与客户价值\n公司为客户提供什么核心产品/服务?其核心价值点是什么?客户为何选择公司的产品而非竞争对手的?\n产品的更新迭代是颠覆性的还是渐进积累型的?分析产品历年的产量、价格及销量变化,并探讨其背后的驱动因素。\n2.2 市场需求与景气度\n客户所处行业的需求是趋势性的高增长,还是周期性波动?或是两者结合?当前处于何种阶段?\n目标客户群体的经营状况和现金流是否健康?\n2.3 议价能力与客户关系\n公司对下游客户的议价能力强弱如何?(结合应收账款周转天数、账龄结构、毛利率等数据进行佐证)\n公司与核心客户的关系是否稳定?客户对公司的评价如何(例如:客户忠诚度、满意度)?\n三、 竞争格局分析 (Competitive Landscape Analysis)\n3.1 竞争对手画像\n列出公司的主要竞争对手,并分析各自的优势与劣势。\n公司的竞争对手是在增多还是减少?行业进入壁垒是在增高还是降低?\n是否存在潜在的跨界竞争者?\n四、 供应链与外部关系 (Supply Chain & External Relations)\n4.1 供应链议价能力\n公司对上游供应商的议价能力如何?(结合应付账款周转天数、采购成本控制等数据进行佐证)\n核心供应商的经营是否稳定?供应链是否存在集中度过高的风险?\n4.2 金融机构关系与融资需求\n公司与金融机构的关系如何?融资渠道是否通畅?\n公司未来的发展是否依赖于大规模的债务或股权融资?\n五、 监管环境与政策风险 (Regulatory Environment & Policy Risks)\n公司所处行业是否存在重要的监管部门?主要的监管政策有哪些?\n监管政策是否稳定?未来可能发生哪些重大变化?对公司有何潜在影响?\n公司是否具备影响或适应监管政策变化的能力?\n\n# 附录:财务数据\n以下是该公司的财务数据摘要,请在分析中充分引用这些数据:\n{{ financial_data }}" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "# 角色\n你是一位专注于长期价值投资的顶级证券分析师,擅长从基本面出发,对公司进行深入、全面的分析。你的分析报告以客观、严谨、逻辑清晰、数据详实著称。\n# 任务\n为公司 {company_name} (股票代码: {ts_code}) 生成一份全面、专业、结构化的投资分析报告。\n# 输出要求\n直接开始:不要进行任何自我介绍或客套话,直接输出报告正文。\nMarkdown格式:使用清晰的多级Markdown标题(如 ## 和 ###)来组织报告结构。\n专业口吻:保持客观、中立、分析性的专业语调。\n信息缺失处理:如果某些信息在公开渠道无法获取,请明确指出“相关信息未公开披露”或类似说明。\n\n# 报告核心结构与分析要点\n一、 公司基本面分析 (Fundamental Analysis)\n1.1 护城河与核心竞争力\n公司通过何种独有优势(如品牌、技术、成本、网络效应、牌照等)获取超额利润?\n该护城河是在增强、维持还是在削弱?请提供论据。\n1.2 管理层与公司治理\n管理能力:管理层过往的战略决策和执行能力如何?是否有卓越的业界声誉?\n股东回报:管理层及大股东是否珍惜股权价值?(分析历史上的增持/减持行为、分红派息政策、是否存在损害小股东利益的体外资产等)\n激励与目标:公司的经营目标是长期主义还是短期化?管理层的激励机制(如股权激励、考核指标)是否与长期战略目标一致?\n1.3 企业文化与财务政策\n公司是否有独特且可观察到的企业文化?(例如:创新文化、成本控制文化等)\n公司的财务政策(如资本结构、现金流管理、投资策略)与同行业公司相比有何显著特点?是激进还是保守?\n1.4 发展历程与战略规划\n梳理公司发展史上的关键事件、重大业务转型或里程碑。\n公司是否有清晰的长期战略目标(未来5-10年)?计划成为一家什么样的企业?\n二、 业务与市场分析 (Business & Market Analysis)\n2.1 产品与客户价值\n公司为客户提供什么核心产品/服务?其核心价值点是什么?客户为何选择公司的产品而非竞争对手的?\n产品的更新迭代是颠覆性的还是渐进积累型的?分析产品历年的产量、价格及销量变化,并探讨其背后的驱动因素。\n2.2 市场需求与景气度\n客户所处行业的需求是趋势性的高增长,还是周期性波动?或是两者结合?当前处于何种阶段?\n目标客户群体的经营状况和现金流是否健康?\n2.3 议价能力与客户关系\n公司对下游客户的议价能力强弱如何?(结合应收账款周转天数、账龄结构、毛利率等数据进行佐证)\n公司与核心客户的关系是否稳定?客户对公司的评价如何(例如:客户忠诚度、满意度)?\n三、 竞争格局分析 (Competitive Landscape Analysis)\n3.1 竞争对手画像\n列出公司的主要竞争对手,并分析各自的优势与劣势。\n公司的竞争对手是在增多还是减少?行业进入壁垒是在增高还是降低?\n是否存在潜在的跨界竞争者?\n四、 供应链与外部关系 (Supply Chain & External Relations)\n4.1 供应链议价能力\n公司对上游供应商的议价能力如何?(结合应付账款周转天数、采购成本控制等数据进行佐证)\n核心供应商的经营是否稳定?供应链是否存在集中度过高的风险?\n4.2 金融机构关系与融资需求\n公司与金融机构的关系如何?融资渠道是否通畅?\n公司未来的发展是否依赖于大规模的债务或股权融资?\n五、 监管环境与政策风险 (Regulatory Environment & Policy Risks)\n公司所处行业是否存在重要的监管部门?主要的监管政策有哪些?\n监管政策是否稳定?未来可能发生哪些重大变化?对公司有何潜在影响?\n公司是否具备影响或适应监管政策变化的能力?\n\n# 附录:财务数据\n以下是该公司的财务数据摘要,请在分析中充分引用这些数据:\n{{ financial_data }}", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "bull_case": { "name": "看涨分析", - "model": "qwen-flash-2025-07-28", - "dependencies": [], - "prompt_template": "#### # 角色\n你是一位顶级的成长股投资分析师,拥有敏锐的洞察力,尤其擅长**挖掘市场尚未充分认识到的潜在价值**和**判断长期行业趋势**。你的任务是为目标公司构建一个令人信服的、由证据支持的看涨论述(Bull Case)。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份深入的看涨分析报告。报告的核心是论证该公司拥有被市场低估的隐藏资产、持续加深的护城河,并且其所处行业将迎来至少3年以上的景气周期。\n\n#### # 输出要求\n1. **直奔主题**:直接开始分析,无需引言。\n2. **Markdown格式**:使用清晰的标题结构来组织你的论点。\n3. **数据与来源**:所有关键论点都必须有数据、事实或合理的逻辑推演作为支撑。请用*斜体*注明信息来源(如:*来源:公司2023年投资者交流纪要* 或 *来源:中信证券行业研报*)。\n4. **聚焦看涨逻辑**:报告内容应完全围绕支撑看涨观点的论据展开,暂时忽略风险和负面因素。\n5. **前瞻性视角**:分析应侧重于未来3-5年的发展潜力,而不仅仅是回顾历史。\n6. **信息缺失处理**:如果某些推论需要的数据无法公开获取,可以基于现有信息进行合理的逻辑推测,并明确标注“(此为基于...的推测)”。\n\n---\n\n### # 看涨核心论证框架\n\n## 一、 深度挖掘:公司的隐藏资产与未被市场充分定价的价值\n\n### 1.1 资产负债表之外的价值 (Off-Balance Sheet Value)\n- **无形资产**:公司是否拥有未被充分计价的核心技术专利、软件著作权、特许经营权或强大的品牌价值?请量化或举例说明其潜在商业价值。\n- **数据资产**:公司是否积累了具有巨大潜在价值的用户或行业数据?这些数据未来可能的变现途径是什么?\n\n### 1.2 低估的实体或股权资产 (Undervalued Physical or Equity Assets)\n- **土地/物业重估**:公司持有的土地、房产等固定资产,其当前市场公允价值是否远超账面价值?\n- **子公司/投资价值**:公司旗下是否有快速增长但未被市场充分关注的子公司或有价值的长期股权投资?分析其独立估值的潜力。\n\n### 1.3 运营中的“隐形冠军” (Operational \"Hidden Champions\")\n- 公司是否存在独特的、难以复制的生产工艺、供应链管理能力或运营效率优势,而这些优势尚未完全体现在当前的利润率中?\n\n## 二、 护城河的加深:竞争优势的动态强化分析\n\n### 2.1 护城河的动态演变:是静态还是在拓宽?\n- 论证公司的核心护城河(例如:网络效应、转换成本、成本优势、技术壁垒)在未来几年将如何被**强化**而非削弱。请提供具体证据(如:研发投入的持续增长、客户续约率的提升、市场份额的扩大等)。\n\n### 2.2 技术与创新壁垒的领先优势\n- 公司的研发投入和创新产出,如何确保其在未来3-5年内保持对竞争对手的技术代差或领先地位?\n- 是否有即将商业化的“杀手级”新产品或新技术?\n\n### 2.3 品牌与客户粘性的正反馈循环\n- 公司的品牌价值或客户关系如何形成一个正反馈循环(即:强品牌带来高议价能力 -> 高利润投入研发/营销 -> 品牌更强)?\n- 客户为何难以转向竞争对手?分析其高昂的转换成本。\n\n## 三、 长期景气度:行业未来3年以上的持续增长动力\n\n### 3.1 长期需求驱动力(Demand-Side Drivers)\n- 驱动行业增长的核心动力是短期的周期性复苏,还是长期的结构性变迁(如:技术革命、消费升级、国产替代、政策驱动)?请深入论证。\n- 行业的市场渗透率是否仍有巨大提升空间?分析未来市场规模(TAM)的扩张潜力。\n\n### 3.2 供给侧格局优化(Supply-Side Dynamics)\n- 行业供给侧是否出现集中度提升、落后产能出清的趋势?这是否意味着龙头企业的定价权和盈利能力将持续增强?\n- 行业的进入壁垒是否在显著提高(如:技术、资金、资质壁垒),从而限制新竞争者的涌入?\n\n### 3.3 关键催化剂(Key Catalysts)\n- 未来1-2年内,是否存在可以显著提升公司估值或盈利的潜在催化剂事件(如:新产品发布、重要政策落地、海外市场突破等)?\n\n# 附录:财务数据\n以下是该公司的财务数据摘要,请在分析中充分引用这些数据:\n{{ financial_data }}" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "#### # 角色\n你是一位顶级的成长股投资分析师,拥有敏锐的洞察力,尤其擅长**挖掘市场尚未充分认识到的潜在价值**和**判断长期行业趋势**。你的任务是为目标公司构建一个令人信服的、由证据支持的看涨论述(Bull Case)。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份深入的看涨分析报告。报告的核心是论证该公司拥有被市场低估的隐藏资产、持续加深的护城河,并且其所处行业将迎来至少3年以上的景气周期。\n\n#### # 输出要求\n1. **直奔主题**:直接开始分析,无需引言。\n2. **Markdown格式**:使用清晰的标题结构来组织你的论点。\n3. **数据与来源**:所有关键论点都必须有数据、事实或合理的逻辑推演作为支撑。请用*斜体*注明信息来源(如:*来源:公司2023年投资者交流纪要* 或 *来源:中信证券行业研报*)。\n4. **聚焦看涨逻辑**:报告内容应完全围绕支撑看涨观点的论据展开,暂时忽略风险和负面因素。\n5. **前瞻性视角**:分析应侧重于未来3-5年的发展潜力,而不仅仅是回顾历史。\n6. **信息缺失处理**:如果某些推论需要的数据无法公开获取,可以基于现有信息进行合理的逻辑推测,并明确标注“(此为基于...的推测)”。\n\n---\n\n### # 看涨核心论证框架\n\n## 一、 深度挖掘:公司的隐藏资产与未被市场充分定价的价值\n\n### 1.1 资产负债表之外的价值 (Off-Balance Sheet Value)\n- **无形资产**:公司是否拥有未被充分计价的核心技术专利、软件著作权、特许经营权或强大的品牌价值?请量化或举例说明其潜在商业价值。\n- **数据资产**:公司是否积累了具有巨大潜在价值的用户或行业数据?这些数据未来可能的变现途径是什么?\n\n### 1.2 低估的实体或股权资产 (Undervalued Physical or Equity Assets)\n- **土地/物业重估**:公司持有的土地、房产等固定资产,其当前市场公允价值是否远超账面价值?\n- **子公司/投资价值**:公司旗下是否有快速增长但未被市场充分关注的子公司或有价值的长期股权投资?分析其独立估值的潜力。\n\n### 1.3 运营中的“隐形冠军” (Operational \"Hidden Champions\")\n- 公司是否存在独特的、难以复制的生产工艺、供应链管理能力或运营效率优势,而这些优势尚未完全体现在当前的利润率中?\n\n## 二、 护城河的加深:竞争优势的动态强化分析\n\n### 2.1 护城河的动态演变:是静态还是在拓宽?\n- 论证公司的核心护城河(例如:网络效应、转换成本、成本优势、技术壁垒)在未来几年将如何被**强化**而非削弱。请提供具体证据(如:研发投入的持续增长、客户续约率的提升、市场份额的扩大等)。\n\n### 2.2 技术与创新壁垒的领先优势\n- 公司的研发投入和创新产出,如何确保其在未来3-5年内保持对竞争对手的技术代差或领先地位?\n- 是否有即将商业化的“杀手级”新产品或新技术?\n\n### 2.3 品牌与客户粘性的正反馈循环\n- 公司的品牌价值或客户关系如何形成一个正反馈循环(即:强品牌带来高议价能力 -> 高利润投入研发/营销 -> 品牌更强)?\n- 客户为何难以转向竞争对手?分析其高昂的转换成本。\n\n## 三、 长期景气度:行业未来3年以上的持续增长动力\n\n### 3.1 长期需求驱动力(Demand-Side Drivers)\n- 驱动行业增长的核心动力是短期的周期性复苏,还是长期的结构性变迁(如:技术革命、消费升级、国产替代、政策驱动)?请深入论证。\n- 行业的市场渗透率是否仍有巨大提升空间?分析未来市场规模(TAM)的扩张潜力。\n\n### 3.2 供给侧格局优化(Supply-Side Dynamics)\n- 行业供给侧是否出现集中度提升、落后产能出清的趋势?这是否意味着龙头企业的定价权和盈利能力将持续增强?\n- 行业的进入壁垒是否在显著提高(如:技术、资金、资质壁垒),从而限制新竞争者的涌入?\n\n### 3.3 关键催化剂(Key Catalysts)\n- 未来1-2年内,是否存在可以显著提升公司估值或盈利的潜在催化剂事件(如:新产品发布、重要政策落地、海外市场突破等)?\n\n# 附录:财务数据\n以下是该公司的财务数据摘要,请在分析中充分引用这些数据:\n{{ financial_data }}", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "bear_case": { "name": "看跌分析", - "model": "qwen-flash-2025-07-28", - "dependencies": [], - "prompt_template": "#### # 角色\n你是一位经验丰富的风险控制分析师和审慎的价值投资者,以“能看到别人看不到的风险”而闻名。你的核心任务是**进行压力测试**,识别出公司潜在的、可能导致价值毁灭的重大风险点,并评估其在最坏情况下的价值底线。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份审慎的看跌分析报告(Bear Case)。报告需要深入探讨可能侵蚀公司护城河的因素、被市场忽视的潜在风险、行业可能面临的逆风,并对公司的价值底线进行评估。\n\n#### # 输出要求\n1. **直奔主题**:直接开始风险分析,无需引言。\n2. **Markdown格式**:使用清晰的标题结构组织风险论点。\n3. **证据驱动**:所有风险点都必须基于事实、数据或严谨的逻辑推演。请用*斜体*注明信息来源(如:*来源:竞争对手2023年财报* 或 *来源:行业监管政策草案*)。\n4. **聚焦看跌逻辑**:报告应完全围绕看跌观点展开,旨在识别和放大潜在的负面因素。\n5. **底线思维**:分析的核心是评估“事情最坏能到什么程度”,并判断公司的安全边际。\n6. **信息缺失处理**:对于难以量化的风险(如管理层风险),进行定性分析和逻辑阐述。\n\n---\n\n### # 看跌核心论证框架\n\n## 一、 护城河的侵蚀:竞争优势的脆弱性分析 (Moat Erosion: Vulnerability of Competitive Advantages)\n\n### 1.1 现有护城河的潜在威胁\n- 公司的核心护城河(技术、品牌、成本等)是否面临被颠覆的风险?(例如:新技术的出现、竞争对手的模仿或价格战)\n- 客户的转换成本是否真的足够高?是否存在某些因素(如行业标准化)可能降低客户的转换壁垒?\n\n### 1.2 竞争格局的恶化\n- 是否有新的、强大的“跨界”竞争者进入市场?\n- 行业是否从“蓝海”变为“红海”?分析导致竞争加剧的因素(如:产能过剩、产品同质化)。\n- 竞争对手的哪些战略举动可能对公司构成致命打击?\n\n## 二、 隐藏的负债与风险:资产负债表之外的“地雷” (Hidden Liabilities & Risks: Off-Balance Sheet \"Mines\")\n\n### 2.1 潜在的财务风险\n- 公司是否存在大量的或有负债、对外担保或未入表的债务?\n- 公司的现金流健康状况是否脆弱?分析其经营现金流能否覆盖资本开支和债务利息,尤其是在收入下滑的情况下。\n- 应收账款或存货是否存在潜在的暴雷风险?(分析其账龄、周转率和减值计提的充分性)\n\n### 2.2 运营与管理风险\n- 公司是否对单一供应商、单一客户或单一市场存在过度依赖?\n- 公司是否存在“关键人物风险”?创始团队或核心技术人员的离开会对公司造成多大影响?\n- 公司的企业文化或治理结构是否存在可能导致重大决策失误的缺陷?\n\n## 三、 行业逆风与最坏情况分析 (Industry Headwinds & Worst-Case Scenario)\n\n### 3.1 行业天花板与需求逆转\n- 行业渗透率是否已接近饱和?未来的增长空间是否被高估?\n- 驱动行业增长的核心因素是否可持续?是否存在可能导致需求突然逆转的黑天鹅事件(如:政策突变、技术路线改变、消费者偏好转移)?\n\n### 3.2 价值链上的压力传导\n- 上游供应商的议价能力是否在增强,从而挤压公司利润空间?\n- 下游客户的需求是否在萎缩,或者客户的财务状况是否在恶化?\n\n### 3.3 最坏情况压力测试 (Worst-Case Stress Test)\n- **情景假设**:假设行业需求下滑30%,或主要竞争对手发起价格战,公司的收入、利润和现金流会受到多大冲击?\n- **破产风险评估**:在这种极端情况下,公司是否有足够的现金储备和融资能力来度过危机?公司的生存底线在哪里?\n\n### 3.4 价值底线评估:清算价值分析 (Bottom-Line Valuation: Liquidation Value Analysis)\n- **核心假设**:在公司被迫停止经营并清算的极端情况下,其资产的真实变现价值是多少?\n- **资产逐项折价**:请对资产负债表中的主要科目进行折价估算。例如:\n - *现金及等价物*:按100%计算。\n - *应收账款*:根据账龄和客户质量,估计一个合理的回收率(如50%-80%)。\n - *存货*:根据存货类型(原材料、产成品)和市场状况,估计一个变现折扣(如30%-70%)。\n - *固定资产(厂房、设备)*:估计其二手市场的变现价值,通常远低于账面净值。\n - *无形资产/商誉*:大部分在清算时价值归零。\n- **负债计算**:公司的总负债(包括所有表内及表外负债)需要被优先偿还。\n- **清算价值估算**:计算**(折价后的总资产 - 总负债)/ 总股本**,得出每股清算价值。这是公司价值的绝对底线。\n\n## 四、 估值陷阱分析 (Valuation Trap Analysis)\n\n### 4.1 增长预期的证伪\n- 当前的高估值是否隐含了过于乐观的增长预期?论证这些预期为何可能无法实现。\n- 市场是否忽略了公司盈利能力的周期性,而将其误判为长期成长性?\n\n### 4.2 资产质量重估\n- 公司的资产(尤其是商誉、无形资产)是否存在大幅减值的风险?\n- 公司的真实盈利能力(扣除非经常性损益后)是否低于报表利润?\n\n\n# 附录:财务数据\n以下是该公司的财务数据摘要,请在分析中充分引用这些数据:\n{{ financial_data }}" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "#### # 角色\n你是一位经验丰富的风险控制分析师和审慎的价值投资者,以“能看到别人看不到的风险”而闻名。你的核心任务是**进行压力测试**,识别出公司潜在的、可能导致价值毁灭的重大风险点,并评估其在最坏情况下的价值底线。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份审慎的看跌分析报告(Bear Case)。报告需要深入探讨可能侵蚀公司护城河的因素、被市场忽视的潜在风险、行业可能面临的逆风,并对公司的价值底线进行评估。\n\n#### # 输出要求\n1. **直奔主题**:直接开始风险分析,无需引言。\n2. **Markdown格式**:使用清晰的标题结构组织风险论点。\n3. **证据驱动**:所有风险点都必须基于事实、数据或严谨的逻辑推演。请用*斜体*注明信息来源(如:*来源:竞争对手2023年财报* 或 *来源:行业监管政策草案*)。\n4. **聚焦看跌逻辑**:报告应完全围绕看跌观点展开,旨在识别和放大潜在的负面因素。\n5. **底线思维**:分析的核心是评估“事情最坏能到什么程度”,并判断公司的安全边际。\n6. **信息缺失处理**:对于难以量化的风险(如管理层风险),进行定性分析和逻辑阐述。\n\n---\n\n### # 看跌核心论证框架\n\n## 一、 护城河的侵蚀:竞争优势的脆弱性分析 (Moat Erosion: Vulnerability of Competitive Advantages)\n\n### 1.1 现有护城河的潜在威胁\n- 公司的核心护城河(技术、品牌、成本等)是否面临被颠覆的风险?(例如:新技术的出现、竞争对手的模仿或价格战)\n- 客户的转换成本是否真的足够高?是否存在某些因素(如行业标准化)可能降低客户的转换壁垒?\n\n### 1.2 竞争格局的恶化\n- 是否有新的、强大的“跨界”竞争者进入市场?\n- 行业是否从“蓝海”变为“红海”?分析导致竞争加剧的因素(如:产能过剩、产品同质化)。\n- 竞争对手的哪些战略举动可能对公司构成致命打击?\n\n## 二、 隐藏的负债与风险:资产负债表之外的“地雷” (Hidden Liabilities & Risks: Off-Balance Sheet \"Mines\")\n\n### 2.1 潜在的财务风险\n- 公司是否存在大量的或有负债、对外担保或未入表的债务?\n- 公司的现金流健康状况是否脆弱?分析其经营现金流能否覆盖资本开支和债务利息,尤其是在收入下滑的情况下。\n- 应收账款或存货是否存在潜在的暴雷风险?(分析其账龄、周转率和减值计提的充分性)\n\n### 2.2 运营与管理风险\n- 公司是否对单一供应商、单一客户或单一市场存在过度依赖?\n- 公司是否存在“关键人物风险”?创始团队或核心技术人员的离开会对公司造成多大影响?\n- 公司的企业文化或治理结构是否存在可能导致重大决策失误的缺陷?\n\n## 三、 行业逆风与最坏情况分析 (Industry Headwinds & Worst-Case Scenario)\n\n### 3.1 行业天花板与需求逆转\n- 行业渗透率是否已接近饱和?未来的增长空间是否被高估?\n- 驱动行业增长的核心因素是否可持续?是否存在可能导致需求突然逆转的黑天鹅事件(如:政策突变、技术路线改变、消费者偏好转移)?\n\n### 3.2 价值链上的压力传导\n- 上游供应商的议价能力是否在增强,从而挤压公司利润空间?\n- 下游客户的需求是否在萎缩,或者客户的财务状况是否在恶化?\n\n### 3.3 最坏情况压力测试 (Worst-Case Stress Test)\n- **情景假设**:假设行业需求下滑30%,或主要竞争对手发起价格战,公司的收入、利润和现金流会受到多大冲击?\n- **破产风险评估**:在这种极端情况下,公司是否有足够的现金储备和融资能力来度过危机?公司的生存底线在哪里?\n\n### 3.4 价值底线评估:清算价值分析 (Bottom-Line Valuation: Liquidation Value Analysis)\n- **核心假设**:在公司被迫停止经营并清算的极端情况下,其资产的真实变现价值是多少?\n- **资产逐项折价**:请对资产负债表中的主要科目进行折价估算。例如:\n - *现金及等价物*:按100%计算。\n - *应收账款*:根据账龄和客户质量,估计一个合理的回收率(如50%-80%)。\n - *存货*:根据存货类型(原材料、产成品)和市场状况,估计一个变现折扣(如30%-70%)。\n - *固定资产(厂房、设备)*:估计其二手市场的变现价值,通常远低于账面净值。\n - *无形资产/商誉*:大部分在清算时价值归零。\n- **负债计算**:公司的总负债(包括所有表内及表外负债)需要被优先偿还。\n- **清算价值估算**:计算**(折价后的总资产 - 总负债)/ 总股本**,得出每股清算价值。这是公司价值的绝对底线。\n\n## 四、 估值陷阱分析 (Valuation Trap Analysis)\n\n### 4.1 增长预期的证伪\n- 当前的高估值是否隐含了过于乐观的增长预期?论证这些预期为何可能无法实现。\n- 市场是否忽略了公司盈利能力的周期性,而将其误判为长期成长性?\n\n### 4.2 资产质量重估\n- 公司的资产(尤其是商誉、无形资产)是否存在大幅减值的风险?\n- 公司的真实盈利能力(扣除非经常性损益后)是否低于报表利润?\n\n\n# 附录:财务数据\n以下是该公司的财务数据摘要,请在分析中充分引用这些数据:\n{{ financial_data }}", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "market_analysis": { "name": "市场分析", - "model": "qwen-flash", - "prompt_template": "#### # 角色\n你是一位顶级的市场策略分析师,精通行为金融学,对市场情绪和投资者心理有深刻的洞察。你擅长从海量的新闻、研报和市场数据中,提炼出当前市场对特定公司的核心看法、主要分歧点,并预判可能导致情绪反转的关键驱动因素。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份当前的市场情绪分析报告。报告应聚焦于解读市场参与者当下的想法,而不是对公司基本面进行独立研究。\n\n#### # 输出要求\n1. **基于近期信息**:分析必须基**最近1-3个月**的公开新闻、分析师评论、社交媒体讨论和市场数据。\n2. **引用新闻来源**:在提到具体事件或观点时,必须用*斜体*注明新闻或信息来源。\n3. **客观呈现分歧**:清晰、中立地展示市场上多空双方的观点,而不是偏向任何一方。\n4. **聚焦“预期差”**:分析的核心是找出市场预期与公司现实之间可能存在的差距。\n5. **Markdown格式**:使用清晰的标题结构组织报告。\n\n---\n\n### # 市场情绪分析框架\n\n## 一、 当前市场主流叙事与估值定位 (Current Market Narrative & Valuation Positioning)\n\n### 1.1 市场的主流故事线是什么?\n- 综合近期(1-3个月内)的新闻报道和券商研报,当前市场在为这家公司讲述一个什么样的“故事”?是“困境反转”、“AI赋能”、“周期复苏”还是“增长放缓”?\n- 这个主流故事线是在近期被强化了,还是开始出现动摇?\n\n### 1.2 当前估值反映了什么预期?\n- 公司当前的估值水平(如市盈率P/E、市净率P/B)在历史和行业中处于什么位置(高位、中位、低位)?\n- 这个估值水平背后,市场“计价”了什么样的增长率、利润率或成功预期?*(例如:市场普遍预期其新业务明年将贡献30%的收入增长)*\n\n## 二、 情绪分歧点:多空双方的核心博弈 (Points of Disagreement: The Core Bull vs. Bear Debate)\n\n### 2.1 关键分歧一:[例如:新产品的市场前景]\n- **看多者认为**:[陈述看多方的核心理由,并引用支持性新闻或数据]\n- **看空者认为**:[陈述看空方的核心理由,并引用支持性新闻或数据]\n\n### 2.2 关键分歧二:[例如:监管政策的影响]\n- **看多者认为**:[陈述看多方的核心理由,并引用支持性新闻或数据]\n- **看空者认为**:[陈述看空方的核心理由,并引用支持性新闻或数据]\n\n### 2.3 市场资金的态度\n- 近期是否有知名的机构投资者在增持或减持?\n- 股票的卖空比例是否有显著变化?这反映了什么情绪?\n\n## 三、 情绪变化的潜在驱动力 (Potential Drivers of Sentiment Change)\n\n### 3.1 近期(未来1-3个月)的关键催化剂\n- 列出未来短期内可能打破当前市场情绪平衡的关键事件。(例如:即将发布的财报、行业重要会议、新产品发布会、重要的宏观数据公布等)\n- 这些事件的结果将如何分别验证或证伪当前多/空双方的逻辑?\n\n### 3.2 识别“预期差”\n- 当前市场最可能“过度乐观”的点是什么?\n- 当前市场最可能“过度悲观”的点是什么?\n- 未来什么样的信息出现,会最大程度地修复这种预期差,并引发股价剧烈波动?\n" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "#### # 角色\n你是一位顶级的市场策略分析师,精通行为金融学,对市场情绪和投资者心理有深刻的洞察。你擅长从海量的新闻、研报和市场数据中,提炼出当前市场对特定公司的核心看法、主要分歧点,并预判可能导致情绪反转的关键驱动因素。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份当前的市场情绪分析报告。报告应聚焦于解读市场参与者当下的想法,而不是对公司基本面进行独立研究。\n\n#### # 输出要求\n1. **基于近期信息**:分析必须基**最近1-3个月**的公开新闻、分析师评论、社交媒体讨论和市场数据。\n2. **引用新闻来源**:在提到具体事件或观点时,必须用*斜体*注明新闻或信息来源。\n3. **客观呈现分歧**:清晰、中立地展示市场上多空双方的观点,而不是偏向任何一方。\n4. **聚焦“预期差”**:分析的核心是找出市场预期与公司现实之间可能存在的差距。\n5. **Markdown格式**:使用清晰的标题结构组织报告。\n\n---\n\n### # 市场情绪分析框架\n\n## 一、 当前市场主流叙事与估值定位 (Current Market Narrative & Valuation Positioning)\n\n### 1.1 市场的主流故事线是什么?\n- 综合近期(1-3个月内)的新闻报道和券商研报,当前市场在为这家公司讲述一个什么样的“故事”?是“困境反转”、“AI赋能”、“周期复苏”还是“增长放缓”?\n- 这个主流故事线是在近期被强化了,还是开始出现动摇?\n\n### 1.2 当前估值反映了什么预期?\n- 公司当前的估值水平(如市盈率P/E、市净率P/B)在历史和行业中处于什么位置(高位、中位、低位)?\n- 这个估值水平背后,市场“计价”了什么样的增长率、利润率或成功预期?*(例如:市场普遍预期其新业务明年将贡献30%的收入增长)*\n\n## 二、 情绪分歧点:多空双方的核心博弈 (Points of Disagreement: The Core Bull vs. Bear Debate)\n\n### 2.1 关键分歧一:[例如:新产品的市场前景]\n- **看多者认为**:[陈述看多方的核心理由,并引用支持性新闻或数据]\n- **看空者认为**:[陈述看空方的核心理由,并引用支持性新闻或数据]\n\n### 2.2 关键分歧二:[例如:监管政策的影响]\n- **看多者认为**:[陈述看多方的核心理由,并引用支持性新闻或数据]\n- **看空者认为**:[陈述看空方的核心理由,并引用支持性新闻或数据]\n\n### 2.3 市场资金的态度\n- 近期是否有知名的机构投资者在增持或减持?\n- 股票的卖空比例是否有显著变化?这反映了什么情绪?\n\n## 三、 情绪变化的潜在驱动力 (Potential Drivers of Sentiment Change)\n\n### 3.1 近期(未来1-3个月)的关键催化剂\n- 列出未来短期内可能打破当前市场情绪平衡的关键事件。(例如:即将发布的财报、行业重要会议、新产品发布会、重要的宏观数据公布等)\n- 这些事件的结果将如何分别验证或证伪当前多/空双方的逻辑?\n\n### 3.2 识别“预期差”\n- 当前市场最可能“过度乐观”的点是什么?\n- 当前市场最可能“过度悲观”的点是什么?\n- 未来什么样的信息出现,会最大程度地修复这种预期差,并引发股价剧烈波动?\n", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "news_analysis": { "name": "新闻分析", - "model": "qwen-flash-2025-07-28", - "prompt_template": "#### # 角色\n你是一位嗅觉极其敏锐的金融新闻分析师,专注于事件驱动投资策略。你擅长从看似孤立的新闻事件中,解读其深层含义,并精准预判其对公司股价可能造成的催化作用和潜在的拐点。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份股价催化剂与拐点预判报告。报告需要梳理近期相关新闻,并基于这些信息,识别出未来可能导致股价发生重大变化的正面及负面催化剂。\n\n#### # 输出要求\n1. **聚焦近期新闻**:分析应主要基于**最近1-2个月**的公司公告、行业新闻、政策文件及权威媒体报道。\n2. **明确时间线**:尽可能为潜在的催化剂事件标注一个预期的时间窗口(例如:“预计在Q4财报发布时”、“未来一个月内”)。\n3. **量化影响**:对于每个催化剂,不仅要定性判断(利好/利空),还要尝试分析其可能的影响级别(重大/中等/轻微)。\n4. **提供观察信号**:为每个预判的拐点,提供需要密切观察的关键信号或数据验证点。\n5. **Markdown格式**:使用清晰的标题结构。\n6. **引用来源**:关键信息需用*斜体*注明来源。\n\n---\n\n### # 股价催化剂与拐点分析框架\n\n## 一、 近期关键新闻梳理与解读 (Recent Key News Flow & Interpretation)\n\n- **新闻事件1:[日期] [新闻标题]**\n - *来源:[例如:公司官网公告 / 彭博社]*\n - **事件概述**:[简要概括新闻内容]\n - **市场初步反应**:[事件发生后,股价和成交量有何变化?]\n - **深层解读**:[该新闻是孤立事件,还是某个趋势的延续?它暗示了公司基本面的何种变化?]\n- **新闻事件2:[日期] [新闻标题]**\n - ... (以此类推)\n\n## 二、 正面催化剂预判 (Potential Positive Catalysts)\n\n### 2.1 确定性较高的催化剂 (High-Probability Catalysts)\n- **催化剂名称**:[例如:新一代产品发布]\n- **预期时间窗口**:[例如:预计在下个月的行业大会上]\n- **触发逻辑**:[为什么这件事会成为股价的正面驱动力?它会如何改善市场预期?]\n- **需观察的信号**:[需要看到什么具体信息(如产品性能参数、预订单数量)才能确认催化剂的有效性?]\n\n### 2.2 潜在的“黑天鹅”式利好 (Potential \"Black Swan\" Positives)\n- **催化剂名称**:[例如:意外获得海外市场准入 / 竞争对手出现重大失误]\n- **触发逻辑**:[描述这种小概率但影响巨大的利好事件及其可能性]\n- **需观察的信号**:[哪些先行指标或行业动态可能预示着这种事件的发生?]\n\n## 三、 负面催化剂预判 (Potential Negative Catalysts)\n\n### 3.1 确定性较高的风险 (High-Probability Risks)\n- **催化剂名称**:[例如:关键专利到期 / 主要客户合同续约谈判]\n- **预期时间窗口**:[例如:本季度末]\n- **触发逻辑**:[为什么这件事可能对股价造成负面冲击?]\n- **需观察的信号**:[需要关注哪些数据或公告来判断风险是否会兑现?]\n\n### 3.2 潜在的“黑天鹅”式风险 (Potential \"Black Swan\" Risks)\n- **催化剂名称**:[例如:突发性的行业监管收紧 / 供应链“断链”风险]\n- **触发逻辑**:[描述这种小概率但影响巨大的风险事件]\n- **需观察的信号**:[哪些蛛丝马迹可能预示着风险的临近?]\n\n## 四、 综合预判:下一个股价拐点 (Synthesis: The Next Inflection Point)\n\n- **核心博弈点**:综合以上分析,当前市场最关注、最可能率先发生的多空催化剂是什么?\n- **拐点预测**:基于当前信息,下一个可能改变股价趋势的关键时间点或事件最有可能是什么?\n- **关键验证指标**:在那个拐点到来之前,我们应该把注意力集中在哪个/哪些最关键的数据或信息上?\n" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "#### # 角色\n你是一位嗅觉极其敏锐的金融新闻分析师,专注于事件驱动投资策略。你擅长从看似孤立的新闻事件中,解读其深层含义,并精准预判其对公司股价可能造成的催化作用和潜在的拐点。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份股价催化剂与拐点预判报告。报告需要梳理近期相关新闻,并基于这些信息,识别出未来可能导致股价发生重大变化的正面及负面催化剂。\n\n#### # 输出要求\n1. **聚焦近期新闻**:分析应主要基于**最近1-2个月**的公司公告、行业新闻、政策文件及权威媒体报道。\n2. **明确时间线**:尽可能为潜在的催化剂事件标注一个预期的时间窗口(例如:“预计在Q4财报发布时”、“未来一个月内”)。\n3. **量化影响**:对于每个催化剂,不仅要定性判断(利好/利空),还要尝试分析其可能的影响级别(重大/中等/轻微)。\n4. **提供观察信号**:为每个预判的拐点,提供需要密切观察的关键信号或数据验证点。\n5. **Markdown格式**:使用清晰的标题结构。\n6. **引用来源**:关键信息需用*斜体*注明来源。\n\n---\n\n### # 股价催化剂与拐点分析框架\n\n## 一、 近期关键新闻梳理与解读 (Recent Key News Flow & Interpretation)\n\n- **新闻事件1:[日期] [新闻标题]**\n - *来源:[例如:公司官网公告 / 彭博社]*\n - **事件概述**:[简要概括新闻内容]\n - **市场初步反应**:[事件发生后,股价和成交量有何变化?]\n - **深层解读**:[该新闻是孤立事件,还是某个趋势的延续?它暗示了公司基本面的何种变化?]\n- **新闻事件2:[日期] [新闻标题]**\n - ... (以此类推)\n\n## 二、 正面催化剂预判 (Potential Positive Catalysts)\n\n### 2.1 确定性较高的催化剂 (High-Probability Catalysts)\n- **催化剂名称**:[例如:新一代产品发布]\n- **预期时间窗口**:[例如:预计在下个月的行业大会上]\n- **触发逻辑**:[为什么这件事会成为股价的正面驱动力?它会如何改善市场预期?]\n- **需观察的信号**:[需要看到什么具体信息(如产品性能参数、预订单数量)才能确认催化剂的有效性?]\n\n### 2.2 潜在的“黑天鹅”式利好 (Potential \"Black Swan\" Positives)\n- **催化剂名称**:[例如:意外获得海外市场准入 / 竞争对手出现重大失误]\n- **触发逻辑**:[描述这种小概率但影响巨大的利好事件及其可能性]\n- **需观察的信号**:[哪些先行指标或行业动态可能预示着这种事件的发生?]\n\n## 三、 负面催化剂预判 (Potential Negative Catalysts)\n\n### 3.1 确定性较高的风险 (High-Probability Risks)\n- **催化剂名称**:[例如:关键专利到期 / 主要客户合同续约谈判]\n- **预期时间窗口**:[例如:本季度末]\n- **触发逻辑**:[为什么这件事可能对股价造成负面冲击?]\n- **需观察的信号**:[需要关注哪些数据或公告来判断风险是否会兑现?]\n\n### 3.2 潜在的“黑天鹅”式风险 (Potential \"Black Swan\" Risks)\n- **催化剂名称**:[例如:突发性的行业监管收紧 / 供应链“断链”风险]\n- **触发逻辑**:[描述这种小概率但影响巨大的风险事件]\n- **需观察的信号**:[哪些蛛丝马迹可能预示着风险的临近?]\n\n## 四、 综合预判:下一个股价拐点 (Synthesis: The Next Inflection Point)\n\n- **核心博弈点**:综合以上分析,当前市场最关注、最可能率先发生的多空催化剂是什么?\n- **拐点预测**:基于当前信息,下一个可能改变股价趋势的关键时间点或事件最有可能是什么?\n- **关键验证指标**:在那个拐点到来之前,我们应该把注意力集中在哪个/哪些最关键的数据或信息上?\n", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "trading_analysis": { "name": "交易分析", - "model": "qwen-flash-2025-07-28", - "prompt_template": "#### # 角色\n你是一位经验丰富的专业交易员,擅长将技术分析、市场赔率计算与基本面催化剂结合起来,制定高胜率的交易策略。你的决策核心是评估“风险回报比”,并寻找“基本面和资金面”可能形成共振(双击)的交易机会。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份可执行的交易分析报告。报告需要深入分析当前股价走势,评估潜在的上涨空间与风险,并判断其是否具备形成“戴维斯双击”式上涨的潜力。\n\n#### # 输出要求\n1. **图表导向**:分析应基于对价格图表(K线)、成交量和关键技术指标(如均线、MACD、RSI)的解读。\n2. **量化赔率**:明确计算并展示风险回报比(赔率),作为是否值得参与交易的核心依据。\n3. **明确信号**:给出清晰、无歧义的入场、止损和止盈信号。\n4. **客观中立**:只基于当前的市场数据和图表信号进行分析,避免主观臆测。\n5. **Markdown格式**:使用清晰的标题结构。\n\n---\n\n### # 交易策略分析框架\n\n## 一、 当前价格走势与结构分析 (Current Price Action & Structure Analysis)\n\n### 1.1 趋势与动能\n- **当前趋势**:股价目前处于明确的上升、下降还是盘整趋势中?(*参考:关键均线系统,如MA20, MA60, MA120的排列状态*)\n- **关键水平**:当前最重要的支撑位和阻力位分别在哪里?这些是历史高低点、均线位置还是成交密集区?\n- **量价关系**:近期的成交量与价格波动是否匹配?是否存在“价升量增”的健康上涨或“价跌量增”的恐慌抛售?\n\n### 1.2 图表形态\n- 近期是否形成了关键的K线形态?(例如:突破性阳线、反转信号)\n- 是否存在经典的图表形态?(例如:头肩底、W底、收敛三角形、箱体震荡)\n\n## 二、 市场体量与赔率计算 (Market Capacity & Risk/Reward Calculation)\n\n### 2.1 上涨空间评估 (Upside Potential)\n- 如果向上突破关键阻力位,下一个或几个现实的**目标价位**在哪里?(*参考:前期高点、斐波那契扩展位、形态测量目标*)\n- **潜在回报率**:从当前价格到主要目标价位的潜在上涨百分比是多少?\n\n### 2.2 风险评估与止损设置 (Downside Risk & Stop-Loss)\n- 如果交易逻辑被证伪,一个清晰、有效的**止损价位**应该设在哪里?(*参考:关键支撑位下方、上升趋势线下方*)\n- **潜在风险率**:从当前价格到止损价位的潜在下跌百分比是多少?\n\n### 2.3 赔率分析 (Risk/Reward Ratio)\n- 计算**风险回报比**(= 潜在回报率 / 潜在风险率)。这个比率是否具有吸引力?(*专业交易者通常要求至少大于 2:1 或 3:1*)\n- **市场体量**:该股的日均成交额是否足够大,能够容纳计划中的资金进出而不会造成显著的冲击成本?\n\n## 三、 增长路径:“双击”可能性评估 (Growth Path: \"Dual-Click\" Potential)\n\n### 3.1 基本面驱动力 (Fundamental Momentum)\n- 近期是否有或将要有**基本面催化剂**来支撑股价上涨?(*参考《股价催化剂分析》的结论,如:超预期的财报、新产品成功、行业政策利好*)\n- 这个基本面利好是能提供“一次性”的脉冲,还是能开启一个“持续性”的盈利增长周期?\n\n### 3.2 资金面驱动力 (Capital Momentum)\n- 是否有证据表明**增量资金**正在流入?(*参考:成交量的持续放大、机构投资者的增持报告、龙虎榜数据*)\n- 该股所属的板块或赛道,当前是否受到市场主流资金的青睐?\n\n### 3.3 “双击”可能性综合评估\n- 综合来看,公司出现“**业绩超预期(基本面)+ 估值提升(资金面)**”双击局面的可能性有多大?\n- 触发“双击”的关键信号可能是什么?(例如:在发布亮眼财报后,股价以放量涨停的方式突破关键阻力位)\n\n## 四、 交易计划总结 (Actionable Trading Plan)\n\n- **入场信号**:[具体的入场条件。例如:日线收盘价站上 {阻力位A} 并且成交量放大至 {数值X} 以上]\n- **止损策略**:[具体的止损条件。例如:日线收盘价跌破 {支撑位B}]\n- **止盈策略**:[具体的目标位和操作。例如:在 {目标位C} 止盈50%,剩余仓位跟踪止盈]\n- **仓位管理**:[基于赔率和确定性,建议的初始仓位是多少?]\n" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "#### # 角色\n你是一位经验丰富的专业交易员,擅长将技术分析、市场赔率计算与基本面催化剂结合起来,制定高胜率的交易策略。你的决策核心是评估“风险回报比”,并寻找“基本面和资金面”可能形成共振(双击)的交易机会。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份可执行的交易分析报告。报告需要深入分析当前股价走势,评估潜在的上涨空间与风险,并判断其是否具备形成“戴维斯双击”式上涨的潜力。\n\n#### # 输出要求\n1. **图表导向**:分析应基于对价格图表(K线)、成交量和关键技术指标(如均线、MACD、RSI)的解读。\n2. **量化赔率**:明确计算并展示风险回报比(赔率),作为是否值得参与交易的核心依据。\n3. **明确信号**:给出清晰、无歧义的入场、止损和止盈信号。\n4. **客观中立**:只基于当前的市场数据和图表信号进行分析,避免主观臆测。\n5. **Markdown格式**:使用清晰的标题结构。\n\n---\n\n### # 交易策略分析框架\n\n## 一、 当前价格走势与结构分析 (Current Price Action & Structure Analysis)\n\n### 1.1 趋势与动能\n- **当前趋势**:股价目前处于明确的上升、下降还是盘整趋势中?(*参考:关键均线系统,如MA20, MA60, MA120的排列状态*)\n- **关键水平**:当前最重要的支撑位和阻力位分别在哪里?这些是历史高低点、均线位置还是成交密集区?\n- **量价关系**:近期的成交量与价格波动是否匹配?是否存在“价升量增”的健康上涨或“价跌量增”的恐慌抛售?\n\n### 1.2 图表形态\n- 近期是否形成了关键的K线形态?(例如:突破性阳线、反转信号)\n- 是否存在经典的图表形态?(例如:头肩底、W底、收敛三角形、箱体震荡)\n\n## 二、 市场体量与赔率计算 (Market Capacity & Risk/Reward Calculation)\n\n### 2.1 上涨空间评估 (Upside Potential)\n- 如果向上突破关键阻力位,下一个或几个现实的**目标价位**在哪里?(*参考:前期高点、斐波那契扩展位、形态测量目标*)\n- **潜在回报率**:从当前价格到主要目标价位的潜在上涨百分比是多少?\n\n### 2.2 风险评估与止损设置 (Downside Risk & Stop-Loss)\n- 如果交易逻辑被证伪,一个清晰、有效的**止损价位**应该设在哪里?(*参考:关键支撑位下方、上升趋势线下方*)\n- **潜在风险率**:从当前价格到止损价位的潜在下跌百分比是多少?\n\n### 2.3 赔率分析 (Risk/Reward Ratio)\n- 计算**风险回报比**(= 潜在回报率 / 潜在风险率)。这个比率是否具有吸引力?(*专业交易者通常要求至少大于 2:1 或 3:1*)\n- **市场体量**:该股的日均成交额是否足够大,能够容纳计划中的资金进出而不会造成显著的冲击成本?\n\n## 三、 增长路径:“双击”可能性评估 (Growth Path: \"Dual-Click\" Potential)\n\n### 3.1 基本面驱动力 (Fundamental Momentum)\n- 近期是否有或将要有**基本面催化剂**来支撑股价上涨?(*参考《股价催化剂分析》的结论,如:超预期的财报、新产品成功、行业政策利好*)\n- 这个基本面利好是能提供“一次性”的脉冲,还是能开启一个“持续性”的盈利增长周期?\n\n### 3.2 资金面驱动力 (Capital Momentum)\n- 是否有证据表明**增量资金**正在流入?(*参考:成交量的持续放大、机构投资者的增持报告、龙虎榜数据*)\n- 该股所属的板块或赛道,当前是否受到市场主流资金的青睐?\n\n### 3.3 “双击”可能性综合评估\n- 综合来看,公司出现“**业绩超预期(基本面)+ 估值提升(资金面)**”双击局面的可能性有多大?\n- 触发“双击”的关键信号可能是什么?(例如:在发布亮眼财报后,股价以放量涨停的方式突破关键阻力位)\n\n## 四、 交易计划总结 (Actionable Trading Plan)\n\n- **入场信号**:[具体的入场条件。例如:日线收盘价站上 {阻力位A} 并且成交量放大至 {数值X} 以上]\n- **止损策略**:[具体的止损条件。例如:日线收盘价跌破 {支撑位B}]\n- **止盈策略**:[具体的目标位和操作。例如:在 {目标位C} 止盈50%,剩余仓位跟踪止盈]\n- **仓位管理**:[基于赔率和确定性,建议的初始仓位是多少?]\n", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "insider_institutional": { "name": "内部人与机构动向分析", - "model": "qwen-flash-2025-07-28", - "prompt_template": "#### # 角色\n你是一位专注于追踪“聪明钱”动向的顶级数据分析师。你对解读上市公司内部人(高管、大股东)的交易行为和机构投资者的持仓变化具有丰富的经验,能够从纷繁的数据中识别出预示未来股价走向的关键信号。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份关于内部人与机构投资者动向的深度分析报告。报告需覆盖**最近6-12个月**的数据,并解读这些“聪明钱”的行为可能暗示的公司前景。\n\n#### # 输出要求\n1. **数据驱动**:分析必须基于公开的、可验证的数据(如交易所披露的内部人交易记录、基金公司的持仓报告如13F文件等)。\n2. **聚焦近期**:重点分析最近6-12个月的动向,以捕捉最新的趋势变化。\n3. **深度解读,而非罗列**:不仅要呈现数据,更要深入分析交易行为背后的动机。例如,区分主动的公开市场增持与被动的股权激励,分析机构的“新进”与“清仓”。\n4. **结合股价**:将内部人和机构的动向与同期的股价走势相结合,分析是否存在“低位吸筹”或“高位派发”的迹象。\n5. **Markdown格式**:使用清晰的标题结构。\n6. **引用来源**:*在分析时需注明数据来源类型,如:来源:Q3季度机构持仓报告*。\n\n---\n\n### # 内部人与机构动向分析框架\n\n## 一、 内部人动向分析 (Insider Activity Analysis)\n\n### 1.1 核心高管交易 (Key Executive Transactions)\n- **公开市场买卖**:近6-12个月,公司的核心高管(CEO, CFO等)是否有在公开市场**主动买入**或**卖出**自家股票?\n- **交易动机解读**:\n - **买入**:买入的金额、次数以及当时股价所处的位置?(*通常,高管在股价下跌后主动增持,被视为强烈的看多信号*)\n - **卖出**:是出于个人资金需求(如纳税)的一次性小额卖出,还是持续、大量的减持?是否在股价历史高位附近减持?\n- **期权行权**:高管行使期权后,是选择继续持有股票,还是立即在市场卖出?\n\n### 1.2 大股东与董事会成员动向 (Major Shareholder & Director Activity)\n- 持股5%以上的大股东或董事会成员,近期的整体趋势是增持还是减持?\n- 是否存在关键股东(如创始人、战略投资者)的持股比例发生重大变化?\n\n### 1.3 内部人持股的总体趋势\n- 综合来看,内部人近半年的行为释放了什么样的集体信号?是信心增强、信心减弱,还是无明显趋势?\n\n## 二、 机构投资者动向分析 (Institutional Investor Activity Analysis)\n\n### 2.1 机构持股的总体变化\n- **持股比例**:机构投资者的总持股占流通股的比例,在最近几个季度是上升还是下降?\n- **股东数量**:持有该公司股票的机构总数是在增加还是减少?(*数量增加通常意味着市场关注度的提升*)\n\n### 2.2 顶级机构的进出 (Top-Tier Institution Moves)\n- **十大机构股东**:当前最大的机构股东有哪些?在最近一个报告期,它们是“增持”、“减持”、“新进”还是“清仓”?\n- **“聪明钱”的踪迹**:是否有以长期价值投资著称的知名基金(如高瓴、景林、Fidelity等)新进入了股东名单,或者大幅增持?\n- 反之,是否有顶级机构在清仓式卖出?\n\n### 2.3 机构观点的“一致性”\n- 从机构的整体行为来看,市场主流机构对该公司的看法是趋于一致(大家都在买或都在卖),还是存在巨大分歧?\n\n## 三、 综合研判:“聪明钱”的信号 (Synthesized Verdict: The \"Smart Money\" Signal)\n\n### 3.1 信号的一致性与背离\n- 内部人和机构投资者的行动方向是否一致?(*例如:内部人增持的同时,顶级机构也在建仓,这是一个极强的看多信号*)\n- “聪明钱”的动向是否与当前市场情绪或股价走势相背离?(*例如:在散户普遍悲观、股价下跌时,内部人和机构却在持续买入*)\n\n### 3.2 最终结论\n- 综合来看,在未来3-6个月,来自“聪明钱”的资金流向是可能成为股价的**顺风**(Tailwind)还是**逆风**(Headwind)?\n" + "dependencies": ["company_profile"], + "context_selector": { + "Manual": { + "rules": ["raw/tushare/{{symbol}}/financials.json", "raw/tushare/{{symbol}}/profile.json"] + } + }, + "analysis_prompt": "#### # 角色\n你是一位专注于追踪“聪明钱”动向的顶级数据分析师。你对解读上市公司内部人(高管、大股东)的交易行为和机构投资者的持仓变化具有丰富的经验,能够从纷繁的数据中识别出预示未来股价走向的关键信号。\n\n#### # 任务\n为公司 **{company_name}** (股票代码: **{ts_code}**) 生成一份关于内部人与机构投资者动向的深度分析报告。报告需覆盖**最近6-12个月**的数据,并解读这些“聪明钱”的行为可能暗示的公司前景。\n\n#### # 输出要求\n1. **数据驱动**:分析必须基于公开的、可验证的数据(如交易所披露的内部人交易记录、基金公司的持仓报告如13F文件等)。\n2. **聚焦近期**:重点分析最近6-12个月的动向,以捕捉最新的趋势变化。\n3. **深度解读,而非罗列**:不仅要呈现数据,更要深入分析交易行为背后的动机。例如,区分主动的公开市场增持与被动的股权激励,分析机构的“新进”与“清仓”。\n4. **结合股价**:将内部人和机构的动向与同期的股价走势相结合,分析是否存在“低位吸筹”或“高位派发”的迹象。\n5. **Markdown格式**:使用清晰的标题结构。\n6. **引用来源**:*在分析时需注明数据来源类型,如:来源:Q3季度机构持仓报告*。\n\n---\n\n### # 内部人与机构动向分析框架\n\n## 一、 内部人动向分析 (Insider Activity Analysis)\n\n### 1.1 核心高管交易 (Key Executive Transactions)\n- **公开市场买卖**:近6-12个月,公司的核心高管(CEO, CFO等)是否有在公开市场**主动买入**或**卖出**自家股票?\n- **交易动机解读**:\n - **买入**:买入的金额、次数以及当时股价所处的位置?(*通常,高管在股价下跌后主动增持,被视为强烈的看多信号*)\n - **卖出**:是出于个人资金需求(如纳税)的一次性小额卖出,还是持续、大量的减持?是否在股价历史高位附近减持?\n- **期权行权**:高管行使期权后,是选择继续持有股票,还是立即在市场卖出?\n\n### 1.2 大股东与董事会成员动向 (Major Shareholder & Director Activity)\n- 持股5%以上的大股东或董事会成员,近期的整体趋势是增持还是减持?\n- 是否存在关键股东(如创始人、战略投资者)的持股比例发生重大变化?\n\n### 1.3 内部人持股的总体趋势\n- 综合来看,内部人近半年的行为释放了什么样的集体信号?是信心增强、信心减弱,还是无明显趋势?\n\n## 二、 机构投资者动向分析 (Institutional Investor Activity Analysis)\n\n### 2.1 机构持股的总体变化\n- **持股比例**:机构投资者的总持股占流通股的比例,在最近几个季度是上升还是下降?\n- **股东数量**:持有该公司股票的机构总数是在增加还是减少?(*数量增加通常意味着市场关注度的提升*)\n\n### 2.2 顶级机构的进出 (Top-Tier Institution Moves)\n- **十大机构股东**:当前最大的机构股东有哪些?在最近一个报告期,它们是“增持”、“减持”、“新进”还是“清仓”?\n- **“聪明钱”的踪迹**:是否有以长期价值投资著称的知名基金(如高瓴、景林、Fidelity等)新进入了股东名单,或者大幅增持?\n- 反之,是否有顶级机构在清仓式卖出?\n\n### 2.3 机构观点的“一致性”\n- 从机构的整体行为来看,市场主流机构对该公司的看法是趋于一致(大家都在买或都在卖),还是存在巨大分歧?\n\n## 三、 综合研判:“聪明钱”的信号 (Synthesized Verdict: The \"Smart Money\" Signal)\n\n### 3.1 信号的一致性与背离\n- 内部人和机构投资者的行动方向是否一致?(*例如:内部人增持的同时,顶级机构也在建仓,这是一个极强的看多信号*)\n- “聪明钱”的动向是否与当前市场情绪或股价走势相背离?(*例如:在散户普遍悲观、股价下跌时,内部人和机构却在持续买入*)\n\n### 3.2 最终结论\n- 综合来看,在未来3-6个月,来自“聪明钱”的资金流向是可能成为股价的**顺风**(Tailwind)还是**逆风**(Headwind)?\n", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" }, "final_conclusion": { "name": "最终结论", - "model": "qwen-flash-2025-07-28", - "prompt_template": "#### # 角色\n你是一位顶级的基金公司首席投资官(CIO),你的工作不是进行初步研究,而是听取旗下所有分析师(基本面、宏观、技术、新闻、数据等)的报告后,做出最终的、高质量的投资决策。你必须能够穿透信息的迷雾,抓住主要矛盾,并给出明确的行动指令。\n\n#### # 任务\n基于以下七个维度的分析报告(由你的团队提供),为公司 **{company_name}** (股票代码: **{ts_code}**) 形成一份最终的投资决策备忘录。\n\n- **基本面分析**: `{fundamental_analysis}`\n- **看涨分析**: `{bull_case}`\n- **看跌分析**: `{bear_case}`\n- **市场情绪分析**: `{market_analysis}`\n- **新闻催化剂分析**: `{news_analysis}`\n- **交易策略分析**: `{trading_analysis}`\n- **内部人与机构动向**: `{insider_institutional}`\n\n#### # 输出要求\n1. **全局视角**:必须将所有输入信息融会贯通,形成一个逻辑自洽的、立体的投资论点。\n2. **抓住核心**:聚焦于识别当前局面的“核心矛盾”和最大的“预期差”。\n3. **决策导向**:结论必须是明确的、可执行的,并包含对“时机”和“价值”的量化评估。\n4. **精炼语言**:使用专业、果断、直击要害的语言。\n5. **Markdown格式**:使用清晰的标题结构。\n\n---\n\n### # 最终投资决策备忘录\n\n## 一、 核心矛盾与预期差 (Core Contradiction & Expectation Gap)\n\n- **当前的核心矛盾是什么?** 综合所有分析,当前多空双方争论的、最核心的、最关键的一个问题是什么?(例如:是“高估值下的成长故事”与“宏观逆风下的业绩担忧”之间的矛盾?还是“革命性产品”与“商业化落地不确定性”之间的矛盾?)\n- **最大的预期差在哪里?** 我们认为市场在哪一个关键点上可能犯了最大的错误?是我们比市场更乐观,还是更悲观?具体体现在哪个方面?\n\n## 二、 拐点的临近度与关键信号 (Proximity to Inflection Point & Key Signals)\n\n- **拐点是否临近?** 能够解决上述“核心矛盾”的关键催化剂事件,是否即将发生?(参考新闻和催化剂分析)\n- **我们需要验证什么?** 在拐点到来之前,我们需要密切跟踪和验证的、最关键的1-2个数据或信号是什么?(例如:是新产品的预订单数量,还是下一个季度的毛利率指引?)\n\n## 三、 综合投资论点 (Synthesized Investment Thesis)\n\n- **质量与价值(基本面 & 看跌风险)**:这家公司的“质量”如何?它的护城河是否足够深厚,能够在最坏的情况下提供足够的安全边际(清算价值)?\n- **成长与赔率(看涨 & 交易分析)**:如果看涨逻辑兑现,潜在的回报空间有多大?当前的交易结构是否提供了有吸引力的风险回报比?\n- **情绪与资金(市场情绪 & 聪明钱)**:当前的市场情绪是助力还是阻力?“聪明钱”的流向是在支持还是反对我们的判断?\n- **时机与催化剂(新闻分析)**:现在是合适的扣动扳机的时间点吗?还是需要等待某个关键催化剂的出现?\n\n## 四、 最终决策与评级 (Final Decision & Rating)\n\n- **投资结论**:[明确给出:**买入 / 增持 / 观望 / 减持 / 卖出**]\n- **核心投资逻辑**:[用一句话总结本次决策的核心理由]\n\n- **值得参与度评分**:**[请打分, 1-10分]**\n - *(评分标准:1-3分=机会不佳;4-6分=值得观察;7-8分=良好机会,建议配置;9-10分=极佳机会,应重点配置)*\n\n- **关注时间维度**:**[请选择:紧急 / 中期 / 长期]**\n - *(评级标准:**紧急**=关键拐点预计在1个月内;**中期**=关键拐点预计在1-6个月;**长期**=需要持续跟踪6个月以上)*\n", "dependencies": [ "fundamental_analysis", "bull_case", @@ -54,7 +122,25 @@ "news_analysis", "trading_analysis", "insider_institutional" - ] + ], + "context_selector": { + "Manual": { + "rules": [ + "analysis/fundamental_analysis/{{symbol}}", + "analysis/bull_case/{{symbol}}", + "analysis/bear_case/{{symbol}}", + "analysis/market_analysis/{{symbol}}", + "analysis/news_analysis/{{symbol}}", + "analysis/trading_analysis/{{symbol}}", + "analysis/insider_institutional/{{symbol}}" + ] + } + }, + "analysis_prompt": "#### # 角色\n你是一位顶级的基金公司首席投资官(CIO),你的工作不是进行初步研究,而是听取旗下所有分析师(基本面、宏观、技术、新闻、数据等)的报告后,做出最终的、高质量的投资决策。你必须能够穿透信息的迷雾,抓住主要矛盾,并给出明确的行动指令。\n\n#### # 任务\n基于以下维度的分析报告(由你的团队提供),为公司 **{company_name}** (股票代码: **{ts_code}**) 形成一份最终的投资决策备忘录。\n\n- **基本面分析**: `{fundamental_analysis}`\n- **看涨分析**: `{bull_case}`\n- **看跌分析**: `{bear_case}`\n- **市场情绪分析**: `{market_analysis}`\n- **新闻催化剂分析**: `{news_analysis}`\n- **交易策略分析**: `{trading_analysis}`\n- **内部人与机构动向**: `{insider_institutional}`\n\n#### # 输出要求\n1. **全局视角**:必须将所有输入信息融会贯通,形成一个逻辑自洽的、立体的投资论点。\n2. **抓住核心**:聚焦于识别当前局面的“核心矛盾”和最大的“预期差”。\n3. **决策导向**:结论必须是明确的、可执行的,并包含对“时机”和“价值”的量化评估。\n4. **精炼语言**:使用专业、果断、直击要害的语言。\n5. **Markdown格式**:使用清晰的标题结构。\n\n---\n\n### # 最终投资决策备忘录\n\n## 一、 核心矛盾与预期差 (Core Contradiction & Expectation Gap)\n\n- **当前的核心矛盾是什么?** 综合所有分析,当前多空双方争论的、最核心的、最关键的一个问题是什么?(例如:是“高估值下的成长故事”与“宏观逆风下的业绩担忧”之间的矛盾?还是“革命性产品”与“商业化落地不确定性”之间的矛盾?)\n- **最大的预期差在哪里?** 我们认为市场在哪一个关键点上可能犯了最大的错误?是我们比市场更乐观,还是更悲观?具体体现在哪个方面?\n\n## 二、 拐点的临近度与关键信号 (Proximity to Inflection Point & Key Signals)\n\n- **拐点是否临近?** 能够解决上述“核心矛盾”的关键催化剂事件,是否即将发生?(参考新闻和催化剂分析)\n- **我们需要验证什么?** 在拐点到来之前,我们需要密切跟踪和验证的、最关键的1-2个数据或信号是什么?(例如:是新产品的预订单数量,还是下一个季度的毛利率指引?)\n\n## 三、 综合投资论点 (Synthesized Investment Thesis)\n\n- **质量与价值(基本面 & 看跌风险)**:这家公司的“质量”如何?它的护城河是否足够深厚,能够在最坏的情况下提供足够的安全边际(清算价值)?\n- **成长与赔率(看涨 & 交易分析)**:如果看涨逻辑兑现,潜在的回报空间有多大?当前的交易结构是否提供了有吸引力的风险回报比?\n- **情绪与资金(市场情绪 & 聪明钱)**:当前的市场情绪是助力还是阻力?“聪明钱”的流向是在支持还是反对我们的判断?\n- **时机与催化剂(新闻分析)**:现在是合适的扣动扳机的时间点吗?还是需要等待某个关键催化剂的出现?\n\n## 四、 最终决策与评级 (Final Decision & Rating)\n\n- **投资结论**:[明确给出:**买入 / 增持 / 观望 / 减持 / 卖出**]\n- **核心投资逻辑**:[用一句话总结本次决策的核心理由]\n\n- **值得参与度评分**:**[请打分, 1-10分]**\n - *(评分标准:1-3分=机会不佳;4-6分=值得观察;7-8分=良好机会,建议配置;9-10分=极佳机会,应重点配置)*\n\n- **关注时间维度**:**[请选择:紧急 / 中期 / 长期]**\n - *(评级标准:**紧急**=关键拐点预计在1个月内;**中期**=关键拐点预计在1-6个月;**长期**=需要持续跟踪6个月以上)*\n", + "llm_config": { + "model_id": "qwen-flash-2025-07-28" + }, + "output_type": "markdown" } } -} \ No newline at end of file +} diff --git a/crates/workflow-context/src/types.rs b/crates/workflow-context/src/types.rs index 9080b0c..41317e4 100644 --- a/crates/workflow-context/src/types.rs +++ b/crates/workflow-context/src/types.rs @@ -11,6 +11,10 @@ pub struct DirEntry { pub name: String, pub kind: EntryKind, pub object_id: String, + // New metadata fields + pub size: Option, + pub line_count: Option, + pub word_count: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/crates/workflow-context/src/vgcs.rs b/crates/workflow-context/src/vgcs.rs index 4d19fae..4a1d813 100644 --- a/crates/workflow-context/src/vgcs.rs +++ b/crates/workflow-context/src/vgcs.rs @@ -1,7 +1,6 @@ use std::path::{Path, PathBuf}; use std::fs::{self, File}; -use std::io::{Cursor, Read, Write}; - +use std::io::{Cursor, Read, Write, BufRead, BufReader}; use anyhow::{Context, Result, anyhow}; use git2::{Repository, Oid, ObjectType, Signature, Index, IndexEntry, IndexTime}; use sha2::{Sha256, Digest}; @@ -97,7 +96,29 @@ impl ContextStore for Vgcs { _ => EntryKind::File, }; let object_id = entry.id().to_string(); - entries.push(DirEntry { name, kind, object_id }); + + // Metadata extraction (Expensive but necessary for the prompt) + let mut size = None; + let mut line_count = None; + let mut word_count = None; + + if kind == EntryKind::File { + if let Ok(object) = entry.to_object(&repo) { + if let Some(blob) = object.as_blob() { + let content = blob.content(); + size = Some(content.len() as u64); + + // Check for binary content or just use heuristic + if !content.contains(&0) { + let s = String::from_utf8_lossy(content); + line_count = Some(s.lines().count()); + word_count = Some(s.split_whitespace().count()); + } + } + } + } + + entries.push(DirEntry { name, kind, object_id, size, line_count, word_count }); } Ok(entries) @@ -338,4 +359,3 @@ fn create_index_entry(path: &str, mode: u32) -> IndexEntry { path: path.as_bytes().to_vec(), } } - diff --git a/docs/tasks/completed/20251127_refactor_context_mechanism.md b/docs/tasks/completed/20251127_refactor_context_mechanism.md new file mode 100644 index 0000000..133a83c --- /dev/null +++ b/docs/tasks/completed/20251127_refactor_context_mechanism.md @@ -0,0 +1,160 @@ +# 任务:重构分析模块上下文机制 (两阶段选择与统一 I/O 绑定的融合) + +**状态**: 设计中 (Finalizing) +**日期**: 2025-11-27 +**优先级**: 高 +**负责人**: @User / @Assistant + +## 1. 核心理念:意图与实现的解耦 + +我们经历了三个思维阶段,现在需要将其融合成一个完整的体系: +1. **Context Projection**: 模块需要从全局上下文中“投影”出自己需要的数据。 +2. **Two-Stage Selection**: 这种投影过程分为“选择(我需要什么?)”和“分析(怎么处理它?)”两个阶段,且都需要 Prompt/Model 驱动。 +3. **Unified I/O Binding**: 模块本身不应处理物理路径,应由 Orchestrator 负责 I/O 绑定。 + +**融合方案**: +* **Module 定义意图 (Intent)**: 模块通过 Configuration (Prompt/Rules) 描述“我需要什么样的输入”(例如:“我需要去年的财务数据” 或 “按此 Glob 规则匹配”)。 +* **Orchestrator 负责解析 (Resolution)**: Orchestrator(借助 IO Binder)根据模块的意图和当前的全局上下文状态,计算出具体的**物理路径**绑定。 +* **Module 执行实现 (Execution)**: 模块接收 Orchestrator 传来的物理路径,执行读取、分析和写入。 + +## 2. 架构设计 + +### 2.1. 模块配置:描述“我需要什么” + +`AnalysisModuleConfig` 依然保持两阶段结构,但这里的“Input/Context Selector”描述的是**逻辑需求**。 + +```rust +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AnalysisModuleConfig { + pub id: String, + + // Phase 1: Input Intent (我需要什么数据?) + pub context_selector: ContextSelectorConfig, + // Manual: 明确的规则 (e.g., "financials/*.json") + // Auto: 模糊的需求,交给 Orchestrator/Agent 自动推断 + // Hybrid: 具体的 Prompt (e.g., "Find all news about 'Environment' from last year") + + // Phase 2: Analysis Intent (怎么处理这些数据?) + pub analysis_prompt: String, + pub llm_config: Option, + + // Output Intent (结果是什么?) + // 模块只定义它产生什么类型的结果,物理路径由 Orchestrator 分配 + pub output_type: String, // e.g., "markdown_report", "json_summary" +} +``` + +### 2.2. Orchestrator 运行时:解析“在哪里” + +Orchestrator 在调度任务前,会执行一个 **Resolution Step**。 + +* **对于 Manual Selector**: + * Orchestrator 根据规则(Glob)在当前 VGCS Head Commit 中查找匹配的文件。 + * 生成具体的 `InputBindings` (Map)。 +* **对于 Auto/Hybrid Selector**: + * **这里是关键融合点**:Orchestrator (或专门的 Resolution Agent) 会运行一个轻量级的 LLM 任务。 + * Input: 当前 VGCS 目录树 + 模块定义的 Selection Prompt (或 Auto 策略)。 + * Output: 具体的 VGCS 文件路径列表。 + * Orchestrator 将这些路径打包成 `InputBindings`。 + +### 2.3. 模块执行:执行“转换” + +当模块真正启动时(Worker 接收到 Command),它看到的是**已经被解析过**的确定的世界。 + +```rust +// 最终发给 Worker 的指令 +pub struct GenerateReportCommand { + pub request_id: Uuid, + pub commit_hash: String, // 锁定的世界状态 + + // 具体的 I/O 绑定 (由 Orchestrator 解析完毕) + pub input_bindings: Vec, // e.g., ["raw/tushare/AAPL/financials.json", ...] + pub output_path: String, // e.g., "analysis/financial_v1/report.md" + + // 分析逻辑 (透传给 Worker) + pub analysis_prompt: String, + pub llm_config: Option, +} +``` + +**变化点**: +* **复杂的 Selection 逻辑上移**:原本打算放在 Worker 里的 `Select_Smart` 逻辑,现在看来更适合作为 Orchestrator 的预处理步骤(或者一个独立的微任务)。 +* **Worker 变轻**:Worker 变得非常“傻”,只负责 `Read(paths) -> Expand -> Prompt -> Write(output_path)`。这就实现了真正的“模块只关注核心任务”。 +* **灵活性保留**:如果是 Auto/Hybrid 模式,Orchestrator 会动态决定 Input Bindings;如果是 Manual 模式,则是静态规则解析。对 Worker 来说,它收到的永远是确定的文件列表。 + +## 3. 实施路线图 (Revised) + +### Phase 1: 协议与配置 (Contracts) +1. 定义 `AnalysisModuleConfig` (包含 Selector, Prompt, LlmConfig)。 +2. 定义 `GenerateReportCommand` (包含 `input_bindings` 物理路径列表, `output_path`, `commit_hash`)。 + +### Phase 2: Orchestrator Resolution Logic +1. 实现 `ContextResolver` 组件: + * 支持 Glob 解析 (Manual)。 + * (后续) 支持 LLM 目录树推理 (Auto/Hybrid)。 +2. 在调度循环中,在生成 Command 之前调用 `ContextResolver`。 + +### Phase 3: 模块改造 (Module Refactor) +1. **Provider**: 接收 `output_path` (由 Orchestrator 按约定生成,如 `raw/{provider}/{symbol}`) 并写入。 +2. **Generator**: + * 移除所有选择逻辑。 + * 直接读取 `cmd.input_bindings` 中的文件。 + * 执行 Expander (JSON->Table 等)。 + * 执行 Prompt。 + * 写入 `cmd.output_path`。 + +## 4. 总结 +这个方案完美融合了我们的讨论: +* **Input/Output Symmetry**: 都在 Command 中明确绑定。 +* **Two-Stage**: + * Stage 1 (Selection) 发生在 **Orchestration Time** (解析 Binding)。 + * Stage 2 (Analysis) 发生在 **Execution Time** (Worker 运行)。 +* **Module Focus**: 模块不需要知道“去哪找”,只知道“给我这些文件,我给你那个结果”。 + +## 5. 实施步骤清单 (Checklist) + +### Phase 1: 协议与配置定义 (Contracts & Configs) +- [x] **Common Contracts**: 在 `services/common-contracts/src` 创建或更新 `configs.rs`。 + - [x] 定义 `SelectionMode` (Manual, Auto, Hybrid)。 + - [x] 定义 `LlmConfig` (model_id, parameters)。 + - [x] 定义 `ContextSelectorConfig` (mode, rules, prompt, llm_config)。 + - [x] 定义 `AnalysisModuleConfig` (id, selector, analysis_prompt, llm_config, output_type)。 +- [x] **Messages**: 更新 `services/common-contracts/src/messages.rs`。 + - [x] `GenerateReportCommand`: 添加 `commit_hash`, `input_bindings: Vec`, `output_path: String`, `llm_config`. + - [x] `FetchCompanyDataCommand`: 添加 `output_path: Option`. +- [x] **VGCS Types**: 确保 `workflow-context` crate 中的类型足以支持路径操作。(Confirmed: Vgcs struct has methods) + +### Phase 2: Orchestrator 改造 (Resolution Logic) +- [x] **Context Resolver**: 在 `workflow-orchestrator-service` 中创建 `context_resolver.rs`。 + - [x] 实现 `resolve_input(selector, vgcs_client, commit_hash) -> Result>`。 + - [x] 针对 `Manual` 模式:实现 Glob 匹配逻辑 (调用 VGCS `list_dir` 递归查找)。 + - [x] 针对 `Auto/Hybrid` 模式:(暂留接口) 返回 Empty 或 NotImplemented,后续接入 LLM。 +- [x] **IO Binder**: 实现 `io_binder.rs`。 + - [x] 实现 `allocate_output_path(task_type, task_id) -> String` 约定生成逻辑。 +- [x] **Scheduler**: 更新 `dag_scheduler.rs`。 + - [x] 在 dispatch 任务前,调用 `ContextResolver` 和 `IOBinder`。 + - [x] 将解析结果填入 Command。 + +### Phase 3: 写入端改造 (Provider Adaptation) +- [x] **Tushare Provider**: 更新 `services/tushare-provider-service/src/generic_worker.rs`。 + - [x] 读取 Command 中的 `output_path` (如果存在)。 + - [x] 使用 `WorkerContext` 写入数据到指定路径 (不再硬编码 `raw/tushare/...`,而是信任 Command)。 + - [x] 提交并返回 New Commit Hash。 + +### Phase 4: 读取端改造 (Report Generator Adaptation) +- [x] **Worker Refactor**: 重写 `services/report-generator-service/src/worker.rs`。 + - [x] **Remove**: 删除 `fetch_data_and_configs` (旧的 DB 读取逻辑)。 + - [x] **Checkout**: 使用 `vgcs.checkout(cmd.commit_hash)`。 + - [x] **Read Input**: 遍历 `cmd.input_bindings`,使用 `vgcs.read_file` 读取内容。 + - [x] **Expand**: 实现简单 `Expander` (JSON -> Markdown Table)。 + - [x] **Prompt**: 渲染 `cmd.analysis_prompt`。 + - [x] **LLM Call**: 使用 `cmd.llm_config` 初始化 Client 并调用。 + - [x] **Write Output**: 将结果写入 `cmd.output_path`。 + - [x] **Commit**: 提交更改并广播 Event。 + +### Phase 5: 集成与验证 (Integration) +- [x] **Config Migration**: 更新 `config/analysis-config.json` (或 DB 中的配置),适配新的 `AnalysisModuleConfig` 结构。 +- [ ] **End-to-End Test**: 运行完整流程,验证: + 1. Provider 写文件到 Git。 + 2. Orchestrator 解析路径。 + 3. Generator 读文件并生成报告。 diff --git a/docs/tasks/completed/20251128_refactor_worker_generic.md b/docs/tasks/completed/20251128_refactor_worker_generic.md new file mode 100644 index 0000000..f946bd0 --- /dev/null +++ b/docs/tasks/completed/20251128_refactor_worker_generic.md @@ -0,0 +1,71 @@ +# 任务:重构 Report Worker 为通用执行器 (Generic Execution) + +**状态**: 规划中 -> 实施准备中 +**优先级**: 高 +**相关组件**: `report-generator-service`, `common-contracts` + +## 1. 问题背景 + +当前的 `report-generator-service/src/worker.rs` 存在严重的设计缺陷:**业务逻辑泄露**。 + +Worker 代码中硬编码了对 `financials.json` 的特殊处理逻辑(反序列化 `TimeSeriesFinancialDto` 并转换为 Markdown Table)。这导致 Worker 不再是一个通用的分析执行器,而是与特定的财务分析业务强耦合。这违背了系统设计的初衷,即 Worker 应该只负责通用的 `IO -> Context Assembly -> LLM` 流程。 + +## 2. 目标 + +将 Worker 彻底重构为 **Generic Analysis Worker**。它不应该知道什么是 "Financials",什么是 "Profile"。它只知道: +1. 我有输入文件(JSON, Text, etc.)。 +2. 我需要把它们转换成 Prompt Context(优先对人类可读,如 YAML)。 +3. 我调用 LLM。 +4. 我写入结果。 + +## 3. 核心变更点 + +### 3.1 移除硬编码的 DTO 解析 +* **彻底删除** `worker.rs` 中所有关于 `TimeSeriesFinancialDto` 的引用。Worker 不应该知道任何业务特定的数据结构。 +* 删除 `formatter.rs` 中专门针对 Financials 的表格生成逻辑。 + +### 3.2 通用格式化策略 (Generic Formatter) -> YAML First +我们需要一种通用的方式来将结构化数据展示给 LLM,同时兼顾人类调试时的可读性。 + +**方案: YAML Pretty Print (首选)** +* **理由**:YAML 相比 JSON 更干净,没有大量的括号和引号,人类阅读体验更好。LLM 对 YAML 的理解能力也很好。既然我们目前处于开发调试阶段,**人类可读性 (Human Readability)** 优于极致的 Token 效率。 +* **策略**: + * 尝试将输入文件内容解析为 JSON Value。 + * 如果成功,将其转换为 **YAML** 字符串。 + * 如果解析失败(非结构化文本),则保持原样 (Raw Text)。 +* **Context 结构**:避免使用 XML Tags,采用更直观的分隔符。 + ```yaml + --- + # Data Source: financials.json (Original Size: 1.2MB) + data: + - date: 2023-12-31 + revenue: 10000 + ... + ``` + +### 3.3 增强的 Execution Trace 与截断策略 +* **Sidecar Log**: 必须记录详细的执行过程。 +* **截断策略 (Truncation)**: + * 保留字符级截断作为最后的安全防线 (Safety Net)。 + * **Critical Logging**: 一旦发生截断,必须在 Log 中留下醒目的警告。 + * **详细信息**: 必须记录“截断前大小” vs “截断后大小”(例如:`Original: 1MB, Truncated to: 64KB`),让开发者清楚意识到数据丢失的程度。 + +## 4. 实施步骤 + +1. **Cleanup**: 移除 `worker.rs` 和 `formatter.rs` 中所有特定业务 DTO 的代码。 +2. **Generic Implementation**: + * 引入 `serde_yaml` 依赖。 + * 实现通用的 `context_builder`: + * Input -> `serde_json::Value` -> `serde_yaml::to_string`. + * Fallback: Raw Text. + * 组装 Context String。 +3. **Safety & Logging**: + * 实现截断逻辑,计算 `original_size` 和 `truncated_size`。 + * 在 `execution_trace.md` 中记录详细的文件处理情况。 +4. **Verify**: 运行测试,查看生成的 Context 是否清晰易读。 + +## 5. 预期效果 + +* **解耦**: 彻底切断 Worker 与 Financial Domain 的耦合。 +* **直观**: Context 变得像配置文件一样易读,方便人工 Review LLM 的输入。 +* **透明**: 明确知道哪些数据喂给了 LLM,哪些被截断了。 diff --git a/frontend/src/api/schema.gen.ts b/frontend/src/api/schema.gen.ts index 0a9175e..34557ec 100644 --- a/frontend/src/api/schema.gen.ts +++ b/frontend/src/api/schema.gen.ts @@ -1,18 +1,43 @@ import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core"; import { z } from "zod"; -export type AnalysisTemplateSet = { - modules: Record; - name: string; -}; export type AnalysisModuleConfig = { + analysis_prompt: string; + context_selector: ContextSelectorConfig; dependencies: Array; - model_id: string; + id?: (string | null) | undefined; + llm_config?: (null | LlmConfig) | undefined; name: string; - prompt_template: string; - provider_id: string; + output_type: string; }; -export type AnalysisTemplateSets = Record; +export type ContextSelectorConfig = SelectionMode; +export type SelectionMode = + | { + Manual: { + rules: Array; + }; + } + | { + Auto: Partial<{ + llm_config: null | LlmConfig; + }>; + } + | { + Hybrid: { + llm_config?: (null | LlmConfig) | undefined; + selection_prompt: string; + }; + }; +export type LlmConfig = Partial<{ + max_tokens: number | null; + model_id: string | null; + temperature: number | null; +}>; +export type AnalysisTemplateSet = { + modules: {}; + name: string; +}; +export type AnalysisTemplateSets = {}; export type ConfigFieldSchema = { default_value?: (string | null) | undefined; description?: (string | null) | undefined; @@ -41,9 +66,9 @@ export type DataSourceConfig = { provider: DataSourceProvider; }; export type DataSourceProvider = "Tushare" | "Finnhub" | "Alphavantage" | "Yfinance"; -export type DataSourcesConfig = Record; +export type DataSourcesConfig = {}; export type HealthStatus = { - details: Record; + details: {}; module_id: string; status: ServiceStatus; version: string; @@ -60,7 +85,7 @@ export type LlmModel = { model_id: string; name?: (string | null) | undefined; }; -export type LlmProvidersConfig = Record; +export type LlmProvidersConfig = {}; export type ProviderMetadata = { config_schema: Array; description: string; @@ -119,7 +144,9 @@ export type WorkflowEvent = } | { payload: { + input_commit?: (string | null) | undefined; message?: (string | null) | undefined; + output_commit?: (string | null) | undefined; progress?: (number | null) | undefined; status: TaskStatus; task_id: string; @@ -148,7 +175,7 @@ export type WorkflowEvent = | { payload: { end_timestamp: number; - result_summary?: unknown; + result_summary?: unknown | undefined; }; type: "WorkflowCompleted"; } @@ -163,25 +190,58 @@ export type WorkflowEvent = | { payload: { task_graph: WorkflowDag; - tasks_output: Record; - tasks_status: Record; + tasks_output: {}; + tasks_status: {}; timestamp: number; }; type: "WorkflowStateSnapshot"; }; -export const AnalysisModuleConfig: z.ZodType = z.object({ +export const LlmConfig = z + .object({ + max_tokens: z.union([z.number(), z.null()]), + model_id: z.union([z.string(), z.null()]), + temperature: z.union([z.number(), z.null()]), + }) + .partial(); +export const SelectionMode = z.union([ + z + .object({ Manual: z.object({ rules: z.array(z.string()) }).passthrough() }) + .passthrough(), + z + .object({ + Auto: z + .object({ llm_config: z.union([z.null(), LlmConfig]) }) + .partial() + .passthrough(), + }) + .passthrough(), + z + .object({ + Hybrid: z + .object({ + llm_config: z.union([z.null(), LlmConfig]).optional(), + selection_prompt: z.string(), + }) + .passthrough(), + }) + .passthrough(), +]); +export const ContextSelectorConfig = SelectionMode; +export const AnalysisModuleConfig = z.object({ + analysis_prompt: z.string(), + context_selector: ContextSelectorConfig, dependencies: z.array(z.string()), - model_id: z.string(), + id: z.union([z.string(), z.null()]).optional(), + llm_config: z.union([z.null(), LlmConfig]).optional(), name: z.string(), - prompt_template: z.string(), - provider_id: z.string(), + output_type: z.string(), }); -export const AnalysisTemplateSet: z.ZodType = z.object({ +export const AnalysisTemplateSet = z.object({ modules: z.record(AnalysisModuleConfig), name: z.string(), }); -export const AnalysisTemplateSets: z.ZodType = +export const AnalysisTemplateSets = z.record(AnalysisTemplateSet); export const DataSourceProvider = z.enum([ "Tushare", @@ -189,50 +249,36 @@ export const DataSourceProvider = z.enum([ "Alphavantage", "Yfinance", ]); -export const DataSourceConfig: z.ZodType = z.object({ +export const DataSourceConfig = z.object({ api_key: z.union([z.string(), z.null()]).optional(), api_url: z.union([z.string(), z.null()]).optional(), enabled: z.boolean(), provider: DataSourceProvider, }); -export const DataSourcesConfig: z.ZodType = +export const DataSourcesConfig = z.record(DataSourceConfig); -export type TestLlmConfigRequest = { - api_base_url: string; - api_key: string; - model_id: string; -}; export const TestLlmConfigRequest = z.object({ api_base_url: z.string(), api_key: z.string(), model_id: z.string(), }); -export const LlmModel: z.ZodType = z.object({ +export const LlmModel = z.object({ is_active: z.boolean(), model_id: z.string(), name: z.union([z.string(), z.null()]).optional(), }); -export const LlmProvider: z.ZodType = z.object({ +export const LlmProvider = z.object({ api_base_url: z.string(), api_key: z.string(), models: z.array(LlmModel), name: z.string(), }); -export const LlmProvidersConfig: z.ZodType = z.record(LlmProvider); -export type TestConfigRequest = { data: unknown; type: string }; +export const LlmProvidersConfig = z.record(LlmProvider); export const TestConfigRequest = z.object({ data: z.unknown(), type: z.string() }); -export type TestConnectionResponse = { - message: string; - success: boolean; -}; export const TestConnectionResponse = z.object({ message: z.string(), success: z.boolean(), }); -export type DiscoverPreviewRequest = { - api_base_url: string; - api_key: string; -}; export const DiscoverPreviewRequest = z.object({ api_base_url: z.string(), api_key: z.string(), @@ -249,7 +295,7 @@ export const ConfigKey = z.enum([ "SandboxMode", "Region", ]); -export const ConfigFieldSchema: z.ZodType = z.object({ +export const ConfigFieldSchema = z.object({ default_value: z.union([z.string(), z.null()]).optional(), description: z.union([z.string(), z.null()]).optional(), field_type: FieldType, @@ -259,7 +305,7 @@ export const ConfigFieldSchema: z.ZodType = z.object({ placeholder: z.union([z.string(), z.null()]).optional(), required: z.boolean(), }); -export const ProviderMetadata: z.ZodType = z.object({ +export const ProviderMetadata = z.object({ config_schema: z.array(ConfigFieldSchema), description: z.string(), icon_url: z.union([z.string(), z.null()]).optional(), @@ -268,37 +314,19 @@ export const ProviderMetadata: z.ZodType = z.object({ name_en: z.string(), supports_test_connection: z.boolean(), }); -export type SymbolResolveRequest = { - market?: (string | null) | undefined; - symbol: string; -}; export const SymbolResolveRequest = z.object({ market: z.union([z.string(), z.null()]).optional(), symbol: z.string(), }); -export type SymbolResolveResponse = { - market: string; - symbol: string; -}; export const SymbolResolveResponse = z.object({ market: z.string(), symbol: z.string(), }); -export type DataRequest = { - market?: (string | null) | undefined; - symbol: string; - template_id: string; -}; export const DataRequest = z.object({ market: z.union([z.string(), z.null()]).optional(), symbol: z.string(), template_id: z.string(), }); -export type RequestAcceptedResponse = { - market: string; - request_id: string; - symbol: string; -}; export const RequestAcceptedResponse = z.object({ market: z.string(), request_id: z.string().uuid(), @@ -310,7 +338,7 @@ export const ObservabilityTaskStatus = z.enum([ "Completed", "Failed", ]); -export const TaskProgress: z.ZodType = z.object({ +export const TaskProgress = z.object({ details: z.string(), progress_percent: z.number().int().gte(0), request_id: z.string().uuid(), @@ -320,19 +348,19 @@ export const TaskProgress: z.ZodType = z.object({ }); export const CanonicalSymbol = z.string(); export const ServiceStatus = z.enum(["Ok", "Degraded", "Unhealthy"]); -export const HealthStatus: z.ZodType = z.object({ +export const HealthStatus = z.object({ details: z.record(z.string()), module_id: z.string(), status: ServiceStatus, version: z.string(), }); -export const StartWorkflowCommand: z.ZodType = z.object({ +export const StartWorkflowCommand = z.object({ market: z.string(), request_id: z.string().uuid(), symbol: CanonicalSymbol, template_id: z.string(), }); -export const TaskDependency: z.ZodType = z.object({ +export const TaskDependency = z.object({ from: z.string(), to: z.string(), }); @@ -345,18 +373,18 @@ export const TaskStatus = z.enum([ "Skipped", ]); export const TaskType = z.enum(["DataFetch", "DataProcessing", "Analysis"]); -export const TaskNode: z.ZodType = z.object({ +export const TaskNode = z.object({ display_name: z.union([z.string(), z.null()]).optional(), id: z.string(), initial_status: TaskStatus, name: z.string(), type: TaskType, }); -export const WorkflowDag: z.ZodType = z.object({ +export const WorkflowDag = z.object({ edges: z.array(TaskDependency), nodes: z.array(TaskNode), }); -export const WorkflowEvent: z.ZodType = z.union([ +export const WorkflowEvent = z.union([ z .object({ payload: z @@ -369,7 +397,9 @@ export const WorkflowEvent: z.ZodType = z.union([ .object({ payload: z .object({ + input_commit: z.union([z.string(), z.null()]).optional(), message: z.union([z.string(), z.null()]).optional(), + output_commit: z.union([z.string(), z.null()]).optional(), progress: z.union([z.number(), z.null()]).optional(), status: TaskStatus, task_id: z.string(), @@ -410,7 +440,7 @@ export const WorkflowEvent: z.ZodType = z.union([ payload: z .object({ end_timestamp: z.number().int(), - result_summary: z.unknown(), + result_summary: z.unknown().optional(), }) .passthrough(), type: z.literal("WorkflowCompleted"), @@ -444,6 +474,9 @@ export const WorkflowEvent: z.ZodType = z.union([ ]); export const schemas = { + LlmConfig, + SelectionMode, + ContextSelectorConfig, AnalysisModuleConfig, AnalysisTemplateSet, AnalysisTemplateSets, @@ -479,7 +512,7 @@ export const schemas = { WorkflowEvent, }; -const endpoints = makeApi([ +export const endpoints = makeApi([ { method: "get", path: "/api/v1/configs/analysis_template_sets", diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 52682ec..963385c 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -45,13 +45,13 @@ export function Dashboard() { const missingConfigs: string[] = []; Object.values(selectedTemplate.modules).forEach(module => { - if (!llmProviders[module.provider_id]) { - missingConfigs.push(`Module '${module.name}': Provider '${module.provider_id}' not found`); - } else { - const provider = llmProviders[module.provider_id]; - const modelExists = provider.models.some(m => m.model_id === module.model_id); + const modelId = module.llm_config?.model_id; + if (modelId && llmProviders) { + const modelExists = Object.values(llmProviders).some(provider => + provider.models.some(m => m.model_id === modelId) + ); if (!modelExists) { - missingConfigs.push(`Module '${module.name}': Model '${module.model_id}' not found in provider '${provider.name}'`); + missingConfigs.push(`Module '${module.name}': Model '${modelId}' not found in any active provider`); } } }); diff --git a/frontend/src/pages/config/TemplateTab.tsx b/frontend/src/pages/config/TemplateTab.tsx index df28c26..cf9a474 100644 --- a/frontend/src/pages/config/TemplateTab.tsx +++ b/frontend/src/pages/config/TemplateTab.tsx @@ -1,6 +1,8 @@ -import { useState, useEffect } from "react" +import { useState, useEffect, useMemo } from "react" import { useAnalysisTemplates, useUpdateAnalysisTemplates, useLlmProviders } from "@/hooks/useConfig" import { AnalysisTemplateSet, AnalysisModuleConfig } from "@/types/config" +import { schemas } from "@/api/schema.gen" +import { z } from "zod" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { ScrollArea } from "@/components/ui/scroll-area" @@ -54,53 +56,53 @@ export function TemplateTab() { }); } - const handleDeleteTemplate = (id: string) => { - if (!templates) return; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { [id]: removed, ...rest } = templates; - updateTemplates.mutate(rest, { - onSuccess: () => { - toast({ title: "Success", description: "Template deleted" }); - if (selectedId === id) setSelectedId(null); - }, - onError: () => toast({ title: "Error", description: "Failed to delete template", type: "error" }) - }); - } + const handleDeleteTemplate = (id: string) => { + if (!templates) return; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { [id]: removed, ...rest } = templates; + updateTemplates.mutate(rest, { + onSuccess: () => { + toast({ title: "Success", description: "Template deleted" }); + if (selectedId === id) setSelectedId(null); + }, + onError: () => toast({ title: "Error", description: "Failed to delete template", type: "error" }) + }); + } - const activeTemplate = (templates && selectedId) ? templates[selectedId] : null; + const activeTemplate = (templates && selectedId) ? (templates as Record)[selectedId] : null; - return ( -
- {/* Sidebar List */} -
-
-

模板列表

-

选择一个分析流程模板进行编辑。

-
- -
- {templates && Object.entries(templates).map(([id, t]) => ( -
- - {/* Delete button visible on hover */} - -
- ))} + return ( +
+ {/* Sidebar List */} +
+
+

模板列表

+

选择一个分析流程模板进行编辑。

- + +
+ {templates && Object.entries(templates).map(([id, t]) => ( +
+ + {/* Delete button visible on hover */} + +
+ ))} +
+
+
+
+ + +
+ + {currentMode === 'Manual' && ( +
+ +