diff --git a/TODO.md b/TODO.md index f2442871..fb245dc8 100644 --- a/TODO.md +++ b/TODO.md @@ -27,4 +27,6 @@ # 锦鲤 1. LOGO更加简洁 agent <-> experience 2. default等名字的说明 -3. 查看所有的op代码 \ No newline at end of file +3. 查看所有的op代码 +4. ba增加client +5. 在appworld上跑 \ No newline at end of file diff --git a/experiencemaker/embedding_model/base_embedding_model.py b/experiencemaker/embedding_model/base_embedding_model.py index 53c9db22..3cdf13e2 100644 --- a/experiencemaker/embedding_model/base_embedding_model.py +++ b/experiencemaker/embedding_model/base_embedding_model.py @@ -12,6 +12,7 @@ class BaseEmbeddingModel(BaseModel, ABC): dimensions: int = Field(default=..., description="dimensions") max_retries: int = Field(default=3, description="max retries") raise_exception: bool = Field(default=True, description="raise exception") + max_batch_size: int = Field(default=10, description="text-embedding-v4 batch size should not be larger than 10") def _get_embeddings(self, input_text: str | List[str]): raise NotImplementedError @@ -34,9 +35,9 @@ class BaseEmbeddingModel(BaseModel, ABC): return nodes elif isinstance(nodes, list): - max_batch_size = 10 # text-embedding-v4 batch size should not be larger than 10 - embeddings = [emb for i in range(0, len(nodes), max_batch_size) for emb in - self.get_embeddings(input_text=[node.content for node in nodes[i:i + max_batch_size]])] + + embeddings = [emb for i in range(0, len(nodes), self.max_batch_size) for emb in + self.get_embeddings(input_text=[node.content for node in nodes[i:i + self.max_batch_size]])] if len(embeddings) != len(nodes): logger.warning(f"embeddings.size={len(embeddings)} <> nodes.size={len(nodes)}") else: diff --git a/experiencemaker/llm/base_llm.py b/experiencemaker/llm/base_llm.py index b45a8c98..8dfb0369 100644 --- a/experiencemaker/llm/base_llm.py +++ b/experiencemaker/llm/base_llm.py @@ -24,7 +24,7 @@ class BaseLLM(BaseModel, ABC): parallel_tool_calls: bool = Field(default=True) max_retries: int = Field(default=5, description="max retries") - raise_exception: bool = Field(default=True, description="raise exception") + raise_exception: bool = Field(default=False, description="raise exception") def stream_chat(self, messages: List[Message], tools: List[BaseTool] = None, **kwargs): raise NotImplementedError @@ -35,7 +35,8 @@ class BaseLLM(BaseModel, ABC): def _chat(self, messages: List[Message], tools: List[BaseTool] = None, **kwargs) -> Message: raise NotImplementedError - def chat(self, messages: List[Message], tools: List[BaseTool] = None, callback_fn: Callable = None, **kwargs): + def chat(self, messages: List[Message], tools: List[BaseTool] = None, callback_fn: Callable = None, + default_value=None, **kwargs): for i in range(self.max_retries): try: message: Message = self._chat(messages, tools, **kwargs) @@ -48,7 +49,10 @@ class BaseLLM(BaseModel, ABC): logger.exception(f"chat with model={self.model_name} encounter error with e={e.args}") time.sleep(1 + i) - if i == self.max_retries - 1 and self.raise_exception: - raise e + if i == self.max_retries - 1: + if self.raise_exception: + raise e + else: + return default_value return None diff --git a/experiencemaker/op/base_op.py b/experiencemaker/op/base_op.py index e31a8ac6..9b2c075e 100644 --- a/experiencemaker/op/base_op.py +++ b/experiencemaker/op/base_op.py @@ -76,7 +76,12 @@ class BaseOp(PromptMixin, ABC): def join_task(self, task_desc: str = None) -> list: result = [] for task in tqdm(self.task_list, desc=task_desc or (self.simple_name + ".join_task")): - result.append(task.result()) + t_result = task.result() + if t_result: + if isinstance(t_result, list): + result.extend(t_result) + else: + result.append(t_result) self.task_list.clear() return result diff --git a/experiencemaker/op/retriever/rewrite_experience_op.py b/experiencemaker/op/retriever/rewrite_experience_op.py index f7fabb4b..99c80e33 100644 --- a/experiencemaker/op/retriever/rewrite_experience_op.py +++ b/experiencemaker/op/retriever/rewrite_experience_op.py @@ -46,7 +46,7 @@ class RewriteExperienceOp(BaseOp): def _generate_context_message(self, query: str, messages: List[Message], nodes: List[VectorNode], - retrieval_query: str) -> Message: + retrieval_query: str) -> str: """Generate context message from retrieved experiences""" if not nodes: return "" diff --git a/experiencemaker/op/summarizer/extract_experience_op.py b/experiencemaker/op/summarizer/extract_experience_op.py deleted file mode 100644 index e69de29b..00000000 diff --git a/experiencemaker/op/summarizer/simple_summary_op.py b/experiencemaker/op/summarizer/simple_summary_op.py index 383e9a25..ebf91e6d 100644 --- a/experiencemaker/op/summarizer/simple_summary_op.py +++ b/experiencemaker/op/summarizer/simple_summary_op.py @@ -57,10 +57,7 @@ class SimpleSummaryOp(BaseOp): for trajectory in request.traj_list: self.submit_task(self.summary_trajectory, trajectory=trajectory) - experience_list: List[BaseExperience] = [] - for task_result in self.join_task(): - if task_result: - experience_list.extend(task_result) + experience_list: List[BaseExperience] = self.join_task() response: SummarizerResponse = self.context.response response.experience_list = experience_list @@ -68,4 +65,4 @@ class SimpleSummaryOp(BaseOp): logger.info(f"add experience when_to_use={e.when_to_use}\ncontent={e.content}") from experiencemaker.op.vector_store.update_vector_store_op import UpdateVectorStoreOp - self.context.set_context(UpdateVectorStoreOp.INSERT_NODES, [x.to_vector_node() for x in experience_list]) + self.context.set_context(UpdateVectorStoreOp.INSERT_EXPERIENCE_LIST, [x.to_vector_node() for x in experience_list]) diff --git a/experiencemaker/op/summarizer/step_summarizer_prompts.yaml b/experiencemaker/op/summarizer/step_summarizer_prompts.yaml deleted file mode 100644 index 8c7441b4..00000000 --- a/experiencemaker/op/summarizer/step_summarizer_prompts.yaml +++ /dev/null @@ -1,274 +0,0 @@ -success_step_experience_prompt: | - You are an expert AI analyst reviewing successful step sequences from an AI agent execution. - - Your task is to extract reusable, actionable step-level experiences that can guide future agent executions. - Focus on identifying specific patterns, techniques, and decision points that contributed to success. - - ANALYSIS FRAMEWORK: - ● STEP PATTERN ANALYSIS: Identify the specific sequence of actions that led to success - ● DECISION POINTS: Highlight critical decisions made during these steps - ● TECHNIQUE EFFECTIVENESS: Analyze why specific approaches worked well - ● REUSABILITY: Extract patterns that can be applied to similar scenarios - - EXTRACTION PRINCIPLES: - ● Focus on TRANSFERABLE TECHNIQUES and decision frameworks - ● Frame insights as actionable guidelines and best practices - - # Original Query - {query} - - # Step Sequence Analysis - {step_sequence} - - # Context Information - {context} - - # Outcome - This step sequence was part of a {outcome} trajectory. - - OUTPUT FORMAT: - Generate 1-3 step-level success insights as JSON objects: - ```json - [ - {{ - "when_to_use": "Specific conditions when this step pattern should be applied", - "experience": "Detailed description of the successful step pattern and why it works", - "tags": ["relevant", "keywords", "for", "categorization"], - "confidence": 0.8, - "step_type": "reasoning|action|observation|decision", - "tools_used": ["list", "of", "tools"] - }} - ] - ``` - -failure_step_experience_prompt: | - You are an expert AI analyst reviewing failed step sequences from an AI agent execution. - - Your task is to extract learning experiences from failures to prevent similar mistakes in future executions. - Focus on identifying error patterns, missed opportunities, and alternative approaches. - - ANALYSIS FRAMEWORK: - ● FAILURE POINT IDENTIFICATION: Pinpoint where and why the steps went wrong - ● ERROR PATTERN ANALYSIS: Identify recurring mistakes or problematic approaches - ● ALTERNATIVE APPROACHES: Suggest what could have been done differently - ● PREVENTION STRATEGIES: Extract actionable insights to avoid similar failures - - EXTRACTION PRINCIPLES: - ● Extract GENERAL PRINCIPLES as well as SPECIFIC INSTRUCTIONS - ● Focus on PATTERNS and RULES as well as particular instances - - # Original Query - {query} - - # Step Sequence Analysis - {step_sequence} - - # Context Information - {context} - - # Outcome - This step sequence was part of a {outcome} trajectory. - - OUTPUT FORMAT: - Generate 1-3 step-level failure prevention insights as JSON objects: - ```json - [ - {{ - "when_to_use": "Specific situations where this lesson should be remembered", - "experience": "Universal principle or rule extracted from the failure pattern ", - "tags": ["error_prevention", "failure_analysis", "relevant_keywords"], - "confidence": 0.7, - "step_type": "reasoning|action|observation|decision", - "tools_used": ["list", "of", "tools"] - }} - ] - ``` - -comparative_step_experience_prompt: | - You are an expert AI analyst comparing successful and failed step sequences to extract differential insights. - - Your task is to identify the key differences between success and failure patterns at the step level. - Focus on critical decision points, technique variations, and approach differences. - - COMPARATIVE ANALYSIS FRAMEWORK: - ● DECISION CONTRAST: Compare critical decisions made in success vs failure cases - ● TECHNIQUE VARIATIONS: Identify different approaches and their outcomes - ● TIMING DIFFERENCES: Analyze when certain actions were taken and their impact - ● SUCCESS FACTORS: Extract what specifically made the difference - - EXTRACTION PRINCIPLES: - ● Frame comparisons as PRINCIPLES as well as case-specific SOLUTIONS - ● Identify PATTERNS that differentiate effective vs ineffective approaches - ● Extract RULES that can guide future similar situations - ● Focus on UNDERLYING MECHANISMS rather than surface-level differences - - # Successful Step Sequence - {success_steps} - - # Failed Step Sequence - {failure_steps} - - # Similarity Score: {similarity_score} - - OUTPUT FORMAT: - Generate 1-2 comparative insights as JSON objects: - ```json - [ - {{ - "when_to_use": "Specific scenarios where this comparative insight applies", - "experience": "Detailed comparison highlighting why success approach works better", - "tags": ["comparative_analysis", "success_factors", "relevant_keywords"], - "confidence": 0.8, - "step_type": "reasoning|action|observation|decision" - }} - ] - ``` - -general_step_experience_prompt: | - You are an expert AI analyst reviewing step sequences to extract general patterns and insights. - - Your task is to identify valuable step-level patterns without explicit success/failure labels. - Focus on effective techniques, common patterns, and general best practices. - - ANALYSIS FRAMEWORK: - ● PATTERN RECOGNITION: Identify recurring effective patterns in the steps - ● TECHNIQUE ANALYSIS: Analyze the effectiveness of different approaches - ● BEST PRACTICES: Extract general principles that appear beneficial - ● APPLICABILITY: Determine when these patterns would be most useful - - GENERALIZATION PRINCIPLES: - ● Extract UNIVERSAL PATTERNS that transcend specific contexts - ● Identify TRANSFERABLE METHODOLOGIES and approaches - ● Focus on PRINCIPLE-LEVEL insights as well as tactical details - ● Formulate insights as REUSABLE FRAMEWORKS and guidelines - - GENERALIZATION PRINCIPLES: - ● Extract UNIVERSAL PATTERNS that transcend specific contexts - ● Identify TRANSFERABLE METHODOLOGIES and approaches - ● Focus on PRINCIPLE-LEVEL insights - ● Formulate insights as REUSABLE FRAMEWORKS and guidelines - - # Original Query - {query} - - # Step Sequence Analysis - {step_sequence} - - # Context Information - {context} - - OUTPUT FORMAT: - Generate 1-2 general step insights as JSON objects: - ```json - [ - {{ - "when_to_use": "General conditions where this pattern is applicable", - "experience": "Detailed description of the effective step pattern", - "tags": ["general_pattern", "best_practice", "relevant_keywords"], - "confidence": 0.6, - "step_type": "reasoning|action|observation|decision", - "tools_used": ["list", "of", "tools"] - }} - ] - ``` - -step_segmentation_prompt: | - You are an expert AI analyst tasked with segmenting a trajectory into meaningful step sequences. - - Your task is to identify natural breakpoints in the execution where one logical unit of work ends and another begins. - Consider factors like: task completion, context switches, tool changes, reasoning phases, and logical groupings. - - SEGMENTATION CRITERIA: - ● LOGICAL COMPLETION: Steps that complete a specific sub-task or reasoning phase - ● CONTEXT SWITCHES: Points where the agent shifts focus or approach - ● TOOL BOUNDARIES: Natural breaks around tool usage patterns - ● REASONING PHASES: Distinct phases of analysis, planning, or execution - - # Original Query - {query} - - # Full Trajectory (Total steps: {total_steps}) - {trajectory_content} - - OUTPUT FORMAT: - Provide segmentation points as a JSON array of step indices where splits should occur: - ```json - {{ - "segment_points": [3, 7, 12, 18], - "reasoning": "Brief explanation of segmentation logic" - }} - ``` - - Note: Segment points indicate the END of each segment. For example, [3, 7] means: - - Segment 1: steps 0-3 - - Segment 2: steps 4-7 - - Segment 3: steps 8-end - -experience_validation_prompt: | - You are an expert AI analyst tasked with validating the quality and usefulness of extracted step-level experiences. - - Your task is to assess whether the extracted experience is actionable, accurate, and valuable for future agent executions. - - VALIDATION CRITERIA: - ● ACTIONABILITY: Is the experience specific enough to guide future actions? - ● ACCURACY: Does the experience correctly reflect the patterns observed? - ● RELEVANCE: Is the experience applicable to similar future scenarios? - ● CLARITY: Is the experience clearly articulated and understandable? - ● UNIQUENESS: Does the experience provide novel insights or common knowledge? - - # Experience to Validate - Condition: {condition} - Experience Content: {experience_content} - - OUTPUT FORMAT: - Provide validation assessment: - ```json - {{ - "is_valid": true/false, - "score": 0.8, - "feedback": "Detailed explanation of validation decision", - "recommendations": "Suggestions for improvement if applicable" - }} - ``` - - Score should be between 0.0 (poor quality) and 1.0 (excellent quality). - Mark as invalid if score is below 0.3 or if there are fundamental issues with the experience. - -soft_comparative_step_experience_prompt: | - You are an expert AI analyst comparing higher-scoring and lower-scoring step sequences to extract performance insights. - - Your task is to identify the key differences between higher and lower performing approaches at the step level. - Focus on what made the higher-scoring approach more effective, even when both approaches may have had partial success. - - SOFT COMPARATIVE ANALYSIS FRAMEWORK: - ● PERFORMANCE FACTORS: Identify what specifically contributed to the higher score - ● APPROACH DIFFERENCES: Compare methodologies and execution strategies - ● EFFICIENCY ANALYSIS: Analyze why one approach was more efficient or effective - ● OPTIMIZATION INSIGHTS: Extract lessons for improving performance - - EXTRACTION PRINCIPLES: - ● Focus on INCREMENTAL IMPROVEMENTS and performance optimization - ● Extract QUALITY INDICATORS that differentiate better vs good approaches - ● Identify REFINEMENT STRATEGIES that lead to higher scores - ● Frame insights as PERFORMANCE ENHANCEMENT guidelines - - # Higher-Scoring Step Sequence (Score: {higher_score}) - {higher_steps} - - # Lower-Scoring Step Sequence (Score: {lower_score}) - {lower_steps} - - - OUTPUT FORMAT: - Generate 1-2 performance improvement insights as JSON objects: - ```json - [ - {{ - "when_to_use": "Specific scenarios where this performance insight applies", - "experience": "Detailed analysis of what made the higher-scoring approach more effective", - "tags": ["performance_optimization", "score_improvement", "relevant_keywords"], - "confidence": 0.7, - "step_type": "reasoning|action|observation|decision", - "tools_used": ["list", "of", "tools"] - }} - ] \ No newline at end of file diff --git a/experiencemaker/op/summarizer/success_extraction_op.py b/experiencemaker/op/summarizer/success_extraction_op.py index 9932fcc8..253f86c7 100644 --- a/experiencemaker/op/summarizer/success_extraction_op.py +++ b/experiencemaker/op/summarizer/success_extraction_op.py @@ -1,13 +1,15 @@ import json import re from typing import List + from loguru import logger +from experiencemaker.enumeration.role import Role from experiencemaker.op import OP_REGISTRY from experiencemaker.op.base_op import BaseOp from experiencemaker.schema.experience import TextExperience, ExperienceMeta from experiencemaker.schema.message import Message, Trajectory -from experiencemaker.enumeration.role import Role +from experiencemaker.schema.response import SummarizerResponse @OP_REGISTRY.register() @@ -26,31 +28,27 @@ class SuccessExtractionOp(BaseOp): # Use thread pool for parallel processing for trajectory in success_trajectories: - if hasattr(trajectory, 'segments') and trajectory.segments: + if "segments" in trajectory.metadata: # Process segmented step sequences - for segment in trajectory.segments: - self.submit_task(self._extract_success_experience_from_steps, - steps=segment, trajectory=trajectory) + for segment in trajectory.metadata["segments"]: + self.submit_task(self._extract_success_experience_from_steps, steps=segment, trajectory=trajectory) else: # Process entire trajectory - self.submit_task(self._extract_success_experience_from_steps, - steps=trajectory.messages, trajectory=trajectory) + self.submit_task(self._extract_success_experience_from_steps, + steps=trajectory.messages, trajectory=trajectory) # Collect all experiences - all_experiences = [] - for task_result in self.join_task(): - if task_result: - all_experiences.extend(task_result) + all_experiences = self.join_task() logger.info(f"Extracted {len(all_experiences)} success experiences") # Add experiences to context - existing_experiences = self.context.get_context("extracted_experiences", []) - existing_experiences.extend(all_experiences) - self.context.set_context("extracted_experiences", existing_experiences) + response: SummarizerResponse = self.context.response + response.experience_list.extend(all_experiences) def _extract_success_experience_from_steps(self, steps: List[Message], trajectory: Trajectory) -> List[TextExperience]: """Extract experience from successful step sequences""" + # TODO remove try catch try: step_content = self._format_step_sequence(steps) context = self._get_trajectory_context(trajectory, steps) @@ -92,7 +90,7 @@ class SuccessExtractionOp(BaseOp): def _format_step_sequence(self, steps: List[Message]) -> str: """Format step sequence to string""" step_content_collector = [] - + # TODO merge_messages_content for step in steps: step_index = len(step_content_collector) diff --git a/experiencemaker/op/summarizer/trajectory_preprocess_op.py b/experiencemaker/op/summarizer/trajectory_preprocess_op.py index ba8f47d8..4d8f8bc9 100644 --- a/experiencemaker/op/summarizer/trajectory_preprocess_op.py +++ b/experiencemaker/op/summarizer/trajectory_preprocess_op.py @@ -1,5 +1,7 @@ from typing import List, Dict + from loguru import logger + from experiencemaker.op import OP_REGISTRY from experiencemaker.op.base_op import BaseOp from experiencemaker.schema.message import Trajectory @@ -13,25 +15,9 @@ class TrajectoryPreprocessOp(BaseOp): def execute(self): """Preprocess trajectories: validate and classify""" request: SummarizerRequest = self.context.request - if request.traj_list: - self.context.set_context("trajectories", request.traj_list) - elif request.trajectories: - self.context.set_context("trajectories", request.trajectories) - else: - logger.error("No trajectories is recognized. Please send requests containing traj_list.") - - trajectories: List[Trajectory] = self.context.get_context("trajectories", []) - - if not trajectories: - logger.warning("No trajectories found in context") - return - - # Validate trajectories - valid_trajectories = self._validate_trajectories(trajectories) - logger.info(f"Validated {len(valid_trajectories)} out of {len(trajectories)} trajectories") # Classify trajectories - classified = self._classify_trajectories(valid_trajectories) + classified = self._classify_trajectories(request.traj_list) logger.info(f"Classified trajectories - Success: {len(classified['success'])}, " f"Failure: {len(classified['failure'])}, All: {len(classified['all'])}") @@ -40,28 +26,6 @@ class TrajectoryPreprocessOp(BaseOp): self.context.set_context("failure_trajectories", classified['failure']) self.context.set_context("all_trajectories", classified['all']) - def _validate_trajectories(self, trajectories: List[Trajectory]) -> List[Trajectory]: - """Validate trajectory validity""" - valid_trajectories = [] - - for traj in trajectories: - if self._is_valid_trajectory(traj): - valid_trajectories.append(traj) - else: - logger.debug("Invalid trajectory filtered out") - - return valid_trajectories - - def _is_valid_trajectory(self, traj: Trajectory) -> bool: - """Check if trajectory is valid""" - if traj is None: - return False - if not hasattr(traj, 'score') or traj.score is None: - return False - if not hasattr(traj, 'messages') or not traj.messages or len(traj.messages) == 0: - return False - return True - def _classify_trajectories(self, trajectories: List[Trajectory]) -> Dict[str, List[Trajectory]]: """Classify trajectories based on score threshold""" success_trajectories = [] diff --git a/experiencemaker/op/summarizer/trajectory_segmentation_op.py b/experiencemaker/op/summarizer/trajectory_segmentation_op.py index 5a72a499..9208bc60 100644 --- a/experiencemaker/op/summarizer/trajectory_segmentation_op.py +++ b/experiencemaker/op/summarizer/trajectory_segmentation_op.py @@ -1,11 +1,12 @@ -import re import json -from typing import List, Dict, Any +import re +from typing import List + from loguru import logger + from experiencemaker.op import OP_REGISTRY from experiencemaker.op.base_op import BaseOp from experiencemaker.schema.message import Message, Trajectory -from experiencemaker.enumeration.role import Role @OP_REGISTRY.register() @@ -30,15 +31,12 @@ class TrajectorySegmentationOp(BaseOp): # Add segmentation info to trajectories segmented_count = 0 for trajectory in target_trajectories: - segments = self._segment_trajectory(trajectory) - trajectory.segments = segments + segments = self._llm_segment_trajectory(trajectory) + trajectory.metadata["segments"] = segments segmented_count += 1 logger.info(f"Segmented {segmented_count} trajectories") - # Update context with segmented trajectories - self.context.set_context("segmented_trajectories", target_trajectories) - def _get_target_trajectories(self, all_trajectories: List[Trajectory], success_trajectories: List[Trajectory], failure_trajectories: List[Trajectory]) -> List[Trajectory]: @@ -52,55 +50,38 @@ class TrajectorySegmentationOp(BaseOp): else: return all_trajectories - def _segment_trajectory(self, trajectory: Trajectory) -> List[List[Message]]: - """Segment trajectory into step sequences using LLM""" - try: - return self._llm_segment_trajectory(trajectory) - except Exception as e: - logger.error(f"Error segmenting trajectory: {e}") - return [trajectory.messages] - def _llm_segment_trajectory(self, trajectory: Trajectory) -> List[List[Message]]: """Use LLM for trajectory segmentation""" - try: - trajectory_content = self._format_trajectory_content(trajectory) + trajectory_content = self._format_trajectory_content(trajectory) - prompt = self.prompt_format( - prompt_name="step_segmentation_prompt", - query=trajectory.metadata.get('query', ''), - trajectory_content=trajectory_content, - total_steps=len(trajectory.messages) - ) + prompt = self.prompt_format( + prompt_name="step_segmentation_prompt", + query=trajectory.metadata.get('query', ''), + trajectory_content=trajectory_content, + total_steps=len(trajectory.messages) + ) - def parse_segmentation(message: Message) -> List[List[Message]]: - try: - content = message.content - segment_points = self._parse_segmentation_response(content) + def parse_segmentation(message: Message) -> List[List[Message]]: + content = message.content + segment_points = self._parse_segmentation_response(content) - # Segment trajectory based on segmentation points - segments = [] - start_idx = 0 + # Segment trajectory based on segmentation points + segments = [] + start_idx = 0 - for end_idx in segment_points: - if start_idx < end_idx <= len(trajectory.messages): - segments.append(trajectory.messages[start_idx:end_idx]) - start_idx = end_idx + for end_idx in segment_points: + if start_idx < end_idx <= len(trajectory.messages): + segments.append(trajectory.messages[start_idx:end_idx]) + start_idx = end_idx - # Add remaining steps - if start_idx < len(trajectory.messages): - segments.append(trajectory.messages[start_idx:]) + # Add remaining steps + if start_idx < len(trajectory.messages): + segments.append(trajectory.messages[start_idx:]) - return segments if segments else [trajectory.messages] + return segments if segments else [trajectory.messages] - except Exception as e: - logger.error(f"Error parsing segmentation: {e}") - return [trajectory.messages] - - return self.llm.chat(messages=[Message(content=prompt)], callback_fn=parse_segmentation) - - except Exception as e: - logger.error(f"LLM segmentation failed: {e}") - return [trajectory.messages] + return self.llm.chat(messages=[Message(content=prompt)], callback_fn=parse_segmentation, + default_value=[trajectory.messages]) def _format_trajectory_content(self, trajectory: Trajectory) -> str: """Format trajectory content for LLM processing""" diff --git a/experiencemaker/op/summarizer/experience_storage_op.py b/experiencemaker/op/vector_store/experience_storage_op.py similarity index 100% rename from experiencemaker/op/summarizer/experience_storage_op.py rename to experiencemaker/op/vector_store/experience_storage_op.py diff --git a/experiencemaker/op/vector_store/update_vector_store_op.py b/experiencemaker/op/vector_store/update_vector_store_op.py index cc2d6a18..2ec98871 100644 --- a/experiencemaker/op/vector_store/update_vector_store_op.py +++ b/experiencemaker/op/vector_store/update_vector_store_op.py @@ -5,24 +5,26 @@ from loguru import logger from experiencemaker.op import OP_REGISTRY from experiencemaker.op.base_op import BaseOp +from experiencemaker.schema.experience import BaseExperience from experiencemaker.schema.request import BaseRequest from experiencemaker.schema.vector_node import VectorNode @OP_REGISTRY.register() class UpdateVectorStoreOp(BaseOp): - INSERT_NODES = "insert_nodes" - DELETE_NODE_IDS = "delete_node_ids" + INSERT_EXPERIENCE_LIST = "insert_experience_list" + DELETE_EXPERIENCE_IDS = "delete_experience_ids" def execute(self): request: BaseRequest = self.context.request - node_ids: List[str] | None = self.context.get_context(self.DELETE_NODE_IDS) - if node_ids: - self.vector_store.delete(node_ids=node_ids, workspace_id=request.workspace_id) - logger.info(f"delete node_ids={json.dumps(node_ids, indent=2)}") + experience_ids: List[str] | None = self.context.get_context(self.DELETE_EXPERIENCE_IDS) + if experience_ids: + self.vector_store.delete(node_ids=experience_ids, workspace_id=request.workspace_id) + logger.info(f"delete experience_ids={json.dumps(experience_ids, indent=2)}") - insert_nodes: List[VectorNode] | None = self.context.get_context(self.INSERT_NODES) - if insert_nodes: + insert_experience_list: List[BaseExperience] | None = self.context.get_context(self.INSERT_EXPERIENCE_LIST) + if insert_experience_list: + insert_nodes: List[VectorNode] = [x.to_vector_node() for x in insert_experience_list] self.vector_store.insert(nodes=insert_nodes, workspace_id=request.workspace_id) logger.info(f"insert insert_node.size={len(insert_nodes)}")