添加代理执行和模型配置:创建代理执行控制台UI和API,扩展config模型添加default_model/default_temperature/enable_streaming字段
This commit is contained in:
parent
ee4b416b50
commit
3e33bc0924
@ -80,6 +80,29 @@
|
||||
"nullable": "no",
|
||||
"default": "30"
|
||||
},
|
||||
{
|
||||
"name": "default_model",
|
||||
"title": "Default LLM model for agent tasks",
|
||||
"type": "str",
|
||||
"length": 64,
|
||||
"nullable": "yes",
|
||||
"default": "qwen3-max"
|
||||
},
|
||||
{
|
||||
"name": "default_temperature",
|
||||
"title": "Default temperature for LLM calls",
|
||||
"type": "float",
|
||||
"nullable": "no",
|
||||
"default": "0.7"
|
||||
},
|
||||
{
|
||||
"name": "enable_streaming",
|
||||
"title": "Enable streaming response for LLM calls",
|
||||
"type": "str",
|
||||
"length": "1",
|
||||
"nullable": "no",
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "Creation timestamp",
|
||||
|
||||
@ -12,10 +12,14 @@
|
||||
"css": "ios-navbar",
|
||||
"items": [
|
||||
{
|
||||
"text": "Configuration",
|
||||
"icon": "settings",
|
||||
"css": "ios-navbar-title",
|
||||
"disabled": true
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "Configuration",
|
||||
"css": "ios-navbar-title",
|
||||
"fontSize": "18px",
|
||||
"fontWeight": "bold",
|
||||
"color": "#059669"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -23,7 +27,7 @@
|
||||
{
|
||||
"widgettype": "urlwidget",
|
||||
"options": {
|
||||
"url": "{{entire_url('harnessed_agent_config_view')}}",
|
||||
"url": "{{entire_url('agent_config_form.ui')}}",
|
||||
"width": "100%",
|
||||
"height": "100%"
|
||||
}
|
||||
|
||||
114
wwwroot/agent_config_form.ui
Normal file
114
wwwroot/agent_config_form.ui
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"widgettype": "TabPanel",
|
||||
"options": {
|
||||
"tab_pos": "top",
|
||||
"items": [
|
||||
{
|
||||
"name": "agent_config",
|
||||
"label": "代理配置",
|
||||
"icon": "settings",
|
||||
"content": {
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"padding": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Form",
|
||||
"id": "agent_config_form",
|
||||
"options": {
|
||||
"data_url": "{{entire_url('../api/agent_config_get.dspy')}}",
|
||||
"data_method": "GET",
|
||||
"submit_url": "{{entire_url('../api/agent_config_save.dspy')}}",
|
||||
"method": "POST",
|
||||
"layout": "vertical",
|
||||
"fields": [
|
||||
{
|
||||
"name": "work_dir",
|
||||
"label": "工作目录",
|
||||
"uitype": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "skills_path",
|
||||
"label": "技能路径",
|
||||
"uitype": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "max_memory_tokens",
|
||||
"label": "最大记忆 token 数",
|
||||
"uitype": "int",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "default_priority",
|
||||
"label": "默认优先级",
|
||||
"uitype": "int",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "high_priority_threshold",
|
||||
"label": "高优先级阈值",
|
||||
"uitype": "int",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "low_priority_threshold",
|
||||
"label": "低优先级阈值",
|
||||
"uitype": "int",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "auto_cleanup_enabled",
|
||||
"label": "启用自动清理",
|
||||
"uitype": "check"
|
||||
},
|
||||
{
|
||||
"name": "min_retention_days",
|
||||
"label": "最小保留天数",
|
||||
"uitype": "int",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "default_model",
|
||||
"label": "默认模型",
|
||||
"uitype": "text",
|
||||
"placeholder": "qwen3-max"
|
||||
},
|
||||
{
|
||||
"name": "default_temperature",
|
||||
"label": "默认温度",
|
||||
"uitype": "float",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "enable_streaming",
|
||||
"label": "启用流式输出",
|
||||
"uitype": "check"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"type": "submit",
|
||||
"label": "保存代理配置",
|
||||
"variant": "primary"
|
||||
}
|
||||
],
|
||||
"maxWidth": "500px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "submited",
|
||||
"actiontype": "script",
|
||||
"script": "await bricks.show_resp_message_or_error(event.params)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
88
wwwroot/agent_console.ui
Normal file
88
wwwroot/agent_console.ui
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "16px",
|
||||
"spacing": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "代理执行控制台",
|
||||
"fontSize": "20px",
|
||||
"fontWeight": "bold",
|
||||
"color": "#059669"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Form",
|
||||
"id": "agent_execute_form",
|
||||
"options": {
|
||||
"submit_url": "{{entire_url('api/agent_execute.dspy')}}",
|
||||
"method": "POST",
|
||||
"layout": "vertical",
|
||||
"fields": [
|
||||
{
|
||||
"name": "tool_name",
|
||||
"label": "工具名称",
|
||||
"uitype": "text",
|
||||
"required": true,
|
||||
"placeholder": "例如: memory, skill_view, terminal, read_file"
|
||||
},
|
||||
{
|
||||
"name": "parameters",
|
||||
"label": "工具参数 (JSON)",
|
||||
"uitype": "textarea",
|
||||
"rows": 6,
|
||||
"placeholder": "{\"key\": \"value\"}"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"type": "submit",
|
||||
"label": "执行工具",
|
||||
"variant": "primary"
|
||||
}
|
||||
],
|
||||
"maxWidth": "700px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "submited",
|
||||
"actiontype": "script",
|
||||
"script": "await bricks.show_resp_message_or_error(event.params)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"padding": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "可用工具列表",
|
||||
"fontSize": "16px",
|
||||
"fontWeight": "bold",
|
||||
"color": "#374151",
|
||||
"marginBottom": "8px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "memory: 保存和检索记忆 | skill_view: 查看技能 | skill_manage: 管理技能 | session_search: 搜索历史会话 | delegate_task: 委托任务 | execute_code: 执行代码 | terminal: 运行命令 | read_file: 读取文件 | write_file: 写入文件 | search_files: 搜索文件 | patch: 修改文件 | vision_analyze: 图像分析 | cronjob: 定时任务 | todo: 任务管理",
|
||||
"fontSize": "12px",
|
||||
"color": "#6B7280"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
43
wwwroot/api/agent_config_get.dspy
Normal file
43
wwwroot/api/agent_config_get.dspy
Normal file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Get agent configuration for current user"""
|
||||
import json
|
||||
|
||||
result = {'success': False, 'config': {}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('harnessed_agent')
|
||||
user_id = await get_user()
|
||||
|
||||
sql = """SELECT * FROM harnessed_agent_config
|
||||
WHERE user_id = ${user_id}$
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1"""
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
rows = await sor.sqlExe(sql, {'user_id': user_id})
|
||||
if rows and len(rows) > 0:
|
||||
config = dict(rows[0])
|
||||
config['auto_cleanup_enabled'] = config.get('auto_cleanup_enabled', '1') == '1'
|
||||
config['enable_streaming'] = config.get('enable_streaming', '1') == '1'
|
||||
result['config'] = config
|
||||
else:
|
||||
result['config'] = {
|
||||
'work_dir': './hermes_work',
|
||||
'skills_path': '~/.hermes/skills',
|
||||
'max_memory_tokens': 2000,
|
||||
'default_priority': 50,
|
||||
'high_priority_threshold': 70,
|
||||
'low_priority_threshold': 30,
|
||||
'auto_cleanup_enabled': True,
|
||||
'min_retention_days': 30,
|
||||
'default_model': 'qwen3-max',
|
||||
'default_temperature': 0.7,
|
||||
'enable_streaming': True
|
||||
}
|
||||
result['success'] = True
|
||||
|
||||
except Exception as e:
|
||||
result['error'] = str(e)
|
||||
|
||||
return json.dumps(result, ensure_ascii=False, default=str)
|
||||
91
wwwroot/api/agent_config_save.dspy
Normal file
91
wwwroot/api/agent_config_save.dspy
Normal file
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Save agent configuration for current user"""
|
||||
import json, uuid, time
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
dbname = get_module_dbname('harnessed_agent')
|
||||
user_id = await get_user()
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
work_dir = params_kw.get('work_dir', './hermes_work').strip()
|
||||
skills_path = params_kw.get('skills_path', '~/.hermes/skills').strip()
|
||||
max_memory_tokens = int(params_kw.get('max_memory_tokens', 2000))
|
||||
default_priority = int(params_kw.get('default_priority', 50))
|
||||
high_priority_threshold = int(params_kw.get('high_priority_threshold', 70))
|
||||
low_priority_threshold = int(params_kw.get('low_priority_threshold', 30))
|
||||
auto_cleanup_enabled = '1' if params_kw.get('auto_cleanup_enabled') == '1' else '0'
|
||||
min_retention_days = int(params_kw.get('min_retention_days', 30))
|
||||
default_model = params_kw.get('default_model', 'qwen3-max').strip()
|
||||
default_temperature = float(params_kw.get('default_temperature', 0.7))
|
||||
enable_streaming = '1' if params_kw.get('enable_streaming') == '1' else '0'
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
rows = await sor.sqlExe("SELECT id FROM harnessed_agent_config WHERE user_id = ${user_id}$", {'user_id': user_id})
|
||||
|
||||
if rows and len(rows) > 0:
|
||||
config_id = rows[0]['id']
|
||||
await sor.sqlExe("""UPDATE harnessed_agent_config SET
|
||||
work_dir = ${work_dir}$,
|
||||
skills_path = ${skills_path}$,
|
||||
max_memory_tokens = ${max_memory_tokens}$,
|
||||
default_priority = ${default_priority}$,
|
||||
high_priority_threshold = ${high_priority_threshold}$,
|
||||
low_priority_threshold = ${low_priority_threshold}$,
|
||||
auto_cleanup_enabled = ${auto_cleanup_enabled}$,
|
||||
min_retention_days = ${min_retention_days}$,
|
||||
default_model = ${default_model}$,
|
||||
default_temperature = ${default_temperature}$,
|
||||
enable_streaming = ${enable_streaming}$,
|
||||
updated_at = ${updated_at}$
|
||||
WHERE id = ${id}$""", {
|
||||
'id': config_id,
|
||||
'work_dir': work_dir,
|
||||
'skills_path': skills_path,
|
||||
'max_memory_tokens': max_memory_tokens,
|
||||
'default_priority': default_priority,
|
||||
'high_priority_threshold': high_priority_threshold,
|
||||
'low_priority_threshold': low_priority_threshold,
|
||||
'auto_cleanup_enabled': auto_cleanup_enabled,
|
||||
'min_retention_days': min_retention_days,
|
||||
'default_model': default_model,
|
||||
'default_temperature': default_temperature,
|
||||
'enable_streaming': enable_streaming,
|
||||
'updated_at': now
|
||||
})
|
||||
else:
|
||||
config_id = str(uuid.uuid4()).replace('-', '')[:32]
|
||||
await sor.sqlExe("""INSERT INTO harnessed_agent_config
|
||||
(id, user_id, work_dir, skills_path, max_memory_tokens,
|
||||
default_priority, high_priority_threshold, low_priority_threshold,
|
||||
auto_cleanup_enabled, min_retention_days, default_model,
|
||||
default_temperature, enable_streaming, created_at, updated_at)
|
||||
VALUES (${id}$, ${user_id}$, ${work_dir}$, ${skills_path}$, ${max_memory_tokens}$,
|
||||
${default_priority}$, ${high_priority_threshold}$, ${low_priority_threshold}$,
|
||||
${auto_cleanup_enabled}$, ${min_retention_days}$, ${default_model}$,
|
||||
${default_temperature}$, ${enable_streaming}$, ${created_at}$, ${updated_at}$)""", {
|
||||
'id': config_id,
|
||||
'user_id': user_id,
|
||||
'work_dir': work_dir,
|
||||
'skills_path': skills_path,
|
||||
'max_memory_tokens': max_memory_tokens,
|
||||
'default_priority': default_priority,
|
||||
'high_priority_threshold': high_priority_threshold,
|
||||
'low_priority_threshold': low_priority_threshold,
|
||||
'auto_cleanup_enabled': auto_cleanup_enabled,
|
||||
'min_retention_days': min_retention_days,
|
||||
'default_model': default_model,
|
||||
'default_temperature': default_temperature,
|
||||
'enable_streaming': enable_streaming,
|
||||
'created_at': now,
|
||||
'updated_at': now
|
||||
})
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '配置保存成功', 'type': 'success'}}
|
||||
|
||||
except Exception as e:
|
||||
result['options'] = {'title': 'Error', 'message': '保存失败: ' + str(e), 'type': 'error'}
|
||||
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
51
wwwroot/api/agent_execute.dspy
Normal file
51
wwwroot/api/agent_execute.dspy
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Execute agent task - call tool/skill with given parameters"""
|
||||
import json, time
|
||||
|
||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
|
||||
|
||||
try:
|
||||
tool_name = params_kw.get('tool_name', '').strip()
|
||||
parameters = params_kw.get('parameters', '{}').strip()
|
||||
|
||||
if not tool_name:
|
||||
result['options'] = {'title': 'Error', 'message': '请指定工具名称', 'type': 'error'}
|
||||
else:
|
||||
user_id = await get_user()
|
||||
|
||||
try:
|
||||
tool_params = json.loads(parameters)
|
||||
except:
|
||||
tool_params = {}
|
||||
|
||||
context = {'user_id': user_id}
|
||||
|
||||
# Call agent tool execution
|
||||
exec_result = await harnessed_execute_tool(
|
||||
tool_name=tool_name,
|
||||
parameters=tool_params,
|
||||
context=context
|
||||
)
|
||||
|
||||
if exec_result.get('success'):
|
||||
output = json.dumps(exec_result, ensure_ascii=False, indent=2)
|
||||
result = {
|
||||
'widgettype': 'Message',
|
||||
'options': {
|
||||
'title': '执行成功',
|
||||
'message': f'工具: {tool_name}\n\n结果:\n{output}',
|
||||
'type': 'success'
|
||||
}
|
||||
}
|
||||
else:
|
||||
result['options'] = {
|
||||
'title': '执行失败',
|
||||
'message': exec_result.get('error', '未知错误'),
|
||||
'type': 'error'
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
result['options'] = {'title': 'Error', 'message': '执行异常: ' + str(e), 'type': 'error'}
|
||||
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
@ -10,6 +10,21 @@
|
||||
"cwidth":10,
|
||||
"items":[
|
||||
{% if get_user() %}
|
||||
{
|
||||
"name":"hermes_agent",
|
||||
"label":"代理控制台",
|
||||
"url":"{{entire_url('/harnessed_agent/hermes_agent.ui')}}"
|
||||
},
|
||||
{
|
||||
"name":"agent_console",
|
||||
"label":"代理执行",
|
||||
"url":"{{entire_url('/harnessed_agent/agent_console.ui')}}"
|
||||
},
|
||||
{
|
||||
"name":"agent_config",
|
||||
"label":"代理配置",
|
||||
"url":"{{entire_url('/harnessed_agent/agent_config.ui')}}"
|
||||
},
|
||||
{
|
||||
"name":"sessions",
|
||||
"label":"会话管理",
|
||||
@ -25,31 +40,21 @@
|
||||
"label":"记忆管理",
|
||||
"url":"{{entire_url('/harnessed_agent/hermes_memory')}}"
|
||||
},
|
||||
{
|
||||
"name":"tasks",
|
||||
"label":"任务管理",
|
||||
"url":"{{entire_url('/harnessed_agent/hermes_tasks')}}"
|
||||
},
|
||||
{
|
||||
"name":"workflows",
|
||||
"label":"工作流管理",
|
||||
"url":"{{entire_url('/harnessed_agent/hermes_workflows')}}"
|
||||
},
|
||||
{
|
||||
"name":"tasks",
|
||||
"label":"任务管理",
|
||||
"url":"{{entire_url('/harnessed_agent/hermes_tasks')}}"
|
||||
},
|
||||
{
|
||||
"name":"remote_skills",
|
||||
"label":"远程技能",
|
||||
"url":"{{entire_url('/harnessed_agent/harnessed_remote_skills')}}"
|
||||
},
|
||||
{
|
||||
"name":"agent_config",
|
||||
"label":"代理配置",
|
||||
"url":"{{entire_url('/harnessed_agent/harnessed_agent_config_view')}}"
|
||||
},
|
||||
{
|
||||
"name":"hermes_agent",
|
||||
"label":"代理控制台",
|
||||
"url":"{{entire_url('/harnessed_agent/hermes_agent.ui')}}"
|
||||
},
|
||||
{
|
||||
"name":"tools",
|
||||
"label":"工具管理",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user