2025-03-28 11:40:46 +08:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
|
import os
|
2025-05-13 19:49:54 +08:00
|
|
|
|
import yaml
|
2025-05-20 22:21:51 +08:00
|
|
|
|
import uuid
|
2025-05-13 19:49:54 +08:00
|
|
|
|
from pathlib import Path
|
2025-07-22 04:12:55 +08:00
|
|
|
|
from typing import Annotated, TypedDict, Optional, Any
|
2025-03-24 19:07:51 +08:00
|
|
|
|
from abc import abstractmethod
|
2025-03-28 11:40:46 +08:00
|
|
|
|
from dataclasses import dataclass, fields, field
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
2025-07-22 17:29:38 +08:00
|
|
|
|
from pydantic import BaseModel, Field
|
2025-03-28 11:40:46 +08:00
|
|
|
|
from langchain_core.runnables import RunnableConfig
|
2025-03-25 05:40:07 +08:00
|
|
|
|
from langchain_core.messages import BaseMessage
|
2025-03-24 19:07:51 +08:00
|
|
|
|
from langgraph.graph.state import CompiledStateGraph
|
2025-03-25 05:40:07 +08:00
|
|
|
|
from langgraph.graph.message import add_messages
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
2025-04-02 13:00:25 +08:00
|
|
|
|
from src.utils import logger
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
class State(TypedDict):
|
|
|
|
|
|
messages: Annotated[list[BaseMessage], add_messages]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(kw_only=True)
|
2025-04-02 13:00:25 +08:00
|
|
|
|
class Configuration(dict):
|
2025-03-24 19:07:51 +08:00
|
|
|
|
"""
|
|
|
|
|
|
定义一个基础 Configuration 供 各类 graph 继承
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
配置优先级:
|
|
|
|
|
|
1. 运行时配置(RunnableConfig):最高优先级,直接从函数参数传入
|
|
|
|
|
|
2. 文件配置(config.private.yaml):中等优先级,从文件加载
|
|
|
|
|
|
3. 类默认配置:最低优先级,类中定义的默认值
|
2025-03-24 19:07:51 +08:00
|
|
|
|
"""
|
2025-03-28 11:40:46 +08:00
|
|
|
|
|
2025-07-22 04:12:55 +08:00
|
|
|
|
thread_id: str = field(
|
|
|
|
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
|
|
|
|
metadata={
|
|
|
|
|
|
"name": "线程ID",
|
|
|
|
|
|
"configurable": False,
|
|
|
|
|
|
"description": "用来描述智能体的角色和行为"
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
user_id: str = field(
|
|
|
|
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
|
|
|
|
metadata={
|
|
|
|
|
|
"name": "用户ID",
|
|
|
|
|
|
"configurable": False,
|
|
|
|
|
|
"description": "用来描述智能体的角色和行为"
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-03-28 11:40:46 +08:00
|
|
|
|
@classmethod
|
|
|
|
|
|
def from_runnable_config(
|
2025-07-22 17:30:00 +08:00
|
|
|
|
cls, config: RunnableConfig | None = None, module_name: str | None = None
|
2025-03-28 11:40:46 +08:00
|
|
|
|
) -> Configuration:
|
2025-05-13 19:49:54 +08:00
|
|
|
|
"""Create a Configuration instance from a RunnableConfig object.
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
config: RunnableConfig object with highest priority
|
2025-07-22 17:30:00 +08:00
|
|
|
|
module_name: Name of the agent to load config file for
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Configuration instance with merged config values
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 获取类默认配置:创建一个实例获取所有默认值
|
|
|
|
|
|
instance = cls()
|
2025-03-28 11:40:46 +08:00
|
|
|
|
_fields = {f.name for f in fields(cls) if f.init}
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
# 尝试加载文件配置(中等优先级)
|
|
|
|
|
|
file_config = {}
|
2025-07-22 17:30:00 +08:00
|
|
|
|
if module_name:
|
|
|
|
|
|
file_config = cls.from_file(module_name)
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
# 获取运行时配置(最高优先级)
|
|
|
|
|
|
configurable = (config.get("configurable") or {}) if config else {}
|
|
|
|
|
|
|
|
|
|
|
|
# 合并三级配置,注意优先级
|
|
|
|
|
|
merged_config = {}
|
2025-05-24 11:29:45 +08:00
|
|
|
|
for config_field in _fields:
|
2025-05-13 19:49:54 +08:00
|
|
|
|
# 1. 默认使用类默认值
|
2025-05-24 11:29:45 +08:00
|
|
|
|
if hasattr(instance, config_field):
|
|
|
|
|
|
merged_config[config_field] = getattr(instance, config_field)
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
# 2. 如果文件配置中有此字段,则覆盖
|
2025-05-24 11:29:45 +08:00
|
|
|
|
if config_field in file_config:
|
|
|
|
|
|
merged_config[config_field] = file_config[config_field]
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
# 3. 如果运行时配置中有此字段,则覆盖
|
2025-05-24 11:29:45 +08:00
|
|
|
|
if config_field in configurable:
|
|
|
|
|
|
merged_config[config_field] = configurable[config_field]
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
# 创建并返回配置实例
|
|
|
|
|
|
# logger.debug(f"合并配置: {merged_config}")
|
|
|
|
|
|
return cls(**merged_config)
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2025-07-22 17:30:00 +08:00
|
|
|
|
def from_file(cls, module_name: str) -> Configuration:
|
2025-05-13 19:49:54 +08:00
|
|
|
|
"""从文件加载配置"""
|
2025-07-22 17:30:00 +08:00
|
|
|
|
config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
|
2025-05-13 19:49:54 +08:00
|
|
|
|
file_config = {}
|
|
|
|
|
|
if os.path.exists(config_file_path):
|
|
|
|
|
|
try:
|
2025-05-24 11:29:45 +08:00
|
|
|
|
with open(config_file_path, encoding='utf-8') as f:
|
2025-05-13 19:49:54 +08:00
|
|
|
|
file_config = yaml.safe_load(f) or {}
|
2025-07-22 17:30:00 +08:00
|
|
|
|
# logger.info(f"从文件加载智能体 {module_name} 配置: {file_config}")
|
2025-05-13 19:49:54 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载智能体配置文件出错: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
return file_config
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2025-07-22 17:30:00 +08:00
|
|
|
|
def save_to_file(cls, config: dict, module_name: str) -> bool:
|
2025-05-13 19:49:54 +08:00
|
|
|
|
"""Save configuration to a YAML file
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
config: Configuration dictionary to save
|
2025-07-22 17:30:00 +08:00
|
|
|
|
module_name: Name of the agent to save config for
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
True if saving was successful, False otherwise
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
2025-07-22 17:30:00 +08:00
|
|
|
|
config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
|
2025-05-13 19:49:54 +08:00
|
|
|
|
# 确保目录存在
|
|
|
|
|
|
os.makedirs(os.path.dirname(config_file_path), exist_ok=True)
|
|
|
|
|
|
with open(config_file_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
yaml.dump(config, f, indent=2, allow_unicode=True)
|
|
|
|
|
|
|
2025-07-22 17:30:00 +08:00
|
|
|
|
# logger.info(f"智能体 {module_name} 配置已保存到 {config_file_path}")
|
2025-05-13 19:49:54 +08:00
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"保存智能体配置文件出错: {e}")
|
|
|
|
|
|
return False
|
2025-03-28 11:40:46 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
def to_dict(cls):
|
2025-04-02 00:00:04 +08:00
|
|
|
|
# 创建一个实例来处理 default_factory
|
|
|
|
|
|
instance = cls()
|
2025-04-02 13:00:25 +08:00
|
|
|
|
confs = {}
|
|
|
|
|
|
configurable_items = {}
|
|
|
|
|
|
for f in fields(cls):
|
|
|
|
|
|
if f.init and not f.metadata.get("hide", False):
|
|
|
|
|
|
value = getattr(instance, f.name)
|
|
|
|
|
|
if callable(value) and hasattr(value, "__call__"):
|
|
|
|
|
|
confs[f.name] = value()
|
|
|
|
|
|
else:
|
|
|
|
|
|
confs[f.name] = value
|
|
|
|
|
|
|
2025-07-22 04:12:55 +08:00
|
|
|
|
if f.metadata.get("configurable", True):
|
2025-04-02 13:00:25 +08:00
|
|
|
|
configurable_items[f.name] = {
|
|
|
|
|
|
"type": f.type.__name__,
|
|
|
|
|
|
"name": f.metadata.get("name", f.name),
|
|
|
|
|
|
"options": f.metadata.get("options", []),
|
|
|
|
|
|
"default": f.default,
|
|
|
|
|
|
"description": f.metadata.get("description", ""),
|
|
|
|
|
|
}
|
|
|
|
|
|
confs["configurable_items"] = configurable_items
|
|
|
|
|
|
return confs
|
2025-03-25 05:40:07 +08:00
|
|
|
|
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-07-22 17:29:38 +08:00
|
|
|
|
class BaseModelConfiguration(BaseModel):
|
|
|
|
|
|
|
|
|
|
|
|
thread_id: str = Field(
|
|
|
|
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
|
|
|
|
metadata={
|
|
|
|
|
|
"title": "线程ID",
|
|
|
|
|
|
"description": "用来描述智能体的角色和行为",
|
|
|
|
|
|
"configurable": False,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
user_id: str = Field(
|
|
|
|
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
|
|
|
|
metadata={
|
|
|
|
|
|
"title": "用户ID",
|
|
|
|
|
|
"description": "用来描述智能体的角色和行为",
|
|
|
|
|
|
"configurable": False,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
def from_runnable_config(
|
|
|
|
|
|
cls,
|
2025-07-26 03:36:54 +08:00
|
|
|
|
config: RunnableConfig | None = None,
|
|
|
|
|
|
module_name: str | None = None,
|
|
|
|
|
|
) -> BaseModelConfiguration:
|
2025-07-22 17:29:38 +08:00
|
|
|
|
"""
|
|
|
|
|
|
从 RunnableConfig 和 YAML 文件中构建 Configuration 实例
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 默认配置
|
|
|
|
|
|
default_instance = cls()
|
|
|
|
|
|
default_values = default_instance.dict()
|
|
|
|
|
|
|
|
|
|
|
|
# 文件配置
|
2025-07-22 17:30:00 +08:00
|
|
|
|
file_config = cls.from_file(module_name) if module_name else {}
|
2025-07-22 17:29:38 +08:00
|
|
|
|
|
|
|
|
|
|
# 运行时配置(最高优先级)
|
|
|
|
|
|
runtime_config = config.get("configurable") if config else {}
|
|
|
|
|
|
|
|
|
|
|
|
merged_config = {
|
|
|
|
|
|
**default_values,
|
|
|
|
|
|
**file_config,
|
|
|
|
|
|
**runtime_config,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return cls(**merged_config)
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2025-07-22 17:30:00 +08:00
|
|
|
|
def from_file(cls, module_name: str) -> dict[str, Any]:
|
2025-07-22 17:29:38 +08:00
|
|
|
|
"""
|
|
|
|
|
|
从 YAML 文件加载配置
|
|
|
|
|
|
"""
|
2025-07-22 17:30:00 +08:00
|
|
|
|
config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
|
2025-07-22 17:29:38 +08:00
|
|
|
|
if os.path.exists(config_file_path):
|
|
|
|
|
|
try:
|
|
|
|
|
|
with open(config_file_path, encoding="utf-8") as f:
|
|
|
|
|
|
return yaml.safe_load(f) or {}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载配置文件失败: {e}")
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2025-07-22 17:30:00 +08:00
|
|
|
|
def save_to_file(cls, config: dict, module_name: str) -> bool:
|
2025-07-22 17:29:38 +08:00
|
|
|
|
"""
|
|
|
|
|
|
保存配置到 YAML 文件
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
2025-07-22 17:30:00 +08:00
|
|
|
|
config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
|
2025-07-22 17:29:38 +08:00
|
|
|
|
os.makedirs(config_file_path.parent, exist_ok=True)
|
|
|
|
|
|
with open(config_file_path, "w", encoding="utf-8") as f:
|
|
|
|
|
|
yaml.dump(config, f, indent=2, allow_unicode=True)
|
|
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"保存配置文件失败: {e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
def to_dict(cls) -> dict[str, Any]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
提取类字段的默认值与字段元数据,主要用于前端动态生成配置项。
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 创建实例以获得 default_factory 值
|
|
|
|
|
|
instance = cls()
|
|
|
|
|
|
|
|
|
|
|
|
confs: dict[str, Any] = {}
|
|
|
|
|
|
configurable_items: dict[str, Any] = {}
|
|
|
|
|
|
|
2025-07-26 03:36:54 +08:00
|
|
|
|
for name, _field in cls.model_fields.items():
|
2025-07-22 17:29:38 +08:00
|
|
|
|
value = getattr(instance, name)
|
|
|
|
|
|
|
|
|
|
|
|
confs[name] = value
|
|
|
|
|
|
|
|
|
|
|
|
# 安全地处理 Pydantic 字段元数据
|
|
|
|
|
|
field_metadata = {}
|
2025-07-26 03:36:54 +08:00
|
|
|
|
if hasattr(_field, 'json_schema_extra') and _field.json_schema_extra:
|
|
|
|
|
|
if isinstance(_field.json_schema_extra, dict):
|
|
|
|
|
|
field_metadata = _field.json_schema_extra['metadata']
|
|
|
|
|
|
elif isinstance(_field.json_schema_extra, list | tuple):
|
2025-07-22 17:29:38 +08:00
|
|
|
|
# 在 Pydantic v2 中,metadata 可能是列表,合并所有字典项
|
2025-07-26 03:36:54 +08:00
|
|
|
|
for item in _field.json_schema_extra:
|
2025-07-22 17:29:38 +08:00
|
|
|
|
if isinstance(item, dict):
|
|
|
|
|
|
field_metadata.update(item['metadata'])
|
|
|
|
|
|
|
|
|
|
|
|
# 检查字段是否应该可配置 - 支持不同的元数据格式
|
|
|
|
|
|
if field_metadata.get("configurable", True):
|
2025-07-26 03:36:54 +08:00
|
|
|
|
default_type = _field.annotation.__name__ if hasattr(_field.annotation, '__name__') else 'str'
|
2025-07-22 17:29:38 +08:00
|
|
|
|
configurable_items[name] = {
|
2025-07-26 03:36:54 +08:00
|
|
|
|
"type": field_metadata.get("type", default_type),
|
2025-07-22 17:29:38 +08:00
|
|
|
|
"name": field_metadata.get("name") or name,
|
|
|
|
|
|
"options": field_metadata.get("options") or [],
|
2025-07-26 03:36:54 +08:00
|
|
|
|
"default": _field.default if _field.default is not None else None,
|
2025-07-22 17:29:38 +08:00
|
|
|
|
"description": field_metadata.get("description") or "",
|
|
|
|
|
|
"x_oap_ui_config": field_metadata.get("x_oap_ui_config", {}),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
confs["configurable_items"] = configurable_items
|
|
|
|
|
|
return confs
|
|
|
|
|
|
|
2025-05-24 11:29:45 +08:00
|
|
|
|
class BaseAgent:
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
|
"""
|
|
|
|
|
|
定义一个基础 Agent 供 各类 graph 继承
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2025-05-20 22:21:51 +08:00
|
|
|
|
name = "base_agent"
|
|
|
|
|
|
description = "base_agent"
|
2025-03-31 22:20:29 +08:00
|
|
|
|
config_schema: Configuration = Configuration
|
|
|
|
|
|
|
2025-03-28 11:40:46 +08:00
|
|
|
|
def __init__(self, **kwargs):
|
2025-07-24 00:46:15 +08:00
|
|
|
|
pass
|
2025-03-31 22:20:29 +08:00
|
|
|
|
|
2025-07-22 17:30:00 +08:00
|
|
|
|
@property
|
|
|
|
|
|
def module_name(self) -> str:
|
|
|
|
|
|
"""Get the module name of the agent class."""
|
|
|
|
|
|
return self.__class__.__module__.split('.')[-2]
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
def id(self) -> str:
|
|
|
|
|
|
"""Get the agent's class name."""
|
|
|
|
|
|
return self.__class__.__name__
|
|
|
|
|
|
|
2025-05-20 22:21:51 +08:00
|
|
|
|
async def get_info(self):
|
2025-04-01 22:16:07 +08:00
|
|
|
|
return {
|
2025-07-22 17:30:00 +08:00
|
|
|
|
"id": self.id,
|
2025-05-20 22:21:51 +08:00
|
|
|
|
"name": self.name if hasattr(self, "name") else "Unknown",
|
|
|
|
|
|
"description": self.description if hasattr(self, "description") else "Unknown",
|
|
|
|
|
|
"config_schema": self.config_schema.to_dict(),
|
|
|
|
|
|
"all_tools": self.all_tools if hasattr(self, "all_tools") else [],
|
|
|
|
|
|
"has_checkpointer": await self.check_checkpointer(),
|
2025-04-01 22:16:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-24 00:46:15 +08:00
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
|
|
2025-05-16 23:46:25 +08:00
|
|
|
|
async def stream_values(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs):
|
2025-05-20 22:21:51 +08:00
|
|
|
|
graph = await self.get_graph()
|
2025-05-15 22:34:32 +08:00
|
|
|
|
logger.debug(f"stream_values: {config_schema}")
|
2025-05-16 23:46:25 +08:00
|
|
|
|
for event in graph.astream({"messages": messages}, stream_mode="values", config=config_schema):
|
2025-03-31 22:20:29 +08:00
|
|
|
|
yield event["messages"]
|
|
|
|
|
|
|
2025-05-16 23:46:25 +08:00
|
|
|
|
async def stream_messages(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs):
|
2025-05-20 22:21:51 +08:00
|
|
|
|
graph = await self.get_graph()
|
2025-05-15 22:34:32 +08:00
|
|
|
|
logger.debug(f"stream_messages: {config_schema}")
|
2025-03-31 22:20:29 +08:00
|
|
|
|
|
2025-05-16 23:46:25 +08:00
|
|
|
|
async for msg, metadata in graph.astream({"messages": messages}, stream_mode="messages", config=config_schema):
|
2025-04-02 00:00:04 +08:00
|
|
|
|
yield msg, metadata
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
2025-05-20 22:21:51 +08:00
|
|
|
|
async def check_checkpointer(self):
|
|
|
|
|
|
app = await self.get_graph()
|
|
|
|
|
|
if not hasattr(app, "checkpointer") or app.checkpointer is None:
|
|
|
|
|
|
logger.warning(f"智能体 {self.name} 的 Graph 未配置 checkpointer,无法获取历史记录")
|
|
|
|
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
2025-05-16 23:46:25 +08:00
|
|
|
|
async def get_history(self, user_id, thread_id) -> list[dict]:
|
2025-05-15 22:34:32 +08:00
|
|
|
|
"""获取历史消息"""
|
2025-05-20 22:21:51 +08:00
|
|
|
|
try:
|
|
|
|
|
|
app = await self.get_graph()
|
2025-05-15 22:34:32 +08:00
|
|
|
|
|
2025-05-20 22:21:51 +08:00
|
|
|
|
if not await self.check_checkpointer():
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
config = {"configurable": {"thread_id": thread_id, "user_id": user_id}}
|
|
|
|
|
|
state = await app.aget_state(config)
|
|
|
|
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
|
if state:
|
|
|
|
|
|
messages = state.values.get('messages', [])
|
|
|
|
|
|
for msg in messages:
|
|
|
|
|
|
if hasattr(msg, 'model_dump'):
|
|
|
|
|
|
msg_dict = msg.model_dump() # 转换成字典
|
|
|
|
|
|
else:
|
|
|
|
|
|
msg_dict = dict(msg) if hasattr(msg, '__dict__') else {"content": str(msg)}
|
|
|
|
|
|
result.append(msg_dict)
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取智能体 {self.name} 历史消息出错: {e}")
|
|
|
|
|
|
return []
|
2025-05-15 22:34:32 +08:00
|
|
|
|
|
2025-03-24 19:07:51 +08:00
|
|
|
|
@abstractmethod
|
2025-05-20 22:21:51 +08:00
|
|
|
|
async def get_graph(self, **kwargs) -> CompiledStateGraph:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取并编译对话图实例。
|
|
|
|
|
|
必须确保在编译时设置 checkpointer,否则将无法获取历史记录。
|
|
|
|
|
|
例如: graph = workflow.compile(checkpointer=sqlite_checkpointer)
|
|
|
|
|
|
"""
|
2025-05-24 11:29:45 +08:00
|
|
|
|
pass
|