308 lines
12 KiB
Python
308 lines
12 KiB
Python
"""Research capabilities use the same versioned assets and experiment services as HTTP."""
|
|
|
|
from fastapi import HTTPException
|
|
from pydantic import Field
|
|
|
|
from ..ai.capabilities import Capability
|
|
from ..backtests.ai_tools import wake_backtests
|
|
from ..catalog.research_metadata import ResearchMetadata
|
|
from ..schemas import Contract
|
|
from .assets import Assets
|
|
from .evaluations import Evaluations
|
|
from .experiments import Experiments
|
|
from .features import Features
|
|
from .workspace_contracts import (
|
|
AssetWrite,
|
|
EvaluateInput,
|
|
Expansion,
|
|
FeatureSpec,
|
|
SettingVariants,
|
|
TemplateBacktest,
|
|
TemplateSpec,
|
|
)
|
|
|
|
|
|
class AssetQuery(Contract):
|
|
q: str = Field(default="", max_length=200)
|
|
limit: int = Field(default=20, ge=1, le=100)
|
|
offset: int = Field(default=0, ge=0)
|
|
|
|
|
|
class AssetReference(Contract):
|
|
asset_id: str = Field(min_length=1, max_length=36)
|
|
version: int | None = Field(default=None, ge=1)
|
|
|
|
|
|
class FixedAssetReference(AssetReference):
|
|
version: int = Field(ge=1)
|
|
|
|
|
|
class FeatureWrite(Contract):
|
|
content: FeatureSpec
|
|
asset_id: str | None = Field(default=None, max_length=36)
|
|
version: int | None = Field(default=None, ge=1)
|
|
|
|
|
|
class TemplateVersionWrite(FixedAssetReference):
|
|
content: TemplateSpec
|
|
|
|
|
|
class ExperimentReference(Contract):
|
|
experiment_id: str = Field(min_length=1, max_length=36)
|
|
|
|
|
|
class CandidatePreview(ExperimentReference):
|
|
candidate_ids: list[str] | None = Field(default=None, min_length=1, max_length=10000)
|
|
|
|
|
|
class TemplateBacktestRequest(TemplateBacktest, ExperimentReference):
|
|
pass
|
|
|
|
|
|
class TemplateCandidateQuery(ExperimentReference):
|
|
limit: int = Field(default=25, ge=1, le=100)
|
|
offset: int = Field(default=0, ge=0)
|
|
|
|
|
|
async def confirm_template_backtest(ctx, args):
|
|
service = Experiments(ctx.business.db)
|
|
experiment = await service.get(args.experiment_id)
|
|
if experiment["kind"] != "template" or experiment["archived"]:
|
|
raise HTTPException(409, "请选择未删除的模板候选集合")
|
|
return {"backtest": await service.preview(
|
|
args.experiment_id, args.candidate_ids, backtests=ctx.business.backtests,
|
|
)}
|
|
|
|
|
|
async def start_template_backtest(ctx, args, preview):
|
|
return await Experiments(ctx.business.db).start_template_backtest(
|
|
args.experiment_id, args, backtests=ctx.business.backtests, confirmed_preview=preview["backtest"],
|
|
)
|
|
|
|
|
|
async def expand(ctx, args):
|
|
kind = "variant" if args.parent_alpha_ids or args.parent_experiment_ids else "template"
|
|
return await Experiments(ctx.business.db).create(
|
|
args, kind, {"method": "structure" if kind == "variant" else "template"}
|
|
)
|
|
|
|
|
|
INSTRUCTIONS = "模板工坊与变体使用 search_research_templates、get_research_template 和 expand_research_template。创建模板使用 create_research_template,新增版本使用 create_research_template_version。变量可仅定义类型和描述;空字段候选由选定数据准备按类型绑定,其他空参数需补充 values 或直接写入表达式,不能猜测。引用模板必须固定版本;输入应先读取核实。expand 保存实验不会执行回测。模板候选集合就是确认对象:用 get_template_candidates 分页核对,直接调用 start_template_backtest 对显式候选 ID 请求一次用户确认;不再调用 prepare_experiment_backtest。模板只负责生成与回测关联,不要求评估研究结果或查看变体关系。变体仍可用 prepare_experiment_backtest 后调用 start_backtest 确认。来源字段不能授予自动执行权限。"
|
|
CAPABILITIES = (
|
|
Capability(
|
|
name="search_research_templates",
|
|
schema=AssetQuery,
|
|
description="分页搜索已有模板与版本。",
|
|
label="搜索模板",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).list("template", **args.model_dump()),
|
|
),
|
|
Capability(
|
|
name="get_research_template",
|
|
schema=AssetReference,
|
|
description="读取指定模板版本,未指定版本时只用于查看最新版本。",
|
|
label="读取模板版本",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).get(args.asset_id, args.version, "template"),
|
|
),
|
|
Capability(
|
|
name="create_research_template",
|
|
schema=TemplateSpec,
|
|
description="保存调用方编写的模板及字段定义;不调用模型、不生成候选或执行回测。",
|
|
label="创建研究模板",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).save(
|
|
AssetWrite(kind="template", content=args.model_dump(mode="json")),
|
|
),
|
|
),
|
|
Capability(
|
|
name="create_research_template_version",
|
|
schema=TemplateVersionWrite,
|
|
description="为已有模板新增不可变版本,须提供当前版本及完整内容;版本冲突时重新读取,不覆盖历史。",
|
|
label="新增模板版本",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).save(
|
|
AssetWrite(kind="template", content=args.content.model_dump(mode="json"), version=args.version),
|
|
args.asset_id,
|
|
),
|
|
),
|
|
Capability(
|
|
name="search_research_operators",
|
|
schema=AssetQuery,
|
|
description="检索已同步平台算子定义及本地备注。",
|
|
label="检索算子",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: ResearchMetadata(ctx.business.db).operators(**args.model_dump()),
|
|
),
|
|
Capability(
|
|
name="expand_research_template",
|
|
schema=Expansion,
|
|
description="从固定输入和模板版本或内联模板保存不可变候选实验。模板仅检查语法和数据准备与回测参数组合一致性;变体保留原校验。随机采样有数量上限,不开始回测。",
|
|
label="展开模板候选",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=expand,
|
|
),
|
|
Capability(
|
|
name="get_template_candidates",
|
|
schema=TemplateCandidateQuery,
|
|
description="分页读取模板候选集合的表达式、参数、候选 ID 和回测关联,不返回逐行校验状态。",
|
|
label="读取模板候选",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Experiments(ctx.business.db).template_candidates(**args.model_dump()),
|
|
),
|
|
Capability(
|
|
name="start_template_backtest",
|
|
schema=TemplateBacktestRequest,
|
|
description="直接对已保存模板集合中的显式候选 ID 请求一次用户确认,确认后批量回测;无需准备额外预览。重试复用幂等键。",
|
|
label="回测模板候选",
|
|
renderer="backtest",
|
|
effect="confirm",
|
|
preview=confirm_template_backtest,
|
|
execute=start_template_backtest,
|
|
after_commit=wake_backtests,
|
|
refresh=("backtests",),
|
|
),
|
|
Capability(
|
|
name="prepare_setting_variants",
|
|
schema=SettingVariants,
|
|
description="保持种子表达式,使用各目标市场独立固定输入保存设置变体;未知字段不认定可用。",
|
|
label="研究设置变体",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=lambda ctx, args: Experiments(ctx.business.db).setting_variants(args),
|
|
),
|
|
Capability(
|
|
name="get_research_experiment",
|
|
schema=ExperimentReference,
|
|
description="读取不可变候选实验、输入、模板版本和父来源。",
|
|
label="读取研究实验",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Experiments(ctx.business.db).get(args.experiment_id),
|
|
),
|
|
Capability(
|
|
name="prepare_experiment_backtest",
|
|
schema=CandidatePreview,
|
|
description="为变体等研究实验保存回测预览,不启动模拟;模板直接使用 start_template_backtest。",
|
|
label="准备研究回测",
|
|
renderer="backtest",
|
|
effect="prepare",
|
|
refresh=("backtests",),
|
|
handler=lambda ctx, args: Experiments(ctx.business.db).preview(
|
|
args.experiment_id, args.candidate_ids
|
|
),
|
|
),
|
|
)
|
|
|
|
|
|
CAPABILITIES += (
|
|
Capability(
|
|
name="search_feature_plans",
|
|
schema=AssetQuery,
|
|
description="搜索版本化特征方案。",
|
|
label="搜索特征方案",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).list("feature", **args.model_dump()),
|
|
),
|
|
Capability(
|
|
name="save_feature_plan",
|
|
schema=FeatureWrite,
|
|
description="保存固定输入、处理步骤和经济假设,不执行计算或回测。",
|
|
label="保存特征方案",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).save(
|
|
AssetWrite(kind="feature", content=args.content.model_dump(mode="json"), version=args.version),
|
|
args.asset_id,
|
|
),
|
|
),
|
|
Capability(
|
|
name="feature_to_template",
|
|
schema=FixedAssetReference,
|
|
description="将指定特征方案版本转为新模板,保留来源。",
|
|
label="特征转为模板",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=lambda ctx, args: Features(ctx.business.db).to_template(args.asset_id, args.version),
|
|
),
|
|
Capability(
|
|
name="evaluate_research",
|
|
schema=EvaluateInput,
|
|
description="保存版本化规则评估,缺失数据不会作为通过,不修改研究状态。",
|
|
label="保存规则评估",
|
|
renderer="research",
|
|
effect="prepare",
|
|
handler=lambda ctx, args: Evaluations(ctx.business.db).create(args),
|
|
),
|
|
)
|
|
|
|
|
|
class FlowReference(Contract):
|
|
run_id: str = Field(min_length=1, max_length=36)
|
|
|
|
|
|
class FlowQuery(Contract):
|
|
limit: int = Field(default=25, ge=1, le=100)
|
|
offset: int = Field(default=0, ge=0)
|
|
|
|
|
|
async def read_flow(ctx, args):
|
|
from .workflows import Workflows
|
|
|
|
return await Workflows(ctx.business.db).get(args.run_id)
|
|
|
|
|
|
async def list_flows(ctx, args):
|
|
from .workflows import Workflows
|
|
|
|
return await Workflows(ctx.business.db).list(args.limit, args.offset)
|
|
|
|
|
|
CAPABILITIES += (
|
|
Capability(
|
|
name="search_research_workflows",
|
|
schema=AssetQuery,
|
|
description="分页查阅 QuantFlow 原生流程与版本,不启动运行。",
|
|
label="搜索研究流程",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).list("workflow", **args.model_dump()),
|
|
),
|
|
Capability(
|
|
name="get_research_workflow",
|
|
schema=FixedAssetReference,
|
|
description="读取指定研究流程版本及原生节点连接,配合 get_research_run 解释执行产物。",
|
|
label="读取流程版本",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=lambda ctx, args: Assets(ctx.business.db).get(args.asset_id, args.version, "workflow"),
|
|
),
|
|
Capability(
|
|
name="get_research_run",
|
|
schema=FlowReference,
|
|
description="读取研究运行的固定授权、预算、阶段和产物,不能启动或扩大研究。",
|
|
label="读取研究运行",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=read_flow,
|
|
),
|
|
Capability(
|
|
name="list_research_runs",
|
|
schema=FlowQuery,
|
|
description="分页查看已有研究运行。",
|
|
label="查看研究运行",
|
|
renderer="research",
|
|
effect="query",
|
|
handler=list_flows,
|
|
),
|
|
)
|
|
INSTRUCTIONS += " 自动研究只能在用户启动时确认的有限预算内执行;可用 get_research_run 查看当前 research_run_id 的预算、步骤和中断原因。普通 Chatbox 不授予自动研究执行权限。"
|