This commit is contained in:
jack
2026-03-26 08:48:53 +08:00
parent 0e39f21012
commit 8564a6ec00
14 changed files with 1070 additions and 440 deletions
+191
View File
@@ -0,0 +1,191 @@
# -*- coding: utf-8 -*-
import random
import time
from xmlrpc.client import ServerProxy, Fault
is_local = 0
if is_local:
url = 'http://127.0.0.1:28888'
else:
url = 'http://192.168.31.41:32000'
username = 'rpc'
password = 'aaaAAA111'
db = "quantify"
common = ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, password, {})
models = ServerProxy(f"{url}/xmlrpc/2/object")
# 1. 在 alpha.generate.research.direction 表中创建一条数据
# 需要准备: region_id 和 universe_id
region = 'USA'
universe = 'TOP3000'
# 根据名称搜索 region 和 universe 的ID
region_ids = models.execute_kw(db, uid, password, 'alpha.region.settings', 'search', [[['name', '=', region]]], {'limit': 1})
universe_ids = models.execute_kw(db, uid, password, 'alpha.universe.settings', 'search', [[['name', '=', universe]]], {'limit': 1})
if not region_ids:
print(f"没有找到 region: '{region}',请检查名称是否正确")
exit(1)
if not universe_ids:
print(f"没有找到 universe: '{universe}',请检查名称是否正确")
exit(1)
region_id = region_ids[0]
universe_id = universe_ids[0]
# 获取 region 和 universe 的 name
region_data = models.execute_kw(db, uid, password, 'alpha.region.settings', 'read', [[region_id], ['name']])
universe_data = models.execute_kw(db, uid, password, 'alpha.universe.settings', 'read', [[universe_id], ['name']])
region_name = region_data[0]['name'] if region_data else 'Unknown'
universe_name = universe_data[0]['name'] if universe_data else 'Unknown'
print(f"使用 region_id: {region_id} (name: {region_name}), universe_id: {universe_id} (name: {universe_name})")
# 随机选择一个 llm_settings_line_id
llm_line_ids = models.execute_kw(db, uid, password, 'llm.settings.line', 'search', [[['id', '!=', 0]]])
if not llm_line_ids:
print("没有找到 llm.settings.line 记录")
exit(1)
llm_settings_line_id = random.choice(llm_line_ids)
llm_line_data = models.execute_kw(db, uid, password, 'llm.settings.line', 'read', [[llm_settings_line_id], ['display_name', 'model_name']])
llm_display_name = llm_line_data[0].get('display_name', 'Unknown') if llm_line_data else 'Unknown'
print(f"随机选择 llm_settings_line_id: {llm_settings_line_id} (name: {llm_display_name})")
# 搜索 Research Direction Prompt
rd_prompt_ids = models.execute_kw(db, uid, password, 'alpha.prompt.settings', 'search', [[['prompt_type', '=', 'Research Direction']]], {'limit': 1})
if not rd_prompt_ids:
print("没有找到 prompt_type='Research Direction' 的 alpha.prompt.settings 记录")
exit(1)
research_direction_prompt_id = rd_prompt_ids[0]
print(f"使用 Research Direction Prompt ID: {research_direction_prompt_id}")
# 搜索 Feature Engineering Prompt
fe_prompt_ids = models.execute_kw(db, uid, password, 'alpha.prompt.settings', 'search', [[['prompt_type', '=', 'Feature Engineering']]], {'limit': 1})
if not fe_prompt_ids:
print("没有找到 prompt_type='Feature Engineering' 的 alpha.prompt.settings 记录")
exit(1)
feature_engineering_prompt_id = fe_prompt_ids[0]
print(f"使用 Feature Engineering Prompt ID: {feature_engineering_prompt_id}")
# 创建研究方向记录
record_vals = {
'region': region_id,
'universe': universe_id,
'llm_settings_line_id': llm_settings_line_id,
'research_direction_prompt': research_direction_prompt_id,
'feature_engineering_prompt': feature_engineering_prompt_id,
}
record_id = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'create', [record_vals])
print(f"成功创建研究方向记录,ID: {record_id}")
# 2. 调用 btn_quick_selection_datasets 按钮方法
result = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'btn_quick_selection_datasets', [[record_id]])
print(f"btn_quick_selection_datasets 执行结果: {result}")
# 验证数据集行是否已创建
datasets_line_ids = models.execute_kw(db, uid, password, 'alpha.research.datasets.line', 'search', [[['research_direction_id', '=', record_id]]])
print(f"已创建的数据集行数量: {len(datasets_line_ids)}")
# 获取数据集行的 dataset_code(数据集名字)
if datasets_line_ids:
datasets_lines = models.execute_kw(db, uid, password, 'alpha.research.datasets.line', 'read', [datasets_line_ids, ['datasets_code']])
dataset_codes = [line.get('datasets_code') for line in datasets_lines if line.get('datasets_code')]
print(f"关联的数据集名字: {dataset_codes}")
print(f"数据集总数: {len(dataset_codes)}")
# 3. 点击 btn_generate_research 按钮
print("\n开始生成研究方向...")
try:
result = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'btn_generate_research', [[record_id]])
print(f"btn_generate_research 执行结果: {result}")
except Fault as e:
# 忽略 "cannot marshal None" 错误,因为方法返回 None 是正常的
if "cannot marshal None" in str(e):
print("btn_generate_research 已执行(返回 None,这是正常的)")
else:
raise
# 死循环等待 status = 'generated_research'
print("等待研究方向生成完成...")
while True:
time.sleep(5) # 每5秒检查一次
record_data = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'read', [[record_id], ['status', 'message']])
if not record_data:
print("记录不存在")
exit(1)
current_status = record_data[0].get('status')
message = record_data[0].get('message', '')
print(f"当前状态: {current_status}, 消息: {message}")
if current_status == 'generated_research':
print("研究方向生成完成!")
break
elif current_status == 'failed':
print(f"研究方向生成失败: {message}")
exit(1)
# 4. 点击 btn_generate_prepare_feature_engineering_prompt 按钮
print("\n开始生成特征工程提示词...")
try:
result = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'btn_generate_prepare_feature_engineering_prompt', [[record_id]])
print(f"btn_generate_prepare_feature_engineering_prompt 执行结果: {result}")
except Fault as e:
# 忽略 "cannot marshal None" 错误,因为方法返回 None 是正常的
if "cannot marshal None" in str(e):
print("btn_generate_prepare_feature_engineering_prompt 已执行(返回 None,这是正常的)")
else:
raise
# 5. 点击 btn_generate_feature 按钮
print("\n开始生成特征工程...")
try:
result = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'btn_generate_feature', [[record_id]])
print(f"btn_generate_feature 执行结果: {result}")
except Fault as e:
# 忽略 "cannot marshal None" 错误,因为方法返回 None 是正常的
if "cannot marshal None" in str(e):
print("btn_generate_feature 已执行(返回 None,这是正常的)")
else:
raise
# 死循环等待 status = 'done'
print("等待特征工程生成完成...")
while True:
time.sleep(5) # 每5秒检查一次
record_data = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'read', [[record_id], ['status', 'message']])
if not record_data:
print("记录不存在")
exit(1)
current_status = record_data[0].get('status')
message = record_data[0].get('message', '')
print(f"当前状态: {current_status}")
if current_status == 'done':
print("特征工程生成完成!")
break
elif current_status == 'failed':
print(f"特征工程生成失败: {message}")
exit(1)
# 6. 点击 btn_generate_idea 按钮
print("\n开始生成 Idea...")
try:
result = models.execute_kw(db, uid, password, 'alpha.generate.research.direction', 'btn_generate_idea', [[record_id]])
print(f"btn_generate_idea 执行结果: {result}")
except Fault as e:
# 忽略 "cannot marshal None" 错误,因为方法返回 None 是正常的
if "cannot marshal None" in str(e):
print("btn_generate_idea 已执行(返回 None,这是正常的)")
else:
raise
print("\n全部流程执行完成!收工!")
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from xmlrpc.client import ServerProxy
is_local = 0
if is_local:
url = 'http://127.0.0.1:28888'
else:
url = 'http://192.168.31.41:32000'
username = 'rpc'
password = 'aaaAAA111'
db = "quantify"
common = ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, password, {})
print(uid)
+17
View File
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from xmlrpc.client import ServerProxy
is_local = 0
if is_local:
url = 'http://127.0.0.1:28888'
else:
url = 'http://192.168.31.41:32000'
username = 'rpc'
password = 'aaaAAA111'
db = "quantify"
common = ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, password, {})
print(uid)
+519
View File
@@ -0,0 +1,519 @@
# -*- coding: utf-8 -*-
import uuid
import sys
import httpx
from odoo import models, fields
class AlphaGenerateResearchDirection(models.Model):
_name = 'alpha.generate.research.direction'
_description = 'Alpha Generate Research Direction'
_order = 'id desc'
name = fields.Char(string='Name', required=True, default=lambda self: str(uuid.uuid4()))
status = fields.Selection([
('draft', 'Draft'),
('generated_research', 'Generated Research'),
('generated_feature', 'Generated Feature'),
('done', 'Done'),
('failed', 'Failed'),
('cancel', 'Cancel')
], string='Status', default='draft')
region = fields.Many2one('alpha.region.settings', string='Region', required=True)
universe = fields.Many2one('alpha.universe.settings', string='Universe', required=True)
research_direction_prompt = fields.Many2one('alpha.prompt.settings', string='Research Direction Prompt')
feature_engineering_prompt = fields.Many2one('alpha.prompt.settings', string='Feature Engineering Prompt') # 用于加上替换的研究方向, 组合新的工程特征提示词
results = fields.Text(string='Results')
message = fields.Char(string='Message')
llm_settings_line_id = fields.Many2one('llm.settings.line', string='Model')
models_name = fields.Char(string='Model Name', related='llm_settings_line_id.model_name')
provider_name = fields.Char(string='Provider Name', related='llm_settings_line_id.llm_setting_id.name')
research_direction = fields.Text(string='Research Direction')
prepare_feature_engineering_prompt = fields.Text(string='Feature Engineering Prompt') # 用于已组合完成, 准备生成工程特征的提示词
feature_engineering = fields.Text(string='Feature Engineering')
use_datasets = fields.Many2one('alpha.datasets', string='Use Datasets')
datasets_line_ids = fields.One2many('alpha.research.datasets.line', 'research_direction_id', string='Datasets Lines')
def _get_callback_url(self):
"""根据平台获取回调 URL"""
platform_info = sys.platform
if platform_info == "darwin":
# Mac 开发环境使用本地地址
base_url_odoo = self.env['ir.config_parameter'].sudo().get_param('web.base.url.local', 'http://127.0.0.1:8069')
else:
base_url_odoo = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
return f"{base_url_odoo}/api/alpha-generate-research-direction/result"
def btn_generate_research(self):
"""
生成研究方向按钮
通过选择的基础提示词,推送到 LLM 微服务生成研究方向
post 无需等待,LLM 会回写结果到 research_direction 字段
成功后状态变为 generated_research,失败不修改状态,失败信息写到 message
"""
self.ensure_one()
# 如果research_direction字段已存在, 则跳过不生成
if self.research_direction:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Research direction already exists, please delete it first.',
'sticky': False,
},
}
# 校验必填字段
if not self.research_direction_prompt:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please select a research direction prompt template.',
'type': 'danger',
'sticky': False
}
}
if not self.llm_settings_line_id:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please select a model.',
'type': 'danger',
'sticky': False
}
}
model_name = self.llm_settings_line_id.model_name
base_url = self.llm_settings_line_id.llm_setting_id.base_url
api_key = self.llm_settings_line_id.llm_setting_id.api_key
final_prompt = self.research_direction_prompt.prompt
ms_config = self.get_ms_config()
ms_url = ms_config.get('url')
callback_url = self._get_callback_url()
post_data = {
"record_id": self.id,
"odoo_model": self._name,
"action_type": "research_direction",
"prompt": final_prompt,
"model_name": model_name,
"base_url": base_url,
"api_key": api_key,
"callback_url": callback_url
}
if not ms_url:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Microservice URL is not available.',
'type': 'danger',
'sticky': False
}
}
# 发送请求,超短超时,不等待响应
try:
httpx.post(f'{ms_url}:32004/api_llm_generate', json=post_data, timeout=0.001)
except httpx.TimeoutException:
pass
except Exception as e:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': f'Failed to send microservices: {e}',
'type': 'danger',
'sticky': False
}
}
self.write({
'status': 'draft',
'message': 'Request sent to microservice, waiting for research direction...'
})
def btn_generate_prepare_feature_engineering_prompt(self):
# 手动填入生成好的研究方向, 跳过第一步的 llm 生成
self.ensure_one()
# 如果没有研究方向, 需要生成
if not self.research_direction or not self.feature_engineering_prompt:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Need to generate research direction and need select feature_engineering_prompt',
'sticky': False,
},
}
# 将已有的研究方向, 和基础的feature_engineering_prompt组合
# feature_engineering_prompt + research_direction
feature_engineering_prompt = self.feature_engineering_prompt.prompt
research_direction = self.research_direction
replaced_feature_engineering_prompt = feature_engineering_prompt.replace('[#replace#]', research_direction)
self.write({
'status': 'generated_research',
'prepare_feature_engineering_prompt': replaced_feature_engineering_prompt
})
return True
def btn_generate_feature(self):
"""
生成特征工程按钮
通过 feature_engineering_prompt 作为提示词,推送到 LLM 微服务生成特征工程
post 无需等待,LLM 会回写结果到 feature_engineering 字段
成功后状态变为 done,失败状态变为 failed,失败信息写到 message
"""
self.ensure_one()
# 如果未生成prepare_feature_engineering_prompt, 则不允许使用此按钮
if not self.prepare_feature_engineering_prompt:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please generate prepare_feature_engineering_prompt first',
'type': 'danger',
'sticky': False
}
}
# 如果feature_engineering字段不为空, 则提示需要清空才可以使用这个按钮, 避免重复生成
if self.feature_engineering:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please clear feature_engineering field first',
'type': 'danger',
'sticky': False
}
}
# 校验状态
if self.status != 'generated_research':
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please generate research direction first.',
'type': 'danger',
'sticky': False
}
}
# 校验必填字段
if not self.research_direction:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Research direction is empty, please generate it first.',
'type': 'danger',
'sticky': False
}
}
if not self.llm_settings_line_id:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please select a model.',
'type': 'danger',
'sticky': False
}
}
if not self.feature_engineering_prompt:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Please select a feature engineering prompt template.',
'type': 'danger',
'sticky': False
}
}
model_name = self.llm_settings_line_id.model_name
base_url = self.llm_settings_line_id.llm_setting_id.base_url
api_key = self.llm_settings_line_id.llm_setting_id.api_key
# 使用 feature_engineering_prompt 作为提示词
final_prompt = self.feature_engineering_prompt.prompt
ms_config = self.get_ms_config()
ms_url = ms_config.get('url')
callback_url = self._get_callback_url()
post_data = {
"record_id": self.id,
"odoo_model": self._name,
"action_type": "feature_engineering",
"prompt": final_prompt,
"model_name": model_name,
"base_url": base_url,
"api_key": api_key,
"callback_url": callback_url
}
if not ms_url:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Microservice URL is not available.',
'type': 'danger',
'sticky': False
}
}
# 发送请求,超短超时,不等待响应
try:
httpx.post(f'{ms_url}:32004/api_llm_generate', json=post_data, timeout=0.001)
except httpx.TimeoutException:
pass
except Exception as e:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Failed to send microservice.',
'type': 'danger',
'sticky': False
}
}
self.write({
'status': 'generated_research',
'message': 'Request sent to microservice, waiting for feature engineering...'
})
def get_ms_config(self):
# TODO 先从 nacos 获取微服务 url
nacos_url = ''
platform_info = sys.platform
if platform_info == "darwin":
nacos_url = 'http://192.168.31.41:30848/nacos/v1/cs/configs?dataId=microservices_dev&group=quantify'
elif platform_info.startswith("linux"):
nacos_url = 'http://192.168.31.41:30848/nacos/v1/cs/configs?dataId=microservices&group=quantify'
try:
ms_config_resp = httpx.get(nacos_url)
ms_config_resp.raise_for_status()
except Exception as e:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'Nacos request failed.',
'type': 'danger',
'sticky': False
}
}
ms_config = ms_config_resp.json()
return ms_config
def btn_generate_idea(self):
# 最后一步, 当工程特征已经生成, 并且已选择数据字段, 则生成idea
# 同一份特征工程, 多个数据字段, 生成与数据字段相同数量的 idea
self.ensure_one()
# if self.status != 'generated_feature':
# return {
# 'type': 'ir.actions.client',
# 'tag': 'display_notification',
# 'params': {
# 'title': 'ERROR',
# 'message': 'Feature engineering not completed.',
# 'type': 'danger',
# 'sticky': False
# }
# }
if not self.datasets_line_ids:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'No data fields selected.',
'type': 'danger',
'sticky': False
}
}
if not self.feature_engineering:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'ERROR',
'message': 'No feature engineering selected.',
'type': 'danger',
'sticky': False
}
}
# 开始生成 idea, 先获取到数据集的 name 和 data_sets_type
for data_field in self.datasets_line_ids:
data_fields_name = data_field.datasets_id.datasets_id
data_sets_type = ''
region_id = self.region.id
universe_id = self.universe.id
for data_field_line in data_field.datasets_id.line_ids:
data_sets_type = data_field_line.data_sets_type
break
# 创建一个alpha.idea记录, 为了防止创建重复的 idea, 当前模型的 name 是 uuid4, 加上 region_id, universe_id, data_fields_name, data_sets_type,这几个字段, 先搜索是否存在相应的记录
# 如果不存在, 创建, 如果存在, 跳过
# 根据 data_sets_type 确定 data_type
data_type = 'MATRIX' if data_sets_type == 'MATRIX' else 'VECTOR'
# 检查是否已存在相同的 idea 记录(特征工程 + 数据集 都相同)
existing_idea = self.env['alpha.idea'].search([
('region', '=', region_id),
('universe', '=', universe_id),
('data_type', '=', data_type),
('replace_prompt', '=', self.feature_engineering),
('needed_data_set_ids.name', '=', data_fields_name)
], limit=1)
if existing_idea:
continue
# 创建新的 alpha.idea 记录
idea_vals = {
'region': region_id,
'universe': universe_id,
'data_type': data_type,
'delay': self.use_datasets.delay if self.use_datasets else '1',
'replace_prompt': self.feature_engineering,
'needed_data_set_ids': [(0, 0, {'name': data_fields_name})],
}
# 找到最终的 meta_prompt
meta_prompt = self.env['alpha.prompt.settings'].search([('prompt_type', '=', 'meta_prompt')], limit=1)
if meta_prompt:
idea_vals.update({'meta_prompt': meta_prompt.id})
new_record = self.env['alpha.idea'].create(idea_vals)
# 创建成功之后, 顺便点一下
try:
new_record.btn_check_and_fetch_data()
new_record.btn_generate_final_prompt()
except Exception as e:
print(f'error: {e}')
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': 'Success',
'message': 'Ideas generated successfully.',
'type': 'success',
'sticky': False,
}
}
def btn_quick_selection_datasets(self):
self.ensure_one()
# 1, 检查是否存在已选择的数据集, 如果存在, 清空self.datasets_line_ids
if self.datasets_line_ids:
self.datasets_line_ids.unlink()
# 2, 查询数据集, 并创建关联行
datasets = self.env['alpha.datasets'].search([
('region', '=', self.region.id),
('universe', '=', self.universe.id),
])
# 3, 遍历数据, 插入到 datasets_line_ids
for dataset in datasets:
self.datasets_line_ids.create({
'research_direction_id': self.id,
'datasets_id': dataset.id,
})
return True
def btn_action_cancel(self):
self.ensure_one()
self.write({
'status': 'cancel'
})
class AlphaResearchDatasetsLine(models.Model):
"""研究方向数据集关联行"""
_name = 'alpha.research.datasets.line'
_description = 'Alpha Research Datasets Line'
_order = 'id desc'
research_direction_id = fields.Many2one('alpha.generate.research.direction', string='Research Direction', required=True, ondelete='cascade')
datasets_id = fields.Many2one('alpha.datasets', string='Dataset', required=True)
data_field_count = fields.Integer(string='Data Field Count', related='datasets_id.data_field_count', readonly=True)
datasets_code = fields.Char(string='Dataset Code', related='datasets_id.datasets_id', readonly=True)
datasets_name = fields.Char(string='Dataset Name', related='datasets_id.name', readonly=True)
region = fields.Char(string='Region', related='datasets_id.region.name', readonly=True)
universe = fields.Char(string='Universe', related='datasets_id.universe.name', readonly=True)
delay = fields.Selection([
('1', '1'),
('0', '0')
], string='Delay', related='datasets_id.delay', readonly=True)