pzp5700's picture
HF Space (clean): Paper2PPT + PPT Polish
d82bbe4
Raw
History Blame Contribute Delete
9.4 kB
"""
轨迹管理器 - 统一的轨迹管理入口
提供简单易用的 API 来:
1. 开始/停止轨迹记录
2. 自动构建和导出轨迹
3. 批量管理轨迹
"""
from typing import Any, Dict, List, Optional, Union
from pathlib import Path
from dataflow_agent.trajectory.models import Trajectory
from dataflow_agent.trajectory.collector import TrajectoryCollector
from dataflow_agent.trajectory.builder import TrajectoryBuilder
from dataflow_agent.trajectory.exporter import TrajectoryExporter
from dataflow_agent.state import MainState
from dataflow_agent.logger import get_logger
log = get_logger(__name__)
class TrajectoryManager:
"""
轨迹管理器 - 统一入口
使用示例:
```python
# 1. 创建管理器
trj_manager = TrajectoryManager()
# 2. 开始记录
trj_manager.start_recording(inputs={"query": "..."})
# 3. 在 workflow 执行过程中,collector 会自动记录
# (需要在 workflow 中集成 collector 的 hook)
# 4. 停止记录并生成轨迹
trajectory = trj_manager.stop_recording(
state=final_state,
workflow_name="my_workflow"
)
# 5. 导出
filepath = trj_manager.export(trajectory, format="json")
```
"""
def __init__(self, output_dir: str = None):
"""
Args:
output_dir: 导出目录
"""
self.collector = TrajectoryCollector()
self.builder = TrajectoryBuilder()
self.exporter = TrajectoryExporter(output_dir)
self.is_recording = False
self.current_trajectory: Optional[Trajectory] = None
log.info("[TrajectoryManager] 初始化完成")
def start_recording(self,
inputs: Dict[str, Any] = None,
metadata: Dict[str, Any] = None):
"""
开始记录轨迹
Args:
inputs: 初始输入数据
metadata: 额外元数据
"""
self.collector.start(inputs=inputs, metadata=metadata)
self.is_recording = True
log.info("[TrajectoryManager] 开始记录轨迹")
def stop_recording(self,
state: MainState,
workflow_name: str,
user_id: str = None,
session_id: str = None) -> Trajectory:
"""
停止记录并生成轨迹
Args:
state: Workflow 执行后的最终状态
workflow_name: Workflow 名称
user_id: 用户 ID
session_id: 会话 ID
Returns:
生成的 Trajectory 对象
"""
if not self.is_recording:
log.warning("[TrajectoryManager] 未在记录状态,无法停止")
return None
# 构建轨迹
trajectory = self.builder.build_from_state(
state=state,
collector=self.collector,
workflow_name=workflow_name,
user_id=user_id,
session_id=session_id
)
self.is_recording = False
self.current_trajectory = trajectory
log.info(f"[TrajectoryManager] 轨迹记录完成: {trajectory.trace_id}")
return trajectory
def export(self,
trajectory: Trajectory = None,
format: str = "json",
filepath: str = None,
**kwargs) -> str:
"""
导出轨迹
Args:
trajectory: 要导出的轨迹,如果为 None 则使用当前轨迹
format: 导出格式(json/jsonl/sft/dpo)
filepath: 文件路径
**kwargs: 其他参数
Returns:
保存的文件路径
"""
if trajectory is None:
trajectory = self.current_trajectory
if trajectory is None:
log.error("[TrajectoryManager] 没有可导出的轨迹")
return None
if format == "json":
return self.exporter.export_to_json(trajectory, filepath, **kwargs)
elif format == "jsonl":
return self.exporter.export_to_jsonl([trajectory], filepath, **kwargs)
elif format == "sft":
return self.exporter.export_to_jsonl([trajectory], filepath, mode="sft")
elif format == "dpo":
return self.exporter.export_to_jsonl([trajectory], filepath, mode="dpo")
else:
raise ValueError(f"Unknown format: {format}")
def export_batch(self,
trajectories: List[Trajectory],
format: str = "jsonl",
filepath: str = None,
**kwargs) -> str:
"""
批量导出轨迹
Args:
trajectories: 轨迹列表
format: 导出格式
filepath: 文件路径
**kwargs: 其他参数
Returns:
保存的文件路径
"""
if format == "jsonl":
return self.exporter.export_to_jsonl(trajectories, filepath, **kwargs)
elif format == "sft":
return self.exporter.export_sft_dataset(trajectories, filepath, **kwargs)
else:
raise ValueError(f"Batch export not supported for format: {format}")
def get_collector(self) -> TrajectoryCollector:
"""获取收集器实例(用于手动集成)"""
return self.collector
def get_current_trajectory(self) -> Optional[Trajectory]:
"""获取当前轨迹"""
return self.current_trajectory
def add_feedback(self,
trajectory: Trajectory = None,
score: int = None,
comment: str = None,
edited_response: str = None,
labels: List[str] = None):
"""
添加用户反馈
Args:
trajectory: 轨迹对象,如果为 None 则使用当前轨迹
score: 评分 1-5
comment: 评论
edited_response: 用户修改后的回答
labels: 标签列表
"""
if trajectory is None:
trajectory = self.current_trajectory
if trajectory is None:
log.error("[TrajectoryManager] 没有可添加反馈的轨迹")
return
trajectory.set_feedback(
score=score,
comment=comment,
edited_response=edited_response,
labels=labels
)
log.info(f"[TrajectoryManager] 已添加反馈到轨迹: {trajectory.trace_id}")
# ==================== 全局单例 ====================
_global_manager: Optional[TrajectoryManager] = None
def get_trajectory_manager(output_dir: str = None) -> TrajectoryManager:
"""
获取全局轨迹管理器(单例模式)
Args:
output_dir: 输出目录
Returns:
TrajectoryManager 实例
"""
global _global_manager
if _global_manager is None:
_global_manager = TrajectoryManager(output_dir)
return _global_manager
def reset_trajectory_manager():
"""重置全局轨迹管理器"""
global _global_manager
_global_manager = None
# ==================== 便捷函数 ====================
def quick_record(workflow_func,
workflow_name: str,
inputs: Dict[str, Any] = None,
export_format: str = "json",
**workflow_kwargs):
"""
快速记录 workflow 执行轨迹的装饰器/函数
使用示例:
```python
# 作为装饰器
@quick_record(workflow_name="my_workflow")
async def my_workflow(state):
# workflow 逻辑
return final_state
# 或作为函数
final_state = await quick_record(
my_workflow,
workflow_name="my_workflow",
inputs={"query": "..."},
state=initial_state
)
```
"""
import asyncio
from functools import wraps
# 如果是装饰器用法
if callable(workflow_func):
@wraps(workflow_func)
async def wrapper(*args, **kwargs):
manager = get_trajectory_manager()
# 开始记录
manager.start_recording(inputs=inputs)
try:
# 执行 workflow
if asyncio.iscoroutinefunction(workflow_func):
result = await workflow_func(*args, **kwargs)
else:
result = workflow_func(*args, **kwargs)
# 停止记录
trajectory = manager.stop_recording(
state=result,
workflow_name=workflow_name
)
# 导出
filepath = manager.export(trajectory, format=export_format)
log.info(f"[quick_record] 轨迹已导出: {filepath}")
return result
except Exception as e:
log.exception(f"[quick_record] Workflow 执行失败: {e}")
raise
return wrapper
# 如果是函数调用用法
else:
raise ValueError("quick_record 应该作为装饰器使用")