feat(engine): 优化智能体循环中的助手消息处理逻辑

- 在没有工具调用时才添加助手消息到上下文
- 确保工具调用响应正确添加到消息上下文中
- 修复了消息构建的条件逻辑

fix(cron): 改进定时任务调度的时间解析功能

- 添加正则表达式导入用于时间显示解析
- 实现从显示文本中提取毫秒间隔的功能
- 增强整数转换的安全性,避免类型错误
- 优化定时任务配置的解析逻辑

feat(outlook): 增强Outlook集成的功能和稳定性

- 将默认超时时间从10秒增加到180秒
- 为状态检查函数添加可选的验证参数
- 串行执行邮件概览获取操作而非并行
- 改进连接状态验证逻辑

feat(channel): 添加设备名称作为会话标识的选项

- 为终端WebSocket适配器添加新的配置选项
- 实现基于设备名称生成会话对等ID的功能
- 记录原始对等ID和设备名称的元数据
- 支持从设备名称创建会话对等ID

feat(skills): 完善技能学习评估系统和进度跟踪

- 在应用启动时自动调度待评估的技能草稿
- 为技能评估工作创建独立的循环工厂
- 实现异步技能评估任务的取消和清理机制
- 添加技能评估进度报告和状态跟踪功能
- 扩展会话列表API以包含更多详细信息
- 防止对不存在的会话进行操作
- 优化技能草稿提交和评估的业务逻辑

perf(skills): 提升技能评估的并发性能

- 实现并行技能案例评估以提高效率
- 添加最大并行案例数的环境变量控制
- 实现实时评估进度更新和回调机制
- 优化评估过程中的资源管理和同步

refactor(services): 创建隔离的智能体循环实例

- 添加创建独立智能体循环的工厂方法
- 确保新循环继承运行时服务配置
- 支持技能评估等需要隔离环境的场景
```
This commit is contained in:
2026-06-15 14:48:16 +08:00
parent 8aeb97a5fc
commit 4b0bf65ace
53 changed files with 4328 additions and 292 deletions

View File

@ -331,6 +331,10 @@ class ChannelRuntime:
event_recorder=self.record_event,
heartbeat_seconds=float(cfg.config.get("heartbeat_seconds") or 30),
max_message_chars=int(cfg.config.get("max_message_chars") or 20000),
session_peer_from_device_name=bool(
cfg.config.get("session_peer_from_device_name")
or cfg.config.get("sessionPeerFromDeviceName")
),
)
if cfg.kind == "telegram" and cfg.mode in {"polling", "webhook"}:

View File

@ -51,6 +51,7 @@ class TerminalWebSocketAdapter:
event_recorder: Callable[..., None] | None = None,
heartbeat_seconds: float = 30,
max_message_chars: int = 20000,
session_peer_from_device_name: bool = False,
) -> None:
self.channel_id = channel_id
self.kind = kind
@ -61,6 +62,7 @@ class TerminalWebSocketAdapter:
self.event_recorder = event_recorder
self.heartbeat_seconds = max(1.0, float(heartbeat_seconds))
self.max_message_chars = max(1, int(max_message_chars))
self.session_peer_from_device_name = bool(session_peer_from_device_name)
self.started = False
self._connections_by_session: dict[str, TerminalConnection] = {}
self._session_by_peer: dict[str, str] = {}
@ -131,14 +133,15 @@ class TerminalWebSocketAdapter:
*,
current: TerminalConnection | None,
) -> TerminalConnection | None:
peer_id = _clean(payload.get("peer_id"))
if not peer_id:
raw_peer_id = _clean(payload.get("peer_id"))
if not raw_peer_id:
await websocket.send_json({"type": "error", "error": "peer_id is required"})
return current
thread_id = _clean(payload.get("thread_id")) or None
user_id = _clean(payload.get("user_id")) or None
device_name = _clean(payload.get("device_name"))
peer_id = self._session_peer_id(raw_peer_id, device_name)
capabilities = [str(item) for item in payload.get("capabilities") or [] if item is not None]
identity = ChannelIdentity(
channel_id=self.channel_id,
@ -171,7 +174,12 @@ class TerminalWebSocketAdapter:
self._record(
kind="terminal_connected",
session_id=session_id,
metadata={"peer_id": peer_id, "device_name": device_name, "capabilities": capabilities},
metadata={
"peer_id": peer_id,
"raw_peer_id": raw_peer_id,
"device_name": device_name,
"capabilities": capabilities,
},
)
await websocket.send_json(
{
@ -299,3 +307,13 @@ class TerminalWebSocketAdapter:
error=error,
metadata=metadata,
)
def _session_peer_id(self, peer_id: str, device_name: str) -> str:
if self.session_peer_from_device_name and device_name:
return f"device-{_clean_session_part(device_name)}"
return peer_id
def _clean_session_part(value: str) -> str:
cleaned = "-".join(str(value or "").strip().split())
return cleaned.replace(":", "_") or "unknown"