feat(app): 移除内置agents并添加CORS支持和技能上传优化
移除了agents/registry.json中的所有内置agents配置,将agents数组清空。 为web应用添加了CORS中间件支持,允许指定的前端地址跨域访问。 重构了技能上传功能,增加了LLM重写机制,自动规范化上传的技能格式。 新增了工具名称提取逻辑,从技能正文中自动识别Required Tools段落。 更新了技能学习候选者和草稿的载荷结构,添加评估报告统计信息。 修改了意图路由技能的说明,改进任务状态管理逻辑。
This commit is contained in:
@ -25,7 +25,11 @@ class MainAgentRouter:
|
||||
timeout_seconds: float = 8.0,
|
||||
) -> MainAgentDecision:
|
||||
if provider is None:
|
||||
return self._fallback(active_task=active_task, reason="router_provider_unavailable")
|
||||
return self._apply_active_task_boundary(
|
||||
self._fallback(active_task=active_task, reason="router_provider_unavailable"),
|
||||
message=message,
|
||||
active_task=active_task,
|
||||
)
|
||||
chat_kwargs: dict[str, Any] = {
|
||||
"messages": [
|
||||
{
|
||||
@ -58,10 +62,18 @@ class MainAgentRouter:
|
||||
for attempt_timeout in (timeout_seconds, 12.0):
|
||||
try:
|
||||
response = await asyncio.wait_for(provider.chat(**chat_kwargs), timeout=attempt_timeout)
|
||||
return self.from_json(response.content or "", active_task=active_task)
|
||||
return self._apply_active_task_boundary(
|
||||
self.from_json(response.content or "", active_task=active_task),
|
||||
message=message,
|
||||
active_task=active_task,
|
||||
)
|
||||
except Exception as exc:
|
||||
last_error = exc
|
||||
return self._fallback(active_task=active_task, reason=f"router_failed: {last_error}")
|
||||
return self._apply_active_task_boundary(
|
||||
self._fallback(active_task=active_task, reason=f"router_failed: {last_error}"),
|
||||
message=message,
|
||||
active_task=active_task,
|
||||
)
|
||||
|
||||
def from_json(self, text: str, *, active_task: TaskRecord | None = None) -> MainAgentDecision:
|
||||
payload = self._parse_json_object(text)
|
||||
@ -121,6 +133,31 @@ class MainAgentRouter:
|
||||
return MainAgentDecision(mode="task", reason=reason, action="continue_task")
|
||||
return MainAgentDecision(mode="simple", reason=reason, action="simple_chat")
|
||||
|
||||
def _apply_active_task_boundary(
|
||||
self,
|
||||
decision: MainAgentDecision,
|
||||
*,
|
||||
message: str,
|
||||
active_task: TaskRecord | None,
|
||||
) -> MainAgentDecision:
|
||||
if active_task is None or decision.action != "continue_task":
|
||||
return decision
|
||||
if not _looks_like_fresh_task_request(message):
|
||||
return decision
|
||||
if _looks_like_explicit_task_followup(message):
|
||||
return decision
|
||||
title = decision.short_title or active_task.metadata.get("short_title")
|
||||
return MainAgentDecision(
|
||||
mode="task",
|
||||
reason=(
|
||||
"fresh standalone task request in the same session; "
|
||||
"do not attach it to the active task without explicit follow-up wording"
|
||||
),
|
||||
starts_new_task=True,
|
||||
short_title=title,
|
||||
action="create_task",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _prompt(
|
||||
*,
|
||||
@ -159,15 +196,19 @@ class MainAgentRouter:
|
||||
"- close_task: user explicitly says the active Task is done/satisfactory/finished.\n"
|
||||
"- abandon_task: user explicitly says to stop, cancel, abandon, or no longer do the active Task.\n\n"
|
||||
"Critical policy:\n"
|
||||
"- If there is an active Task, choose continue_task or revise_task unless the user's topic is completely unrelated "
|
||||
"to that Task or the user explicitly closes/abandons it.\n"
|
||||
"- A Session is the durable conversation/device/group context. A Task is one unit of work inside that Session. "
|
||||
"Do not use an active Task as a reason to merge every later message into the same work item.\n"
|
||||
"- If there is an active Task, choose continue_task only when the current message explicitly depends on, extends, "
|
||||
"or asks a direct follow-up about that active Task's latest result.\n"
|
||||
"- With an active Task, choose simple_chat for unrelated lightweight conversation and new_task for unrelated work "
|
||||
"that needs Task capabilities. Either decision starts a new topic.\n"
|
||||
"- An unrelated lightweight conversation must not be classified as revise_task merely because the active Task is awaiting acceptance.\n"
|
||||
"- Choose revise_task when the active Task is awaiting feedback or needs revision and the user asks for changes "
|
||||
"such as '改一下', '加上', '删除', '换成', '再详细点', '格式改成', '不要', or equivalent wording.\n"
|
||||
"- Choose continue_task for neutral follow-up questions or additional next steps that do not imply dissatisfaction with the previous result.\n"
|
||||
"- Use new_task only when the user clearly asks to start a different task.\n"
|
||||
"- Choose continue_task for neutral follow-up questions or additional next steps that refer to the previous result, "
|
||||
"for example '顺便查一下深圳', '这个也加上', or '继续'.\n"
|
||||
"- A standalone tool-dependent request such as a fresh weather/search/file/run/test request is new_task even when it is "
|
||||
"similar to the active Task. Repeating '珠海天气怎么样' later is a new Task unless the user says to revise or continue the old result.\n"
|
||||
"- If there is no active Task, choose new_task only for work that requires execution, iteration, tools, files, "
|
||||
"implementation, validation, or multi-step completion. Otherwise choose simple_chat.\n"
|
||||
"- Requests that need current, real-time, external, user-private, local-file, web, weather, price, news, "
|
||||
@ -203,3 +244,99 @@ def _clean_short_title(value: Any) -> str | None:
|
||||
return None
|
||||
title = " ".join(str(value).strip().split())
|
||||
return title[:40] or None
|
||||
|
||||
|
||||
def _looks_like_explicit_task_followup(message: str) -> bool:
|
||||
text = _compact_text(message)
|
||||
if not text:
|
||||
return False
|
||||
markers = (
|
||||
"继续",
|
||||
"接着",
|
||||
"上面",
|
||||
"刚才",
|
||||
"前面",
|
||||
"这个",
|
||||
"那个",
|
||||
"它",
|
||||
"结果",
|
||||
"再",
|
||||
"也",
|
||||
"顺便",
|
||||
"补充",
|
||||
"加上",
|
||||
"加入",
|
||||
"删除",
|
||||
"去掉",
|
||||
"改",
|
||||
"换成",
|
||||
"重做",
|
||||
"详细",
|
||||
"展开",
|
||||
"格式",
|
||||
"continue",
|
||||
"same task",
|
||||
"previous",
|
||||
"above",
|
||||
"that result",
|
||||
"revise",
|
||||
"update it",
|
||||
"add",
|
||||
"remove",
|
||||
"change",
|
||||
"also",
|
||||
)
|
||||
return any(marker in text for marker in markers)
|
||||
|
||||
|
||||
def _looks_like_fresh_task_request(message: str) -> bool:
|
||||
text = _compact_text(message)
|
||||
if not text:
|
||||
return False
|
||||
markers = (
|
||||
"天气",
|
||||
"气温",
|
||||
"下雨",
|
||||
"降雨",
|
||||
"空气质量",
|
||||
"预报",
|
||||
"查一下",
|
||||
"帮我查",
|
||||
"搜索",
|
||||
"搜一下",
|
||||
"看看最新",
|
||||
"最新",
|
||||
"今天",
|
||||
"明天",
|
||||
"上传",
|
||||
"下载",
|
||||
"文件",
|
||||
"运行",
|
||||
"执行",
|
||||
"测试",
|
||||
"构建",
|
||||
"部署",
|
||||
"修复",
|
||||
"weather",
|
||||
"forecast",
|
||||
"temperature",
|
||||
"search",
|
||||
"look up",
|
||||
"latest",
|
||||
"today",
|
||||
"tomorrow",
|
||||
"upload",
|
||||
"download",
|
||||
"file",
|
||||
"run",
|
||||
"execute",
|
||||
"test",
|
||||
"build",
|
||||
"deploy",
|
||||
"fix",
|
||||
)
|
||||
return any(marker in text for marker in markers)
|
||||
|
||||
|
||||
def _compact_text(message: str) -> str:
|
||||
return " ".join(str(message or "").strip().lower().split())
|
||||
|
||||
Reference in New Issue
Block a user