```
feat(agent): 添加技能自动路由功能 - 引入 SkillRouter 实现根据用户消息自动推荐技能 - 在构建引擎时集成技能路由器 - 从对话历史中提取最后一条用户消息作为路由输入 - 为技能添加触发关键词和中文标题字段支持 refactor(browser): 重构浏览器自动化为子进程架构 - 将 Playwright 浏览器控制移至独立的子进程 worker - 解决 PyInstaller 打包环境下 C 扩展兼容性问题 - 通过 JSON RPC 协议与浏览器 worker 通信 - 添加工具目录和脚本路径查找机制 feat(skills): 增强技能元数据和UI展示 - 为技能添加 triggers 和 title 字段 - 在技能商店中包含中文标题信息 - 添加 office-viz 技能优先级排序 - 在服务器管理器中返回技能标题 feat(gui): 实现技能选择器UI组件 - 添加带下拉菜单的技能选择器按钮 - 支持中文标题和拼音首字母显示 - 集成会话技能加载和状态管理 - 提供通用技能选项和已启用技能列表 ```
This commit is contained in:
@@ -39,6 +39,7 @@ from .providers import ProviderClient, ProviderRouter
|
||||
from .overrides import RiskOverrideStore
|
||||
from .secrets import SecretStore, state_dir
|
||||
from .skills import SkillLoader, save_skill_tool, skill_catalog_text, skill_tools
|
||||
from .skills.router import SkillRouter
|
||||
from .tools import ToolRegistry
|
||||
from .tools.ask import ask_user_tool
|
||||
from .tools.directories import request_directory_tool
|
||||
@@ -416,6 +417,8 @@ def build_engine(
|
||||
# Persona dirs come FIRST so a user's global/workspace copy of the same name shadows
|
||||
# the bundle's (later dirs overwrite earlier in the loader).
|
||||
skill_loader = SkillLoader([Path(d) for d in (extra_skill_dirs or [])] + _skill_dirs(ws))
|
||||
# Skill Router: 根据用户消息自动推荐技能(OPE-xxx 技能自动路由)
|
||||
skill_router = SkillRouter(skill_loader)
|
||||
# Per-session effective menu (SKILLS-SPEC §3). The manager passes a CALLABLE so
|
||||
# load_skill consults the LIVE state per call (a Settings disable applies to running
|
||||
# sessions; a skill created after this build is still loadable). The catalog itself
|
||||
@@ -512,6 +515,28 @@ def build_engine(
|
||||
skills_ctx = skill_catalog_text(skill_loader, allowed=allowed)
|
||||
if skills_ctx:
|
||||
parts.append(skills_ctx)
|
||||
# Skill Router: 根据最后一条用户消息自动推荐技能
|
||||
eng_ref = _engine_box[0] if _engine_box else None
|
||||
if eng_ref is not None and skill_router is not None:
|
||||
# 找到最后一条用户消息
|
||||
last_user_msg = ""
|
||||
for msg in reversed(eng_ref.messages):
|
||||
if msg.get("role") == "user":
|
||||
content = msg.get("content", "")
|
||||
if isinstance(content, str):
|
||||
last_user_msg = content
|
||||
elif isinstance(content, list):
|
||||
# content-parts:提取 text 类型
|
||||
text_parts = [
|
||||
p.get("text", "") for p in content
|
||||
if isinstance(p, dict) and p.get("type") == "text"
|
||||
]
|
||||
last_user_msg = "\n".join(text_parts)
|
||||
break
|
||||
if last_user_msg:
|
||||
router_ctx = skill_router.build_context_text(last_user_msg)
|
||||
if router_ctx:
|
||||
parts.append(router_ctx)
|
||||
# Disable countermand (§3): instructions already loaded into this conversation keep
|
||||
# steering the model even after the skill is turned off/deleted — history can't be
|
||||
# un-read. So a loaded-but-no-longer-available skill gets an explicit stop note,
|
||||
|
||||
Reference in New Issue
Block a user