确认PokieTicker新闻库数据源
This commit is contained in:
@@ -48,15 +48,19 @@ class AnalystAgent(ReActAgent):
|
||||
f"Must be one of: {list(ANALYST_TYPES.keys())}",
|
||||
)
|
||||
|
||||
self.analyst_type_key = analyst_type
|
||||
self.analyst_persona = ANALYST_TYPES[analyst_type]["display_name"]
|
||||
object.__setattr__(self, "analyst_type_key", analyst_type)
|
||||
object.__setattr__(
|
||||
self,
|
||||
"analyst_persona",
|
||||
ANALYST_TYPES[analyst_type]["display_name"],
|
||||
)
|
||||
|
||||
if agent_id is None:
|
||||
agent_id = analyst_type
|
||||
self.agent_id = agent_id
|
||||
object.__setattr__(self, "agent_id", agent_id)
|
||||
|
||||
self.config = config or {}
|
||||
self.toolkit = toolkit
|
||||
object.__setattr__(self, "config", config or {})
|
||||
object.__setattr__(self, "toolkit", toolkit)
|
||||
sys_prompt = self._load_system_prompt()
|
||||
|
||||
kwargs = {
|
||||
@@ -125,4 +129,12 @@ class AnalystAgent(ReActAgent):
|
||||
self.config.get("config_name", "default"),
|
||||
active_skill_dirs=active_skill_dirs,
|
||||
)
|
||||
self.sys_prompt = self._load_system_prompt()
|
||||
self._apply_runtime_sys_prompt(self._load_system_prompt())
|
||||
|
||||
def _apply_runtime_sys_prompt(self, sys_prompt: str) -> None:
|
||||
"""Update the prompt used by future turns and the cached system msg."""
|
||||
self._sys_prompt = sys_prompt
|
||||
for msg, _marks in self.memory.content:
|
||||
if getattr(msg, "role", None) == "system":
|
||||
msg.content = sys_prompt
|
||||
break
|
||||
|
||||
@@ -38,21 +38,29 @@ class PMAgent(ReActAgent):
|
||||
toolkit_factory_kwargs: Optional[Dict[str, Any]] = None,
|
||||
toolkit: Optional[Toolkit] = None,
|
||||
):
|
||||
self.config = config or {}
|
||||
object.__setattr__(self, "config", config or {})
|
||||
|
||||
# Portfolio state
|
||||
self.portfolio = {
|
||||
"cash": initial_cash,
|
||||
"positions": {},
|
||||
"margin_used": 0.0,
|
||||
"margin_requirement": margin_requirement,
|
||||
}
|
||||
object.__setattr__(
|
||||
self,
|
||||
"portfolio",
|
||||
{
|
||||
"cash": initial_cash,
|
||||
"positions": {},
|
||||
"margin_used": 0.0,
|
||||
"margin_requirement": margin_requirement,
|
||||
},
|
||||
)
|
||||
|
||||
# Decisions made in current cycle
|
||||
self._decisions: Dict[str, Dict] = {}
|
||||
object.__setattr__(self, "_decisions", {})
|
||||
toolkit_factory_kwargs = toolkit_factory_kwargs or {}
|
||||
self._toolkit_factory = toolkit_factory
|
||||
self._toolkit_factory_kwargs = toolkit_factory_kwargs
|
||||
object.__setattr__(self, "_toolkit_factory", toolkit_factory)
|
||||
object.__setattr__(
|
||||
self,
|
||||
"_toolkit_factory_kwargs",
|
||||
toolkit_factory_kwargs,
|
||||
)
|
||||
|
||||
# Create toolkit after local state is ready so bound tool methods can be registered.
|
||||
if toolkit is None:
|
||||
@@ -65,7 +73,7 @@ class PMAgent(ReActAgent):
|
||||
)
|
||||
else:
|
||||
toolkit = self._create_toolkit()
|
||||
self.toolkit = toolkit
|
||||
object.__setattr__(self, "toolkit", toolkit)
|
||||
|
||||
sys_prompt = build_agent_system_prompt(
|
||||
agent_id=name,
|
||||
@@ -205,6 +213,42 @@ class PMAgent(ReActAgent):
|
||||
"""Update portfolio after external execution"""
|
||||
self.portfolio.update(portfolio)
|
||||
|
||||
def _has_open_positions(self) -> bool:
|
||||
"""Return whether the current portfolio still has non-zero positions."""
|
||||
for position in self.portfolio.get("positions", {}).values():
|
||||
if position.get("long", 0) or position.get("short", 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_apply_initial_cash(self) -> bool:
|
||||
"""Only allow cash rebasing before any positions or margin exist."""
|
||||
return (
|
||||
not self._has_open_positions()
|
||||
and float(self.portfolio.get("margin_used", 0.0) or 0.0) == 0.0
|
||||
)
|
||||
|
||||
def apply_runtime_portfolio_config(
|
||||
self,
|
||||
*,
|
||||
margin_requirement: Optional[float] = None,
|
||||
initial_cash: Optional[float] = None,
|
||||
) -> Dict[str, bool]:
|
||||
"""Apply safe run-time portfolio config updates."""
|
||||
result = {
|
||||
"margin_requirement": False,
|
||||
"initial_cash": False,
|
||||
}
|
||||
|
||||
if margin_requirement is not None:
|
||||
self.portfolio["margin_requirement"] = float(margin_requirement)
|
||||
result["margin_requirement"] = True
|
||||
|
||||
if initial_cash is not None and self.can_apply_initial_cash():
|
||||
self.portfolio["cash"] = float(initial_cash)
|
||||
result["initial_cash"] = True
|
||||
|
||||
return result
|
||||
|
||||
def reload_runtime_assets(self, active_skill_dirs: Optional[list] = None) -> None:
|
||||
"""Reload toolkit and system prompt from current run assets."""
|
||||
from .toolkit_factory import create_agent_toolkit
|
||||
@@ -221,8 +265,18 @@ class PMAgent(ReActAgent):
|
||||
owner=self,
|
||||
**toolkit_kwargs,
|
||||
)
|
||||
self.sys_prompt = build_agent_system_prompt(
|
||||
agent_id=self.name,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
self._apply_runtime_sys_prompt(
|
||||
build_agent_system_prompt(
|
||||
agent_id=self.name,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
),
|
||||
)
|
||||
|
||||
def _apply_runtime_sys_prompt(self, sys_prompt: str) -> None:
|
||||
"""Update the prompt used by future turns and the cached system msg."""
|
||||
self._sys_prompt = sys_prompt
|
||||
for msg, _marks in self.memory.content:
|
||||
if getattr(msg, "role", None) == "system":
|
||||
msg.content = sys_prompt
|
||||
break
|
||||
|
||||
@@ -39,12 +39,12 @@ class RiskAgent(ReActAgent):
|
||||
config: Configuration dictionary
|
||||
long_term_memory: Optional ReMeTaskLongTermMemory instance
|
||||
"""
|
||||
self.config = config or {}
|
||||
self.agent_id = name
|
||||
object.__setattr__(self, "config", config or {})
|
||||
object.__setattr__(self, "agent_id", name)
|
||||
|
||||
if toolkit is None:
|
||||
toolkit = Toolkit()
|
||||
self.toolkit = toolkit
|
||||
object.__setattr__(self, "toolkit", toolkit)
|
||||
|
||||
sys_prompt = self._load_system_prompt()
|
||||
|
||||
@@ -99,4 +99,12 @@ class RiskAgent(ReActAgent):
|
||||
self.config.get("config_name", "default"),
|
||||
active_skill_dirs=active_skill_dirs,
|
||||
)
|
||||
self.sys_prompt = self._load_system_prompt()
|
||||
self._apply_runtime_sys_prompt(self._load_system_prompt())
|
||||
|
||||
def _apply_runtime_sys_prompt(self, sys_prompt: str) -> None:
|
||||
"""Update the prompt used by future turns and the cached system msg."""
|
||||
self._sys_prompt = sys_prompt
|
||||
for msg, _marks in self.memory.content:
|
||||
if getattr(msg, "role", None) == "system":
|
||||
msg.content = sys_prompt
|
||||
break
|
||||
|
||||
@@ -62,6 +62,59 @@ class SkillsManager:
|
||||
|
||||
raise FileNotFoundError(f"Unknown skill: {skill_name}")
|
||||
|
||||
def _persist_runtime_edits(
|
||||
self,
|
||||
config_name: str,
|
||||
skill_name: str,
|
||||
active_dir: Path,
|
||||
) -> None:
|
||||
"""
|
||||
Persist run-time edits from active skills into customized skills.
|
||||
|
||||
This keeps active skill experiments from being lost on the next reload
|
||||
while still allowing the active directory to be re-synced cleanly.
|
||||
"""
|
||||
if not active_dir.exists():
|
||||
return
|
||||
|
||||
source_dir = self._resolve_source_dir(skill_name)
|
||||
if active_dir.resolve() == source_dir.resolve():
|
||||
return
|
||||
|
||||
if not self._directories_match(active_dir, source_dir):
|
||||
customized_dir = self.customized_root / skill_name
|
||||
customized_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||
if customized_dir.exists():
|
||||
shutil.rmtree(customized_dir)
|
||||
shutil.copytree(active_dir, customized_dir)
|
||||
|
||||
@staticmethod
|
||||
def _directories_match(left: Path, right: Path) -> bool:
|
||||
"""Compare two directory trees by file contents."""
|
||||
if not left.exists() or not right.exists():
|
||||
return False
|
||||
|
||||
left_items = sorted(
|
||||
path.relative_to(left)
|
||||
for path in left.rglob("*")
|
||||
)
|
||||
right_items = sorted(
|
||||
path.relative_to(right)
|
||||
for path in right.rglob("*")
|
||||
)
|
||||
if left_items != right_items:
|
||||
return False
|
||||
|
||||
for relative_path in left_items:
|
||||
left_path = left / relative_path
|
||||
right_path = right / relative_path
|
||||
if left_path.is_dir() != right_path.is_dir():
|
||||
return False
|
||||
if left_path.is_file():
|
||||
if left_path.read_bytes() != right_path.read_bytes():
|
||||
return False
|
||||
return True
|
||||
|
||||
def resolve_agent_skill_names(
|
||||
self,
|
||||
config_name: str,
|
||||
@@ -103,12 +156,22 @@ class SkillsManager:
|
||||
|
||||
for existing in active_root.iterdir():
|
||||
if existing.is_dir() and existing.name not in wanted:
|
||||
self._persist_runtime_edits(
|
||||
config_name=config_name,
|
||||
skill_name=existing.name,
|
||||
active_dir=existing,
|
||||
)
|
||||
shutil.rmtree(existing)
|
||||
|
||||
for skill_name in skill_names:
|
||||
source_dir = self._resolve_source_dir(skill_name)
|
||||
target_dir = active_root / skill_name
|
||||
if target_dir.exists():
|
||||
self._persist_runtime_edits(
|
||||
config_name=config_name,
|
||||
skill_name=skill_name,
|
||||
active_dir=target_dir,
|
||||
)
|
||||
shutil.rmtree(target_dir)
|
||||
shutil.copytree(source_dir, target_dir)
|
||||
synced_paths.append(target_dir)
|
||||
|
||||
Reference in New Issue
Block a user