重命名:agent_name - skill_name

This commit is contained in:
jingrow 2026-06-25 02:11:21 +08:00
parent 8f11cf6dc9
commit cca2946ad4
14 changed files with 174 additions and 174 deletions

View File

@ -86,7 +86,7 @@ const stats = reactive({
const loadStats = async () => {
try {
// 4
//
//
const agentsResult = await getCount('Local Ai Skill')
if (agentsResult.success) {
stats.agents = agentsResult.count || 0

View File

@ -124,7 +124,7 @@ import { api } from '@/shared/api/common'
const props = defineProps<{
show: boolean
agentName: string
skillName: string
}>()
const emit = defineEmits<{
@ -158,7 +158,7 @@ function execStatusType(exec: any) {
}
watch(() => props.show, (val) => {
if (val && props.agentName) {
if (val && props.skillName) {
refresh()
}
})
@ -169,11 +169,11 @@ function afterLeave() {
}
async function refresh() {
if (!props.agentName) return
if (!props.skillName) return
loading.value = true
try {
const res = await api.call('jingrow.ai.pagetype.ai_skill.ai_skill.get_skill_execution_logs', {
agent_name: props.agentName
skill_name: props.skillName
})
const result = res?.message || res
const list = result?.success ? (result.data || []) : []

View File

@ -63,15 +63,15 @@ const executing = ref(false)
async function handleExecute() {
try {
executing.value = true
const agentName = props.context.row.name
const skillName = props.context.row.name
if (!agentName) {
message.error(props.context.t('Agent name is required'))
if (!skillName) {
message.error(props.context.t('Skill name is required'))
return
}
const res = await api.call('jingrow.ai.pagetype.ai_skill.ai_skill.execute_skill_by_name', {
agent_name: agentName
skill_name: skillName
})
const result = res?.message || res
@ -93,27 +93,27 @@ async function handleFlowBuilder() {
const name = props.context.row.name
let flowData: any = {}
// agent_flow API
if (props.context.row.agent_flow !== undefined && props.context.row.agent_flow !== null) {
const raw = props.context.row.agent_flow
// skill_flow API
if (props.context.row.skill_flow !== undefined && props.context.row.skill_flow !== null) {
const raw = props.context.row.skill_flow
flowData = raw
if (typeof raw === 'string') {
try { flowData = JSON.parse(raw) } catch { flowData = {} }
}
} else {
// API agent_flow
// API skill_flow
try {
const response = await axios.get(`/api/data/${encodeURIComponent(props.context.entity)}/${encodeURIComponent(name)}`)
const record = response.data?.data || {}
const raw = record.agent_flow ?? {}
const raw = record.skill_flow ?? {}
flowData = raw
if (typeof raw === 'string') {
try { flowData = JSON.parse(raw) } catch { flowData = {} }
}
} catch (error) {
console.error('获取智能体数据失败:', error)
message.error(props.context.t('Failed to load agent data'))
console.error('获取技能数据失败:', error)
message.error(props.context.t('Failed to load skill data'))
return
}
}

View File

@ -1,5 +1,5 @@
<template>
<div class="ai-agent-toolbar-wrapper">
<div class="ai-skill-toolbar-wrapper">
<n-space align="center">
<!-- 侧边栏位置切换按钮 -->
<n-button
@ -182,7 +182,7 @@
<!-- 执行日志对话框 -->
<ExecutionLogDialog
v-model:show="showLogDialog"
:agent-name="props.record?.name || props.id"
:skill-name="props.record?.name || props.id"
/>
</div>
</template>
@ -264,9 +264,9 @@ onMounted(() => {
async function handleExecute() {
if (isNew.value) return
const agentName = props.record?.name
if (!agentName) {
message.error(t('Agent name is required'))
const skillName = props.record?.name
if (!skillName) {
message.error(t('Skill name is required'))
return
}
@ -274,7 +274,7 @@ async function handleExecute() {
executing.value = true
const res = await api.call('jingrow.ai.pagetype.ai_skill.ai_skill.execute_skill_by_name', {
agent_name: agentName
skill_name: skillName
})
const result = res?.message || res
@ -294,7 +294,7 @@ async function handleExecute() {
//
async function handleFlowBuilder() {
try {
const raw = props.record?.agent_flow ?? {}
const raw = props.record?.skill_flow ?? {}
let flowData: any = raw
if (typeof raw === 'string') {
try { flowData = JSON.parse(raw) } catch { flowData = {} }
@ -313,22 +313,22 @@ async function handleFlowBuilder() {
//
function handlePublish() {
if (isNew.value || !props.record?.agent_name) {
message.warning(t('Please save the agent first'))
if (isNew.value || !props.record?.skill_name) {
message.warning(t('Please save the skill first'))
return
}
const agentName = props.record.agent_name
const agentFlow = props.record.agent_flow
const skillName = props.record.skill_name
const skillFlow = props.record.skill_flow
if (!agentFlow) {
message.warning(t('Agent flow data is required for publishing'))
if (!skillFlow) {
message.warning(t('Skill flow data is required for publishing'))
return
}
dialog.info({
title: t('Publish to Agent Marketplace'),
content: t('Are you sure you want to publish agent "{0}" to the marketplace?').replace('{0}', agentName),
title: t('Publish to Skill Marketplace'),
content: t('Are you sure you want to publish skill "{0}" to the marketplace?').replace('{0}', skillName),
positiveText: t('Confirm'),
negativeText: t('Cancel'),
onPositiveClick: () => {
@ -341,33 +341,33 @@ async function performPublish() {
try {
publishing.value = true
const agentName = props.record?.agent_name || ''
const agentFlow = props.record?.agent_flow
const skillName = props.record?.skill_name || ''
const skillFlow = props.record?.skill_flow
if (!agentFlow) {
throw new Error(t('Agent flow data is required'))
if (!skillFlow) {
throw new Error(t('Skill flow data is required'))
}
// agent_flow JSON
// skill_flow JSON
// 使
let flowData = agentFlow
if (typeof agentFlow === 'string') {
let flowData = skillFlow
if (typeof skillFlow === 'string') {
try {
flowData = JSON.parse(agentFlow)
flowData = JSON.parse(skillFlow)
} catch (e) {
throw new Error(t('Invalid agent flow data format'))
throw new Error(t('Invalid skill flow data format'))
}
} else if (typeof agentFlow !== 'object' || agentFlow === null) {
throw new Error(t('Agent flow data must be a valid JSON object'))
} else if (typeof skillFlow !== 'object' || skillFlow === null) {
throw new Error(t('Skill flow data must be a valid JSON object'))
}
// flowData JSON
const publishData = {
agent_name: agentName,
title: props.record?.title || agentName,
skill_name: skillName,
title: props.record?.title || skillName,
subtitle: props.record?.subtitle || '',
description: props.record?.description || '',
agent_flow: flowData // axios JSON
skill_flow: flowData // axios JSON
}
const response = await axios.post('/jingrow/agent/publish', publishData, {
@ -377,12 +377,12 @@ async function performPublish() {
})
if (response.data.success) {
message.success(response.data.message || t('Agent published successfully'))
message.success(response.data.message || t('Skill published successfully'))
} else {
throw new Error(response.data.message || t('Publish failed'))
}
} catch (error: any) {
console.error('发布智能体失败:', error)
console.error('发布技能失败:', error)
const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || t('Publish failed, please check permission and server logs')
message.error(errorMsg)
} finally {
@ -509,7 +509,7 @@ async function handleRenameConfirm() {
<style scoped>
/* 工具栏外层包裹容器,消除 fragment 警告,对父级 Space 透明 */
.ai-agent-toolbar-wrapper {
.ai-skill-toolbar-wrapper {
display: contents;
}

View File

@ -63,15 +63,15 @@ const executing = ref(false)
async function handleExecute() {
try {
executing.value = true
const agentName = props.context.row.name
const skillName = props.context.row.name
if (!agentName) {
message.error(props.context.t('Agent name is required'))
if (!skillName) {
message.error(props.context.t('Skill name is required'))
return
}
const res = await api.call('jingrow.ai.utils.jlocal.execute_local_ai_skill', {
name: agentName
name: skillName
})
const result = res?.message || res
@ -93,27 +93,27 @@ async function handleFlowBuilder() {
const name = props.context.row.name
let flowData: any = {}
// agent_flow API
if (props.context.row.agent_flow !== undefined && props.context.row.agent_flow !== null) {
const raw = props.context.row.agent_flow
// skill_flow API
if (props.context.row.skill_flow !== undefined && props.context.row.skill_flow !== null) {
const raw = props.context.row.skill_flow
flowData = raw
if (typeof raw === 'string') {
try { flowData = JSON.parse(raw) } catch { flowData = {} }
}
} else {
// API agent_flow
// API skill_flow
try {
const response = await axios.get(`/api/data/${encodeURIComponent(props.context.entity)}/${encodeURIComponent(name)}`)
const record = response.data?.data || {}
const raw = record.agent_flow ?? {}
const raw = record.skill_flow ?? {}
flowData = raw
if (typeof raw === 'string') {
try { flowData = JSON.parse(raw) } catch { flowData = {} }
}
} catch (error) {
console.error('获取智能体数据失败:', error)
message.error(props.context.t('Failed to load agent data'))
console.error('获取技能数据失败:', error)
message.error(props.context.t('Failed to load skill data'))
return
}
}

View File

@ -188,9 +188,9 @@ onMounted(() => {
async function handleExecute() {
if (isNew.value) return
const agentName = props.record?.name
if (!agentName) {
message.error(t('Agent name is required'))
const skillName = props.record?.name
if (!skillName) {
message.error(t('Skill name is required'))
return
}
@ -198,7 +198,7 @@ async function handleExecute() {
executing.value = true
const res = await api.call('jingrow.ai.utils.jlocal.execute_local_ai_skill', {
name: agentName
name: skillName
})
const result = res?.message || res
@ -218,7 +218,7 @@ async function handleExecute() {
//
async function handleFlowBuilder() {
try {
const raw = props.record?.agent_flow ?? {}
const raw = props.record?.skill_flow ?? {}
let flowData: any = raw
if (typeof raw === 'string') {
try { flowData = JSON.parse(raw) } catch { flowData = {} }
@ -237,22 +237,22 @@ async function handleFlowBuilder() {
//
function handlePublish() {
if (isNew.value || !props.record?.agent_name) {
message.warning(t('Please save the agent first'))
if (isNew.value || !props.record?.skill_name) {
message.warning(t('Please save the skill first'))
return
}
const agentName = props.record.agent_name
const agentFlow = props.record.agent_flow
const skillName = props.record.skill_name
const skillFlow = props.record.skill_flow
if (!agentFlow) {
message.warning(t('Agent flow data is required for publishing'))
if (!skillFlow) {
message.warning(t('Skill flow data is required for publishing'))
return
}
dialog.info({
title: t('Publish to Agent Marketplace'),
content: t('Are you sure you want to publish agent "{0}" to the marketplace?').replace('{0}', agentName),
title: t('Publish to Skill Marketplace'),
content: t('Are you sure you want to publish skill "{0}" to the marketplace?').replace('{0}', skillName),
positiveText: t('Confirm'),
negativeText: t('Cancel'),
onPositiveClick: () => {
@ -265,33 +265,33 @@ async function performPublish() {
try {
publishing.value = true
const agentName = props.record?.agent_name || ''
const agentFlow = props.record?.agent_flow
const skillName = props.record?.skill_name || ''
const skillFlow = props.record?.skill_flow
if (!agentFlow) {
throw new Error(t('Agent flow data is required'))
if (!skillFlow) {
throw new Error(t('Skill flow data is required'))
}
// agent_flow JSON
// skill_flow JSON
// 使
let flowData = agentFlow
if (typeof agentFlow === 'string') {
let flowData = skillFlow
if (typeof skillFlow === 'string') {
try {
flowData = JSON.parse(agentFlow)
flowData = JSON.parse(skillFlow)
} catch (e) {
throw new Error(t('Invalid agent flow data format'))
throw new Error(t('Invalid skill flow data format'))
}
} else if (typeof agentFlow !== 'object' || agentFlow === null) {
throw new Error(t('Agent flow data must be a valid JSON object'))
} else if (typeof skillFlow !== 'object' || skillFlow === null) {
throw new Error(t('Skill flow data must be a valid JSON object'))
}
// flowData JSON
const publishData = {
agent_name: agentName,
title: props.record?.title || agentName,
skill_name: skillName,
title: props.record?.title || skillName,
subtitle: props.record?.subtitle || '',
description: props.record?.description || '',
agent_flow: flowData // axios JSON
skill_flow: flowData // axios JSON
}
const response = await axios.post('/jingrow/agent/publish', publishData, {
@ -301,12 +301,12 @@ async function performPublish() {
})
if (response.data.success) {
message.success(response.data.message || t('Agent published successfully'))
message.success(response.data.message || t('Skill published successfully'))
} else {
throw new Error(response.data.message || t('Publish failed'))
}
} catch (error: any) {
console.error('发布智能体失败:', error)
console.error('发布技能失败:', error)
const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || t('Publish failed, please check permission and server logs')
message.error(errorMsg)
} finally {

View File

@ -9,7 +9,7 @@ def get_all_ai_skills():
"""获取所有启用的事件驱动Ai Skill支持按PageType和Module分组"""
skills_list = jingrow.get_all(
"Ai Skill",
fields=["name", "agent_name", "trigger_mode", "event_type", "target_pagetype", "target_module", "condition"],
fields=["name", "skill_name", "trigger_mode", "event_type", "target_pagetype", "target_module", "condition"],
filters={"enabled": True, "trigger_mode": "Event Trigger"},
)
@ -84,7 +84,7 @@ def _add_skill_to_queue(skill, pg):
jingrow.local._ai_skill_queue = []
jingrow.db.after_commit.add(flush_ai_skill_execution_queue)
jingrow.local._ai_skill_queue.append(jingrow._dict(pg=pg, agent=skill))
jingrow.local._ai_skill_queue.append(jingrow._dict(pg=pg, skill=skill))
def flush_ai_skill_execution_queue():
@ -101,7 +101,7 @@ def flush_ai_skill_execution_queue():
# 去重:基于 (skill.name, pg.name)
# 'pg' 保存最后一个实例的值
for execution in jingrow.local._ai_skill_queue:
key = (execution.agent.get("name"), execution.pg.get("name"))
key = (execution.skill.get("name"), execution.pg.get("name"))
if key not in uniq_skills:
uniq_skills.add(key)
unique_last_instances.append(execution)
@ -117,7 +117,7 @@ def flush_ai_skill_execution_queue():
jingrow.enqueue(
"jingrow.ai.pagetype.ai_skill.ai_skill.enqueue_ai_skill",
pg=instance.pg,
agent_name=instance.agent.get("name"),
skill_name=instance.skill.get("name"),
now=jingrow.flags.in_test,
queue="default",
)

View File

@ -9,7 +9,7 @@ jingrow.ui.form.on("Ai Skill", {
updateCustomProgressBar(frm);
}
// 增加可视化节点编辑按钮(直接显示为主按钮,不放在下拉菜单)
frm.add_custom_button(__('智能体编排'), function() {
frm.add_custom_button(__('技能编排'), function() {
open_skill_flow_editor(frm);
});
}
@ -58,8 +58,8 @@ function open_skill_flow_editor(frm) {
const container = dialog.$body.find('#ai-skill-flow-builder-container')[0];
if (!container) return;
// 直接使用当前表单的 agent_flow 数据
let currentFlowData = frm.pg.agent_flow || {};
// 直接使用当前表单的 skill_flow 数据
let currentFlowData = frm.pg.skill_flow || {};
// 如果是字符串,尝试解析为对象
if (typeof currentFlowData === 'string') {
@ -119,7 +119,7 @@ function save_skill_flow(frm, dialog) {
// 格式化为多行美观JSON字符串
const prettyJson = JSON.stringify(flowData, null, 2);
frm.set_value('agent_flow', prettyJson);
frm.set_value('skill_flow', prettyJson);
// 保存表单
frm.save().then(() => {

View File

@ -9,7 +9,7 @@
"progress",
"section_break_xcls",
"column_break_glmw",
"agent_name",
"skill_name"
"ai_repeat",
"enabled",
"column_break_bzai",
@ -20,8 +20,8 @@
"target_module",
"condition",
"trigger_time",
"agent_flow_tab",
"agent_flow",
"skill_flow_tab"
"skill_flow"
"排除关键词_tab",
"exclude_prompts"
],
@ -101,21 +101,21 @@
"label": "Condition"
},
{
"fieldname": "agent_flow",
"fieldname": "skill_flow",
"fieldtype": "JSON",
"label": "Agent Flow"
"label": "Skill Flow"
},
{
"fieldname": "agent_flow_tab",
"fieldname": "skill_flow_tab",
"fieldtype": "Tab Break",
"label": "Agent Flow"
"label": "Skill Flow"
},
{
"fieldname": "agent_name",
"fieldname": "skill_name",
"fieldtype": "Data",
"in_global_search": 1,
"in_list_view": 1,
"label": "智能体名称",
"label": "技能名称",
"reqd": 1
},
{

View File

@ -25,8 +25,8 @@ class AiSkill(Page):
if TYPE_CHECKING:
from jingrow.types import DF
agent_flow: DF.JSON | None
agent_name: DF.Data
skill_flow: DF.JSON | None
skill_name: DF.Data
ai_repeat: DF.Int
condition: DF.SmallText | None
enabled: DF.Check
@ -48,8 +48,8 @@ class AiSkill(Page):
if not self.progress:
self.progress = 0
if self.agent_flow == "" or self.agent_flow is None:
self.agent_flow = None
if self.skill_flow == "" or self.skill_flow is None:
self.skill_flow = None
def on_update(self):
"""Post-update processing"""
@ -64,8 +64,8 @@ class AiSkill(Page):
# Only enqueue, don't execute synchronously
jingrow.enqueue(
"jingrow.ai.pagetype.ai_skill.ai_skill.execute_skill_task",
agent_name=self.name,
agent_pagetype=self.pagetype
skill_name=self.name,
skill_pagetype=self.pagetype
)
def after_insert(self):
@ -80,8 +80,8 @@ class AiSkill(Page):
jingrow.db.commit()
jingrow.enqueue(
"jingrow.ai.pagetype.ai_skill.ai_skill.execute_skill_task",
agent_name=self.name,
agent_pagetype=self.pagetype
skill_name=self.name,
skill_pagetype=self.pagetype
)
def on_trash(self):
@ -94,9 +94,9 @@ class AiSkill(Page):
def _manage_scheduled_job(self):
"""Manage scheduled job for the Ai Skill, create/update/delete based on current status"""
agent_name = self.name
skill_name = self.name
cron = getattr(self, "trigger_time", None) or getattr(self, "cron_format", None)
method_name = f"jingrow.ai.pagetype.ai_skill.ai_skill.execute_scheduled_skills:{agent_name}"
method_name = f"jingrow.ai.pagetype.ai_skill.ai_skill.execute_scheduled_skills:{skill_name}"
# Check if a scheduled job already exists
job = jingrow.db.exists("Scheduled Job Type", {"method": method_name})
@ -140,18 +140,18 @@ class AiSkill(Page):
self.progress = 20
self.save(ignore_permissions=True)
jingrow.db.commit()
flow_data = self.agent_flow
flow_data = self.skill_flow
if isinstance(flow_data, str):
try:
flow_data = json.loads(flow_data)
except Exception as e:
async_update_field(self.pagetype, self.name, "status", "未完成")
async_update_field(self.pagetype, self.name, "progress", 0)
return {"success": False, "error": f"agent_flow is not valid JSON: {e}"}
return {"success": False, "error": f"skill_flow is not valid JSON: {e}"}
if not flow_data:
async_update_field(self.pagetype, self.name, "status", "未完成")
async_update_field(self.pagetype, self.name, "progress", 0)
return {"success": False, "error": "agent_flow is empty"}
return {"success": False, "error": "skill_flow is empty"}
if initial_inputs is None or not initial_inputs:
initial_inputs = {}
repeat_count = getattr(self, "ai_repeat", 1) or 1
@ -202,7 +202,7 @@ class AiSkill(Page):
# Persist to Ai Skill Execution Log
try:
log = jingrow.new_pg("Ai Skill Execution Log")
log.agent_name = self.name
log.skill_name = self.name
log.status = status_text
log.total_iterations = repeat_count
log.successful_iterations = success_count
@ -233,11 +233,11 @@ class AiSkill(Page):
final_result = {"success": False, "error": "All iterations failed", "total_executions": repeat_count, "successful_executions": 0}
return final_result
except Exception as e:
jingrow.log_error("Ai Skill execution exception", f"agent_name={self.name}, error={str(e)}")
jingrow.log_error("Ai Skill execution exception", f"skill_name={self.name}, error={str(e)}")
# Also create Ai Skill Execution Log on exception
try:
log = jingrow.new_pg("Ai Skill Execution Log")
log.agent_name = self.name
log.skill_name = self.name
log.status = "异常"
log.end_time = str(jingrow.utils.now())
log.node_results = json.dumps({"error": str(e)}, ensure_ascii=False, default=str)
@ -411,76 +411,76 @@ def get_context(pg):
}
def execute_skill_task(agent_name, agent_pagetype):
def execute_skill_task(skill_name, skill_pagetype):
"""Execute Ai Skill task in background"""
try:
agent = jingrow.get_pg(agent_pagetype, agent_name)
if not agent:
skill = jingrow.get_pg(skill_pagetype, skill_name)
if not skill:
return
agent.execute_skill()
skill.execute_skill()
except Exception as e:
jingrow.log_error("Ai Skill task execution exception", f"agent_name={agent_name}, agent_pagetype={agent_pagetype}, error={str(e)}")
jingrow.log_error("Ai Skill task execution exception", f"skill_name={skill_name}, skill_pagetype={skill_pagetype}, error={str(e)}")
@jingrow.whitelist()
def execute_skill_by_name(agent_name):
def execute_skill_by_name(skill_name):
"""Execute the specified Ai Skill (via RPC)"""
if not agent_name:
return {"success": False, "error": "agent_name is required"}
if not skill_name:
return {"success": False, "error": "skill_name is required"}
try:
# check if the agent exists
if not jingrow.db.exists('Ai Skill', agent_name):
return {"success": False, "error": f"Ai Skill {agent_name} does not exist"}
# check if the skill exists
if not jingrow.db.exists('Ai Skill', skill_name):
return {"success": False, "error": f"Ai Skill {skill_name} does not exist"}
# Execute asynchronously, don't block the frontend response
jingrow.enqueue(
method=execute_skill_task,
agent_name=agent_name,
agent_pagetype='Ai Skill',
skill_name=skill_name,
skill_pagetype='Ai Skill',
queue='default',
timeout=3600
)
return {
"success": True,
"message": "The agent execution task has been queued. Please check the execution status later."
"message": "技能执行任务已加入队列,请稍后查看执行状态。"
}
except Exception as e:
jingrow.log_error("Ai Skill execution failed", f"agent_name={agent_name}, error={str(e)}")
jingrow.log_error("Ai Skill execution failed", f"skill_name={skill_name}, error={str(e)}")
return {"success": False, "error": str(e)}
def enqueue_ai_skill(pg, agent_name):
def enqueue_ai_skill(pg, skill_name):
"""Execute Ai Skill task in background"""
try:
# Get the full Page instance via agent_name
agent = jingrow.get_pg("Ai Skill", agent_name)
if not agent:
# Get the full Page instance via skill_name
skill = jingrow.get_pg("Ai Skill", skill_name)
if not skill:
return
agent_name = getattr(agent, "agent_name", None)
skill_name = getattr(skill, "skill_name", None)
initial_inputs = {
"pagetype": pg.get("pagetype"),
"name": pg.get("name"),
"agent_name": agent_name
"skill_name": skill_name
}
agent.execute_skill(initial_inputs=initial_inputs)
skill.execute_skill(initial_inputs=initial_inputs)
except Exception as e:
jingrow.log_error("Ai Skill execution failed", f"Ai Skill {agent_name} execution failed: {str(e)}")
jingrow.log_error("Ai Skill execution failed", f"Ai Skill {skill_name} execution failed: {str(e)}")
@jingrow.whitelist()
def get_skill_execution_logs(agent_name):
def get_skill_execution_logs(skill_name):
"""Get all execution logs for the specified Ai Skill (ordered by start_time desc)"""
try:
logs = jingrow.get_all(
"Ai Skill Execution Log",
filters={"agent_name": agent_name},
fields=["name", "agent_name", "status", "start_time", "end_time", "total_iterations", "successful_iterations", "node_results"],
filters={"skill_name": skill_name},
fields=["name", "skill_name", "status", "start_time", "end_time", "total_iterations", "successful_iterations", "node_results"],
order_by="start_time desc"
)
# node_results is a JSON string, parse it
@ -495,19 +495,19 @@ def get_skill_execution_logs(agent_name):
return {"success": False, "error": str(e)}
def execute_scheduled_skills(agent_name):
def execute_scheduled_skills(skill_name):
"""Scheduled task entry point, execute the specified Ai Skill"""
try:
if not agent_name:
if not skill_name:
return
agent = jingrow.get_pg("Ai Skill", agent_name)
if not agent:
skill = jingrow.get_pg("Ai Skill", skill_name)
if not skill:
return
if not agent.enabled or agent.trigger_mode != "Scheduled Trigger":
if not skill.enabled or skill.trigger_mode != "Scheduled Trigger":
return
agent.execute_skill()
skill.execute_skill()
except Exception as e:
jingrow.log_error("Scheduled Ai Skill execution exception", f"agent_name={agent_name}, error={str(e)}")
jingrow.log_error("Scheduled Ai Skill execution exception", f"skill_name={skill_name}, error={str(e)}")

View File

@ -3,7 +3,7 @@
"creation": "2026-06-24 10:00:00.000000",
"engine": "InnoDB",
"field_order": [
"agent_name",
"skill_name"
"status",
"column_break_basic",
"start_time",
@ -17,11 +17,11 @@
],
"fields": [
{
"fieldname": "agent_name",
"fieldname": "skill_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Agent Name",
"label": "Skill Name",
"options": "Ai Skill",
"reqd": 1,
"search_index": 1

View File

@ -14,7 +14,7 @@ class AiSkillExecutionLog(Page):
if TYPE_CHECKING:
from jingrow.types import DF
agent_name: DF.Link
skill_name: DF.Link
end_time: DF.Datetime | None
execution_flow: DF.JSON | None
node_results: DF.JSON | None

View File

@ -9,7 +9,7 @@
"progress",
"section_break_xcls",
"column_break_glmw",
"agent_name",
"skill_name"
"ai_repeat",
"enabled",
"column_break_bzai",
@ -20,8 +20,8 @@
"target_module",
"condition",
"trigger_time",
"agent_flow_tab",
"agent_flow",
"skill_flow_tab"
"skill_flow"
"排除关键词_tab",
"exclude_prompts"
],
@ -103,22 +103,22 @@
"label": "Condition"
},
{
"fieldname": "agent_flow",
"fieldname": "skill_flow",
"fieldtype": "JSON",
"label": "Agent Flow"
"label": "Skill Flow"
},
{
"fieldname": "agent_flow_tab",
"fieldname": "skill_flow_tab",
"fieldtype": "Tab Break",
"label": "Agent Flow"
"label": "Skill Flow"
},
{
"fieldname": "agent_name",
"fieldname": "skill_name",
"fieldtype": "Data",
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "智能体名称",
"label": "技能名称",
"reqd": 1
},
{

View File

@ -14,8 +14,8 @@ class LocalAiSkill(Page):
if TYPE_CHECKING:
from jingrow.types import DF
agent_flow: DF.JSON | None
agent_name: DF.Data
skill_flow: DF.JSON | None
skill_name: DF.Data
ai_repeat: DF.Int
condition: DF.SmallText | None
enabled: DF.Check
@ -29,8 +29,8 @@ class LocalAiSkill(Page):
# end: auto-generated types
def validate(self):
if getattr(self, "agent_flow", None) in (None, ""):
self.agent_flow = None
if getattr(self, "skill_flow", None) in (None, ""):
self.skill_flow = None
def on_update(self):
"""更新时管理定时任务"""
@ -45,12 +45,12 @@ class LocalAiSkill(Page):
self._manage_scheduled_job()
def _manage_scheduled_job(self):
"""管理定时Ai Skill的定时任务,根据当前状态创建、更新或删除定时任务
"""管理定时技能的定时任务,根据当前状态创建、更新或删除定时任务
参考 Jingrow 系统的 Ai Skill._manage_scheduled_job 实现
"""
agent_id = self.name
skill_id = self.name
cron = getattr(self, "trigger_time", None) or getattr(self, "cron_format", None)
method_name = f"jingrow/agents/execute_scheduled_skill:{agent_id}"
method_name = f"jingrow/agents/execute_scheduled_skill:{skill_id}"
# 检查是否存在对应的定时任务
job = jingrow.db.exists("Local Scheduled Job", {"method": method_name})