diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d2c541..a1ccf59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,84 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- -## [1.0.0] - YYYY-MM-DD +## [0.1.3] - 2026-02-04 + +### Added + +#### Observation & Traceability +- **OpenTelemetry Observability Support**: Full OpenTelemetry native API integration + - `BaseAgentObservationLifecycleListener`: Base lifecycle listener for Agent observability + - `CodeactObservationDocumentation`: Standardized observation metrics definition (Hook, Interceptor, React, Execution, CodeGen, ToolCall) + - `EvaluationObservationLifecycleListener`: Observability listener for Evaluation Graph +- **Tool Call Tracing**: New tool call recording and tracing capabilities + - `ToolCallRecord`: Tool call record model with call order and tool name + - `ExecutionRecord.callTrace`: Tool call trace list during code execution + - `ToolRegistryBridge`: Tool registry bridge +- **Observation Helper Classes**: + - `HookObservationHelper`: Hook execution observation helper + - `InterceptorObservationHelper`: Interceptor execution observation helper + - `OpenTelemetryObservationHelper`: General OpenTelemetry observation helper +- **Observation Contexts**: + - `HookObservationContext`: Hook observation context + - `InterceptorObservationContext`: Interceptor observation context + - `ReactPhaseObservationContext`: React phase observation context + - `CodeGenerationObservationContext`: Code generation observation context + - `CodeactExecutionObservationContext`: Code execution observation context + - `CodeactToolCallObservationContext`: Tool call observation context + +#### Prompt Contributor Module +- **PromptContributor Mechanism Refactoring**: Replaced PromptBuilder/PromptManager with a more flexible Prompt contribution system + - `PromptContributor`: Prompt contributor interface + - `PromptContributorManager`: Prompt contributor manager interface + - `DefaultPromptContributorManager`: Default implementation with priority sorting and dynamic registration + - `PromptContributorContext`: Context interface + - `OverAllStatePromptContributorContext`: OverAllState-based context implementation +- **Evaluation-based Prompt Contribution**: + - `EvaluationBasedPromptContributor`: Abstract base class for generating Prompts based on evaluation results + - `PromptContributorModelHook`: Abstract base class for integrating PromptContributor into ModelHook + - `ReactPromptContributorModelHook`: Prompt contribution Hook for React phase + - `CodeactPromptContributorModelHook`: Prompt contribution Hook for Codeact phase +- **Auto Configuration**: `PromptContributorAutoConfiguration` provides out-of-the-box configuration + +#### Other Enhancements +- `ParameterTree`: Enhanced parameter tree definition capabilities +- `CommonSenseInjectionTool`: Common sense injection tool +- `ToolContextHelper`: Tool context helper class +- `CodeactStateKeys`: Codeact state key constants + +### Changed +- **GraalCodeExecutor**: Enhanced code executor with tool call tracing support +- **PythonToolViewRenderer**: Enhanced Python tool view rendering capabilities +- **EvaluationService**: Support for parent Span for distributed tracing +- **EvaluationSuiteBuilder**: Enhanced evaluation suite building capabilities +- **ReplyCodeactToolFactory**: Optimized reply tool factory implementation +- **AfterAgentLearningHook**: Enhanced learning Hook implementation +- **AsyncLearningHandler**: Optimized async learning handler +- **CodeactAgent**: Refactored to support new observation and Prompt contribution mechanism + +### Removed +- `PromptBuilder`: Replaced by PromptContributor +- `PromptManager`: Replaced by PromptContributorManager +- `PromptInjectionInterceptor`: Replaced by PromptContributorModelHook +- `CodeactToolFilter`: Tool filter +- `WhitelistMode`: Whitelist mode enum + +--- + +## [0.1.2] - 2026-01-XX + +### Added +- Baidu Qianfan intelligent search API integration +- HookPhases annotation support +- McpServerAwareToolCallback interface for enhanced MCP dynamic tool creation + +### Fixed +- Fixed context binding path in evaluation criteria +- Fixed null pointer exception in UnifiedSearchCodeactTool + +--- + +## [0.1.0] - Initial Release ### Added @@ -89,9 +166,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version History Summary -| Version | Date | Description | -|---------|------|-------------| -| 1.0.0 | TBD | Initial open source release | +| Version | Date | Description | +|---------|------------|-----------------------------------------------------------------------------| +| 0.1.3 | 2026-02-04 | Enhanced tool call tracing and observability, refactored Prompt Contributor module, bug fixes | +| 0.1.2 | 2026-01-XX | Baidu Qianfan search integration, HookPhases annotation, bug fixes | +| 0.1.0 | TBD | Initial open source release | --- diff --git a/README.md b/README.md index 4457146..ca2117d 100644 --- a/README.md +++ b/README.md @@ -205,455 +205,46 @@ public class MyKnowledgeSearchProvider implements SearchProvider { > ๐Ÿ“– For more details, refer to: [Knowledge Search Module Documentation](assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/search/README.md) -## ๐Ÿงฉ Core Module Introduction +## ๐Ÿงฉ Core Modules -### Evaluation Module +For detailed documentation on each module, please visit our [Documentation Site](https://java2ai.com/agents/assistantagent/quick-start). -**Role**: Multi-dimensional intent recognition framework that performs multi-layer trait recognition through Evaluation Graph. +### Core Modules -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Evaluation Graph Example โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ User Input: "Query today's orders" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Layer 1 (parallel execution) โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ Is Vague? โ”‚ โ”‚ Rewrite โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ clear/vagueโ”‚ โ”‚ (enhance) โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Layer 2 (based on rewritten content, parallel) โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚Experienceโ”‚ โ”‚ Tool โ”‚ โ”‚ Knowledge โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚available โ”‚ โ”‚ Available โ”‚ โ”‚ Available โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ yes/no โ”‚ โ”‚ yes/no โ”‚ โ”‚ yes/no โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Integrate evaluationโ”‚ โ”‚ -โ”‚ โ”‚ results from โ”‚ โ”‚ -โ”‚ โ”‚ different dimensionsโ”‚ โ”‚ -โ”‚ โ”‚ โ†’ Pass to modules โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` +| Module | Description | Documentation | +|--------|-------------|---------------| +| **Evaluation** | Multi-dimensional intent recognition through Evaluation Graph with LLM and rule-based engines | [Quick Start](https://java2ai.com/agents/assistantagent/features/evaluation/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/evaluation/advanced) | +| **Prompt Builder** | Dynamic prompt assembly based on evaluation results and runtime context | [Quick Start](https://java2ai.com/agents/assistantagent/features/prompt-builder/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/prompt-builder/advanced) | -**Core Capabilities**: -- **Dual Evaluation Engines**: - - **LLM Evaluation**: Complex semantic judgment through large models. Users can fully customize evaluation prompts (`customPrompt`), or use default prompt assembly (supports `description`, `workingMechanism`, `fewShots` configurations) - - **Rule-based Evaluation**: Implement rule logic through Java functions. Users can customize `Function` to execute any rule judgment, suitable for threshold detection, format validation, exact matching, etc. -- **Custom Dependencies**: Evaluation items can declare dependencies via `dependsOn`. The system automatically builds an evaluation graph for topological execution - items without dependencies run in parallel, items with dependencies run sequentially. Subsequent evaluation items can access results from preceding items. -- **Evaluation Results**: Support `BOOLEAN`, `ENUM`, `SCORE`, `JSON`, `TEXT` and other types, passed to Prompt Builder to drive dynamic assembly +### Tool Extensions ---- +| Module | Description | Documentation | +|--------|-------------|---------------| +| **MCP Tools** | Integration with Model Context Protocol servers for tool ecosystem reuse | [Quick Start](https://java2ai.com/agents/assistantagent/features/mcp/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/mcp/advanced) | +| **Dynamic HTTP Tools** | REST API integration through OpenAPI specification | [Quick Start](https://java2ai.com/agents/assistantagent/features/dynamic-http/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/dynamic-http/advanced) | +| **Custom CodeAct Tools** | Build custom tools using the CodeactTool interface | [Quick Start](https://java2ai.com/agents/assistantagent/features/custom-codeact-tool/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/custom-codeact-tool/advanced) | -### Prompt Builder Module +### Intelligence Capabilities -**Role**: Dynamically assemble prompts sent to the model based on evaluation results and runtime context. Example: +| Module | Description | Documentation | +|--------|-------------|---------------| +| **Experience** | Accumulate and reuse historical successful execution experiences with FastIntent support | [Quick Start](https://java2ai.com/agents/assistantagent/features/experience/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/experience/advanced) | +| **Learning** | Automatically extract valuable experiences from Agent execution history | [Quick Start](https://java2ai.com/agents/assistantagent/features/learning/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/learning/advanced) | +| **Search** | Multi-source unified search engine for knowledge-based Q&A | [Quick Start](https://java2ai.com/agents/assistantagent/features/search/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/search/advanced) | -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Prompt Builder - Conditional Dynamic Generation โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ Evaluation Results Input: โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Vague: yes โ”‚ Experience: yes โ”‚ Tools: yes โ”‚ Knowledge: noโ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Custom PromptBuilder Condition Matching โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ vague=yes โ”€โ”€โ”€โ”€โ–ถ inject [Clarification Prompt] โ”‚ โ”‚ -โ”‚ โ”‚ vague=no โ”€โ”€โ”€โ”€โ–ถ inject [Direct Execution Prompt] โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ experience=yes โ”€โ”€โ–ถ inject [Historical Experience Reference] โ”‚ โ”‚ -โ”‚ โ”‚ tools=yes โ”€โ”€โ–ถ inject [Tool Usage Instructions] โ”‚ โ”‚ -โ”‚ โ”‚ knowledge=yes โ”€โ”€โ–ถ inject [Relevant Knowledge Snippets] โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ Combo 1: vague + no tools + no knowledge โ”€โ”€โ–ถ [Ask User Prompt]โ”‚ โ”‚ -โ”‚ โ”‚ Combo 2: clear + tools + experience โ”€โ”€โ–ถ [Fast Execute Prompt] โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Final Dynamic Prompt: โ”‚ โ”‚ -โ”‚ โ”‚ [System Prompt] + [Clarification Guide] + [Experience] + โ”‚ โ”‚ -โ”‚ โ”‚ [Tool Instructions] + [User Query] โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ LLM โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` +### Interaction Capabilities -**Core Capabilities**: -- Multiple PromptBuilders execute in priority order -- Each Builder decides whether to contribute and what content based on evaluation results -- Support custom Builders for business-specific prompt logic -- Non-intrusive, intercepts at model invocation level - -**Comparison with Traditional Approaches**: - -| Comparison | Traditional Approach | Evaluation + PromptBuilder | -|------------|---------------------|---------------------------| -| **Prompt Length** | Need to enumerate handling instructions for various situations ("when encountering situation A..., when encountering situation B..."), prompts become bloated | Through pre-evaluation to identify scenarios, only inject context needed for current scenario, prompts are shorter and more precise | -| **Agent Behavior Controllability** | Relies on model's "understanding" of lengthy instructions, prone to misjudgment | Behavior driven by evaluation results, reducing model misjudgment, more controllable | -| **Extension Flexibility** | Adding new scenarios requires modifying prompts, difficult to maintain | Modify relevant evaluation items and PromptBuilder based on business needs | -| **Code Architecture** | Evaluation logic coupled with prompts | Evaluation logic decoupled from prompt templates, separation of concerns, independent maintenance and iteration | - ---- - -### Learning Module - -**Role**: Automatically extract and save valuable experiences from Agent execution history. - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Learning Module Workflow โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent Execution Process โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ Input โ”€โ”€โ–ถ Reasoning โ”€โ”€โ–ถ Code Gen โ”€โ”€โ–ถ Execute โ”€โ”€โ–ถ Output โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Learning Context Capture โ”‚ โ”‚ -โ”‚ โ”‚ - User Input โ”‚ โ”‚ -โ”‚ โ”‚ - Reasoning Steps โ”‚ โ”‚ -โ”‚ โ”‚ - Generated Code โ”‚ โ”‚ -โ”‚ โ”‚ - Execution Result โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Learning Extractors Analysis โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ Experience โ”‚ โ”‚ Pattern โ”‚ โ”‚ Error โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ Extractor โ”‚ โ”‚ Extractor โ”‚ โ”‚ Extractor โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚Success Modeโ”‚ โ”‚Common Mode โ”‚ โ”‚Failure Modeโ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Persist & โ”‚ โ”€โ”€โ–ถ Available for future tasks โ”‚ -โ”‚ โ”‚ Store โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Core Capabilities**: -- **After-Agent Learning**: Extract experiences after each Agent execution -- **After-Model Learning**: Extract experiences after each model call -- **Tool Interceptor**: Extract experiences from tool invocations -- **Offline Learning**: Batch analyze historical data to extract patterns -- **Learning Process**: Capture execution context โ†’ Extractor analysis and recognition โ†’ Generate experience records โ†’ Persist for subsequent reuse - ---- - -### Experience Module - -**Role**: Accumulate and reuse historical successful execution experiences. - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Experience Module Workflow โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ [Scenario 1: Experience Accumulation] โ”‚ -โ”‚ โ”‚ -โ”‚ User: "Query order status" โ”€โ”€โ–ถ Agent Success โ”€โ”€โ–ถ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Save: โ”‚ โ”‚ -โ”‚ โ”‚ - ReAct Exp โ”‚ โ”‚ -โ”‚ โ”‚ - Code Exp โ”‚ โ”‚ -โ”‚ โ”‚ - Common Expโ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Experience DB โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ [Scenario 2: Experience Reuse] โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ User: "Query my order status" โ—€โ”€โ”€ Match Similar โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent references history, faster decision + โ”‚ โ”‚ -โ”‚ โ”‚ generates correct code โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ [Scenario 3: FastIntent Quick Response] โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Experience DB โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ Experience A โ”‚ โ”‚ Experience B โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ (Normal) โ”‚ โ”‚ (โœ“ FastIntent configured) โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ No FastIntent configโ”‚ โ”‚ Condition: prefix "View โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ†’ Inject to prompt โ”‚ โ”‚ *sales" โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ for LLM reference โ”‚ โ”‚ Action: Call sales API โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ Condition matched โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ User: "View today's sales" โ”€โ”€โ–ถ Match Exp B โ”€โ”€โ–ถ Skip LLM, execute โ”‚ -โ”‚ FastIntent directly โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Core Capabilities**: -- **Multiple Experience Types**: Code generation experience, ReAct decision experience, common sense experience, providing historical reference for similar tasks -- **Flexible Reuse**: Experiences can be injected into prompts or used for FastIntent matching -- **Lifecycle Management**: Support experience creation, update, and deletion -- **FastIntent Quick Response**: - - Experience must explicitly configure `fastIntentConfig` to enable - - When matching configured conditions, skip full LLM reasoning and directly execute pre-recorded tool calls or code -- Support multi-condition matching: message prefix, regex, metadata, state, etc. - ---- - -### Trigger Module - -**Role**: Create and manage scheduled tasks or event-triggered Agent executions. - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Trigger Module Capabilities โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ [Scheduled Trigger] โ”‚ -โ”‚ โ”‚ -โ”‚ User: "Send me daily sales report at 9am" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent creates โ”‚ โ”‚ Scheduler โ”‚ โ”‚ Auto Execute โ”‚ โ”‚ -โ”‚ โ”‚ Cron trigger โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ 0 9 * * * โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ Generate reportโ”‚ โ”‚ -โ”‚ โ”‚ (self-schedule)โ”‚ โ”‚ โ”‚ โ”‚ Send notify โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ [Delayed Trigger] โ”‚ -โ”‚ โ”‚ -โ”‚ User: "Remind me about the meeting in 30 minutes" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent creates โ”‚ โ”‚ After 30min โ”‚ โ”‚ Send reminder โ”‚ โ”‚ -โ”‚ โ”‚ one-time triggerโ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ fire โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ "Time to meet" โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ [Callback Trigger] โ”‚ -โ”‚ โ”‚ -โ”‚ User: "Help me with xx when xx condition is met" โ”‚ -โ”‚ โ”‚ -โ”‚ External System: Send event to Webhook โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Receive โ”‚ โ”‚ Trigger Agent โ”‚ โ”‚ Process event โ”‚ โ”‚ -โ”‚ โ”‚ Webhook event โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ execute task โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ Return responseโ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Core Capabilities**: -- `TIME_CRON` Trigger: Support Cron expression for scheduled task triggers -- `TIME_ONCE` Trigger: Support one-time delayed trigger -- `CALLBACK` Trigger: Support callback event trigger -- Agent can autonomously create triggers through tools, achieving "self-scheduling" - ---- +| Module | Description | Documentation | +|--------|-------------|---------------| +| **Reply Channel** | Multi-channel message reply with routing support | [Quick Start](https://java2ai.com/agents/assistantagent/features/reply/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/reply/advanced) | +| **Trigger** | Scheduled tasks, delayed execution, and event callback triggers | [Quick Start](https://java2ai.com/agents/assistantagent/features/trigger/quickstart) ๏ฝœ [Advanced](https://java2ai.com/agents/assistantagent/features/trigger/advanced) | -### Reply Channel Module - -**Role**: Provide flexible message reply capability, supporting multiple output channels. - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Reply Channel Module Capabilities โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ Agent needs to reply to user โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Channel Router โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ DEFAULT โ”‚ โ”‚ IDE_CARD โ”‚ โ”‚ IM_NOTIFY โ”‚ โ”‚ WEBHOOK โ”‚ โ”‚ -โ”‚ โ”‚ Text Reply โ”‚ โ”‚ Card Displayโ”‚ โ”‚ Push Notify โ”‚ โ”‚ JSON Push โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Console โ”‚ โ”‚ IDE โ”‚ โ”‚ IM โ”‚ โ”‚ External โ”‚ โ”‚ -โ”‚ โ”‚ Terminal โ”‚ โ”‚ Rich Card โ”‚ โ”‚(Extendable)โ”‚ โ”‚ System โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ [Usage Example] โ”‚ -โ”‚ โ”‚ -โ”‚ User: "Send results after analysis" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ Agent: send_message(text="Analysis results...") โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ User receives: "๐Ÿ“Š Analysis Results: ..." โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Core Capabilities**: -- **Multi-channel Routing**: Agent can choose different channels to reply based on scenario -- **Configuration-driven**: Dynamically generate reply tools, no coding required -- **Sync/Async Support**: Support both synchronous and asynchronous reply modes -- **Unified Interface**: Shield underlying implementation differences -- **Built-in Demo Channel**: `IDE_TEXT` (for demonstration) -- **Extendable Channels** (by implementing `ReplyChannelDefinition` SPI): e.g. `IDE_CARD`, `IM_NOTIFICATION` (DingTalk/Feishu/WeCom), `WEBHOOK_JSON`, etc. - requires custom implementation - ---- - -### Dynamic Tools Module - -**Role**: Provide highly extensible tool system, enabling Agent to call various external tools to complete tasks. - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Tool Extension Architecture โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ Agent needs to execute operation โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ CodeactTool System โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ MCP โ”‚ โ”‚ HTTP โ”‚ โ”‚ Search โ”‚ โ”‚ Trigger โ”‚ โ”‚Customโ”‚ โ”‚ -โ”‚ โ”‚ Tools โ”‚ โ”‚ API โ”‚ โ”‚ Tools โ”‚ โ”‚ Tools โ”‚ โ”‚Tools โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ Tools โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Any MCP โ”‚ โ”‚ REST API โ”‚ โ”‚ Knowledge โ”‚ โ”‚ Scheduledโ”‚ โ”‚ ... โ”‚ โ”‚ -โ”‚ โ”‚ Server โ”‚ โ”‚ OpenAPI โ”‚ โ”‚ Search โ”‚ โ”‚ Tasks โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ Project โ”‚ โ”‚ Callbacksโ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ Search โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Core Capabilities**: -- **MCP Tool Support**: One-click integration with any MCP Server, reuse MCP tool ecosystem -- **HTTP API Support**: Integrate REST APIs through OpenAPI specification, call existing enterprise interfaces -- **Built-in Tool Types**: Search, Reply, Trigger, Learning, etc. -- **Custom Tool SPI**: Implement `CodeactTool` interface to easily extend new tools - ---- - -### Knowledge Search Module - -**Role**: Multi-source unified search engine, providing knowledge support for Agent Q&A and decision-making. - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Multi-Source Search Architecture โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ User Question: "How to configure database connection pool?" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Unified Search Interface โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Knowledge โ”‚ โ”‚ Project โ”‚ โ”‚ Web โ”‚ โ”‚ Custom โ”‚ โ”‚ -โ”‚ โ”‚ Provider โ”‚ โ”‚ Provider โ”‚ โ”‚ Provider โ”‚ โ”‚Providerโ”‚ โ”‚ -โ”‚ โ”‚ (Primary) โ”‚ โ”‚ (Optional) โ”‚ โ”‚ (Optional) โ”‚ โ”‚ (SPI) โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ FAQ / Docs โ”‚ โ”‚ Source Code โ”‚ โ”‚ Web Articles โ”‚ โ”‚ ... โ”‚ โ”‚ -โ”‚ โ”‚ Q&A History โ”‚ โ”‚ Config Files โ”‚ โ”‚ Tech Forums โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ Team Notes โ”‚ โ”‚ Logs โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Aggregate & Rank โ”‚ โ”‚ -โ”‚ โ”‚ โ†’ Inject into Prompt โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Core Capabilities**: -- **Unified Search Interface**: `SearchProvider` SPI, supports pluggable data sources -- **Demo Providers**: Built-in Mock implementations for Knowledge, Project, Web (for demonstration and testing only) -- **Custom Extension**: Implement `SearchProvider` interface to connect any data source (databases, vector stores, APIs) -- **Result Aggregation**: Support configurable ranking strategies -- **Business Value**: Connect enterprise knowledge base to provide accurate answers, support answer traceability, reduce manual customer service pressure - -**Configuration Example**: - -```yaml -spring: - ai: - alibaba: - codeact: - extension: - search: - enabled: true - knowledge-search-enabled: true # Knowledge base (Mock implementation by default) - project-search-enabled: false # Project code (Mock implementation by default) - web-search-enabled: false # Web search (Mock implementation by default) - default-top-k: 5 - search-timeout-ms: 5000 -``` +### Additional Resources -> ๐Ÿ’ก The above search features provide Mock implementations by default for demonstration and testing. For production use, implement `SearchProvider` SPI to connect actual data sources. +| Resource | Link | +|----------|------| +| Quick Start Guide | [AssistantAgent Quick Start](https://java2ai.com/agents/assistantagent/quick-start) | +| Secondary Development Guide | [Development Guide](https://java2ai.com/agents/assistantagent/secondary-development) | --- diff --git a/README_zh.md b/README_zh.md index 5239016..3fb9b2b 100644 --- a/README_zh.md +++ b/README_zh.md @@ -200,443 +200,46 @@ public class MyKnowledgeSearchProvider implements SearchProvider { > ๐Ÿ“– ๆ›ดๅคš็ป†่Š‚่ฏทๅ‚่€ƒ๏ผš[็Ÿฅ่ฏ†ๆฃ€็ดขๆจกๅ—ๆ–‡ๆกฃ](assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/search/README.md) -## ๐Ÿงฉ ๆ ธๅฟƒๆจกๅ—ไป‹็ป +## ๐Ÿงฉ ๆ ธๅฟƒๆจกๅ— -### ่ฏ„ไผฐๆจกๅ—๏ผˆEvaluation๏ผ‰ +ๅ„ๆจกๅ—็š„่ฏฆ็ป†ๆ–‡ๆกฃ่ฏท่ฎฟ้—ฎ [ๆ–‡ๆกฃ็ซ™็‚น](https://java2ai.com/agents/assistantagent/quick-start)ใ€‚ -**ไฝœ็”จ**๏ผšๅคš็ปดๅบฆๆ„ๅ›พ่ฏ†ๅˆซๆก†ๆžถ๏ผŒ้€š่ฟ‡่ฏ„ไผฐๅ›พ๏ผˆGraph๏ผ‰ๅฏนไฟกๆฏ่ฟ›่กŒๅคšๅฑ‚ๆฌก็‰น่ดจ่ฏ†ๅˆซใ€‚ +### ๆ ธๅฟƒๆจกๅ— -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ่ฏ„ไผฐๅ›พ (Evaluation Graph) ็คบไพ‹ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ ็”จๆˆท่พ“ๅ…ฅ: "ๆŸฅ่ฏขไปŠๆ—ฅ่ฎขๅ•" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Layer 1 ๏ผˆๅนถ่กŒๆ‰ง่กŒ๏ผ‰ โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ๆ˜ฏๅฆๆจก็ณŠ? โ”‚ โ”‚ ่พ“ๅ…ฅๆ”นๅ†™ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ๆธ…ๆ™ฐ/ๆจก็ณŠ โ”‚ โ”‚๏ผˆๅขžๅผบ๏ผ‰ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Layer 2 (ๅŸบไบŽๆ”นๅ†™ๅ†…ๅฎน๏ผŒๅนถ่กŒๆ‰ง่กŒ) โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ๆฃ€็ดข็ป้ชŒ โ”‚ โ”‚ ๅŒน้…ๅทฅๅ…ท โ”‚ โ”‚ ๆœ็ดข็Ÿฅ่ฏ† โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ๆœ‰/ๆ—  โ”‚ โ”‚ ๆœ‰/ๆ—  โ”‚ โ”‚ ๆœ‰/ๆ—  โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆ•ดๅˆไธๅŒ็ปดๅบฆ่ฏ„ไผฐ็ป“ๆžœ โ”‚ โ”‚ -โ”‚ โ”‚ โ†’ ไผ ้€’็ป™ๅŽ็ปญๆจกๅ— โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- **ๅŒ่ฏ„ไผฐๅผ•ๆ“Ž**๏ผš - - **LLM ่ฏ„ไผฐ**๏ผš้€š่ฟ‡ๅคงๆจกๅž‹่ฟ›่กŒๅคๆ‚่ฏญไน‰ๅˆคๆ–ญ๏ผŒ็”จๆˆทๅฏๅฎŒๅ…จ่‡ชๅฎšไน‰่ฏ„ไผฐ Prompt๏ผˆ`customPrompt`๏ผ‰๏ผŒไนŸๅฏไฝฟ็”จ้ป˜่ฎค Prompt ็ป„่ฃ…๏ผˆๆ”ฏๆŒ `description`ใ€`workingMechanism`ใ€`fewShots` ็ญ‰้…็ฝฎ๏ผ‰ - - **Rule-based ่ฏ„ไผฐ**๏ผš้€š่ฟ‡ Java ๅ‡ฝๆ•ฐๅฎž็Žฐ่ง„ๅˆ™้€ป่พ‘๏ผŒ็”จๆˆท่‡ชๅฎšไน‰ `Function` ๆ‰ง่กŒไปปๆ„่ง„ๅˆ™ๅˆคๆ–ญ๏ผŒ้€‚ๅˆ้˜ˆๅ€ผๆฃ€ๆต‹ใ€ๆ ผๅผๆ ก้ชŒใ€็ฒพ็กฎๅŒน้…็ญ‰ๅœบๆ™ฏ -- **ไพ่ต–ๅ…ณ็ณป่‡ชๅฎšไน‰**๏ผš่ฏ„ไผฐ้กนๅฏ้€š่ฟ‡ `dependsOn` ๅฃฐๆ˜Žๅ‰็ฝฎไพ่ต–๏ผŒ็ณป็ปŸ่‡ชๅŠจๆž„ๅปบ่ฏ„ไผฐๅ›พๆŒ‰ๆ‹“ๆ‰‘ๆ‰ง่กŒ๏ผŒๆ— ไพ่ต–้กนๅนถ่กŒใ€ๆœ‰ไพ่ต–้กน้กบๅบๆ‰ง่กŒ๏ผŒๅŽ็ปญ่ฏ„ไผฐ้กนๅฏ่ฎฟ้—ฎๅ‰็ฝฎ่ฏ„ไผฐ้กน็š„็ป“ๆžœ -- **่ฏ„ไผฐ็ป“ๆžœ**๏ผšๆ”ฏๆŒ `BOOLEAN`ใ€`ENUM`ใ€`SCORE`ใ€`JSON`ใ€`TEXT` ็ญ‰็ฑปๅž‹๏ผŒไผ ้€’็ป™ Prompt Builder ้ฉฑๅŠจๅŠจๆ€็ป„่ฃ… - ---- - -### Prompt Builder ๆจกๅ— - -**ไฝœ็”จ**๏ผšๆ นๆฎ่ฏ„ไผฐ็ป“ๆžœๅ’Œ่ฟ่กŒๆ—ถไธŠไธ‹ๆ–‡๏ผŒๅŠจๆ€็ป„่ฃ…ๅ‘้€็ป™ๆจกๅž‹็š„ Promptใ€‚็คบไพ‹๏ผš - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Prompt Builder - ๆกไปถๅŒ–ๅŠจๆ€็”Ÿๆˆ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ ่ฏ„ไผฐ็ป“ๆžœ่พ“ๅ…ฅ: โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆจก็ณŠ: ๆ˜ฏ โ”‚ ็ป้ชŒ: ๆœ‰ โ”‚ ๅทฅๅ…ท: ๆœ‰ โ”‚ ็Ÿฅ่ฏ†: ๆ—  โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ่‡ชๅฎšไน‰ PromptBuilder ๆกไปถๅŒน้… โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ ๆจก็ณŠ=ๆ˜ฏ โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ ๆณจๅ…ฅ [ๆพ„ๆธ…ๅผ•ๅฏผ Prompt] โ”‚ โ”‚ -โ”‚ โ”‚ ๆจก็ณŠ=ๅฆ โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ ๆณจๅ…ฅ [็›ดๆŽฅๆ‰ง่กŒ Prompt] โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ ็ป้ชŒ=ๆœ‰ โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ ๆณจๅ…ฅ [ๅކๅฒ็ป้ชŒๅ‚่€ƒ] โ”‚ โ”‚ -โ”‚ โ”‚ ๅทฅๅ…ท=ๆœ‰ โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ ๆณจๅ…ฅ [ๅทฅๅ…ทไฝฟ็”จ่ฏดๆ˜Ž] โ”‚ โ”‚ -โ”‚ โ”‚ ็Ÿฅ่ฏ†=ๆœ‰ โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ ๆณจๅ…ฅ [็›ธๅ…ณ็Ÿฅ่ฏ†็‰‡ๆฎต] โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ ็ป„ๅˆ็คบไพ‹1: ๆจก็ณŠ+ๆ— ๅทฅๅ…ท+ๆ— ็Ÿฅ่ฏ† โ”€โ”€โ–ถ [่ฟฝ้—ฎ็”จๆˆท Prompt] โ”‚ โ”‚ -โ”‚ โ”‚ ็ป„ๅˆ็คบไพ‹2: ๆธ…ๆ™ฐ+ๆœ‰ๅทฅๅ…ท+ๆœ‰็ป้ชŒ โ”€โ”€โ–ถ [ๅฟซ้€Ÿๆ‰ง่กŒ Prompt] โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆœ€็ปˆๅŠจๆ€ Prompt: โ”‚ โ”‚ -โ”‚ โ”‚ [็ณป็ปŸๆ็คบ] + [ๆพ„ๆธ…ๅผ•ๅฏผ] + [ๅކๅฒ็ป้ชŒ] + [ๅทฅๅ…ท่ฏดๆ˜Ž] + [็”จๆˆท้—ฎ้ข˜] โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆจกๅž‹ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- ๅคšไธช PromptBuilder ๆŒ‰ไผ˜ๅ…ˆ็บง้กบๅบๆ‰ง่กŒ -- ๆฏไธช Builder ๆ นๆฎ่ฏ„ไผฐ็ป“ๆžœๅ†ณๅฎšๆ˜ฏๅฆ่ดก็Œฎใ€่ดก็Œฎไป€ไนˆๅ†…ๅฎน -- ๆ”ฏๆŒ่‡ชๅฎšไน‰ Builder๏ผŒๆ นๆฎไธšๅŠก้œ€ๆฑ‚ๅฎšๅˆถ Prompt ้€ป่พ‘ -- ้žไพตๅ…ฅๅผ๏ผŒๅœจๆจกๅž‹่ฐƒ็”จๅฑ‚ๆ‹ฆๆˆช - -**ๅฏนๆฏ”ไผ ็ปŸๆ–นๆกˆ**๏ผš - -| ๅฏนๆฏ”็ปดๅบฆ | ไผ ็ปŸๆ–นๆกˆ | ่ฏ„ไผฐ + PromptBuilder | -|---------|---------|------------------------------------| -| **Prompt ้•ฟๅบฆ** | ้œ€่ฆ็ฉทไธพๅ„็งๆƒ…ๅ†ต็š„ๅค„็†ๆŒ‡ไปค๏ผˆ"้‡ๅˆฐ A ๆƒ…ๅ†ตๅบ”่ฏฅ...๏ผŒ้‡ๅˆฐ B ๆƒ…ๅ†ตๅบ”่ฏฅ..."๏ผ‰๏ผŒPrompt ่‡ƒ่‚ฟ | ้€š่ฟ‡ๅ‰็ฝฎ่ฏ„ไผฐ่ฏ†ๅˆซๅœบๆ™ฏ๏ผŒไป…ๆณจๅ…ฅๅฝ“ๅ‰ๅœบๆ™ฏๆ‰€้œ€็š„ไธŠไธ‹ๆ–‡๏ผŒPrompt ๆ›ด็Ÿญๆ›ด็ฒพ็กฎ | -| **Agent ่กŒไธบๅฏๆŽงๆ€ง** | ไพ่ต–ๆจกๅž‹ๅฏนๅ†—้•ฟๆŒ‡ไปค็š„"็†่งฃ"๏ผŒๅฎนๆ˜“่ฏฏๅˆค | ่กŒไธบ็”ฑ่ฏ„ไผฐ็ป“ๆžœ้ฉฑๅŠจ๏ผŒๅ‡ๅฐ‘ๆจกๅž‹่ฏฏๅˆค๏ผŒๆ›ดๅฏๆŽง | -| **ๆ‰ฉๅฑ•็ตๆดปๆ€ง** | ๆ–ฐๅขžๅœบๆ™ฏ้œ€ไฟฎๆ”น Prompt๏ผŒ็ปดๆŠคๅ›ฐ้šพ | ๆ นๆฎไธšๅŠก้œ€ๆฑ‚ไฟฎๆ”น็›ธๅ…ณ่ฏ„ไผฐ้กนไธŽPromptBuilder | -| **ไปฃ็ ๆžถๆž„** | ่ฏ„ไผฐ้€ป่พ‘ไธŽ Prompt ่€ฆๅˆๅœจไธ€่ตท | ่ฏ„ไผฐ้€ป่พ‘ไธŽ Prompt ๆจกๆฟ่งฃ่€ฆ๏ผŒๅ…ณๆณจ็‚นๅˆ†็ฆป๏ผŒ็‹ฌ็ซ‹็ปดๆŠคๅ’Œ่ฟญไปฃ | - ---- - -### ๅญฆไน ๆจกๅ—๏ผˆLearning๏ผ‰ - -**ไฝœ็”จ**๏ผšไปŽ Agent ๆ‰ง่กŒๅކๅฒไธญ่‡ชๅŠจๆๅ–ๅนถไฟๅญ˜ๆœ‰ไปทๅ€ผ็š„็ป้ชŒใ€‚ - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๅญฆไน ๆจกๅ—ๅทฅไฝœๆต็จ‹ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent ๆ‰ง่กŒ่ฟ‡็จ‹ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ ่พ“ๅ…ฅ โ”€โ”€โ–ถ ๆŽจ็† โ”€โ”€โ–ถ ไปฃ็ ็”Ÿๆˆ โ”€โ”€โ–ถ ๆ‰ง่กŒ โ”€โ”€โ–ถ ่พ“ๅ‡บ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๅญฆไน ไธŠไธ‹ๆ–‡ๆ•่Žท โ”‚ โ”‚ -โ”‚ โ”‚ - ็”จๆˆท่พ“ๅ…ฅ โ”‚ โ”‚ -โ”‚ โ”‚ - ไธญ้—ดๆŽจ็†ๆญฅ้ชค โ”‚ โ”‚ -โ”‚ โ”‚ - ็”Ÿๆˆ็š„ไปฃ็  โ”‚ โ”‚ -โ”‚ โ”‚ - ๆ‰ง่กŒ็ป“ๆžœ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๅญฆไน ๆๅ–ๅ™จๅˆ†ๆž โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ็ป้ชŒๆๅ–ๅ™จ โ”‚ โ”‚ ๆจกๅผๆๅ–ๅ™จ โ”‚ โ”‚ ้”™่ฏฏๆๅ–ๅ™จ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ๆˆๅŠŸๆจกๅผ โ”‚ โ”‚ ้€š็”จๆจกๅผ โ”‚ โ”‚ ๅคฑ่ดฅๆ•™่ฎญ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆŒไน…ๅŒ–ๅญ˜ๅ‚จ โ”‚ โ”€โ”€โ–ถ ไพ›ๅŽ็ปญไปปๅŠกๅ‚่€ƒไฝฟ็”จ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- **After-Agent ๅญฆไน **๏ผšๆฏๆฌก Agent ่ฟ่กŒๅฎŒๆˆๅŽๆๅ–็ป้ชŒ -- **After-Model ๅญฆไน **๏ผšๆฏๆฌกๆจกๅž‹่ฐƒ็”จๅŽๆๅ–็ป้ชŒ -- **Tool Interceptor**๏ผšไปŽๅทฅๅ…ท่ฐƒ็”จไธญๆๅ–็ป้ชŒ -- **็ฆป็บฟๅญฆไน **๏ผšๆ‰น้‡ๅˆ†ๆžๅކๅฒๆ•ฐๆฎๆๅ–ๆจกๅผ -- **ๅญฆไน ่ฟ‡็จ‹**๏ผšๆ•่Žทๆ‰ง่กŒไธŠไธ‹ๆ–‡ โ†’ ๆๅ–ๅ™จๅˆ†ๆž่ฏ†ๅˆซ โ†’ ็”Ÿๆˆ็ป้ชŒ่ฎฐๅฝ• โ†’ ๆŒไน…ๅŒ–ๅญ˜ๅ‚จไพ›ๅŽ็ปญๅค็”จ - ---- - -### ็ป้ชŒๆจกๅ—๏ผˆExperience๏ผ‰ - -**ไฝœ็”จ**๏ผš็งฏ็ดฏๅ’Œๅค็”จๅކๅฒๆˆๅŠŸๆ‰ง่กŒ็ป้ชŒใ€‚ - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ็ป้ชŒๆจกๅ—ๅทฅไฝœ็คบๆ„ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ ใ€ๅœบๆ™ฏ1: ็ป้ชŒ็งฏ็ดฏใ€‘ โ”‚ -โ”‚ โ”‚ -โ”‚ ็”จๆˆท: "ๆŸฅ่ฏข่ฎขๅ•็Šถๆ€" โ”€โ”€โ–ถ Agent ๆˆๅŠŸๆ‰ง่กŒ โ”€โ”€โ–ถ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ไฟๅญ˜็ป้ชŒ: โ”‚ โ”‚ -โ”‚ โ”‚ - Reactๅ†ณ็ญ–็ป้ชŒ โ”‚ โ”‚ -โ”‚ โ”‚ - Code็ป้ชŒ โ”‚ โ”‚ -โ”‚ โ”‚ - ๅธธ่ฏ†็ป้ชŒ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ็ป้ชŒๅบ“ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ ใ€ๅœบๆ™ฏ2: ็ป้ชŒๅค็”จใ€‘ ๏ฝœ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ ็”จๆˆท: "ๆŸฅ่ฏขๆˆ‘็š„่ฎขๅ•็Šถๆ€" โ—€โ”€โ”€โ”€โ”€ ๅŒน้…็›ธไผผ็ป้ชŒ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent ๅ‚่€ƒๅކๅฒ็ป้ชŒ๏ผŒๆ›ดๅฟซๅ†ณ็ญ–+็”Ÿๆˆๆญฃ็กฎไปฃ็  โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ ใ€ๅœบๆ™ฏ3: ๅฟซ้€Ÿๆ„ๅ›พๅ“ๅบ”ใ€‘ โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ็ป้ชŒๅบ“ โ”‚ โ”‚ -โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ็ป้ชŒA (ๆ™ฎ้€š) โ”‚ โ”‚ ็ป้ชŒB (โœ“ ๅทฒ้…็ฝฎๅฟซ้€Ÿๆ„ๅ›พ) โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ ๆ— ๅฟซ้€Ÿๆ„ๅ›พ้…็ฝฎ โ”‚ โ”‚ ๆกไปถ: ๅ‰็ผ€ๅŒน้…"ๆŸฅ็œ‹*้”€้‡" โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ†’ ๆณจๅ…ฅpromptไพ›llmๅ‚่€ƒโ”‚ โ”‚ ๅŠจไฝœ: ่ฐƒ็”จ้”€้‡ๆŸฅ่ฏขAPI โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ ๆกไปถๅ‘ฝไธญ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ ็”จๆˆท: "ๆŸฅ็œ‹ไปŠๆ—ฅ้”€้‡" โ”€โ”€โ–ถ ๅŒน้…็ป้ชŒBๅฟซ้€Ÿๆ„ๅ›พ โ”€โ”€โ–ถ ่ทณ่ฟ‡LLM๏ผŒ็›ดๆŽฅๆ‰ง่กŒ โ”‚ -โ”‚ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- **ๅคš็ฑปๅž‹็ป้ชŒ**๏ผšไปฃ็ ็”Ÿๆˆ็ป้ชŒใ€ReAct ๅ†ณ็ญ–็ป้ชŒใ€ๅธธ่ฏ†็ป้ชŒ๏ผŒไธบ็ฑปไผผไปปๅŠกๆไพ›ๅކๅฒๅ‚่€ƒ -- **็ตๆดปๅค็”จ**๏ผš็ป้ชŒๅฏๆณจๅ…ฅ Prompt ๆˆ–็”จไบŽๅฟซ้€Ÿๆ„ๅ›พๅŒน้… -- **็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†**๏ผšๆ”ฏๆŒ็ป้ชŒ็š„ๅˆ›ๅปบใ€ๆ›ดๆ–ฐใ€ๅˆ ้™ค -- **ๅฟซ้€Ÿๆ„ๅ›พๅ“ๅบ”**๏ผš - - ็ป้ชŒ้œ€ๆ˜พๅผ้…็ฝฎ `fastIntentConfig` ๆ‰่ƒฝๅฏ็”จ - - ๅŒน้…ๅทฒ้…็ฝฎๆกไปถๆ—ถ๏ผŒ่ทณ่ฟ‡ LLM ๅฎŒๆ•ดๆŽจ็†๏ผŒ็›ดๆŽฅๆ‰ง่กŒ้ข„่ฎฐๅฝ•็š„ๅทฅๅ…ท่ฐƒ็”จๆˆ–ไปฃ็  - - ๆ”ฏๆŒๅคšๆกไปถๅŒน้…๏ผšๆถˆๆฏๅ‰็ผ€ใ€ๆญฃๅˆ™ใ€ๅ…ƒๆ•ฐๆฎใ€็Šถๆ€็ญ‰ - ---- - -### ่งฆๅ‘ๅ™จๆจกๅ—๏ผˆTrigger๏ผ‰ - -**ไฝœ็”จ**๏ผšๅˆ›ๅปบๅ’Œ็ฎก็†ๅฎšๆ—ถไปปๅŠกๆˆ–ไบ‹ไปถ่งฆๅ‘็š„ Agent ๆ‰ง่กŒใ€‚ +| ๆจกๅ— | ่ฏดๆ˜Ž | ๆ–‡ๆกฃ | +|------|------|------| +| **่ฏ„ไผฐๆจกๅ—** | ้€š่ฟ‡่ฏ„ไผฐๅ›พ๏ผˆGraph๏ผ‰่ฟ›่กŒๅคš็ปดๅบฆๆ„ๅ›พ่ฏ†ๅˆซ๏ผŒๆ”ฏๆŒ LLM ๅ’Œ่ง„ๅˆ™ๅผ•ๆ“Ž | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/evaluation/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/evaluation/advanced) | +| **Prompt Builder** | ๆ นๆฎ่ฏ„ไผฐ็ป“ๆžœๅ’Œ่ฟ่กŒๆ—ถไธŠไธ‹ๆ–‡ๅŠจๆ€็ป„่ฃ… Prompt | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/prompt-builder/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/prompt-builder/advanced) | -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ่งฆๅ‘ๅ™จๆจกๅ—่ƒฝๅŠ›็คบๆ„ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ ใ€ๅฎšๆ—ถ่งฆๅ‘ใ€‘ โ”‚ -โ”‚ โ”‚ -โ”‚ ็”จๆˆท: "ๆฏๅคฉๆ—ฉไธŠ9็‚น็ป™ๆˆ‘ๅ‘้€้”€ๅ”ฎๆ—ฅๆŠฅ" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent ๅˆ›ๅปบ โ”‚ โ”‚ ่ฐƒๅบฆๅ™จ โ”‚ โ”‚ ่‡ชๅŠจๆ‰ง่กŒ โ”‚ โ”‚ -โ”‚ โ”‚ Cron ่งฆๅ‘ๅ™จ โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ 0 9 * * * โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ ็”Ÿๆˆๆ—ฅๆŠฅ โ”‚ โ”‚ -โ”‚ โ”‚ (่‡ชๆˆ‘่ฐƒๅบฆ) โ”‚ โ”‚ โ”‚ โ”‚ ๅ‘้€้€š็Ÿฅ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ ใ€ๅปถ่ฟŸ่งฆๅ‘ใ€‘ โ”‚ -โ”‚ โ”‚ -โ”‚ ็”จๆˆท: "30ๅˆ†้’ŸๅŽๆ้†’ๆˆ‘ๅผ€ไผš" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Agent ๅˆ›ๅปบ โ”‚ โ”‚ 30ๅˆ†้’ŸๅŽ โ”‚ โ”‚ ๅ‘้€ๆ้†’ โ”‚ โ”‚ -โ”‚ โ”‚ ไธ€ๆฌกๆ€ง่งฆๅ‘ๅ™จ โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ ่งฆๅ‘ โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ "่ฏฅๅผ€ไผšไบ†" โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ ใ€ๅ›ž่ฐƒ่งฆๅ‘ใ€‘ โ”‚ -โ”‚ โ”‚ -โ”‚ ็”จๆˆท: "ๆปก่ถณxxๆกไปถๆ—ถๅธฎๆˆ‘xx" โ”‚ -โ”‚ โ”‚ -โ”‚ ๅค–้ƒจ็ณป็ปŸ: ๅ‘้€ไบ‹ไปถๅˆฐ Webhook โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆŽฅๆ”ถๅ›ž่ฐƒ โ”‚ โ”‚ ่งฆๅ‘ Agent โ”‚ โ”‚ ๅค„็†ไบ‹ไปถ โ”‚ โ”‚ -โ”‚ โ”‚ Webhook ไบ‹ไปถ โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ ๆ‰ง่กŒไปปๅŠก โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚ ่ฟ”ๅ›žๅ“ๅบ” โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` +### ๅทฅๅ…ทๆ‰ฉๅฑ• -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- `TIME_CRON`่งฆๅ‘ๅ™จ๏ผšๆ”ฏๆŒ Cron ่กจ่พพๅผๅฎšๆ—ถ่งฆๅ‘ไปปๅŠก -- `TIME_ONCE`่งฆๅ‘ๅ™จ๏ผšๆ”ฏๆŒไธ€ๆฌกๆ€งๅปถ่ฟŸ่งฆๅ‘ -- `CALLBACK`่งฆๅ‘ๅ™จ๏ผšๆ”ฏๆŒๅ›ž่ฐƒไบ‹ไปถ่งฆๅ‘ -- Agent ๅฏ้€š่ฟ‡ๅทฅๅ…ท่‡ชไธปๅˆ›ๅปบ่งฆๅ‘ๅ™จ๏ผŒๅฎž็Žฐ"่‡ชๆˆ‘่ฐƒๅบฆ" +| ๆจกๅ— | ่ฏดๆ˜Ž | ๆ–‡ๆกฃ | +|------|------|------| +| **MCP ๅทฅๅ…ท** | ๆŽฅๅ…ฅ Model Context Protocol ๆœๅŠกๅ™จ๏ผŒๅค็”จ MCP ๅทฅๅ…ท็”Ÿๆ€ | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/mcp/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/mcp/advanced) | +| **ๅŠจๆ€ HTTP ๅทฅๅ…ท** | ้€š่ฟ‡ OpenAPI ่ง„่ŒƒๆŽฅๅ…ฅ REST API | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/dynamic-http/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/dynamic-http/advanced) | +| **่‡ชๅฎšไน‰ CodeAct ๅทฅๅ…ท** | ้€š่ฟ‡ CodeactTool ๆŽฅๅฃๆž„ๅปบ่‡ชๅฎšไน‰ๅทฅๅ…ท | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/custom-codeact-tool/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/custom-codeact-tool/advanced) | ---- +### ๆ™บ่ƒฝ่ƒฝๅŠ› -### ๅ›žๅคๆธ ้“ๆจกๅ—๏ผˆReply Channel๏ผ‰ +| ๆจกๅ— | ่ฏดๆ˜Ž | ๆ–‡ๆกฃ | +|------|------|------| +| **็ป้ชŒๆจกๅ—** | ็งฏ็ดฏๅ’Œๅค็”จๅކๅฒๆˆๅŠŸๆ‰ง่กŒ็ป้ชŒ๏ผŒๆ”ฏๆŒๅฟซ้€Ÿๆ„ๅ›พๅ“ๅบ” | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/experience/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/experience/advanced) | +| **ๅญฆไน ๆจกๅ—** | ไปŽ Agent ๆ‰ง่กŒๅކๅฒไธญ่‡ชๅŠจๆๅ–ๆœ‰ไปทๅ€ผ็š„็ป้ชŒ | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/learning/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/learning/advanced) | +| **ๆœ็ดขๆจกๅ—** | ๅคšๆ•ฐๆฎๆบ็ปŸไธ€ๆฃ€็ดขๅผ•ๆ“Ž๏ผŒๆ”ฏๆŒ็Ÿฅ่ฏ†้—ฎ็ญ” | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/search/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/search/advanced) | -**ไฝœ็”จ**๏ผšๆไพ›็ตๆดป็š„ๆถˆๆฏๅ›žๅค่ƒฝๅŠ›๏ผŒๆ”ฏๆŒๅคš็ง่พ“ๅ‡บๆธ ้“ใ€‚ - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๅ›žๅคๆธ ้“ๆจกๅ—่ƒฝๅŠ›็คบๆ„ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ Agent ้œ€่ฆๅ‘็”จๆˆทๅ›žๅคๆถˆๆฏ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๅ›žๅคๆธ ้“่ทฏ็”ฑ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ DEFAULT โ”‚ โ”‚ IDE_CARD โ”‚ โ”‚ IM_NOTIFY โ”‚ โ”‚ WEBHOOK โ”‚ โ”‚ -โ”‚ โ”‚ ๆ–‡ๆœฌๅ›žๅค โ”‚ โ”‚ ๅก็‰‡ๅฑ•็คบ โ”‚ โ”‚ ๆถˆๆฏๆŽจ้€ โ”‚ โ”‚ JSONๆŽจ้€ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ๆŽงๅˆถๅฐ โ”‚ โ”‚ IDE โ”‚ โ”‚ IM โ”‚ โ”‚ ็ฌฌไธ‰ๆ–น โ”‚ โ”‚ -โ”‚ โ”‚ ็ปˆ็ซฏๅ›žๅค โ”‚ โ”‚ ๅฏŒๆ–‡ๆœฌๅก็‰‡ โ”‚ โ”‚ (ๅฏๆ‰ฉๅฑ•) โ”‚ โ”‚ ็ณป็ปŸ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ”‚ ใ€ไฝฟ็”จ็คบไพ‹ใ€‘ โ”‚ -โ”‚ โ”‚ -โ”‚ ็”จๆˆท: "ๅˆ†ๆžๅฎŒๆˆๅŽๅ‘้€็ป“ๆžœ" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ Agent: send_message(text="ๅˆ†ๆž็ป“ๆžœ...") โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ ็”จๆˆทๆ”ถๅˆฐๆถˆๆฏ: "๐Ÿ“Š ๅˆ†ๆž็ป“ๆžœ: ..." โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- **ๅคšๆธ ้“่ทฏ็”ฑ**๏ผšAgent ๅฏๆ นๆฎๅœบๆ™ฏ้€‰ๆ‹ฉไธๅŒๆธ ้“ๅ›žๅค -- **้…็ฝฎ้ฉฑๅŠจ**๏ผšๅŠจๆ€็”Ÿๆˆๅ›žๅคๅทฅๅ…ท๏ผŒๆ— ้œ€็ผ–็  -- **ๅŒๆญฅๅผ‚ๆญฅๆ”ฏๆŒ**๏ผšๆ”ฏๆŒๅŒๆญฅๅ’Œๅผ‚ๆญฅๅ›žๅคๆจกๅผ -- **็ปŸไธ€ๆŽฅๅฃ**๏ผšๅฑ่”ฝๅบ•ๅฑ‚ๅฎž็Žฐๅทฎๅผ‚ -- **ๅ†…็ฝฎ็คบไพ‹ๆธ ้“**๏ผš`IDE_TEXT`๏ผˆๆผ”็คบ็”จ๏ผ‰ -- **ๅฏๆ‰ฉๅฑ•ๆธ ้“**๏ผˆ้€š่ฟ‡ๅฎž็Žฐ `ReplyChannelDefinition` SPI๏ผ‰๏ผšๅฆ‚ `IDE_CARD`ใ€`IM_NOTIFICATION`๏ผˆ้’‰้’‰/้ฃžไนฆ/ไผๅพฎ๏ผ‰ใ€`WEBHOOK_JSON` ็ญ‰๏ผŒ้œ€็”จๆˆท่‡ช่กŒๅฎž็Žฐ - ---- - -### ๅทฅๅ…ทๆ‰ฉๅฑ•ๆจกๅ—๏ผˆDynamic Tools๏ผ‰ - -**ไฝœ็”จ**๏ผšๆไพ›้ซ˜ๅบฆๅฏๆ‰ฉๅฑ•็š„ๅทฅๅ…ทไฝ“็ณป๏ผŒ่ฎฉ Agent ่ƒฝๅคŸ่ฐƒ็”จๅ„็ฑปๅค–้ƒจๅทฅๅ…ทๅฎŒๆˆไปปๅŠกใ€‚ - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๅทฅๅ…ทๆ‰ฉๅฑ•ๆžถๆž„ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ Agent ้œ€่ฆๆ‰ง่กŒๆ“ไฝœ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ CodeactTool ๅทฅๅ…ทไฝ“็ณป โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ MCP โ”‚ โ”‚ HTTP โ”‚ โ”‚ Search โ”‚ โ”‚ Trigger โ”‚ โ”‚ ่‡ชๅฎšไน‰ โ”‚ โ”‚ -โ”‚ โ”‚ Tools โ”‚ โ”‚ API โ”‚ โ”‚ Tools โ”‚ โ”‚ Tools โ”‚ โ”‚ Tools โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ Tools โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ไปปๆ„ MCP โ”‚ โ”‚ REST API โ”‚ โ”‚ ็Ÿฅ่ฏ†ๆฃ€็ดข โ”‚ โ”‚ ๅฎšๆ—ถไปปๅŠก โ”‚ โ”‚ ... โ”‚ โ”‚ -โ”‚ โ”‚ Server โ”‚ โ”‚ OpenAPI โ”‚ โ”‚ ้กน็›ฎๆœ็ดข โ”‚ โ”‚ ไบ‹ไปถๅ›ž่ฐƒ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- **MCP ๅทฅๅ…ทๆ”ฏๆŒ**๏ผšไธ€้”ฎๆŽฅๅ…ฅไปปๆ„ MCP Server๏ผŒๅค็”จ MCP ๅทฅๅ…ท็”Ÿๆ€ -- **HTTP API ๆ”ฏๆŒ**๏ผš้€š่ฟ‡ OpenAPI ่ง„่ŒƒๆŽฅๅ…ฅ REST API๏ผŒ่ฐƒ็”จไผไธš็Žฐๆœ‰ๆŽฅๅฃ -- **ๅ†…็ฝฎๅทฅๅ…ท็ฑปๅž‹**๏ผšๆœ็ดข๏ผˆSearch๏ผ‰ใ€ๅ›žๅค๏ผˆReply๏ผ‰ใ€่งฆๅ‘ๅ™จ๏ผˆTrigger๏ผ‰ใ€ๅญฆไน ๏ผˆLearning๏ผ‰็ญ‰ -- **่‡ชๅฎšไน‰ๅทฅๅ…ท SPI**๏ผšๅฎž็Žฐ `CodeactTool` ๆŽฅๅฃ๏ผŒ่ฝปๆพๆ‰ฉๅฑ•ๆ–ฐๅทฅๅ…ท - ---- +### ไบคไบ’่ƒฝๅŠ› -### ็Ÿฅ่ฏ†ๆฃ€็ดขๆจกๅ—๏ผˆKnowledge Search๏ผ‰ +| ๆจกๅ— | ่ฏดๆ˜Ž | ๆ–‡ๆกฃ | +|------|------|------| +| **ๅ›žๅคๆธ ้“** | ๅคšๆธ ้“ๆถˆๆฏๅ›žๅค๏ผŒๆ”ฏๆŒๆธ ้“่ทฏ็”ฑ | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/reply/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/reply/advanced) | +| **่งฆๅ‘ๅ™จ** | ๅฎšๆ—ถไปปๅŠกใ€ๅปถ่ฟŸๆ‰ง่กŒใ€ไบ‹ไปถๅ›ž่ฐƒ่งฆๅ‘ | [ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/features/trigger/quickstart) ๏ฝœ [้ซ˜็บง็‰นๆ€ง](https://java2ai.com/agents/assistantagent/features/trigger/advanced) | -**ไฝœ็”จ**๏ผšๅคšๆ•ฐๆฎๆบ็ปŸไธ€ๆฃ€็ดขๅผ•ๆ“Ž๏ผŒไธบ Agent ็š„้—ฎ็ญ”ๅ’Œๅ†ณ็ญ–ๆไพ›็Ÿฅ่ฏ†ๆ”ฏๆ’‘ใ€‚ +### ๆ›ดๅคš่ต„ๆบ -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๅคšๆ•ฐๆฎๆบๆฃ€็ดขๆžถๆž„ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ ็”จๆˆท้—ฎ้ข˜: "ๅฆ‚ไฝ•้…็ฝฎๆ•ฐๆฎๅบ“่ฟžๆŽฅๆฑ ๏ผŸ" โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ็ปŸไธ€ๆฃ€็ดขๆŽฅๅฃ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ็Ÿฅ่ฏ†ๅบ“ โ”‚ โ”‚ ้กน็›ฎ โ”‚ โ”‚ Web โ”‚ โ”‚ ่‡ชๅฎšไน‰ โ”‚ โ”‚ -โ”‚ โ”‚ Provider โ”‚ โ”‚ Provider โ”‚ โ”‚ Provider โ”‚ โ”‚Provider โ”‚ โ”‚ -โ”‚ โ”‚ (ไธป่ฆ) โ”‚ โ”‚ (ๅฏ้€‰) โ”‚ โ”‚ (ๅฏ้€‰) โ”‚ โ”‚ (SPI) โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ FAQ / ๆ–‡ๆกฃ โ”‚ โ”‚ ๆบไปฃ็  โ”‚ โ”‚ ็ฝ‘็ปœๆ–‡็ซ  โ”‚ โ”‚ ... โ”‚ โ”‚ -โ”‚ โ”‚ ๅކๅฒ้—ฎ็ญ” โ”‚ โ”‚ ้…็ฝฎๆ–‡ไปถ โ”‚ โ”‚ ๆŠ€ๆœฏ่ฎบๅ› โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ ๅ›ข้˜Ÿ็ฌ”่ฎฐ โ”‚ โ”‚ ๆ—ฅๅฟ— โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ่šๅˆๆŽ’ๅบ โ”‚ โ”‚ -โ”‚ โ”‚ โ†’ ๆณจๅ…ฅ Prompt โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ๆ ธๅฟƒ่ƒฝๅŠ›**๏ผš -- **็ปŸไธ€ๆฃ€็ดขๆŽฅๅฃ**๏ผš`SearchProvider` SPI๏ผŒๆ”ฏๆŒๅฏๆ’ๆ‹”ๆ•ฐๆฎๆบ -- **ๆผ”็คบ Provider**๏ผšๅ†…็ฝฎ็Ÿฅ่ฏ†ๅบ“ใ€้กน็›ฎใ€Web ็š„ Mock ๅฎž็Žฐ๏ผˆไป…ไพ›ๆผ”็คบๅ’Œๆต‹่ฏ•๏ผ‰ -- **่‡ชๅฎšไน‰ๆ‰ฉๅฑ•**๏ผš้€š่ฟ‡ๅฎž็Žฐ `SearchProvider` ๆŽฅๅฃ๏ผŒๆŽฅๅ…ฅไปปๆ„ๆ•ฐๆฎๆบ๏ผˆๆ•ฐๆฎๅบ“ใ€ๅ‘้‡ๅบ“ใ€API๏ผ‰ -- **็ป“ๆžœ่šๅˆ**๏ผšๆ”ฏๆŒๅฏ้…็ฝฎ็š„ๆŽ’ๅบ็ญ–็•ฅ -- **ไธšๅŠกไปทๅ€ผ**๏ผšๆŽฅๅ…ฅไผไธš็Ÿฅ่ฏ†ๅบ“ๆไพ›ๅ‡†็กฎ็ญ”ๆกˆใ€ๆ”ฏๆŒ็ญ”ๆกˆๆบฏๆบใ€้™ไฝŽไบบๅทฅๅฎขๆœๅŽ‹ๅŠ› - -**้…็ฝฎ็คบไพ‹**๏ผš - -```yaml -spring: - ai: - alibaba: - codeact: - extension: - search: - enabled: true - knowledge-search-enabled: true # ็Ÿฅ่ฏ†ๅบ“๏ผˆ้ป˜่ฎค Mock ๅฎž็Žฐ๏ผ‰ - project-search-enabled: false # ้กน็›ฎไปฃ็ ๏ผˆ้ป˜่ฎค Mock ๅฎž็Žฐ๏ผ‰ - web-search-enabled: false # Web ๆœ็ดข๏ผˆ้ป˜่ฎค Mock ๅฎž็Žฐ๏ผ‰ - default-top-k: 5 - search-timeout-ms: 5000 -``` - -> ๐Ÿ’ก ไปฅไธŠๆœ็ดขๅŠŸ่ƒฝ้ป˜่ฎคๆไพ› Mock ๅฎž็Žฐไพ›ๆผ”็คบๆต‹่ฏ•ใ€‚็”Ÿไบง็Žฏๅขƒ้œ€ๅฎž็Žฐ `SearchProvider` SPI ๆŽฅๅ…ฅๅฎž้™…ๆ•ฐๆฎๆบใ€‚ +| ่ต„ๆบ | ้“พๆŽฅ | +|------|------| +| ๅฟซ้€Ÿๅผ€ๅง‹ๆŒ‡ๅ— | [AssistantAgent ๅฟซ้€Ÿๅผ€ๅง‹](https://java2ai.com/agents/assistantagent/quick-start) | +| ไบŒๆฌกๅผ€ๅ‘ๆŒ‡ๅ— | [ๅผ€ๅ‘ๆŒ‡ๅ—](https://java2ai.com/agents/assistantagent/secondary-development) | --- diff --git a/assistant-agent-autoconfigure/pom.xml b/assistant-agent-autoconfigure/pom.xml index 467a811..68c242e 100644 --- a/assistant-agent-autoconfigure/pom.xml +++ b/assistant-agent-autoconfigure/pom.xml @@ -6,11 +6,12 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-autoconfigure + 17 17 diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java index 881c68d..45f70fc 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java @@ -175,7 +175,7 @@ public static class CodeactAgentBuilder extends Builder { // ReturnSchemaRegistry (่ฟ›็จ‹ๅ†…ๅ•ไพ‹) private ReturnSchemaRegistry returnSchemaRegistry; - // ToolRegistryBridgeFactory (็”จไบŽ่‡ชๅฎšไน‰ ToolRegistryBridge) + // ToolRegistryBridgeFactory (็”จไบŽ่‡ชๅฎšไน‰ๅทฅๅ…ท่ฐƒ็”จๆกฅๆŽฅ๏ผŒๅฆ‚ๅฏ่ง‚ๆต‹ๆ€ง) private ToolRegistryBridgeFactory toolRegistryBridgeFactory; // CodeactTool support (ๆ–ฐๆœบๅˆถ) @@ -195,11 +195,8 @@ public static class CodeactAgentBuilder extends Builder { // Keep reference to ChatModel for code generation private ChatModel chatModel; - // SubAgent system prompt (for Codeact phase code generation) - private String subAgentSystemPrompt; - - // state keys to propagate from parent agent (react phase) to child agent (codeact phase) - private List stateKeysToPropagate = new ArrayList<>(); + // GraphLifecycleListeners (็”จไบŽๅฏ่ง‚ๆต‹ๆ€ง) + private List lifecycleListeners = new ArrayList<>(); public CodeactAgentBuilder() { super(); @@ -248,22 +245,6 @@ public CodeactAgentBuilder subAgentHooks(List hooks) { return this; } - /** - * Set state keys to propagate from parent agent (react phase) to child agent (codeact phase) - */ - public CodeactAgentBuilder stateKeysToPropagate(List keys) { - this.stateKeysToPropagate = keys != null ? new ArrayList<>(keys) : new ArrayList<>(); - return this; - } - - /** - * Set state keys to propagate from parent agent (react phase) to child agent (codeact phase), in variable argument form - */ - public CodeactAgentBuilder stateKeysToPropagate(String... keys) { - this.stateKeysToPropagate = new ArrayList<>(Arrays.asList(keys)); - return this; - } - public CodeactAgentBuilder experienceProvider(ExperienceProvider experienceProvider) { this.experienceProvider = experienceProvider; return this; @@ -279,14 +260,6 @@ public CodeactAgentBuilder fastIntentService(FastIntentService fastIntentService return this; } - /** - * Set custom system prompt for the Codeact sub-agent (code generation phase). - */ - public CodeactAgentBuilder subAgentSystemPrompt(String systemPrompt) { - this.subAgentSystemPrompt = systemPrompt; - return this; - } - /** * Enable/disable initial code generation hook */ @@ -364,8 +337,8 @@ public CodeactAgentBuilder returnSchemaRegistry(ReturnSchemaRegistry registry) { /** * Set the ToolRegistryBridgeFactory for customizing ToolRegistryBridge creation. * - *

If not set, the default factory will be used which creates standard - * ToolRegistryBridge instances. + *

ๅฏ็”จไบŽๆทปๅŠ ๅฏ่ง‚ๆต‹ๆ€งใ€ไปปๅŠก่ฎฐๅฝ•็ญ‰ๅŠŸ่ƒฝใ€‚ + * ๅฆ‚ๆžœไธ่ฎพ็ฝฎ๏ผŒๅฐ†ไฝฟ็”จ้ป˜่ฎค็š„ ToolRegistryBridgeใ€‚ * * @param factory the ToolRegistryBridgeFactory to use * @return CodeactAgentBuilder instance for chaining @@ -375,6 +348,35 @@ public CodeactAgentBuilder toolRegistryBridgeFactory(ToolRegistryBridgeFactory f return this; } + /** + * Add a GraphLifecycleListener for observability. + * + *

็”จไบŽ็›‘ๅฌ Agent Graph ๆ‰ง่กŒ็š„ๅ…ณ้”ฎ้˜ถๆฎต๏ผŒๅฆ‚ React ้˜ถๆฎตๅผ€ๅง‹/็ป“ๆŸใ€่Š‚็‚นๆ‰ง่กŒๅ‰ๅŽ็ญ‰ใ€‚ + * ๅฏไปฅ้€š่ฟ‡ๆญคๆŽฅๅฃๅฎž็Žฐๆ—ฅๅฟ—่ฎฐๅฝ•ใ€ๆŒ‡ๆ ‡ๆ”ถ้›†ใ€ๅˆ†ๅธƒๅผ่ฟฝ่ธช็ญ‰ๅฏ่ง‚ๆต‹ๆ€งๅŠŸ่ƒฝใ€‚ + * + * @param listener the GraphLifecycleListener to add + * @return CodeactAgentBuilder instance for chaining + */ + public CodeactAgentBuilder lifecycleListener(com.alibaba.cloud.ai.graph.GraphLifecycleListener listener) { + if (listener != null) { + this.lifecycleListeners.add(listener); + } + return this; + } + + /** + * Add multiple GraphLifecycleListeners for observability. + * + * @param listeners the list of GraphLifecycleListeners to add + * @return CodeactAgentBuilder instance for chaining + */ + public CodeactAgentBuilder lifecycleListeners(List listeners) { + if (listeners != null) { + this.lifecycleListeners.addAll(listeners); + } + return this; + } + /** * Set the model name for code generation * For example: "qwen-coder-plus", "qwen-max", etc. @@ -507,6 +509,55 @@ public CodeactAgentBuilder methodTools(Object... toolObjects) { return this; } + /** + * Override buildConfig to include lifecycleListeners for observability. + * + * @return CompileConfig with lifecycleListeners included + */ + @Override + protected CompileConfig buildConfig() { + // If compileConfig is already set, use it + if (compileConfig != null) { + // Add additional lifecycleListeners to existing config + if (!lifecycleListeners.isEmpty()) { + for (com.alibaba.cloud.ai.graph.GraphLifecycleListener listener : lifecycleListeners) { + compileConfig.lifecycleListeners().offer(listener); + } + logger.info("CodeactAgentBuilder#buildConfig - reason=ๆทปๅŠ LifecycleListenersๅˆฐๅทฒๆœ‰CompileConfig, count={}", + lifecycleListeners.size()); + } + return compileConfig; + } + + // Build new CompileConfig with saver and lifecycleListeners + com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig saverConfig = + com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig.builder() + .register(saver) + .build(); + + CompileConfig.Builder builder = CompileConfig.builder() + .saverConfig(saverConfig) + .recursionLimit(Integer.MAX_VALUE) + .releaseThread(releaseThread); + + // Add ObservationRegistry if available + if (observationRegistry != null) { + builder.observationRegistry(observationRegistry); + } + + // Add all lifecycleListeners + for (com.alibaba.cloud.ai.graph.GraphLifecycleListener listener : lifecycleListeners) { + builder.withLifecycleListener(listener); + } + + if (!lifecycleListeners.isEmpty()) { + logger.info("CodeactAgentBuilder#buildConfig - reason=ๅˆ›ๅปบๅธฆๆœ‰LifecycleListeners็š„CompileConfig, count={}", + lifecycleListeners.size()); + } + + return builder.build(); + } + /** * Build the CodeactAgent */ @@ -562,7 +613,7 @@ public CodeactAgent build() { null, // Will be set by ReactAgent new OverAllState(), // Placeholder this.codeactToolRegistry, // Pass CodeactTool registry - this.toolRegistryBridgeFactory, // Pass custom factory (null will use default) + this.toolRegistryBridgeFactory, // Pass ToolRegistryBridgeFactory for observability this.allowIO, this.allowNativeAccess, this.executionTimeoutMs @@ -684,6 +735,27 @@ public CodeactAgent build() { if (!allTools.isEmpty()) { toolBuilder.toolCallbacks(allTools); } + + // Set toolContext if available (like DefaultBuilder does) + if (toolContext != null && !toolContext.isEmpty()) { + toolBuilder.toolContext(toolContext); + } + + // Enable logging if enabled + if (enableLogging) { + toolBuilder.enableActingLog(true); + } + + // Set exception processor (like DefaultBuilder does) + if (toolExecutionExceptionProcessor == null) { + toolBuilder.toolExecutionExceptionProcessor( + org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor.builder() + .alwaysThrow(false) + .build()); + } else { + toolBuilder.toolExecutionExceptionProcessor(toolExecutionExceptionProcessor); + } + llmNode.setInstruction(instruction); AgentToolNode toolNode = toolBuilder.build(); @@ -923,21 +995,19 @@ private ModelInterceptor createCodeactSubAgentInterceptor() { } return CodeactSubAgentInterceptor.builder() - .defaultModel(codeGenModel) - .defaultCodeactTools(this.codeactTools) - .defaultLanguage(language) - .codeContext(this.codeContext) - .environmentManager(this.environmentManager) - .experienceProvider(this.experienceProvider) - .experienceExtensionProperties(this.experienceExtensionProperties) - .fastIntentService(this.fastIntentService) - .includeDefaultCodeGenerator(true) // ไฝฟ็”จ้ป˜่ฎคไปฃ็ ็”Ÿๆˆๅ™จ - .hooks(this.subAgentHooks) // Pass sub-agent hooks - .stateKeysToPropagate(this.stateKeysToPropagate) // ไผ ้€’้œ€่ฆ่ทจ agent ไผ ้€’็š„ state keys - .returnSchemaRegistry(this.codeactToolRegistry != null ? - this.codeactToolRegistry.getReturnSchemaRegistry() : null) - .subAgentSystemPrompt(this.subAgentSystemPrompt) - .build(); + .defaultModel(codeGenModel) + .defaultCodeactTools(this.codeactTools) + .defaultLanguage(language) + .codeContext(this.codeContext) + .environmentManager(this.environmentManager) + .experienceProvider(this.experienceProvider) + .experienceExtensionProperties(this.experienceExtensionProperties) + .fastIntentService(this.fastIntentService) + .includeDefaultCodeGenerator(true) // ไฝฟ็”จ้ป˜่ฎคไปฃ็ ็”Ÿๆˆๅ™จ + .hooks(this.subAgentHooks) // Pass sub-agent hooks + .returnSchemaRegistry(this.codeactToolRegistry != null ? + this.codeactToolRegistry.getReturnSchemaRegistry() : null) + .build(); } } } diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java index a489436..a854c9c 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java @@ -18,17 +18,13 @@ import com.alibaba.cloud.ai.graph.CompiledGraph; import com.alibaba.cloud.ai.graph.OverAllState; import com.alibaba.cloud.ai.graph.agent.BaseAgent; -import com.alibaba.cloud.ai.graph.agent.tools.ToolContextConstants; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ToolContext; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; @@ -41,7 +37,6 @@ *

  • ๆ”ฏๆŒ Map<String, BaseAgent> ่€Œไธๆ˜ฏ Map<String, ReactAgent>
  • *
  • ้€š่ฟ‡ CompiledGraph.invoke() ่ฐƒ็”จ่€Œไธๆ˜ฏ agent.call()
  • *
  • ไปŽ OverAllState ไธญๆๅ–็ป“ๆžœ
  • - *
  • ๆ”ฏๆŒ้…็ฝฎ stateKeysToPropagate๏ผŒๅฐ†็ˆถ agent ็š„ๆŒ‡ๅฎš state ไผ ้€’็ป™ๅญ agent
  • * * * @author Assistant Agent Team @@ -53,22 +48,8 @@ public class BaseAgentTaskTool implements BiFunction subAgents; - /** - * ้œ€่ฆไปŽ็ˆถ agent ็š„ OverAllState ไผ ้€’็ป™ๅญ agent ็š„ state keys ๅˆ—่กจใ€‚ - *

    - * ่ฟ™ๆ˜ฏไธ€ไธช้€š็”จๆ‰ฉๅฑ•็‚น๏ผŒๅ…่ฎธไธšๅŠกๆ–น้…็ฝฎ้œ€่ฆ่ทจ agent ไผ ้€’็š„็Šถๆ€ใ€‚ - */ - private final List stateKeysToPropagate; - public BaseAgentTaskTool(Map subAgents) { - this(subAgents, Collections.emptyList()); - } - - public BaseAgentTaskTool(Map subAgents, List stateKeysToPropagate) { this.subAgents = subAgents; - this.stateKeysToPropagate = stateKeysToPropagate != null - ? new ArrayList<>(stateKeysToPropagate) - : Collections.emptyList(); } @Override @@ -90,25 +71,22 @@ public String apply(TaskRequest request, ToolContext toolContext) { // 3. Build inputs from description or structured inputs Map inputs; if (request.structuredInputs != null && !request.structuredInputs.isEmpty()) { - inputs = new HashMap<>(request.structuredInputs); + inputs = request.structuredInputs; logger.info("BaseAgentTaskTool#apply ไฝฟ็”จ็ป“ๆž„ๅŒ–่พ“ๅ…ฅ: {}", inputs.keySet()); } else { inputs = buildInputsFromDescription(request.description); logger.info("BaseAgentTaskTool#apply ไปŽๆ่ฟฐ่งฃๆž่พ“ๅ…ฅ: {}", inputs.keySet()); } - // 4. Propagate configured state keys from parent agent - propagateParentStateKeys(toolContext, inputs); - - // 5. Invoke the subagent through CompiledGraph + // 4. Invoke the subagent through CompiledGraph CompiledGraph compiledGraph = subAgent.getAndCompileGraph(); Optional resultOpt = compiledGraph.invoke(inputs); - // 6. Extract result from state + // 5. Extract result from state OverAllState resultState = resultOpt.orElseThrow(() -> new IllegalStateException("SubAgent returned empty result")); - // 7. Try to get generated_code or any string result + // 6. Try to get generated_code or any string result String result = resultState.value("generated_code", String.class) .orElseGet(() -> extractAnyStringResult(resultState)); @@ -122,52 +100,6 @@ public String apply(TaskRequest request, ToolContext toolContext) { } } - /** - * ไปŽ็ˆถ agent ็š„ OverAllState ไธญๆๅ–้…็ฝฎ็š„ keys๏ผŒไผ ้€’็ป™ๅญ agentใ€‚ - *

    - * ่ฟ™ๆ˜ฏไธ€ไธช้€š็”จๆ‰ฉๅฑ•็‚น๏ผŒๅ…่ฎธไธšๅŠกๆ–นๅฐ†็ˆถ agent ็š„็Šถๆ€๏ผˆๅฆ‚่ฏ„ไผฐ็ป“ๆžœ๏ผ‰ไผ ้€’็ป™ๅญ agentใ€‚ - * - * @param toolContext ๅทฅๅ…ทไธŠไธ‹ๆ–‡๏ผŒๅŒ…ๅซ็ˆถ agent ็š„ OverAllState - * @param inputs ๅญ agent ็š„่พ“ๅ…ฅ Map๏ผŒๅฐ†่ขซ่ฟฝๅŠ ็ˆถ agent ็š„็Šถๆ€ - */ - private void propagateParentStateKeys(ToolContext toolContext, Map inputs) { - if (stateKeysToPropagate.isEmpty()) { - return; - } - - if (toolContext == null || toolContext.getContext() == null) { - logger.debug("BaseAgentTaskTool#propagateParentStateKeys - toolContext ไธบ็ฉบ๏ผŒ่ทณ่ฟ‡็Šถๆ€ไผ ้€’"); - return; - } - - Object stateObj = toolContext.getContext().get(ToolContextConstants.AGENT_STATE_CONTEXT_KEY); - if (!(stateObj instanceof OverAllState)) { - logger.debug("BaseAgentTaskTool#propagateParentStateKeys - ็ˆถ agent state ไธๅญ˜ๅœจๆˆ–็ฑปๅž‹ไธๅŒน้…๏ผŒ่ทณ่ฟ‡็Šถๆ€ไผ ้€’"); - return; - } - - OverAllState parentState = (OverAllState) stateObj; - int propagatedCount = 0; - - for (String key : stateKeysToPropagate) { - Optional valueOpt = parentState.value(key); - if (valueOpt.isPresent()) { - Object value = valueOpt.get(); - inputs.put(key, value); - propagatedCount++; - logger.debug("BaseAgentTaskTool#propagateParentStateKeys - ไผ ้€’็Šถๆ€: key={}, valueType={}", - key, value.getClass().getSimpleName()); - } else { - logger.debug("BaseAgentTaskTool#propagateParentStateKeys - ็Šถๆ€ key={} ๅœจ็ˆถ agent ไธญไธๅญ˜ๅœจ๏ผŒ่ทณ่ฟ‡", key); - } - } - - if (propagatedCount > 0) { - logger.info("BaseAgentTaskTool#propagateParentStateKeys - ๅฎŒๆˆ็Šถๆ€ไผ ้€’: propagatedCount={}, keys={}", - propagatedCount, stateKeysToPropagate); - } - } - /** * ไปŽไปปๅŠกๆ่ฟฐๆž„ๅปบ่พ“ๅ…ฅๅ‚ๆ•ฐ * ๅฐ่ฏ•่งฃๆžๆ่ฟฐไธญ็š„็ป“ๆž„ๅŒ–ไฟกๆฏ diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java index b09e8e6..9a49b08 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java @@ -74,18 +74,6 @@ public class CodeactSubAgentInterceptor extends ModelInterceptor { private final ExperienceExtensionProperties experienceExtensionProperties; private final FastIntentService fastIntentService; - /** - * ้œ€่ฆไปŽ็ˆถ agent ไผ ้€’็ป™ๅญ agent ็š„ state keysใ€‚ - * ้€š่ฟ‡ๆญค้…็ฝฎ๏ผŒไธšๅŠกๆ–นๅฏไปฅๆŒ‡ๅฎš้œ€่ฆ่ทจ agent ไผ ้€’็š„็Šถๆ€ใ€‚ - */ - private final List stateKeysToPropagate; - - /** - * ๅญ Agent ็š„็ณป็ปŸๆ็คบ่ฏ๏ผˆ็”จไบŽไปฃ็ ็”Ÿๆˆ้˜ถๆฎต๏ผ‰ใ€‚ - * ๅฆ‚ๆžœ่ฎพ็ฝฎไบ†ๆญคๅญ—ๆฎต๏ผŒๅฐ†ไผ ้€’็ป™ CodeGeneratorSubAgent ็”จไบŽๅฎšๅˆถไปฃ็ ็”Ÿๆˆ่กŒไธบใ€‚ - */ - private final String subAgentSystemPrompt; - private CodeactSubAgentInterceptor(Builder builder) { this.systemPrompt = builder.systemPrompt != null ? builder.systemPrompt : DEFAULT_SYSTEM_PROMPT; this.subAgents = new HashMap<>(builder.subAgents); @@ -96,10 +84,6 @@ private CodeactSubAgentInterceptor(Builder builder) { this.experienceProvider = builder.experienceProvider; this.experienceExtensionProperties = builder.experienceExtensionProperties; this.fastIntentService = builder.fastIntentService; - this.stateKeysToPropagate = builder.stateKeysToPropagate != null - ? new ArrayList<>(builder.stateKeysToPropagate) - : Collections.emptyList(); - this.subAgentSystemPrompt = builder.subAgentSystemPrompt; // ๆทปๅŠ ้ป˜่ฎคcode-generatorๅ’Œcondition-code-generator๏ผˆๅฏนๆ ‡general-purpose๏ผ‰ if (includeDefaultCodeGenerator && builder.defaultModel != null) { @@ -111,8 +95,7 @@ private CodeactSubAgentInterceptor(Builder builder) { builder.defaultLanguage, false, // ไธๆ˜ฏๆกไปถๅˆคๆ–ญๅ‡ฝๆ•ฐ builder.hooks, - builder.returnSchemaRegistry, - builder.subAgentSystemPrompt // ไผ ้€’่‡ชๅฎšไน‰็ณป็ปŸๆ็คบ่ฏ + builder.returnSchemaRegistry ); this.subAgents.put("code-generator", codeGenAgent); @@ -124,8 +107,7 @@ private CodeactSubAgentInterceptor(Builder builder) { builder.defaultLanguage, true, // ๆ˜ฏๆกไปถๅˆคๆ–ญๅ‡ฝๆ•ฐ builder.hooks, - builder.returnSchemaRegistry, - builder.subAgentSystemPrompt + builder.returnSchemaRegistry ); this.subAgents.put("condition-code-generator", conditionCodeGenAgent); @@ -133,12 +115,7 @@ private CodeactSubAgentInterceptor(Builder builder) { } // ๅˆ›ๅปบๅ†…้ƒจ BaseAgentTaskTool๏ผˆๅฏนๆ ‡ SubAgentInterceptor ๅˆ›ๅปบ TaskTool๏ผ‰ - // ไผ ๅ…ฅ stateKeysToPropagate๏ผŒๆ”ฏๆŒๅฐ†็ˆถ agent ็š„็Šถๆ€ไผ ้€’็ป™ๅญ agent - BaseAgentTaskTool taskTool = new BaseAgentTaskTool(this.subAgents, this.stateKeysToPropagate); - - if (!this.stateKeysToPropagate.isEmpty()) { - logger.info("CodeactSubAgentInterceptor# ้…็ฝฎ็Šถๆ€ไผ ้€’: keys={}", this.stateKeysToPropagate); - } + BaseAgentTaskTool taskTool = new BaseAgentTaskTool(this.subAgents); // ๅˆ›ๅปบWriteCodeToolๅ’ŒWriteConditionCodeTool๏ผˆๅง”ๆ‰˜็ป™ BaseAgentTaskTool๏ผ‰ CodeFastIntentSupport codeFastIntentSupport = @@ -156,15 +133,6 @@ private CodeactSubAgentInterceptor(Builder builder) { /** * ๅˆ›ๅปบ้ป˜่ฎคไปฃ็ ็”ŸๆˆๅญAgent๏ผˆๅฏนๆ ‡createGeneralPurposeAgent๏ผ‰ - * - * @param model ChatModel ๅฎžไพ‹ - * @param tools CodeactTool ๅˆ—่กจ - * @param interceptors ๆ‹ฆๆˆชๅ™จๅˆ—่กจ - * @param language ็ผ–็จ‹่ฏญ่จ€ - * @param isCondition ๆ˜ฏๅฆไธบๆกไปถๅˆคๆ–ญๅ‡ฝๆ•ฐ - * @param hooks Hook ๅˆ—่กจ - * @param returnSchemaRegistry ่ฟ”ๅ›žๅ€ผ Schema ๆณจๅ†Œ่กจ - * @param customSystemPrompt ่‡ชๅฎšไน‰็ณป็ปŸๆ็คบ่ฏ๏ผˆๅฏ้€‰๏ผŒไธบ null ๆ—ถไฝฟ็”จ้ป˜่ฎคๆ็คบ่ฏ๏ผ‰ */ private BaseAgent createDefaultCodeGeneratorAgent( ChatModel model, @@ -173,8 +141,7 @@ private BaseAgent createDefaultCodeGeneratorAgent( Language language, boolean isCondition, List hooks, - ReturnSchemaRegistry returnSchemaRegistry, - String customSystemPrompt) { + ReturnSchemaRegistry returnSchemaRegistry) { List modelInterceptors = new ArrayList<>(); if (interceptors != null) { @@ -186,7 +153,7 @@ private BaseAgent createDefaultCodeGeneratorAgent( } if (isCondition) { - CodeGeneratorSubAgent.Builder builder = CodeGeneratorSubAgent.builder() + return CodeGeneratorSubAgent.builder() .name("condition-code-generator") .description("Generate condition function code that returns boolean") .chatModel(model) @@ -195,14 +162,10 @@ private BaseAgent createDefaultCodeGeneratorAgent( .modelInterceptors(modelInterceptors) .hooks(hooks) .isCondition(true) - .returnSchemaRegistry(returnSchemaRegistry); - - if (customSystemPrompt != null && !customSystemPrompt.isEmpty()) { - builder.customSystemPrompt(customSystemPrompt); - } - return builder.build(); + .returnSchemaRegistry(returnSchemaRegistry) + .build(); } else { - CodeGeneratorSubAgent.Builder builder = CodeGeneratorSubAgent.builder() + return CodeGeneratorSubAgent.builder() .name("code-generator") .description("Generate function code based on requirements") .chatModel(model) @@ -211,12 +174,8 @@ private BaseAgent createDefaultCodeGeneratorAgent( .modelInterceptors(modelInterceptors) .hooks(hooks) .isCondition(false) - .returnSchemaRegistry(returnSchemaRegistry); - - if (customSystemPrompt != null && !customSystemPrompt.isEmpty()) { - builder.customSystemPrompt(customSystemPrompt); - } - return builder.build(); + .returnSchemaRegistry(returnSchemaRegistry) + .build(); } } @@ -277,28 +236,11 @@ public static class Builder { private ExperienceExtensionProperties experienceExtensionProperties; private FastIntentService fastIntentService; - /** - * ้œ€่ฆไปŽ็ˆถ agent ไผ ้€’็ป™ๅญ agent ็š„ state keysใ€‚ - * ้€š่ฟ‡ๆญค้…็ฝฎ๏ผŒไธšๅŠกๆ–นๅฏไปฅๆŒ‡ๅฎš้œ€่ฆ่ทจ agent ไผ ้€’็š„็Šถๆ€ใ€‚ - */ - private List stateKeysToPropagate; - - /** - * ๅญ Agent ็š„่‡ชๅฎšไน‰็ณป็ปŸๆ็คบ่ฏ๏ผˆ็”จไบŽไปฃ็ ็”Ÿๆˆ้˜ถๆฎต๏ผ‰ใ€‚ - * ๅฆ‚ๆžœ่ฎพ็ฝฎไบ†ๆญคๅญ—ๆฎต๏ผŒๅฐ†ไผ ้€’็ป™ CodeGeneratorSubAgent ็”จไบŽๅฎšๅˆถไปฃ็ ็”Ÿๆˆ่กŒไธบใ€‚ - */ - private String subAgentSystemPrompt; - public Builder systemPrompt(String systemPrompt) { this.systemPrompt = systemPrompt; return this; } - public Builder subAgentSystemPrompt(String systemPrompt) { - this.subAgentSystemPrompt = systemPrompt; - return this; - } - public Builder defaultModel(ChatModel model) { this.defaultModel = model; return this; @@ -354,30 +296,6 @@ public Builder hooks(List hooks) { return this; } - /** - * ้…็ฝฎ้œ€่ฆไปŽ็ˆถ agent ไผ ้€’็ป™ๅญ agent ็š„ state keysใ€‚ - *

    - * ่ฟ™ๆ˜ฏไธ€ไธช้€š็”จๆ‰ฉๅฑ•็‚น๏ผŒๅ…่ฎธไธšๅŠกๆ–นๅฐ†็ˆถ agent ็š„็Šถๆ€ไผ ้€’็ป™ Codeact ๅญ agentใ€‚ - * - * @param keys ้œ€่ฆไผ ้€’็š„ state key ๅˆ—่กจ - * @return this builder - */ - public Builder stateKeysToPropagate(List keys) { - this.stateKeysToPropagate = keys; - return this; - } - - /** - * ้…็ฝฎ้œ€่ฆไปŽ็ˆถ agent ไผ ้€’็ป™ๅญ agent ็š„ state keys๏ผˆๅฏๅ˜ๅ‚ๆ•ฐๅฝขๅผ๏ผ‰ใ€‚ - * - * @param keys ้œ€่ฆไผ ้€’็š„ state keys - * @return this builder - */ - public Builder stateKeysToPropagate(String... keys) { - this.stateKeysToPropagate = Arrays.asList(keys); - return this; - } - /** * ๆทปๅŠ ่‡ชๅฎšไน‰ๅญAgent๏ผˆๅฏนๆ ‡addSubAgent๏ผ‰ */ diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/filter/CodeactToolFilter.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/filter/CodeactToolFilter.java deleted file mode 100644 index 8c8d292..0000000 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/filter/CodeactToolFilter.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.assistant.agent.autoconfigure.subagent.filter; - -import com.alibaba.assistant.agent.common.constant.CodeactStateKeys; -import com.alibaba.assistant.agent.common.enums.Language; -import com.alibaba.assistant.agent.common.tools.CodeactTool; -import com.alibaba.assistant.agent.common.tools.CodeactToolMetadata; -import com.alibaba.cloud.ai.graph.OverAllState; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * CodeactTool ็ญ›้€‰ๅ™จ - * - *

    ๆ นๆฎ OverAllState ไธญ็š„็™ฝๅๅ•้…็ฝฎ็ญ›้€‰ๅฏ็”จๅทฅๅ…ทใ€‚ - * ๆ”ฏๆŒๆŒ‰ๅทฅๅ…ทๅ็งฐๅ’Œๅทฅๅ…ท็ป„่ฟ›่กŒ็ญ›้€‰๏ผŒๅคš็ง็™ฝๅๅ•ๆจกๅผๅฏ้€‰ใ€‚ - * - *

    ไฝฟ็”จๆ–นๅผ๏ผš - *

    {@code
    - * List filtered = CodeactToolFilter.filter(allTools, state, Language.PYTHON);
    - * }
    - * - * @author Assistant Agent Team - * @since 1.0.0 - * @see WhitelistMode - */ -public final class CodeactToolFilter { - - private static final Logger logger = LoggerFactory.getLogger(CodeactToolFilter.class); - - private CodeactToolFilter() { - // ๅทฅๅ…ท็ฑป๏ผŒ็ฆๆญขๅฎžไพ‹ๅŒ– - } - - /** - * ๆ นๆฎ OverAllState ไธญ็š„็™ฝๅๅ•้…็ฝฎ็ญ›้€‰ๅทฅๅ…ท - * - * @param allTools ๅ…จ้ƒจๅทฅๅ…ทๅˆ—่กจ - * @param state OverAllState ๅฎžไพ‹ - * @param language ็›ฎๆ ‡็ผ–็จ‹่ฏญ่จ€๏ผˆ็”จไบŽ่ฏญ่จ€ๅ…ผๅฎนๆ€ง่ฟ‡ๆปค๏ผ‰ - * @return ็ญ›้€‰ๅŽ็š„ๅทฅๅ…ทๅˆ—่กจ - */ - public static List filter(List allTools, OverAllState state, Language language) { - if (allTools == null || allTools.isEmpty()) { - logger.debug("CodeactToolFilter#filter - ่พ“ๅ…ฅๅทฅๅ…ทๅˆ—่กจไธบ็ฉบ๏ผŒ่ฟ”ๅ›ž็ฉบๅˆ—่กจ"); - return Collections.emptyList(); - } - - // 1. ๅ…ˆๆŒ‰่ฏญ่จ€ๅ…ผๅฎนๆ€ง่ฟ‡ๆปค - List languageCompatible = filterByLanguage(allTools, language); - logger.debug("CodeactToolFilter#filter - ่ฏญ่จ€่ฟ‡ๆปคๅŽๅทฅๅ…ทๆ•ฐ: {}/{}", - languageCompatible.size(), allTools.size()); - - // 2. ่ฏปๅ–็™ฝๅๅ•้…็ฝฎ - List toolNameWhitelist = extractStringList(state, CodeactStateKeys.AVAILABLE_TOOL_NAMES); - List toolGroupWhitelist = extractStringList(state, CodeactStateKeys.AVAILABLE_TOOL_GROUPS); - WhitelistMode mode = extractWhitelistMode(state); - - logger.info("CodeactToolFilter#filter - ็™ฝๅๅ•้…็ฝฎ: mode={}, toolNames={}, toolGroups={}", - mode, toolNameWhitelist, toolGroupWhitelist); - - // 3. ๅฆ‚ๆžœๆฒกๆœ‰ไปปไฝ•็™ฝๅๅ•้…็ฝฎ๏ผŒ่ฟ”ๅ›ž่ฏญ่จ€ๅ…ผๅฎน็š„ๅ…จ้ƒจๅทฅๅ…ท - if (isEmpty(toolNameWhitelist) && isEmpty(toolGroupWhitelist)) { - logger.info("CodeactToolFilter#filter - ๆ— ็™ฝๅๅ•้…็ฝฎ๏ผŒไฝฟ็”จๅ…จ้ƒจๅทฅๅ…ท: count={}", - languageCompatible.size()); - return languageCompatible; - } - - // 4. ๆ นๆฎๆจกๅผๆ‰ง่กŒ็ญ›้€‰ - List filtered = applyWhitelist(languageCompatible, toolNameWhitelist, toolGroupWhitelist, mode); - - logger.info("CodeactToolFilter#filter - ็ญ›้€‰ๅฎŒๆˆ: input={}, output={}, filteredTools={}", - languageCompatible.size(), filtered.size(), - filtered.stream().map(CodeactTool::getName).collect(Collectors.toList())); - - return filtered; - } - - /** - * ๆŒ‰่ฏญ่จ€ๅ…ผๅฎนๆ€ง่ฟ‡ๆปค - */ - private static List filterByLanguage(List tools, Language language) { - if (language == null) { - return new ArrayList<>(tools); - } - return tools.stream() - .filter(tool -> { - CodeactToolMetadata meta = tool.getCodeactMetadata(); - return meta.supportedLanguages().contains(language); - }) - .collect(Collectors.toList()); - } - - /** - * ๅบ”็”จ็™ฝๅๅ•็ญ›้€‰ - */ - private static List applyWhitelist( - List tools, - List nameWhitelist, - List groupWhitelist, - WhitelistMode mode) { - - Set nameSet = isEmpty(nameWhitelist) ? null : new HashSet<>(nameWhitelist); - Set groupSet = isEmpty(groupWhitelist) ? null : new HashSet<>(groupWhitelist); - - return tools.stream() - .filter(tool -> matchesWhitelist(tool, nameSet, groupSet, mode)) - .collect(Collectors.toList()); - } - - /** - * ๅˆคๆ–ญๅทฅๅ…ทๆ˜ฏๅฆๅŒน้…็™ฝๅๅ• - */ - private static boolean matchesWhitelist( - CodeactTool tool, - Set nameSet, - Set groupSet, - WhitelistMode mode) { - - String toolName = tool.getName(); - String toolGroup = tool.getCodeactMetadata().targetClassName(); - - boolean matchesName = nameSet == null || nameSet.contains(toolName); - boolean matchesGroup = groupSet == null || - (toolGroup != null && groupSet.contains(toolGroup)); - - switch (mode) { - case INTERSECTION: - // ไธคไธช็™ฝๅๅ•้ƒฝๅญ˜ๅœจๆ—ถๅ–ไบค้›†๏ผŒๅชๆœ‰ไธ€ไธชๆ—ถๅชๆฃ€ๆŸฅ้‚ฃไธ€ไธช - if (nameSet != null && groupSet != null) { - return matchesName && matchesGroup; - } else if (nameSet != null) { - return matchesName; - } else if (groupSet != null) { - return matchesGroup; - } - return true; - - case UNION: - // ๅ–ๅนถ้›†๏ผšๆปก่ถณไปปไธ€ๅณๅฏ - if (nameSet != null && groupSet != null) { - return matchesName || matchesGroup; - } else if (nameSet != null) { - return matchesName; - } else if (groupSet != null) { - return matchesGroup; - } - return true; - - case NAME_ONLY: - return nameSet == null || matchesName; - - case GROUP_ONLY: - return groupSet == null || matchesGroup; - - default: - return true; - } - } - - /** - * ไปŽ state ๆๅ–ๅญ—็ฌฆไธฒๅˆ—่กจ - */ - @SuppressWarnings("unchecked") - private static List extractStringList(OverAllState state, String key) { - if (state == null) { - return null; - } - return state.value(key, List.class).orElse(null); - } - - /** - * ไปŽ state ๆๅ–็™ฝๅๅ•ๆจกๅผ - */ - private static WhitelistMode extractWhitelistMode(OverAllState state) { - if (state == null) { - return WhitelistMode.INTERSECTION; - } - return state.value(CodeactStateKeys.WHITELIST_MODE, String.class) - .map(s -> { - try { - return WhitelistMode.valueOf(s.toUpperCase()); - } catch (IllegalArgumentException e) { - logger.warn("CodeactToolFilter#extractWhitelistMode - ๆ— ๆ•ˆ็š„ๆจกๅผๅ€ผ: {}, ไฝฟ็”จ้ป˜่ฎคๅ€ผ", s); - return WhitelistMode.INTERSECTION; - } - }) - .orElse(WhitelistMode.INTERSECTION); - } - - /** - * ๅˆคๆ–ญๅˆ—่กจๆ˜ฏๅฆไธบ็ฉบ - */ - private static boolean isEmpty(List list) { - return list == null || list.isEmpty(); - } -} diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/filter/WhitelistMode.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/filter/WhitelistMode.java deleted file mode 100644 index d7cffe3..0000000 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/filter/WhitelistMode.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.assistant.agent.autoconfigure.subagent.filter; - -/** - * ๅทฅๅ…ท็™ฝๅๅ•ๆจกๅผๆžšไธพ - * - *

    ๅฎšไน‰ๅทฅๅ…ท็™ฝๅๅ•็š„็ญ›้€‰ๆจกๅผ๏ผŒ็”จไบŽๆŽงๅˆถๅ็งฐ็™ฝๅๅ•ๅ’Œ็ป„็™ฝๅๅ•็š„็ป„ๅˆ้€ป่พ‘ใ€‚ - * - * @author Assistant Agent Team - * @since 1.0.0 - */ -public enum WhitelistMode { - - /** - * ไบค้›†ๆจกๅผ๏ผˆ้ป˜่ฎค๏ผ‰ - * - *

    ๅฝ“ๅ็งฐ็™ฝๅๅ•ๅ’Œ็ป„็™ฝๅๅ•ๅŒๆ—ถๅญ˜ๅœจๆ—ถ๏ผŒๅทฅๅ…ทๅฟ…้กปๅŒๆ—ถๆปก่ถณไธค่€…ใ€‚ - * ๅฝ“ๅชๆœ‰ๅ…ถไธญไธ€ไธชๅญ˜ๅœจๆ—ถ๏ผŒๅชๆฃ€ๆŸฅๅญ˜ๅœจ็š„้‚ฃไธชใ€‚ - */ - INTERSECTION, - - /** - * ๅนถ้›†ๆจกๅผ - * - *

    ๅฝ“ๅ็งฐ็™ฝๅๅ•ๅ’Œ็ป„็™ฝๅๅ•ๅŒๆ—ถๅญ˜ๅœจๆ—ถ๏ผŒๅทฅๅ…ทๆปก่ถณไปปไธ€ๅณๅฏใ€‚ - * ๅฝ“ๅชๆœ‰ๅ…ถไธญไธ€ไธชๅญ˜ๅœจๆ—ถ๏ผŒๅชๆฃ€ๆŸฅๅญ˜ๅœจ็š„้‚ฃไธชใ€‚ - */ - UNION, - - /** - * ไป…ๅ็งฐๆจกๅผ - * - *

    ไป…ไฝฟ็”จๅ็งฐ็™ฝๅๅ•่ฟ›่กŒ็ญ›้€‰๏ผŒๅฟฝ็•ฅ็ป„็™ฝๅๅ•ใ€‚ - */ - NAME_ONLY, - - /** - * ไป…็ป„ๆจกๅผ - * - *

    ไป…ไฝฟ็”จ็ป„็™ฝๅๅ•่ฟ›่กŒ็ญ›้€‰๏ผŒๅฟฝ็•ฅๅ็งฐ็™ฝๅๅ•ใ€‚ - */ - GROUP_ONLY -} diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/node/CodeGeneratorNode.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/node/CodeGeneratorNode.java index 270a961..7161706 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/node/CodeGeneratorNode.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/node/CodeGeneratorNode.java @@ -15,14 +15,12 @@ */ package com.alibaba.assistant.agent.autoconfigure.subagent.node; -import com.alibaba.assistant.agent.common.constant.CodeactStateKeys; import com.alibaba.assistant.agent.common.enums.Language; import com.alibaba.assistant.agent.common.tools.CodeactTool; import com.alibaba.assistant.agent.common.tools.CodeactToolMetadata; import com.alibaba.assistant.agent.common.tools.definition.ParameterNode; import com.alibaba.assistant.agent.common.tools.definition.ParameterTree; import com.alibaba.assistant.agent.common.tools.definition.ReturnSchema; -import com.alibaba.assistant.agent.autoconfigure.subagent.filter.CodeactToolFilter; import com.alibaba.assistant.agent.core.tool.schema.ReturnSchemaRegistry; import com.alibaba.cloud.ai.graph.OverAllState; import com.alibaba.cloud.ai.graph.RunnableConfig; @@ -121,18 +119,15 @@ public Map apply(OverAllState state, RunnableConfig config) { logger.debug("CodeGeneratorNode#apply ๆๅ–่พ“ๅ…ฅ: functionName={}, requirement={}, parameters={}, historyCodeCount={}", functionName, requirement, parameters, historyCode.size()); - // 2. ใ€ๆ–ฐๅขžใ€‘ๆ นๆฎ state ไธญ็š„็™ฝๅๅ•้…็ฝฎ็ญ›้€‰ๅทฅๅ…ท - List effectiveTools = filterTools(state); + // 2. ๆž„ๅปบ็ณป็ปŸๆ็คบ๏ผˆๅŒ…ๅซ่ฏญ่จ€่ง„่Œƒใ€ๅฏ็”จCodeactToolๅ’Œๅކๅฒไปฃ็ ๏ผ‰ + String systemPrompt = buildSystemPrompt(language, codeactTools, isCondition, customSystemPrompt, historyCode); - // 3. ๆž„ๅปบ็ณป็ปŸๆ็คบ๏ผˆไฝฟ็”จ็ญ›้€‰ๅŽ็š„ๅทฅๅ…ท๏ผ‰ - String systemPrompt = buildSystemPrompt(language, effectiveTools, isCondition, customSystemPrompt, historyCode); - - // 4. ๆž„ๅปบ็”จๆˆทๆถˆๆฏ + // 3. ๆž„ๅปบ็”จๆˆทๆถˆๆฏ String userMessage = buildUserMessage(requirement, functionName, parameters, isCondition); logger.info("CodeGeneratorNode#apply ๆž„ๅปบๆถˆๆฏ: systemPrompt={}, userMessage={}", systemPrompt, userMessage); - // 5. ๆž„้€ ModelRequest + // 4. ๆž„้€ ModelRequest List messages = List.of( new SystemMessage(systemPrompt), new UserMessage(userMessage) @@ -142,20 +137,18 @@ public Map apply(OverAllState state, RunnableConfig config) { .messages(messages) .build(); - // 6. ้€š่ฟ‡ๆ‹ฆๆˆชๅ™จ้“พ่ฐƒ็”จๆจกๅž‹ + // 5. ้€š่ฟ‡ๆ‹ฆๆˆชๅ™จ้“พ่ฐƒ็”จๆจกๅž‹ ModelResponse modelResponse = executeWithInterceptors(modelRequest); - // 7. ๆๅ–็”Ÿๆˆ็š„ไปฃ็  + // 6. ๆๅ–็”Ÿๆˆ็š„ไปฃ็  String generatedCode = extractCodeFromResponse(modelResponse); logger.info("CodeGeneratorNode#apply ไปฃ็ ็”ŸๆˆๆˆๅŠŸ: functionName={}, codeLength={}", functionName, generatedCode.length()); - // 8. ่ฟ”ๅ›ž็ป“ๆžœ๏ผˆๆ”พๅ…ฅoutputKey๏ผ‰ + // 7. ่ฟ”ๅ›ž็ป“ๆžœ๏ผˆๆ”พๅ…ฅoutputKey๏ผ‰ Map result = new HashMap<>(); result.put(outputKey, generatedCode); - // ๅฏ้€‰๏ผšๅฐ†็ญ›้€‰ๅŽ็š„ๅทฅๅ…ทๅˆ—่กจๅ†™ๅ›ž state๏ผŒ็”จไบŽ่ฐƒ่ฏ•ๅ’Œๅฎก่ฎก - result.put(CodeactStateKeys.FILTERED_CODEACT_TOOLS, effectiveTools); return result; } catch (Exception e) { @@ -166,24 +159,6 @@ public Map apply(OverAllState state, RunnableConfig config) { } } - /** - * ๆ นๆฎ state ไธญ็š„็™ฝๅๅ•้…็ฝฎ็ญ›้€‰ๅทฅๅ…ท - * - *

    ไฝฟ็”จ {@link CodeactToolFilter} ๆ นๆฎ state ไธญ้…็ฝฎ็š„็™ฝๅๅ•็ญ›้€‰ๅฏ็”จๅทฅๅ…ทใ€‚ - * ๅฆ‚ๆžœๆฒกๆœ‰้…็ฝฎ็™ฝๅๅ•๏ผŒๅˆ™่ฟ”ๅ›žๅ…จ้ƒจๅทฅๅ…ท๏ผˆไฟๆŒๅ‘ๅŽๅ…ผๅฎน๏ผ‰ใ€‚ - * - * @param state OverAllState ๅฎžไพ‹ - * @return ็ญ›้€‰ๅŽ็š„ๅทฅๅ…ทๅˆ—่กจ - */ - private List filterTools(OverAllState state) { - List filtered = CodeactToolFilter.filter(codeactTools, state, language); - - logger.info("CodeGeneratorNode#filterTools - ๅทฅๅ…ท็ญ›้€‰ๅฎŒๆˆ: total={}, filtered={}", - codeactTools != null ? codeactTools.size() : 0, filtered.size()); - - return filtered; - } - /** * ไปŽstateๆๅ–้œ€ๆฑ‚ */ diff --git a/assistant-agent-common/pom.xml b/assistant-agent-common/pom.xml index 6b92ce5..b676856 100644 --- a/assistant-agent-common/pom.xml +++ b/assistant-agent-common/pom.xml @@ -6,7 +6,7 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-common diff --git a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java index 93bd0ba..a244395 100644 --- a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java +++ b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java @@ -28,8 +28,6 @@ private CodeactStateKeys() { // Utility class } - // ==================== ไปฃ็ ็”ŸๆˆไธŠไธ‹ๆ–‡ ==================== - /** * Key for storing the list of generated codes in the current session * Type: List<GeneratedCode> @@ -65,72 +63,5 @@ private CodeactStateKeys() { * Type: Boolean */ public static final String INITIAL_CODE_GEN_DONE = "initial_code_gen_done"; - - // ==================== ๅทฅๅ…ท็™ฝๅๅ•้…็ฝฎ ==================== - - /** - * ๅฏ็”จๅทฅๅ…ทๅ็งฐ็™ฝๅๅ• - * - *

    ็ฑปๅž‹๏ผšList<String> - *

    ็คบไพ‹๏ผš["search_app", "reply_user", "get_project_info"] - *

    ็”จ้€”๏ผš็ฒพ็กฎๆŒ‡ๅฎšๅ…่ฎธไฝฟ็”จ็š„ๅทฅๅ…ท๏ผŒๅทฅๅ…ทๅ็งฐๅฏนๅบ” CodeactTool.getName() - *

    ไธบ็ฉบๆˆ–ไธๅญ˜ๅœจๆ—ถ๏ผšไธๆŒ‰ๅ็งฐ็ญ›้€‰ - * - *

    ๆณจๆ„๏ผš่ฟ™้‡Œๅญ˜ๅ‚จ็š„ๆ˜ฏๅทฅๅ…ท็š„ name๏ผˆCodeactTool.getName()๏ผ‰๏ผŒไธๆ˜ฏ็‹ฌ็ซ‹็š„ IDใ€‚ - * ๅฆ‚ๆžœ่ฏ„ไผฐๆ—ถ LLM ่พ“ๅ‡บ็š„ๆ˜ฏ็ผฉๅ†™/็ฎ€็Ÿญ ID๏ผŒไธŠๅฑ‚ๅบ”็”จ้œ€่ฆๅœจๅ†™ๅ…ฅ state ๅ‰ๅฐ† ID ่ฝฌๆขไธบๅฏนๅบ”็š„ๅทฅๅ…ท nameใ€‚ - */ - public static final String AVAILABLE_TOOL_NAMES = "available_tool_names"; - - /** - * ๅฏ็”จๅทฅๅ…ท็ป„็™ฝๅๅ• - * - *

    ็ฑปๅž‹๏ผšList<String> - *

    ็คบไพ‹๏ผš["search", "reply", "app_helper"] - *

    ็”จ้€”๏ผšๆŒ‰ๅทฅๅ…ท็ป„็ญ›้€‰๏ผŒ็ป„ๅๅฏนๅบ” CodeactToolMetadata.targetClassName() - *

    ไธบ็ฉบๆˆ–ไธๅญ˜ๅœจๆ—ถ๏ผšไธๆŒ‰็ป„็ญ›้€‰ - */ - public static final String AVAILABLE_TOOL_GROUPS = "available_tool_groups"; - - /** - * ็™ฝๅๅ•ๆจกๅผ - * - *

    ็ฑปๅž‹๏ผšString - *

    ๅฏ้€‰ๅ€ผ๏ผš - * - "INTERSECTION"๏ผˆ้ป˜่ฎค๏ผ‰๏ผšๅ็งฐ็™ฝๅๅ•ๅ’Œ็ป„็™ฝๅๅ•ๅ–ไบค้›† - * - "UNION"๏ผšๅ็งฐ็™ฝๅๅ•ๅ’Œ็ป„็™ฝๅๅ•ๅ–ๅนถ้›† - * - "NAME_ONLY"๏ผšไป…ไฝฟ็”จๅ็งฐ็™ฝๅๅ• - * - "GROUP_ONLY"๏ผšไป…ไฝฟ็”จ็ป„็™ฝๅๅ• - *

    ไธบ็ฉบๆˆ–ไธๅญ˜ๅœจๆ—ถ๏ผš้ป˜่ฎคไธบ INTERSECTION - */ - public static final String WHITELIST_MODE = "tool_whitelist_mode"; - - // ==================== ๅทฅๅ…ทไธŠไธ‹ๆ–‡๏ผˆๅช่ฏป๏ผ‰ ==================== - - /** - * ๆณจๅ…ฅ็š„ๅ…จ้ƒจ codeact ๅทฅๅ…ทๅˆ—่กจ - * - *

    ็ฑปๅž‹๏ผšList<CodeactTool> - *

    ็”ฑ CodeGeneratorSubAgent.init_context ่Š‚็‚นๆณจๅ…ฅ - *

    ไธŠๅฑ‚ๅบ”็”จๅฏ่ฏปๅ–ๆญคๅˆ—่กจ่ฟ›่กŒ่ฏ„ไผฐ - */ - public static final String CODEACT_TOOLS = "codeact_tools"; - - /** - * ็ญ›้€‰ๅŽ็š„ codeact ๅทฅๅ…ทๅˆ—่กจ - * - *

    ็ฑปๅž‹๏ผšList<CodeactTool> - *

    ็”ฑ CodeGeneratorNode ็ญ›้€‰ๅŽๅ†™ๅ…ฅ๏ผˆๅฏ้€‰๏ผ‰ - *

    ็”จไบŽ่ฐƒ่ฏ•ๅ’Œๅฎก่ฎก - */ - public static final String FILTERED_CODEACT_TOOLS = "filtered_codeact_tools"; - - /** - * ็ผ–็จ‹่ฏญ่จ€ - * - *

    ็ฑปๅž‹๏ผšString - *

    ็คบไพ‹๏ผš"python", "java" - *

    ็”ฑ CodeGeneratorSubAgent.init_context ่Š‚็‚นๆณจๅ…ฅ - */ - public static final String LANGUAGE = "language"; } diff --git a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/CodeactTool.java b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/CodeactTool.java index 4a0b2bd..fe56225 100644 --- a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/CodeactTool.java +++ b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/CodeactTool.java @@ -81,7 +81,12 @@ default CodeactToolDefinition getCodeactDefinition() { * @return ๅทฅๅ…ทๅ */ default String getName() { - return getCodeactDefinition().name(); + CodeactToolDefinition definition = getCodeactDefinition(); + if (definition != null) { + return definition.name(); + } + // ๅ›ž้€€ๅˆฐ ToolDefinition + return getToolDefinition().name(); } /** @@ -89,7 +94,12 @@ default String getName() { * @return ๅทฅๅ…ทๆ่ฟฐ */ default String getDescription() { - return getCodeactDefinition().description(); + CodeactToolDefinition definition = getCodeactDefinition(); + if (definition != null) { + return definition.description(); + } + // ๅ›ž้€€ๅˆฐ ToolDefinition + return getToolDefinition().description(); } /** diff --git a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/definition/ParameterTree.java b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/definition/ParameterTree.java index 3e2075d..c4b5438 100644 --- a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/definition/ParameterTree.java +++ b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/tools/definition/ParameterTree.java @@ -155,7 +155,9 @@ private String formatDefaultValue(ParameterNode param) { return "None"; } if (defaultValue instanceof String) { - return "\"" + defaultValue + "\""; + // ่ฝฌไน‰ๅญ—็ฌฆไธฒไธญ็š„็‰นๆฎŠๅญ—็ฌฆ + String escaped = escapeStringForPython((String) defaultValue); + return "\"" + escaped + "\""; } if (defaultValue instanceof Boolean) { return (Boolean) defaultValue ? "True" : "False"; @@ -163,6 +165,46 @@ private String formatDefaultValue(ParameterNode param) { return String.valueOf(defaultValue); } + /** + * ่ฝฌไน‰ๅญ—็ฌฆไธฒไปฅไพฟๅœจ Python ไปฃ็ ไธญๅฎ‰ๅ…จไฝฟ็”จใ€‚ + * + * @param str ๅŽŸๅง‹ๅญ—็ฌฆไธฒ + * @return ่ฝฌไน‰ๅŽ็š„ๅญ—็ฌฆไธฒ + */ + private String escapeStringForPython(String str) { + if (str == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + for (char c : str.toCharArray()) { + switch (c) { + case '\\': + sb.append("\\\\"); + break; + case '"': + sb.append("\\\""); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + if (c < 32) { + // ๆŽงๅˆถๅญ—็ฌฆ็”จ Unicode ่ฝฌไน‰ + sb.append(String.format("\\u%04x", (int) c)); + } else { + sb.append(c); + } + } + } + return sb.toString(); + } + /** * ๅˆ›ๅปบๆž„ๅปบๅ™จๅฎžไพ‹ใ€‚ * @return ๆž„ๅปบๅ™จ diff --git a/assistant-agent-core/pom.xml b/assistant-agent-core/pom.xml index 2d04cb2..ccff481 100644 --- a/assistant-agent-core/pom.xml +++ b/assistant-agent-core/pom.xml @@ -6,7 +6,7 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-core @@ -65,6 +65,16 @@ org.slf4j slf4j-api + + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-context + \ No newline at end of file diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/executor/GraalCodeExecutor.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/executor/GraalCodeExecutor.java index ee5fc63..417552b 100644 --- a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/executor/GraalCodeExecutor.java +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/executor/GraalCodeExecutor.java @@ -25,6 +25,7 @@ import com.alibaba.assistant.agent.core.executor.bridge.StateBridge; import com.alibaba.assistant.agent.core.model.ExecutionRecord; import com.alibaba.assistant.agent.core.model.GeneratedCode; +import com.alibaba.assistant.agent.core.model.ToolCallRecord; import com.alibaba.assistant.agent.core.tool.CodeactToolRegistry; import com.alibaba.assistant.agent.core.tool.DefaultToolRegistryBridgeFactory; import com.alibaba.assistant.agent.core.tool.ToolRegistryBridge; @@ -60,6 +61,27 @@ public class GraalCodeExecutor { private static final Logger logger = LoggerFactory.getLogger(GraalCodeExecutor.class); + /** + * ๆ‰ง่กŒ็ป“ๆžœๅŒ…่ฃ…็ฑป๏ผŒๅŒ…ๅซๆ‰ง่กŒ็ป“ๆžœๅ’Œๅทฅๅ…ท่ฐƒ็”จ่ฟฝ่ธช + */ + private static class ExecutionResultWrapper { + private final Object result; + private final List callTrace; + + public ExecutionResultWrapper(Object result, List callTrace) { + this.result = result; + this.callTrace = callTrace != null ? callTrace : new ArrayList<>(); + } + + public Object getResult() { + return result; + } + + public List getCallTrace() { + return callTrace; + } + } + private final RuntimeEnvironmentManager environmentManager; private final CodeContext codeContext; @@ -238,12 +260,14 @@ public ExecutionRecord execute(String functionName, Map args, To logger.debug("GraalCodeExecutor#execute ไปฃ็ ้•ฟๅบฆ: {} ๅญ—็ฌฆ", finalCode.length()); // Execute with GraalVM - Object result = executeWithGraal(finalCode, toolContext); + ExecutionResultWrapper resultWrapper = executeWithGraal(finalCode, toolContext); record.setSuccess(true); - record.setResult(result != null ? String.valueOf(result) : "null"); + record.setResult(resultWrapper.getResult() != null ? String.valueOf(resultWrapper.getResult()) : "null"); + record.setCallTrace(resultWrapper.getCallTrace()); - logger.info("GraalCodeExecutor#execute ๆ‰ง่กŒๆˆๅŠŸ: result={}", result); + logger.info("GraalCodeExecutor#execute ๆ‰ง่กŒๆˆๅŠŸ: result={}, callTraceSize={}", + resultWrapper.getResult(), resultWrapper.getCallTrace().size()); } catch (Exception e) { record.setSuccess(false); @@ -284,12 +308,13 @@ public ExecutionRecord executeDirect(String code, ToolContext toolContext) { String completeCode = environmentManager.generateImports(codeContext) + "\n" + code; // Execute with GraalVM - Object result = executeWithGraal(completeCode, toolContext); + ExecutionResultWrapper resultWrapper = executeWithGraal(completeCode, toolContext); record.setSuccess(true); - record.setResult(String.valueOf(result)); + record.setResult(String.valueOf(resultWrapper.getResult())); + record.setCallTrace(resultWrapper.getCallTrace()); - logger.info("GraalCodeExecutor#executeDirect ๆ‰ง่กŒๆˆๅŠŸ"); + logger.info("GraalCodeExecutor#executeDirect ๆ‰ง่กŒๆˆๅŠŸ, callTraceSize={}", resultWrapper.getCallTrace().size()); } catch (Exception e) { record.setSuccess(false); @@ -312,13 +337,16 @@ public ExecutionRecord executeDirect(String code, ToolContext toolContext) { * @param toolContext the tool context to pass to ToolRegistryBridgeFactory * @return execution result */ - private Object executeWithGraal(String code, ToolContext toolContext) { + private ExecutionResultWrapper executeWithGraal(String code, ToolContext toolContext) { logger.debug("GraalCodeExecutor#executeWithGraal ๅˆ›ๅปบGraalVM Context, hasToolContext={}", toolContext != null); // Capture output ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); + // ็”จไบŽไฟๅญ˜ToolRegistryBridgeไปฅไพฟ่Žทๅ–callTrace + ToolRegistryBridge toolRegistryBridge = null; + try (Context context = Context.newBuilder("python") .allowHostAccess(HostAccess.ALL) .allowIO(allowIO) @@ -336,7 +364,7 @@ private Object executeWithGraal(String code, ToolContext toolContext) { // Inject CodeactTools into Python environment with toolContext if (codeactToolRegistry != null) { - injectCodeactTools(context, codeactToolRegistry, codeContext.getLanguage(), toolContext); + toolRegistryBridge = injectCodeactTools(context, codeactToolRegistry, codeContext.getLanguage(), toolContext); } // Execute code @@ -469,7 +497,11 @@ private Object executeWithGraal(String code, ToolContext toolContext) { javaResult = result.toString(); } - return javaResult; + // ่Žทๅ–ๅทฅๅ…ท่ฐƒ็”จ่ฟฝ่ธช่ฎฐๅฝ• + List callTrace = toolRegistryBridge != null ? toolRegistryBridge.getCallTrace() : new ArrayList<>(); + logger.info("GraalCodeExecutor#executeWithGraal - reason=่Žทๅ–callTraceๅฎŒๆˆ, callTraceSize={}", callTrace.size()); + + return new ExecutionResultWrapper(javaResult, callTrace); } catch (Exception e) { String errors = errorStream.toString(StandardCharsets.UTF_8); @@ -628,7 +660,7 @@ private String getStackTrace(Exception e) { * @param language ็ผ–็จ‹่ฏญ่จ€ * @param toolContext ๅทฅๅ…ทไธŠไธ‹ๆ–‡ */ - private void injectCodeactTools(Context context, + private ToolRegistryBridge injectCodeactTools(Context context, CodeactToolRegistry registry, Language language, ToolContext toolContext) { @@ -654,7 +686,7 @@ private void injectCodeactTools(Context context, if (tools.isEmpty()) { logger.debug("GraalCodeExecutor#injectCodeactTools - reason=ๆฒกๆœ‰ๆ”ฏๆŒ่ฏฅ่ฏญ่จ€็š„ๅทฅๅ…ท, language={}", language); - return; + return bridge; } // Group tools by targetClassName @@ -673,12 +705,173 @@ private void injectCodeactTools(Context context, // Generate and execute Python code String pythonCode = generatePythonToolCode(toolsByClass, globalTools, effectiveToolContext); if (pythonCode != null && !pythonCode.isEmpty()) { - logger.debug("GraalCodeExecutor#injectCodeactTools - reason=็”ŸๆˆPythonๅทฅๅ…ทไปฃ็ , length={}", pythonCode.length()); - context.eval("python", pythonCode); + logger.info("GraalCodeExecutor#injectCodeactTools - reason=็”ŸๆˆPythonๅทฅๅ…ทไปฃ็ , code={}", pythonCode); + try { + context.eval("python", pythonCode); + } catch (Exception e) { + // ๆ‰ง่กŒๅคฑ่ดฅๆ—ถ่ฎฐๅฝ•็”Ÿๆˆ็š„ไปฃ็ ไปฅไพฟ่ฐƒ่ฏ• + // ๆทปๅŠ ่กŒๅทไพฟไบŽๅฎšไฝ้—ฎ้ข˜ + String[] lines = pythonCode.split("\n"); + StringBuilder numberedCode = new StringBuilder(); + for (int i = 0; i < lines.length; i++) { + numberedCode.append(String.format("%4d: %s\n", i + 1, lines[i])); + } + logger.error("GraalCodeExecutor#injectCodeactTools - reason=Pythonๅทฅๅ…ทไปฃ็ ๆ‰ง่กŒๅคฑ่ดฅ, totalLines={}, ็”Ÿๆˆ็š„ไปฃ็ :\n{}", + lines.length, numberedCode); + throw new RuntimeException("Pythonๅทฅๅ…ทไปฃ็ ่ฏญๆณ•้”™่ฏฏ๏ผŒไปฃ็ ่กŒๆ•ฐ=" + lines.length + "๏ผŒ่ฏทๆŸฅ็œ‹ๆ—ฅๅฟ—่Žทๅ–ๅฎŒๆ•ดไปฃ็ ", e); + } } logger.info("GraalCodeExecutor#injectCodeactTools - reason=CodeactToolๆณจๅ…ฅๅฎŒๆˆ, classCount={}, globalToolCount={}", toolsByClass.size(), globalTools.size()); + + return bridge; + } + + /** + * ๆธ…็†ๆ่ฟฐๅญ—็ฌฆไธฒ๏ผŒไฝฟๅ…ถๅœจ Python ไธ‰ๅผ•ๅทๅญ—็ฌฆไธฒไธญๅฎ‰ๅ…จไฝฟ็”จใ€‚ + * + *

    ไธป่ฆๅค„็†ไปฅไธ‹้—ฎ้ข˜๏ผš + *

      + *
    • ๅฐ†ไธ‰ๅผ•ๅท """ ๆ›ฟๆขไธบ่ฝฌไน‰ๅฝขๅผ
    • + *
    • ็กฎไฟๅๆ–œๆ ๆญฃ็กฎ่ฝฌไน‰
    • + *
    • ็งป้™คๅฏ่ƒฝๅฏผ่‡ด้—ฎ้ข˜็š„ๆŽงๅˆถๅญ—็ฌฆ
    • + *
    + * + * @param description ๅŽŸๅง‹ๆ่ฟฐ + * @return ๆธ…็†ๅŽ็š„ๆ่ฟฐ + */ + private String sanitizeDescriptionForPython(String description) { + if (description == null || description.isEmpty()) { + return ""; + } + + // 1. ๅค„็†ไธ‰ๅผ•ๅท - ๅฐ† """ ๆ›ฟๆขไธบ \"\"\" (ๅœจไธ‰ๅผ•ๅทๅญ—็ฌฆไธฒๅ†…่ฝฌไน‰) + String sanitized = description.replace("\"\"\"", "\\\"\\\"\\\""); + + // 2. ๅค„็†ๅ•็‹ฌ็š„ๅๆ–œๆ ๏ผˆไฝ†ไธๅฝฑๅ“ๅทฒๆœ‰็š„่ฝฌไน‰ๅบๅˆ—๏ผ‰ + // ๅชๆœ‰ๅฝ“ๅๆ–œๆ ๅŽ้ขไธๆ˜ฏๅธธ่ง่ฝฌไน‰ๅญ—็ฌฆๆ—ถๆ‰่ฝฌไน‰ + // ่ฟ™้‡Œไฝฟ็”จ็ฎ€ๅ•็ญ–็•ฅ๏ผšๅ…ˆไธๅค„็†๏ผŒๅ› ไธบไธ‰ๅผ•ๅทๅญ—็ฌฆไธฒไธญๅๆ–œๆ ้—ฎ้ข˜่พƒๅฐ‘ + + // 3. ็งป้™คๅฏ่ƒฝๅฏผ่‡ด้—ฎ้ข˜็š„ๆŽงๅˆถๅญ—็ฌฆ๏ผˆ้™คไบ†ๆข่กŒๅ’Œๅˆถ่กจ็ฌฆ๏ผ‰ + StringBuilder sb = new StringBuilder(); + for (char c : sanitized.toCharArray()) { + if (c == '\n' || c == '\r' || c == '\t' || c >= 32) { + sb.append(c); + } + } + + return sb.toString(); + } + + private String formatDocstring(String description, String indent) { + if (description == null) { + return indent; + } + String normalized = description.replace("\r\n", "\n").replace("\r", "\n"); + String[] lines = normalized.split("\n", -1); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lines.length; i++) { + sb.append(indent).append(lines[i]); + if (i < lines.length - 1) { + sb.append("\n"); + } + } + return sb.toString(); + } + + private boolean isPythonKeyword(String name) { + if (name == null || name.isEmpty()) { + return false; + } + switch (name) { + case "False": + case "None": + case "True": + case "and": + case "as": + case "assert": + case "async": + case "await": + case "break": + case "class": + case "continue": + case "def": + case "del": + case "elif": + case "else": + case "except": + case "finally": + case "for": + case "from": + case "global": + case "if": + case "import": + case "in": + case "is": + case "lambda": + case "nonlocal": + case "not": + case "or": + case "pass": + case "raise": + case "return": + case "try": + case "while": + case "with": + case "yield": + return true; + default: + return false; + } + } + + private boolean isValidPythonIdentifier(String name) { + if (name == null || name.isEmpty()) { + return false; + } + if (isPythonKeyword(name)) { + return false; + } + char first = name.charAt(0); + if (!(Character.isLetter(first) || first == '_')) { + return false; + } + for (int i = 1; i < name.length(); i++) { + char c = name.charAt(i); + if (!(Character.isLetterOrDigit(c) || c == '_')) { + return false; + } + } + return true; + } + + private String toSafePythonIdentifier(String name, String fallbackPrefix) { + if (isValidPythonIdentifier(name)) { + return name; + } + String safeSource = name == null ? "" : name; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < safeSource.length(); i++) { + char c = safeSource.charAt(i); + if (Character.isLetterOrDigit(c) || c == '_') { + sb.append(c); + } else { + sb.append('_'); + } + } + if (sb.length() == 0) { + sb.append('_'); + } + char first = sb.charAt(0); + if (!(Character.isLetter(first) || first == '_')) { + sb.insert(0, '_'); + } + String candidate = sb.toString(); + if (isPythonKeyword(candidate)) { + candidate = fallbackPrefix + Math.abs(safeSource.hashCode()); + } + return candidate; } /** @@ -740,7 +933,9 @@ private void generatePythonMethod(StringBuilder code, boolean isClassMethod) { String toolName = tool.getToolDefinition().name(); - String description = tool.getToolDefinition().description(); + String rawDescription = tool.getToolDefinition().description(); + // ๅค„็†ๆ่ฟฐไธญๅฏ่ƒฝๅฏผ่‡ด Python ่ฏญๆณ•้”™่ฏฏ็š„ๅญ—็ฌฆ + String description = sanitizeDescriptionForPython(rawDescription); // ไผ˜ๅ…ˆไฝฟ็”จ ParameterTree ่Žทๅ–ๅ‚ๆ•ฐไฟกๆฏ ParameterTree parameterTree = tool.getParameterTree(); @@ -750,6 +945,7 @@ private void generatePythonMethod(StringBuilder code, List requiredParams = new ArrayList<>(); List optionalParams = new ArrayList<>(); List allParamNames = new ArrayList<>(); + boolean forceKwargs = false; if (parameterTree != null && parameterTree.hasParameters()) { // ไฝฟ็”จ ParameterTree ็”Ÿๆˆๅ‚ๆ•ฐ็ญพๅ @@ -759,6 +955,9 @@ private void generatePythonMethod(StringBuilder code, for (ParameterNode param : parameterTree.getParameters()) { String paramName = param.getName(); allParamNames.add(paramName); + if (!isValidPythonIdentifier(paramName)) { + forceKwargs = true; + } if (param.isRequired()) { requiredParams.add(paramName); } else { @@ -780,9 +979,28 @@ private void generatePythonMethod(StringBuilder code, parameters = "**kwargs"; } } + if (!parameters.equals("**kwargs")) { + String[] params = parameters.split(","); + for (String param : params) { + String paramName = param.trim().split(":")[0].split("=")[0].trim(); + if (!paramName.isEmpty() && !paramName.equals("self") && !isValidPythonIdentifier(paramName)) { + forceKwargs = true; + break; + } + } + } } } + if (forceKwargs) { + parameters = "**kwargs"; + requiredParams.clear(); + optionalParams.clear(); + allParamNames.clear(); + } + + String pythonFunctionName = toSafePythonIdentifier(functionName, "tool_"); + // Generate method/function String indent = isClassMethod ? " " : ""; @@ -792,8 +1010,10 @@ private void generatePythonMethod(StringBuilder code, } // Function definition - code.append(String.format("%sdef %s(%s):\n", indent, functionName, parameters)); - code.append(String.format("%s \"\"\"%s\"\"\"\n", indent, description != null ? description : toolName)); + code.append(String.format("%sdef %s(%s):\n", indent, pythonFunctionName, parameters)); + String docIndent = indent + " "; + String docBody = formatDocstring(description != null ? description : toolName, docIndent); + code.append(String.format("%s \"\"\"\n%s\n%s \"\"\"\n", indent, docBody, indent)); // Function body - call Java tool through proxy code.append(String.format("%s # Call Java CodeactTool\n", indent)); diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ExecutionRecord.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ExecutionRecord.java index 464bb71..51cbb29 100644 --- a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ExecutionRecord.java +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ExecutionRecord.java @@ -19,6 +19,8 @@ import java.io.Serializable; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.Map; /** @@ -77,8 +79,14 @@ public class ExecutionRecord implements Serializable { */ private Map metadata; + /** + * ๅทฅๅ…ท่ฐƒ็”จ่ฟฝ่ธช่ฎฐๅฝ•๏ผŒ่ฎฐๅฝ•ไปฃ็ ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ่ฐƒ็”จ็š„ๅทฅๅ…ทๅˆ—่กจ + */ + private List callTrace; + public ExecutionRecord() { this.executedAt = LocalDateTime.now(); + this.callTrace = new ArrayList<>(); } public ExecutionRecord(String functionName, Language language) { @@ -161,6 +169,25 @@ public void setMetadata(Map metadata) { this.metadata = metadata; } + public List getCallTrace() { + return callTrace; + } + + public void setCallTrace(List callTrace) { + this.callTrace = callTrace; + } + + /** + * ๆทปๅŠ ไธ€ๆกๅทฅๅ…ท่ฐƒ็”จ่ฎฐๅฝ• + * @param toolName ๅทฅๅ…ทๅ็งฐ + */ + public void addToolCall(String toolName) { + if (this.callTrace == null) { + this.callTrace = new ArrayList<>(); + } + this.callTrace.add(new ToolCallRecord(this.callTrace.size() + 1, toolName)); + } + @Override public String toString() { return "ExecutionRecord{" + @@ -171,6 +198,7 @@ public String toString() { ", errorMessage='" + errorMessage + '\'' + ", executedAt=" + executedAt + ", durationMs=" + durationMs + + ", callTrace=" + callTrace + '}'; } } diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ToolCallRecord.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ToolCallRecord.java new file mode 100644 index 0000000..7914acf --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/model/ToolCallRecord.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.model; + +import java.io.Serializable; + +/** + * ๅทฅๅ…ท่ฐƒ็”จ่ฎฐๅฝ•๏ผŒ็”จไบŽ่ฟฝ่ธชไปฃ็ ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ่ฐƒ็”จ็š„ๅทฅๅ…ทใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class ToolCallRecord implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ่ฐƒ็”จ้กบๅบ๏ผˆไปŽ1ๅผ€ๅง‹๏ผ‰ + */ + private int order; + + /** + * ๅทฅๅ…ทๅ็งฐ๏ผˆๆ ผๅผ๏ผštargetClassName.methodName ๆˆ– ็›ดๆŽฅ toolName๏ผ‰ + */ + private String tool; + + public ToolCallRecord() { + } + + public ToolCallRecord(int order, String tool) { + this.order = order; + this.tool = tool; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public String getTool() { + return tool; + } + + public void setTool(String tool) { + this.tool = tool; + } + + @Override + public String toString() { + return "{\"order\": " + order + ", \"tool\": \"" + tool + "\"}"; + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/BaseAgentObservationLifecycleListener.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/BaseAgentObservationLifecycleListener.java new file mode 100644 index 0000000..eae4af6 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/BaseAgentObservationLifecycleListener.java @@ -0,0 +1,506 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import com.alibaba.assistant.agent.core.observation.context.HookObservationContext; +import com.alibaba.assistant.agent.core.observation.context.InterceptorObservationContext; +import com.alibaba.assistant.agent.core.observation.context.ReactPhaseObservationContext; +import com.alibaba.cloud.ai.graph.GraphLifecycleListener; +import com.alibaba.cloud.ai.graph.RunnableConfig; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * Agent ๅฏ่ง‚ๆต‹ๆ€ง็”Ÿๅ‘ฝๅ‘จๆœŸ็›‘ๅฌๅ™จๆŠฝ่ฑกๅŸบ็ฑป + *

    + * ๆไพ›้€š็”จ็š„ OpenTelemetry Span ๅˆ›ๅปบๅ’Œ็ฎก็†่ƒฝๅŠ›๏ผŒๅŒ…ๆ‹ฌ๏ผš + *

      + *
    • Hook ๆ‰ง่กŒ็š„่ง‚ๆต‹
    • + *
    • Interceptor ๆ‰ง่กŒ็š„่ง‚ๆต‹
    • + *
    • React ้˜ถๆฎต๏ผˆLlmNode/ToolNode๏ผ‰็š„่ง‚ๆต‹
    • + *
    • ่‡ชๅฎšไน‰ๆ•ฐๆฎๆณจๅ†Œๆœบๅˆถ
    • + *
    + *

    + * ๅทฒไปŽ Micrometer Observation ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public abstract class BaseAgentObservationLifecycleListener implements GraphLifecycleListener { + + private static final Logger log = LoggerFactory.getLogger(BaseAgentObservationLifecycleListener.class); + + /** + * ็Šถๆ€ key๏ผšReact ้˜ถๆฎตๅผ€ๅง‹ๆ—ถ้—ด + */ + protected static final String REACT_START_TIME_KEY = "_react_start_time_"; + + /** + * ็Šถๆ€ key๏ผšObservationState + */ + protected static final String OBSERVATION_STATE_KEY = "_observation_state_"; + + protected final Tracer tracer; + + /** + * ๅญ˜ๅ‚จๆฏไธช sessionId ็š„ๆ น Span + */ + protected final ConcurrentHashMap sessionSpans = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช sessionId ็š„ๆ น Span Scope + */ + protected final ConcurrentHashMap sessionScopes = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ Span + */ + protected final ConcurrentHashMap nodeSpans = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ Span Scope + */ + protected final ConcurrentHashMap nodeScopes = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช sessionId ็š„่ฟญไปฃ่ฎกๆ•ฐๅ™จ + */ + protected final ConcurrentHashMap iterationCounters = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ๅผ€ๅง‹ๆ—ถ้—ด + */ + protected final ConcurrentHashMap nodeStartTimes = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช sessionId ็š„ ObservationState + */ + protected final ConcurrentHashMap sessionStates = new ConcurrentHashMap<>(); + + protected BaseAgentObservationLifecycleListener(Tracer tracer) { + this.tracer = tracer; + log.info("BaseAgentObservationLifecycleListener# - reason=ๅˆๅง‹ๅŒ–ๅฎŒๆˆ, hasTracer={}", tracer != null); + } + + // ==================== ObservationState Management ==================== + + /** + * ่Žทๅ–ๆˆ–ๅˆ›ๅปบไผš่ฏ็š„ ObservationState + * + * @param sessionId ไผš่ฏID + * @return ObservationState + */ + public ObservationState getOrCreateObservationState(String sessionId) { + return sessionStates.computeIfAbsent(sessionId, k -> new DefaultObservationState()); + } + + /** + * ่Žทๅ–ไผš่ฏ็š„ ObservationState + * + * @param sessionId ไผš่ฏID + * @return ObservationState๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจ่ฟ”ๅ›žnull + */ + public ObservationState getObservationState(String sessionId) { + return sessionStates.get(sessionId); + } + + /** + * ไปŽ OverAllState ไธญ่Žทๅ–ๆˆ–ๅˆ›ๅปบ ObservationState + * + * @param state OverAllState + * @param config RunnableConfig + * @return ObservationState + */ + protected ObservationState getOrCreateObservationState(Map state, RunnableConfig config) { + String sessionId = extractSessionId(config); + + // ๅ…ˆไปŽ็ผ“ๅญ˜ไธญ่Žทๅ– + ObservationState obsState = sessionStates.get(sessionId); + if (obsState != null) { + return obsState; + } + + // ๅฐ่ฏ•ไปŽ state ไธญ่Žทๅ– + Object stateObj = state.get(OBSERVATION_STATE_KEY); + if (stateObj instanceof ObservationState) { + obsState = (ObservationState) stateObj; + sessionStates.put(sessionId, obsState); + return obsState; + } + + // ๅˆ›ๅปบๆ–ฐ็š„ๅนถๅญ˜ๅ…ฅ state + obsState = new DefaultObservationState(); + sessionStates.put(sessionId, obsState); + state.put(OBSERVATION_STATE_KEY, obsState); + return obsState; + } + + // ==================== Hook Span ==================== + + /** + * ๅˆ›ๅปบ Hook ็š„ Span + * + * @param context Hook่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + * @return Span + */ + protected Span createHookSpan(HookObservationContext context) { + if (tracer == null) { + return null; + } + + String spanName = CodeactObservationDocumentation.SPAN_HOOK + "." + + (context.getHookName() != null ? context.getHookName().toLowerCase() : "unknown"); + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.CONVERSATION_ID, + context.getSessionId() != null ? context.getSessionId() : "unknown") + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.SPAN_KIND_NAME, "CHAIN") + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.OPERATION_NAME, "chain") + .setAttribute(CodeactObservationDocumentation.HookAttributes.NAME, + context.getHookName() != null ? context.getHookName() : "unknown") + .setAttribute(CodeactObservationDocumentation.HookAttributes.POSITION, + context.getHookPosition() != null ? context.getHookPosition() : "unknown") + .startSpan(); + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + context.getAllCustomData().forEach((key, value) -> { + if (value != null) { + span.setAttribute("codeact.hook.custom." + key, truncate(value.toString(), 500)); + } + }); + + return span; + } + + // ==================== Interceptor Span ==================== + + /** + * ๅˆ›ๅปบ Interceptor ็š„ Span + * + * @param context Interceptor่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + * @return Span + */ + protected Span createInterceptorSpan(InterceptorObservationContext context) { + if (tracer == null) { + return null; + } + + String typeName = context.getInterceptorType() != null + ? context.getInterceptorType().name().toLowerCase() + : "unknown"; + String spanName = CodeactObservationDocumentation.SPAN_INTERCEPTOR + "." + typeName + "." + + (context.getInterceptorName() != null ? context.getInterceptorName().toLowerCase() : "unknown"); + + SpanKind spanKind = context.getInterceptorType() == InterceptorObservationContext.InterceptorType.MODEL + ? SpanKind.CLIENT + : SpanKind.INTERNAL; + + String genAiSpanKind = context.getInterceptorType() == InterceptorObservationContext.InterceptorType.MODEL + ? "LLM" + : (context.getInterceptorType() == InterceptorObservationContext.InterceptorType.TOOL + ? "TOOL" + : "CHAIN"); + + String operationName = genAiSpanKind.equals("LLM") ? "chat" + : (genAiSpanKind.equals("TOOL") ? "execute_tool" : "chain"); + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(spanKind) + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.CONVERSATION_ID, + context.getSessionId() != null ? context.getSessionId() : "unknown") + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.SPAN_KIND_NAME, genAiSpanKind) + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.OPERATION_NAME, operationName) + .setAttribute(CodeactObservationDocumentation.InterceptorAttributes.NAME, + context.getInterceptorName() != null ? context.getInterceptorName() : "unknown") + .setAttribute(CodeactObservationDocumentation.InterceptorAttributes.TYPE, typeName) + .startSpan(); + + // Model Interceptor specific attributes + if (context.getModelName() != null) { + span.setAttribute(CodeactObservationDocumentation.InterceptorAttributes.MODEL_NAME, context.getModelName()); + } + + // Tool Interceptor specific attributes + if (context.getToolName() != null) { + span.setAttribute(CodeactObservationDocumentation.InterceptorAttributes.TOOL_NAME, context.getToolName()); + } + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + context.getAllCustomData().forEach((key, value) -> { + if (value != null) { + span.setAttribute("codeact.interceptor.custom." + key, truncate(value.toString(), 500)); + } + }); + + return span; + } + + // ==================== React Phase Span ==================== + + /** + * ๅˆ›ๅปบ React ้˜ถๆฎต็š„ Span + * + * @param context React้˜ถๆฎต่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + * @return Span + */ + protected Span createReactPhaseSpan(ReactPhaseObservationContext context) { + if (tracer == null) { + return null; + } + + String nodeTypeName = context.getNodeType() != null + ? context.getNodeType().name().toLowerCase() + : "unknown"; + String spanName = CodeactObservationDocumentation.SPAN_REACT + "." + nodeTypeName; + + SpanKind spanKind = context.getNodeType() == ReactPhaseObservationContext.NodeType.LLM + ? SpanKind.CLIENT + : SpanKind.INTERNAL; + + String genAiSpanKind = context.getNodeType() == ReactPhaseObservationContext.NodeType.LLM + ? "LLM" + : (context.getNodeType() == ReactPhaseObservationContext.NodeType.TOOL + ? "TOOL" + : "CHAIN"); + + String operationName = genAiSpanKind.equals("LLM") ? "chat" + : (genAiSpanKind.equals("TOOL") ? "execute_tool" : "chain"); + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(spanKind) + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.CONVERSATION_ID, + context.getSessionId() != null ? context.getSessionId() : "unknown") + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.SPAN_KIND_NAME, genAiSpanKind) + .setAttribute(CodeactObservationDocumentation.GenAIAttributes.OPERATION_NAME, operationName) + .setAttribute(CodeactObservationDocumentation.ReactPhaseAttributes.NODE_TYPE, nodeTypeName) + .startSpan(); + + // Add model name for LLM nodes + if (context.getModelName() != null) { + span.setAttribute(CodeactObservationDocumentation.ReactPhaseAttributes.MODEL_NAME, context.getModelName()); + } + + // Add iteration info + if (context.getIteration() > 0) { + span.setAttribute(CodeactObservationDocumentation.ReactPhaseAttributes.ITERATION, (long) context.getIteration()); + } + + // Add node ID + if (context.getNodeId() != null) { + span.setAttribute(CodeactObservationDocumentation.ReactPhaseAttributes.NODE_ID, context.getNodeId()); + } + + // ==================== LLM Node ็‰นๅฎšๅฑžๆ€ง ==================== + if (context.getNodeType() == ReactPhaseObservationContext.NodeType.LLM) { + // ่ฎฐๅฝ• prompt ๆถˆๆฏๆ•ฐ้‡ + if (context.getPromptMessageCount() > 0) { + span.setAttribute("alibaba.llm.prompt_message_count", (long) context.getPromptMessageCount()); + } + + // ่ฎฐๅฝ•ๅฏ็”จๅทฅๅ…ทๅˆ—่กจ๏ผˆๅช่ฎฐๅฝ•ๅทฅๅ…ทๅ๏ผ‰ + if (context.getAvailableToolNames() != null && !context.getAvailableToolNames().isEmpty()) { + span.setAttribute("alibaba.llm.available_tools", String.join(",", context.getAvailableToolNames())); + span.setAttribute("alibaba.llm.available_tools_count", (long) context.getAvailableToolNames().size()); + } + + // ่ฎฐๅฝ•่พ“ๅ…ฅๆ‘˜่ฆ + if (context.getInputSummary() != null) { + span.setAttribute("alibaba.llm.input_summary", truncate(context.getInputSummary(), 1000)); + } + + // ่ฎฐๅฝ•่พ“ๅ‡บๆ‘˜่ฆ + if (context.getOutputSummary() != null) { + span.setAttribute("alibaba.llm.output_summary", truncate(context.getOutputSummary(), 1000)); + } + + // ่ฎฐๅฝ• token ไฝฟ็”จ้‡ + if (context.getInputTokens() > 0) { + span.setAttribute("alibaba.llm.input_tokens", (long) context.getInputTokens()); + } + if (context.getOutputTokens() > 0) { + span.setAttribute("alibaba.llm.output_tokens", (long) context.getOutputTokens()); + } + + // ่ฎฐๅฝ•ๅฎŒๆˆๅŽŸๅ›  + if (context.getFinishReason() != null) { + span.setAttribute("alibaba.llm.finish_reason", context.getFinishReason()); + } + } + + // ==================== Tool Node ็‰นๅฎšๅฑžๆ€ง ==================== + if (context.getNodeType() == ReactPhaseObservationContext.NodeType.TOOL) { + List toolCalls = context.getToolCalls(); + if (toolCalls != null && !toolCalls.isEmpty()) { + // ่ฎฐๅฝ•ๅทฅๅ…ท่ฐƒ็”จๆ•ฐ้‡ + span.setAttribute("alibaba.tool.call_count", (long) toolCalls.size()); + + // ่ฎฐๅฝ•ๆ‰€ๆœ‰่ฐƒ็”จ็š„ๅทฅๅ…ทๅ + String toolNames = toolCalls.stream() + .map(ReactPhaseObservationContext.ToolCallInfo::getToolName) + .filter(name -> name != null) + .collect(Collectors.joining(",")); + span.setAttribute("alibaba.tool.names", toolNames); + + // ่ฎฐๅฝ•ๅทฅๅ…ท่ฐƒ็”จๆ€ปๆ—ถ้•ฟ + long totalDuration = toolCalls.stream() + .mapToLong(ReactPhaseObservationContext.ToolCallInfo::getDurationMs) + .sum(); + span.setAttribute("alibaba.tool.total_duration_ms", totalDuration); + + // ่ฎฐๅฝ•ๅทฅๅ…ท่ฐƒ็”จ็ป“ๆžœๆ€ป้•ฟๅบฆ + int totalResultLength = toolCalls.stream() + .mapToInt(ReactPhaseObservationContext.ToolCallInfo::getResultLength) + .sum(); + span.setAttribute("alibaba.tool.total_result_length", (long) totalResultLength); + + // ่ฎฐๅฝ•ๆฏไธชๅทฅๅ…ท่ฐƒ็”จ็š„่ฏฆ็ป†ไฟกๆฏ + for (int i = 0; i < toolCalls.size() && i < 10; i++) { // ๆœ€ๅคš่ฎฐๅฝ•10ไธชๅทฅๅ…ท่ฐƒ็”จ + ReactPhaseObservationContext.ToolCallInfo toolCall = toolCalls.get(i); + String prefix = "alibaba.tool." + i + "."; + span.setAttribute(prefix + "name", + toolCall.getToolName() != null ? toolCall.getToolName() : "unknown"); + span.setAttribute(prefix + "duration_ms", toolCall.getDurationMs()); + span.setAttribute(prefix + "success", toolCall.isSuccess()); + if (toolCall.getArguments() != null) { + span.setAttribute(prefix + "arguments", truncate(toolCall.getArguments(), 500)); + } + if (toolCall.getResult() != null) { + span.setAttribute(prefix + "result", truncate(toolCall.getResult(), 500)); + } + if (!toolCall.isSuccess() && toolCall.getErrorMessage() != null) { + span.setAttribute(prefix + "error", truncate(toolCall.getErrorMessage(), 200)); + } + } + } + } + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + context.getAllCustomData().forEach((key, value) -> { + if (value != null) { + span.setAttribute("codeact.react.custom." + key, truncate(value.toString(), 500)); + } + }); + + return span; + } + + // ==================== Helper Methods ==================== + + /** + * ไปŽ RunnableConfig ไธญๆๅ– sessionId + */ + protected String extractSessionId(RunnableConfig config) { + if (config == null) { + return "unknown"; + } + return config.threadId().orElse("unknown"); + } + + /** + * ไปŽ RunnableConfig ็š„ metadata ไธญๆๅ–ๆŒ‡ๅฎšๅญ—ๆฎต + */ + protected String extractMetadata(RunnableConfig config, String key) { + if (config == null) { + return null; + } + return config.metadata(key) + .map(Object::toString) + .orElse(null); + } + + /** + * ๆˆชๆ–ญๅญ—็ฌฆไธฒ + */ + protected String truncate(String str, int maxLength) { + if (str == null) { + return "null"; + } + if (str.length() <= maxLength) { + return str; + } + return str.substring(0, maxLength) + "...[truncated]"; + } + + /** + * ๅœๆญข่Š‚็‚น Span ๅนถ่ฎฐๅฝ•ๆ—ถ้•ฟ + * + * @param nodeKey ่Š‚็‚นKey + * @param durationMs ๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ + * @param success ๆ˜ฏๅฆๆˆๅŠŸ + * @param error ้”™่ฏฏไฟกๆฏ๏ผˆๅฏ้€‰๏ผ‰ + */ + protected void stopNodeSpan(String nodeKey, long durationMs, boolean success, Throwable error) { + // ๅ…ˆๅ…ณ้—ญ Scope + Scope scope = nodeScopes.remove(nodeKey); + if (scope != null) { + scope.close(); + } + + // ๅ†็ป“ๆŸ Span + Span span = nodeSpans.remove(nodeKey); + if (span != null) { + span.setAttribute("duration.ms", durationMs); + span.setAttribute("success", success); + + if (error != null) { + span.setStatus(StatusCode.ERROR, error.getMessage()); + span.recordException(error); + } + + span.end(); + } + } + + /** + * ๆธ…็†ไผš่ฏ่ต„ๆบ + * + * @param sessionId ไผš่ฏID + */ + protected void cleanupSession(String sessionId) { + // ๅ…ณ้—ญไผš่ฏ Scope + Scope sessionScope = sessionScopes.remove(sessionId); + if (sessionScope != null) { + sessionScope.close(); + } + + // ็ป“ๆŸไผš่ฏ Span + Span sessionSpan = sessionSpans.remove(sessionId); + if (sessionSpan != null) { + sessionSpan.end(); + } + + sessionStates.remove(sessionId); + iterationCounters.remove(sessionId); + + // ๆธ…็†่ฏฅไผš่ฏ็š„ๆ‰€ๆœ‰่Š‚็‚น Span + nodeSpans.keySet().removeIf(key -> key.startsWith(sessionId + ":")); + nodeScopes.keySet().removeIf(key -> key.startsWith(sessionId + ":")); + nodeStartTimes.keySet().removeIf(key -> key.startsWith(sessionId + ":")); + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/CodeactObservationDocumentation.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/CodeactObservationDocumentation.java new file mode 100644 index 0000000..e02df8e --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/CodeactObservationDocumentation.java @@ -0,0 +1,248 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import io.opentelemetry.api.common.AttributeKey; + +/** + * CodeactAgent ๅฏ่ง‚ๆต‹ๆ€งๆ–‡ๆกฃๅฎšไน‰ + *

    + * ๅฎšไน‰้€š็”จ็š„ใ€ๆ ‡ๅ‡†ๅŒ–็š„่ง‚ๆต‹ๆŒ‡ๆ ‡๏ผŒไพ›ๆ‰€ๆœ‰ไฝฟ็”จ CodeactAgent ็š„้กน็›ฎๅค็”จใ€‚ + * ไฝฟ็”จ OpenTelemetry AttributeKey ๅฎšไน‰ๆ ‡ๅ‡†ๅฑžๆ€งใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public final class CodeactObservationDocumentation { + + private CodeactObservationDocumentation() { + // Utility class + } + + // ==================== Span Names ==================== + + public static final String SPAN_HOOK = "codeact.hook"; + public static final String SPAN_INTERCEPTOR = "codeact.interceptor"; + public static final String SPAN_REACT = "codeact.react"; + public static final String SPAN_EXECUTION = "codeact.execution"; + public static final String SPAN_CODE_GENERATION = "codeact.codegen"; + public static final String SPAN_TOOL_CALL = "codeact.tool.call"; + + // ==================== Hook Attribute Keys ==================== + + /** + * Hook ่ง‚ๆต‹ๅฑžๆ€ง้”ฎ + */ + public static final class HookAttributes { + /** Hookๅ็งฐ */ + public static final AttributeKey NAME = AttributeKey.stringKey("codeact.hook.name"); + /** Hookไฝ็ฝฎ๏ผˆBEFORE_AGENT, AFTER_AGENT, BEFORE_MODEL, AFTER_MODEL๏ผ‰ */ + public static final AttributeKey POSITION = AttributeKey.stringKey("codeact.hook.position"); + /** ๆ˜ฏๅฆๆˆๅŠŸ */ + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.hook.success"); + /** Agentๅ็งฐ */ + public static final AttributeKey AGENT_NAME = AttributeKey.stringKey("codeact.hook.agent_name"); + /** ไผš่ฏID */ + public static final AttributeKey SESSION_ID = AttributeKey.stringKey("codeact.hook.session_id"); + /** ๆ‰ง่กŒๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ */ + public static final AttributeKey DURATION_MS = AttributeKey.longKey("codeact.hook.duration_ms"); + /** ้”™่ฏฏ็ฑปๅž‹ */ + public static final AttributeKey ERROR_TYPE = AttributeKey.stringKey("codeact.hook.error_type"); + /** ้”™่ฏฏไฟกๆฏ */ + public static final AttributeKey ERROR_MESSAGE = AttributeKey.stringKey("codeact.hook.error_message"); + + private HookAttributes() {} + } + + // ==================== Interceptor Attribute Keys ==================== + + /** + * Interceptor ่ง‚ๆต‹ๅฑžๆ€ง้”ฎ + */ + public static final class InterceptorAttributes { + /** Interceptorๅ็งฐ */ + public static final AttributeKey NAME = AttributeKey.stringKey("codeact.interceptor.name"); + /** Interceptor็ฑปๅž‹๏ผˆMODEL, TOOL๏ผ‰ */ + public static final AttributeKey TYPE = AttributeKey.stringKey("codeact.interceptor.type"); + /** ๆ˜ฏๅฆๆˆๅŠŸ */ + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.interceptor.success"); + /** Agentๅ็งฐ */ + public static final AttributeKey AGENT_NAME = AttributeKey.stringKey("codeact.interceptor.agent_name"); + /** ไผš่ฏID */ + public static final AttributeKey SESSION_ID = AttributeKey.stringKey("codeact.interceptor.session_id"); + /** ๆ‰ง่กŒๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ */ + public static final AttributeKey DURATION_MS = AttributeKey.longKey("codeact.interceptor.duration_ms"); + /** ๆจกๅž‹ๅ็งฐ๏ผˆไป…ModelInterceptor๏ผ‰ */ + public static final AttributeKey MODEL_NAME = AttributeKey.stringKey("codeact.interceptor.model_name"); + /** ๅทฅๅ…ทๅ็งฐ๏ผˆไป…ToolInterceptor๏ผ‰ */ + public static final AttributeKey TOOL_NAME = AttributeKey.stringKey("codeact.interceptor.tool_name"); + /** ๅทฅๅ…ทๅ‚ๆ•ฐ้•ฟๅบฆ๏ผˆไป…ToolInterceptor๏ผ‰ */ + public static final AttributeKey TOOL_ARGUMENTS_LENGTH = AttributeKey.longKey("codeact.interceptor.tool_arguments_length"); + /** ๅทฅๅ…ท็ป“ๆžœ้•ฟๅบฆ๏ผˆไป…ToolInterceptor๏ผ‰ */ + public static final AttributeKey TOOL_RESULT_LENGTH = AttributeKey.longKey("codeact.interceptor.tool_result_length"); + /** ่พ“ๅ…ฅTokenๆ•ฐ๏ผˆไป…ModelInterceptor๏ผ‰ */ + public static final AttributeKey INPUT_TOKENS = AttributeKey.longKey("codeact.interceptor.input_tokens"); + /** ่พ“ๅ‡บTokenๆ•ฐ๏ผˆไป…ModelInterceptor๏ผ‰ */ + public static final AttributeKey OUTPUT_TOKENS = AttributeKey.longKey("codeact.interceptor.output_tokens"); + + private InterceptorAttributes() {} + } + + // ==================== React Phase Attribute Keys ==================== + + /** + * React้˜ถๆฎต ่ง‚ๆต‹ๅฑžๆ€ง้”ฎ + */ + public static final class ReactPhaseAttributes { + /** ่Š‚็‚น็ฑปๅž‹๏ผˆLLM, TOOL๏ผ‰ */ + public static final AttributeKey NODE_TYPE = AttributeKey.stringKey("codeact.react.node_type"); + /** ๆ˜ฏๅฆๆˆๅŠŸ */ + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.react.success"); + /** ๆจกๅž‹ๅ็งฐ */ + public static final AttributeKey MODEL_NAME = AttributeKey.stringKey("codeact.react.model_name"); + /** ไผš่ฏID */ + public static final AttributeKey SESSION_ID = AttributeKey.stringKey("codeact.react.session_id"); + /** Agentๅ็งฐ */ + public static final AttributeKey AGENT_NAME = AttributeKey.stringKey("codeact.react.agent_name"); + /** ่Š‚็‚นID */ + public static final AttributeKey NODE_ID = AttributeKey.stringKey("codeact.react.node_id"); + /** ่ฟญไปฃ่ฝฎๆฌก */ + public static final AttributeKey ITERATION = AttributeKey.longKey("codeact.react.iteration"); + /** ๆ‰ง่กŒๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ */ + public static final AttributeKey DURATION_MS = AttributeKey.longKey("codeact.react.duration_ms"); + /** ่พ“ๅ…ฅTokenๆ•ฐ */ + public static final AttributeKey INPUT_TOKENS = AttributeKey.longKey("codeact.react.input_tokens"); + /** ่พ“ๅ‡บTokenๆ•ฐ */ + public static final AttributeKey OUTPUT_TOKENS = AttributeKey.longKey("codeact.react.output_tokens"); + /** ๆ็คบๆถˆๆฏๆ•ฐ */ + public static final AttributeKey PROMPT_MESSAGE_COUNT = AttributeKey.longKey("codeact.react.prompt_message_count"); + /** ๅฎŒๆˆๅŽŸๅ›  */ + public static final AttributeKey FINISH_REASON = AttributeKey.stringKey("codeact.react.finish_reason"); + /** ๅทฅๅ…ท่ฐƒ็”จๆ•ฐ */ + public static final AttributeKey TOOL_CALLS_COUNT = AttributeKey.longKey("codeact.react.tool_calls_count"); + /** ๅทฅๅ…ทๅ็งฐๅˆ—่กจ๏ผˆ้€—ๅทๅˆ†้š”๏ผ‰ */ + public static final AttributeKey TOOL_NAMES = AttributeKey.stringKey("codeact.react.tool_names"); + + private ReactPhaseAttributes() {} + } + + // ==================== Codeact Execution Attribute Keys ==================== + + /** + * ไปฃ็ ๆ‰ง่กŒ ่ง‚ๆต‹ๅฑžๆ€ง้”ฎ + */ + public static final class ExecutionAttributes { + /** ็ผ–็จ‹่ฏญ่จ€ */ + public static final AttributeKey LANGUAGE = AttributeKey.stringKey("codeact.language"); + /** ๆ˜ฏๅฆๆˆๅŠŸ */ + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.success"); + /** ๅ‡ฝๆ•ฐๅ */ + public static final AttributeKey FUNCTION_NAME = AttributeKey.stringKey("codeact.function.name"); + /** ๅ‚ๆ•ฐ้•ฟๅบฆ */ + public static final AttributeKey ARGUMENTS_LENGTH = AttributeKey.longKey("codeact.arguments.length"); + /** ็ป“ๆžœ้•ฟๅบฆ */ + public static final AttributeKey RESULT_LENGTH = AttributeKey.longKey("codeact.result.length"); + /** ๆ‰ง่กŒๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ */ + public static final AttributeKey DURATION_MS = AttributeKey.longKey("codeact.duration.ms"); + /** ้”™่ฏฏ็ฑปๅž‹ */ + public static final AttributeKey ERROR_TYPE = AttributeKey.stringKey("codeact.error.type"); + /** ้”™่ฏฏไฟกๆฏ */ + public static final AttributeKey ERROR_MESSAGE = AttributeKey.stringKey("codeact.error.message"); + + private ExecutionAttributes() {} + } + + // ==================== Code Generation Attribute Keys ==================== + + /** + * ไปฃ็ ็”Ÿๆˆ ่ง‚ๆต‹ๅฑžๆ€ง้”ฎ + */ + public static final class CodeGenerationAttributes { + /** ็ผ–็จ‹่ฏญ่จ€ */ + public static final AttributeKey LANGUAGE = AttributeKey.stringKey("codeact.codegen.language"); + /** ๆจกๅž‹ๅ็งฐ */ + public static final AttributeKey MODEL_NAME = AttributeKey.stringKey("codeact.codegen.model"); + /** ๆ˜ฏๅฆๆˆๅŠŸ */ + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.codegen.success"); + /** ๅ‡ฝๆ•ฐๅ */ + public static final AttributeKey FUNCTION_NAME = AttributeKey.stringKey("codeact.codegen.function.name"); + /** ไปฃ็ ่กŒๆ•ฐ */ + public static final AttributeKey CODE_LINES = AttributeKey.longKey("codeact.codegen.code.lines"); + /** ่พ“ๅ…ฅ Token */ + public static final AttributeKey INPUT_TOKENS = AttributeKey.longKey("codeact.codegen.input.tokens"); + /** ่พ“ๅ‡บ Token */ + public static final AttributeKey OUTPUT_TOKENS = AttributeKey.longKey("codeact.codegen.output.tokens"); + /** ๆ‰ง่กŒๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ */ + public static final AttributeKey DURATION_MS = AttributeKey.longKey("codeact.codegen.duration.ms"); + + private CodeGenerationAttributes() {} + } + + // ==================== Codeact Tool Call Attribute Keys ==================== + + /** + * Codeactๅทฅๅ…ท่ฐƒ็”จ ่ง‚ๆต‹ๅฑžๆ€ง้”ฎ + */ + public static final class ToolCallAttributes { + /** ๅทฅๅ…ทๅ็งฐ */ + public static final AttributeKey TOOL_NAME = AttributeKey.stringKey("codeact.tool.name"); + /** ๆ˜ฏๅฆๆˆๅŠŸ */ + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.tool.success"); + /** ๅ‚ๆ•ฐ๏ผˆJSONๆ ผๅผ๏ผ‰ */ + public static final AttributeKey ARGUMENTS = AttributeKey.stringKey("codeact.tool.arguments"); + /** ๅ‚ๆ•ฐ้•ฟๅบฆ */ + public static final AttributeKey ARGUMENTS_LENGTH = AttributeKey.longKey("codeact.tool.arguments.length"); + /** ็ป“ๆžœ้•ฟๅบฆ */ + public static final AttributeKey RESULT_LENGTH = AttributeKey.longKey("codeact.tool.result.length"); + /** ๆ‰ง่กŒๆ—ถ้•ฟ๏ผˆๆฏซ็ง’๏ผ‰ */ + public static final AttributeKey DURATION_MS = AttributeKey.longKey("codeact.tool.duration.ms"); + /** ้”™่ฏฏ็ฑปๅž‹ */ + public static final AttributeKey ERROR_TYPE = AttributeKey.stringKey("codeact.tool.error.type"); + + private ToolCallAttributes() {} + } + + // ==================== GenAI Semantic Convention Attribute Keys ==================== + + /** + * GenAI ่ฏญไน‰็บฆๅฎšๅฑžๆ€ง้”ฎ + */ + public static final class GenAIAttributes { + /** ไผš่ฏID */ + public static final AttributeKey CONVERSATION_ID = AttributeKey.stringKey("gen_ai.conversation.id"); + /** Span ็ฑปๅž‹๏ผˆLLM, TOOL, CHAIN, EVALUATOR๏ผ‰ */ + public static final AttributeKey SPAN_KIND_NAME = AttributeKey.stringKey("gen_ai.span_kind_name"); + /** ๆ“ไฝœๅ็งฐ๏ผˆchat, execute_tool, chain, evaluate๏ผ‰ */ + public static final AttributeKey OPERATION_NAME = AttributeKey.stringKey("gen_ai.operation.name"); + /** Agent ๅ็งฐ */ + public static final AttributeKey AGENT_NAME = AttributeKey.stringKey("gen_ai.agent.name"); + /** ๅ“ๅบ”ๆจกๅž‹ */ + public static final AttributeKey RESPONSE_MODEL = AttributeKey.stringKey("gen_ai.response.model"); + /** ๅทฅๅ…ทๅ็งฐ */ + public static final AttributeKey TOOL_NAME = AttributeKey.stringKey("gen_ai.tool.name"); + /** ่พ“ๅ…ฅ Token ไฝฟ็”จ้‡ */ + public static final AttributeKey INPUT_TOKENS = AttributeKey.longKey("gen_ai.usage.input_tokens"); + /** ่พ“ๅ‡บ Token ไฝฟ็”จ้‡ */ + public static final AttributeKey OUTPUT_TOKENS = AttributeKey.longKey("gen_ai.usage.output_tokens"); + /** ่พ“ๅ…ฅๆถˆๆฏ */ + public static final AttributeKey INPUT_MESSAGES = AttributeKey.stringKey("gen_ai.input.messages"); + /** ่พ“ๅ‡บๆถˆๆฏ */ + public static final AttributeKey OUTPUT_MESSAGES = AttributeKey.stringKey("gen_ai.output.messages"); + + private GenAIAttributes() {} + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/DefaultObservationState.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/DefaultObservationState.java new file mode 100644 index 0000000..7e64a46 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/DefaultObservationState.java @@ -0,0 +1,112 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ้ป˜่ฎค็š„่ง‚ๆต‹็Šถๆ€ๅญ˜ๅ‚จๅฎž็Žฐ + *

    + * ไฝฟ็”จ ConcurrentHashMap ๅญ˜ๅ‚จ่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒไฟ่ฏ็บฟ็จ‹ๅฎ‰ๅ…จใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class DefaultObservationState implements ObservationState { + + private final ConcurrentHashMap data = new ConcurrentHashMap<>(); + + public DefaultObservationState() { + } + + /** + * ไปŽ็Žฐๆœ‰Mapๅˆ›ๅปบObservationState + * + * @param initialData ๅˆๅง‹ๆ•ฐๆฎ + */ + public DefaultObservationState(Map initialData) { + if (initialData != null) { + data.putAll(initialData); + } + } + + @Override + public void put(String key, Object value) { + if (key != null && value != null) { + data.put(key, value); + } + } + + @Override + public void putAll(Map dataMap) { + if (dataMap != null) { + dataMap.forEach((key, value) -> { + if (key != null && value != null) { + data.put(key, value); + } + }); + } + } + + @Override + @SuppressWarnings("unchecked") + public T get(String key) { + return (T) data.get(key); + } + + @Override + @SuppressWarnings("unchecked") + public T getOrDefault(String key, T defaultValue) { + Object value = data.get(key); + return value != null ? (T) value : defaultValue; + } + + @Override + public boolean contains(String key) { + return data.containsKey(key); + } + + @Override + public Object remove(String key) { + return data.remove(key); + } + + @Override + public Map getAll() { + return Collections.unmodifiableMap(data); + } + + @Override + public void clear() { + data.clear(); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public String toString() { + return "DefaultObservationState{" + + "size=" + data.size() + + ", keys=" + data.keySet() + + '}'; + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/HookObservationHelper.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/HookObservationHelper.java new file mode 100644 index 0000000..ed807d1 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/HookObservationHelper.java @@ -0,0 +1,303 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import com.alibaba.assistant.agent.core.observation.context.HookObservationContext; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.function.Supplier; + +/** + * Hook ่ง‚ๆต‹่พ…ๅŠฉๅทฅๅ…ท็ฑป + *

    + * ๆไพ›ไพฟๆท็š„ๆ–นๆณ•ๅœจ Hook ไธญๅˆ›ๅปบๅ’Œ็ฎก็† OpenTelemetry Spanใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public final class HookObservationHelper { + + private static final Logger log = LoggerFactory.getLogger(HookObservationHelper.class); + + private HookObservationHelper() { + // Utility class + } + + /** + * ไธบ Hook ๆ‰ง่กŒๅˆ›ๅปบ่ง‚ๆต‹ๅนถๆ‰ง่กŒๆ“ไฝœ + * + * @param tracer OpenTelemetry Tracer + * @param hookName Hookๅ็งฐ + * @param hookPosition Hookไฝ็ฝฎ๏ผˆbefore/after๏ผ‰ + * @param sessionId ไผš่ฏID + * @param action ่ฆๆ‰ง่กŒ็š„ๆ“ไฝœ + * @param ่ฟ”ๅ›ž็ฑปๅž‹ + * @return ๆ“ไฝœ็ป“ๆžœ + */ + public static T observeHook( + Tracer tracer, + String hookName, + String hookPosition, + String sessionId, + Supplier action) { + + return observeHook(tracer, hookName, hookPosition, sessionId, null, action); + } + + /** + * ไธบ Hook ๆ‰ง่กŒๅˆ›ๅปบ่ง‚ๆต‹ๅนถๆ‰ง่กŒๆ“ไฝœ๏ผˆๅธฆ่‡ชๅฎšไน‰ๆ•ฐๆฎ๏ผ‰ + * + * @param tracer OpenTelemetry Tracer + * @param hookName Hookๅ็งฐ + * @param hookPosition Hookไฝ็ฝฎ๏ผˆbefore/after๏ผ‰ + * @param sessionId ไผš่ฏID + * @param customData ่‡ชๅฎšไน‰ๆ•ฐๆฎ + * @param action ่ฆๆ‰ง่กŒ็š„ๆ“ไฝœ + * @param ่ฟ”ๅ›ž็ฑปๅž‹ + * @return ๆ“ไฝœ็ป“ๆžœ + */ + public static T observeHook( + Tracer tracer, + String hookName, + String hookPosition, + String sessionId, + Map customData, + Supplier action) { + + if (tracer == null) { + return action.get(); + } + + HookObservationContext context = new HookObservationContext(hookName, hookPosition); + context.setSessionId(sessionId); + if (customData != null) { + context.putAllCustomData(customData); + } + + long startTime = System.currentTimeMillis(); + String spanName = "codeact.hook." + (hookName != null ? hookName.toLowerCase() : "unknown"); + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAttribute("gen_ai.conversation.id", sessionId != null ? sessionId : "unknown") + .setAttribute("gen_ai.span_kind_name", "CHAIN") + .setAttribute("gen_ai.operation.name", "chain") + .setAttribute("codeact.hook.name", hookName != null ? hookName : "unknown") + .setAttribute("codeact.hook.position", hookPosition != null ? hookPosition : "unknown") + .startSpan(); + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎๅˆฐ Span + if (customData != null) { + customData.forEach((key, value) -> { + if (value != null) { + span.setAttribute("codeact.hook.custom." + key, truncate(value.toString(), 200)); + } + }); + } + + try (Scope ignored = span.makeCurrent()) { + T result = action.get(); + + long durationMs = System.currentTimeMillis() - startTime; + context.setDurationMs(durationMs); + context.setSuccess(true); + + span.setAttribute("duration.ms", durationMs); + + log.debug("HookObservationHelper#observeHook - reason=Hookๆ‰ง่กŒๆˆๅŠŸ, " + + "hookName={}, hookPosition={}, durationMs={}", hookName, hookPosition, durationMs); + + return result; + + } catch (Exception e) { + long durationMs = System.currentTimeMillis() - startTime; + context.setDurationMs(durationMs); + context.setSuccess(false); + context.setErrorType(e.getClass().getSimpleName()); + context.setErrorMessage(e.getMessage()); + + span.setAttribute("duration.ms", durationMs); + span.setStatus(StatusCode.ERROR, e.getMessage()); + span.recordException(e); + + log.warn("HookObservationHelper#observeHook - reason=Hookๆ‰ง่กŒๅคฑ่ดฅ, " + + "hookName={}, hookPosition={}, durationMs={}, errorType={}", + hookName, hookPosition, durationMs, e.getClass().getSimpleName()); + + throw e; + + } finally { + span.end(); + } + } + + /** + * ไธบๅผ‚ๆญฅ Hook ๆ‰ง่กŒๅˆ›ๅปบ่ง‚ๆต‹ไธŠไธ‹ๆ–‡๏ผˆ็”จไบŽๆ‰‹ๅŠจ็ฎก็† Span ็”Ÿๅ‘ฝๅ‘จๆœŸ๏ผ‰ + * + * @param tracer OpenTelemetry Tracer + * @param hookName Hookๅ็งฐ + * @param hookPosition Hookไฝ็ฝฎ + * @param sessionId ไผš่ฏID + * @return Span ๅ’Œ Context ็š„ๅŒ…่ฃ…ๅฏน่ฑก + */ + public static HookObservationScope startHookObservation( + Tracer tracer, + String hookName, + String hookPosition, + String sessionId) { + + if (tracer == null) { + return new HookObservationScope(null, null, null); + } + + HookObservationContext context = new HookObservationContext(hookName, hookPosition); + context.setSessionId(sessionId); + + String spanName = "codeact.hook." + (hookName != null ? hookName.toLowerCase() : "unknown"); + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAttribute("gen_ai.conversation.id", sessionId != null ? sessionId : "unknown") + .setAttribute("gen_ai.span_kind_name", "CHAIN") + .setAttribute("gen_ai.operation.name", "chain") + .setAttribute("codeact.hook.name", hookName != null ? hookName : "unknown") + .setAttribute("codeact.hook.position", hookPosition != null ? hookPosition : "unknown") + .startSpan(); + + Scope scope = span.makeCurrent(); + + return new HookObservationScope(span, scope, context); + } + + /** + * ๆˆชๆ–ญๅญ—็ฌฆไธฒ + */ + private static String truncate(String str, int maxLength) { + if (str == null) { + return "null"; + } + if (str.length() <= maxLength) { + return str; + } + return str.substring(0, maxLength) + "..."; + } + + /** + * Hook ่ง‚ๆต‹ไฝœ็”จๅŸŸ + *

    + * ็”จไบŽๆ‰‹ๅŠจ็ฎก็† Span ็š„็”Ÿๅ‘ฝๅ‘จๆœŸ๏ผŒ้€‚็”จไบŽๅผ‚ๆญฅๅœบๆ™ฏใ€‚ + */ + public static class HookObservationScope implements AutoCloseable { + private final Span span; + private final Scope scope; + private final HookObservationContext context; + private final long startTime; + + HookObservationScope(Span span, Scope scope, HookObservationContext context) { + this.span = span; + this.scope = scope; + this.context = context; + this.startTime = System.currentTimeMillis(); + } + + /** + * ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + */ + public HookObservationScope putCustomData(String key, Object value) { + if (context != null && key != null && value != null) { + context.putCustomData(key, value); + if (span != null) { + span.setAttribute("codeact.hook.custom." + key, truncate(value.toString(), 200)); + } + } + return this; + } + + /** + * ๆ ‡่ฎฐๆˆๅŠŸๅนถๅ…ณ้—ญ + */ + public void success() { + if (span != null && context != null) { + long durationMs = System.currentTimeMillis() - startTime; + context.setDurationMs(durationMs); + context.setSuccess(true); + span.setAttribute("duration.ms", durationMs); + } + close(); + } + + /** + * ๆ ‡่ฎฐๅคฑ่ดฅๅนถๅ…ณ้—ญ + */ + public void failure(Throwable error) { + if (span != null && context != null) { + long durationMs = System.currentTimeMillis() - startTime; + context.setDurationMs(durationMs); + context.setSuccess(false); + if (error != null) { + context.setErrorType(error.getClass().getSimpleName()); + context.setErrorMessage(error.getMessage()); + span.setStatus(StatusCode.ERROR, error.getMessage()); + span.recordException(error); + } + span.setAttribute("duration.ms", durationMs); + } + close(); + } + + @Override + public void close() { + if (span != null) { + span.end(); + } + if (scope != null) { + scope.close(); + } + } + + /** + * ่Žทๅ–ไธŠไธ‹ๆ–‡ + */ + public HookObservationContext getContext() { + return context; + } + + /** + * ่Žทๅ– Span + */ + public Span getSpan() { + return span; + } + + /** + * ๆฃ€ๆŸฅๆ˜ฏๅฆไธบๆœ‰ๆ•ˆ็š„ Scope + */ + public boolean isValid() { + return span != null; + } + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/InterceptorObservationHelper.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/InterceptorObservationHelper.java new file mode 100644 index 0000000..8bf93c7 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/InterceptorObservationHelper.java @@ -0,0 +1,186 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import com.alibaba.assistant.agent.core.observation.context.InterceptorObservationContext; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.Supplier; + +/** + * Interceptor ่ง‚ๆต‹่พ…ๅŠฉๅทฅๅ…ท็ฑป + *

    + * ๆไพ›ไพฟๆท็š„ๆ–นๆณ•ๅœจ ModelInterceptor ๅ’Œ ToolInterceptor ไธญๅˆ›ๅปบๅ’Œ็ฎก็† OpenTelemetry Spanใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public final class InterceptorObservationHelper { + + private static final Logger log = LoggerFactory.getLogger(InterceptorObservationHelper.class); + + private InterceptorObservationHelper() { + // Utility class + } + + /** + * ไธบ ModelInterceptor ๆ‰ง่กŒๅˆ›ๅปบ่ง‚ๆต‹ๅนถๆ‰ง่กŒๆ“ไฝœ + * + * @param tracer OpenTelemetry Tracer + * @param interceptorName Interceptorๅ็งฐ + * @param sessionId ไผš่ฏID + * @param modelName ๆจกๅž‹ๅ็งฐ + * @param action ่ฆๆ‰ง่กŒ็š„ๆ“ไฝœ + * @param ่ฟ”ๅ›ž็ฑปๅž‹ + * @return ๆ“ไฝœ็ป“ๆžœ + */ + public static T observeModelInterceptor( + Tracer tracer, + String interceptorName, + String sessionId, + String modelName, + Supplier action) { + + InterceptorObservationContext context = new InterceptorObservationContext( + interceptorName, InterceptorObservationContext.InterceptorType.MODEL); + context.setSessionId(sessionId); + context.setModelName(modelName); + + return observeInterceptor(tracer, context, action); + } + + /** + * ไธบ ToolInterceptor ๆ‰ง่กŒๅˆ›ๅปบ่ง‚ๆต‹ๅนถๆ‰ง่กŒๆ“ไฝœ + * + * @param tracer OpenTelemetry Tracer + * @param interceptorName Interceptorๅ็งฐ + * @param sessionId ไผš่ฏID + * @param toolName ๅทฅๅ…ทๅ็งฐ + * @param toolArguments ๅทฅๅ…ทๅ‚ๆ•ฐ + * @param action ่ฆๆ‰ง่กŒ็š„ๆ“ไฝœ + * @param ่ฟ”ๅ›ž็ฑปๅž‹ + * @return ๆ“ไฝœ็ป“ๆžœ + */ + public static T observeToolInterceptor( + Tracer tracer, + String interceptorName, + String sessionId, + String toolName, + String toolArguments, + Supplier action) { + + InterceptorObservationContext context = new InterceptorObservationContext( + interceptorName, InterceptorObservationContext.InterceptorType.TOOL); + context.setSessionId(sessionId); + context.setToolName(toolName); + context.setToolArguments(toolArguments); + + return observeInterceptor(tracer, context, action); + } + + /** + * ้€š็”จ็š„ Interceptor ่ง‚ๆต‹ๆ‰ง่กŒๆ–นๆณ• + */ + private static T observeInterceptor( + Tracer tracer, + InterceptorObservationContext context, + Supplier action) { + + if (tracer == null) { + return action.get(); + } + + long startTime = System.currentTimeMillis(); + String spanName = buildSpanName(context); + + SpanKind spanKind = context.getInterceptorType() == InterceptorObservationContext.InterceptorType.MODEL + ? SpanKind.CLIENT + : SpanKind.INTERNAL; + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(spanKind) + .setAttribute("gen_ai.conversation.id", + context.getSessionId() != null ? context.getSessionId() : "unknown") + .setAttribute("gen_ai.span_kind_name", + context.getInterceptorType() == InterceptorObservationContext.InterceptorType.MODEL + ? "LLM" : "TOOL") + .setAttribute("gen_ai.operation.name", + context.getInterceptorType() == InterceptorObservationContext.InterceptorType.MODEL + ? "chat" : "execute_tool") + .setAttribute("codeact.interceptor.name", + context.getInterceptorName() != null ? context.getInterceptorName() : "unknown") + .setAttribute("codeact.interceptor.type", + context.getInterceptorType() != null ? context.getInterceptorType().name() : "unknown") + .startSpan(); + + try (Scope ignored = span.makeCurrent()) { + T result = action.get(); + + long durationMs = System.currentTimeMillis() - startTime; + context.setDurationMs(durationMs); + context.setSuccess(true); + + span.setAttribute("duration.ms", durationMs); + + log.debug("InterceptorObservationHelper#observeInterceptor - reason=Interceptorๆ‰ง่กŒๆˆๅŠŸ, " + + "interceptorName={}, durationMs={}", context.getInterceptorName(), durationMs); + + return result; + + } catch (Exception e) { + long durationMs = System.currentTimeMillis() - startTime; + context.setDurationMs(durationMs); + context.setSuccess(false); + context.setErrorType(e.getClass().getSimpleName()); + context.setErrorMessage(e.getMessage()); + + span.setAttribute("duration.ms", durationMs); + span.setStatus(StatusCode.ERROR, e.getMessage()); + span.recordException(e); + + log.warn("InterceptorObservationHelper#observeInterceptor - reason=Interceptorๆ‰ง่กŒๅคฑ่ดฅ, " + + "interceptorName={}, durationMs={}, errorType={}", + context.getInterceptorName(), durationMs, e.getClass().getSimpleName()); + + throw e; + + } finally { + span.end(); + } + } + + /** + * ๆž„ๅปบ Span ๅ็งฐ + */ + private static String buildSpanName(InterceptorObservationContext context) { + String typeName = context.getInterceptorType() != null + ? context.getInterceptorType().name().toLowerCase() + : "unknown"; + String interceptorName = context.getInterceptorName() != null + ? context.getInterceptorName().toLowerCase() + : "unknown"; + return "codeact.interceptor." + typeName + "." + interceptorName; + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/ObservationState.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/ObservationState.java new file mode 100644 index 0000000..6370715 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/ObservationState.java @@ -0,0 +1,130 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import java.util.Map; + +/** + * ่ง‚ๆต‹็Šถๆ€ๅญ˜ๅ‚จๆŽฅๅฃ + *

    + * ๅ…่ฎธ Hook ๅ’Œ Interceptor ๅœจๆ‰ง่กŒ่ฟ‡็จ‹ไธญๆณจๅ†Œ่‡ชๅฎšไน‰่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒ + * ่ฟ™ไบ›ๆ•ฐๆฎไผš่ขซๆ”ถ้›†ๅนถ่ฎฐๅฝ•ๅˆฐ Observation ไธญใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public interface ObservationState { + + /** + * ็Šถๆ€Keyๅธธ้‡๏ผšๅฝ“ๅ‰่ฝฎๆฌกไปปๅŠกID + */ + String KEY_CURRENT_ROUND_TASK_ID = "current_round_task_id"; + + /** + * ็Šถๆ€Keyๅธธ้‡๏ผš็”จๆˆท่พ“ๅ…ฅ + */ + String KEY_INPUT = "input"; + + /** + * ็Šถๆ€Keyๅธธ้‡๏ผšๆถˆๆฏๅކๅฒ + */ + String KEY_MESSAGES = "messages"; + + /** + * ็Šถๆ€Keyๅธธ้‡๏ผšไผš่ฏID + */ + String KEY_SESSION_ID = "session_id"; + + /** + * ็Šถๆ€Keyๅธธ้‡๏ผš็งŸๆˆทID + */ + String KEY_TENANT_ID = "tenant_id"; + + /** + * ็Šถๆ€Keyๅธธ้‡๏ผš็”จๆˆทID + */ + String KEY_USER_ID = "user_id"; + + /** + * ๆณจๅ†Œ่ง‚ๆต‹ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ๏ผŒๅปบ่ฎฎไฝฟ็”จๆœ‰ๆ„ไน‰็š„ๅ‰็ผ€๏ผˆๅฆ‚ hook.xxx ๆˆ– interceptor.xxx๏ผ‰ + * @param value ๆ•ฐๆฎๅ€ผ + */ + void put(String key, Object value); + + /** + * ๆ‰น้‡ๆณจๅ†Œ่ง‚ๆต‹ๆ•ฐๆฎ + * + * @param data ๆ•ฐๆฎMap + */ + void putAll(Map data); + + /** + * ่Žทๅ–่ง‚ๆต‹ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @param ๆœŸๆœ›็š„็ฑปๅž‹ + * @return ๆ•ฐๆฎๅ€ผ๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจ่ฟ”ๅ›žnull + */ + T get(String key); + + /** + * ่Žทๅ–่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจๅˆ™่ฟ”ๅ›ž้ป˜่ฎคๅ€ผ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @param defaultValue ้ป˜่ฎคๅ€ผ + * @param ๆœŸๆœ›็š„็ฑปๅž‹ + * @return ๆ•ฐๆฎๅ€ผ๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจ่ฟ”ๅ›ž้ป˜่ฎคๅ€ผ + */ + T getOrDefault(String key, T defaultValue); + + /** + * ๆฃ€ๆŸฅๆ˜ฏๅฆๅญ˜ๅœจๆŒ‡ๅฎš้”ฎ็š„ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @return ๆ˜ฏๅฆๅญ˜ๅœจ + */ + boolean contains(String key); + + /** + * ็งป้™คๆŒ‡ๅฎš้”ฎ็š„ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @return ่ขซ็งป้™ค็š„ๅ€ผ๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจ่ฟ”ๅ›žnull + */ + Object remove(String key); + + /** + * ่Žทๅ–ๆ‰€ๆœ‰่ง‚ๆต‹ๆ•ฐๆฎ + * + * @return ๆ‰€ๆœ‰่ง‚ๆต‹ๆ•ฐๆฎ็š„ไธๅฏๅ˜่ง†ๅ›พ + */ + Map getAll(); + + /** + * ๆธ…็ฉบๆ‰€ๆœ‰่ง‚ๆต‹ๆ•ฐๆฎ + */ + void clear(); + + /** + * ่Žทๅ–ๆ•ฐๆฎๆ•ฐ้‡ + * + * @return ๆ•ฐๆฎๆ•ฐ้‡ + */ + int size(); +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/OpenTelemetryObservationHelper.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/OpenTelemetryObservationHelper.java new file mode 100644 index 0000000..851107a --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/OpenTelemetryObservationHelper.java @@ -0,0 +1,393 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation; + +import com.alibaba.assistant.agent.core.observation.context.HookObservationContext; +import com.alibaba.assistant.agent.core.observation.context.InterceptorObservationContext; +import com.alibaba.assistant.agent.core.observation.context.ReactPhaseObservationContext; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * OpenTelemetry ๅฏ่ง‚ๆต‹ๆ€ง่พ…ๅŠฉ็ฑป + *

    + * ๆไพ›็ปŸไธ€็š„ Span ๅˆ›ๅปบๅ’Œ็ฎก็†่ƒฝๅŠ›๏ผŒๆ›ฟไปฃๅŽŸๆœ‰็š„ Micrometer Observation ๅฎž็Žฐใ€‚ + *

    + * ไธป่ฆๅŠŸ่ƒฝ๏ผš + *

      + *
    • Hook ๆ‰ง่กŒ็š„ Span ๅˆ›ๅปบๅ’Œ็ฎก็†
    • + *
    • Interceptor ๆ‰ง่กŒ็š„ Span ๅˆ›ๅปบๅ’Œ็ฎก็†
    • + *
    • React ้˜ถๆฎต๏ผˆLlmNode/ToolNode๏ผ‰็š„ Span ๅˆ›ๅปบๅ’Œ็ฎก็†
    • + *
    • Context ไผ ๆ’ญๅ’Œ Scope ็ฎก็†
    • + *
    + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class OpenTelemetryObservationHelper { + + private static final Logger log = LoggerFactory.getLogger(OpenTelemetryObservationHelper.class); + + private final Tracer tracer; + + /** + * ๅญ˜ๅ‚จๆดป่ทƒ็š„ Span๏ผˆๆŒ‰ๅ”ฏไธ€ๆ ‡่ฏ†๏ผ‰ + */ + private final ConcurrentHashMap activeSpans = new ConcurrentHashMap<>(); + + public OpenTelemetryObservationHelper(Tracer tracer) { + this.tracer = tracer; + log.info("OpenTelemetryObservationHelper# - reason=ๅˆๅง‹ๅŒ–ๅฎŒๆˆ"); + } + + // ==================== Hook Span ==================== + + /** + * ๅผ€ๅง‹ Hook Span + * + * @param spanKey ๅ”ฏไธ€ๆ ‡่ฏ†๏ผˆๅฆ‚ sessionId:hookName๏ผ‰ + * @param context Hook่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + * @return ๅˆ›ๅปบ็š„ Span + */ + public Span startHookSpan(String spanKey, HookObservationContext context) { + String spanName = "codeact.hook." + (context.getHookName() != null + ? context.getHookName().toLowerCase() + : "unknown"); + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAllAttributes(context.toAttributes()) + .startSpan(); + + Scope scope = span.makeCurrent(); + activeSpans.put(spanKey, new SpanHolder(span, scope)); + + log.debug("OpenTelemetryObservationHelper#startHookSpan - reason=ๅผ€ๅง‹Hook Span, " + + "spanKey={}, hookName={}", spanKey, context.getHookName()); + + return span; + } + + /** + * ็ป“ๆŸ Hook Span + * + * @param spanKey ๅ”ฏไธ€ๆ ‡่ฏ† + * @param context ๆ›ดๆ–ฐๅŽ็š„ไธŠไธ‹ๆ–‡ + * @param error ๅผ‚ๅธธ๏ผˆๅฏ้€‰๏ผ‰ + */ + public void endHookSpan(String spanKey, HookObservationContext context, Throwable error) { + SpanHolder holder = activeSpans.remove(spanKey); + if (holder == null) { + log.warn("OpenTelemetryObservationHelper#endHookSpan - reason=ๆœชๆ‰พๅˆฐSpan, spanKey={}", spanKey); + return; + } + + try { + Span span = holder.span; + + // ๆทปๅŠ ็ป“ๆŸๆ—ถ็š„ๅฑžๆ€ง + if (context.getDurationMs() > 0) { + span.setAttribute("duration.ms", context.getDurationMs()); + } + span.setAttribute("codeact.hook.success", context.isSuccess()); + + if (error != null) { + span.setStatus(StatusCode.ERROR, error.getMessage()); + span.recordException(error); + } else if (!context.isSuccess()) { + span.setStatus(StatusCode.ERROR, context.getErrorMessage()); + } + + span.end(); + } finally { + holder.scope.close(); + } + + log.debug("OpenTelemetryObservationHelper#endHookSpan - reason=็ป“ๆŸHook Span, " + + "spanKey={}, success={}", spanKey, context.isSuccess()); + } + + // ==================== Interceptor Span ==================== + + /** + * ๅผ€ๅง‹ Interceptor Span + * + * @param spanKey ๅ”ฏไธ€ๆ ‡่ฏ† + * @param context Interceptor่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + * @return ๅˆ›ๅปบ็š„ Span + */ + public Span startInterceptorSpan(String spanKey, InterceptorObservationContext context) { + String typeName = context.getInterceptorType() != null + ? context.getInterceptorType().name().toLowerCase() + : "unknown"; + String spanName = "codeact.interceptor." + typeName + "." + + (context.getInterceptorName() != null ? context.getInterceptorName().toLowerCase() : "unknown"); + + SpanKind kind = context.getInterceptorType() == InterceptorObservationContext.InterceptorType.MODEL + ? SpanKind.CLIENT + : SpanKind.INTERNAL; + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(kind) + .setAllAttributes(context.toAttributes()) + .startSpan(); + + Scope scope = span.makeCurrent(); + activeSpans.put(spanKey, new SpanHolder(span, scope)); + + log.debug("OpenTelemetryObservationHelper#startInterceptorSpan - reason=ๅผ€ๅง‹Interceptor Span, " + + "spanKey={}, interceptorName={}", spanKey, context.getInterceptorName()); + + return span; + } + + /** + * ็ป“ๆŸ Interceptor Span + * + * @param spanKey ๅ”ฏไธ€ๆ ‡่ฏ† + * @param context ๆ›ดๆ–ฐๅŽ็š„ไธŠไธ‹ๆ–‡ + * @param error ๅผ‚ๅธธ๏ผˆๅฏ้€‰๏ผ‰ + */ + public void endInterceptorSpan(String spanKey, InterceptorObservationContext context, Throwable error) { + SpanHolder holder = activeSpans.remove(spanKey); + if (holder == null) { + log.warn("OpenTelemetryObservationHelper#endInterceptorSpan - reason=ๆœชๆ‰พๅˆฐSpan, spanKey={}", spanKey); + return; + } + + try { + Span span = holder.span; + + // ๆทปๅŠ ็ป“ๆŸๆ—ถ็š„ๅฑžๆ€ง + if (context.getDurationMs() > 0) { + span.setAttribute("duration.ms", context.getDurationMs()); + } + span.setAttribute("codeact.interceptor.success", context.isSuccess()); + + // Token usage for model interceptors + if (context.getInputTokens() > 0) { + span.setAttribute("gen_ai.usage.input_tokens", (long) context.getInputTokens()); + } + if (context.getOutputTokens() > 0) { + span.setAttribute("gen_ai.usage.output_tokens", (long) context.getOutputTokens()); + } + + if (error != null) { + span.setStatus(StatusCode.ERROR, error.getMessage()); + span.recordException(error); + } else if (!context.isSuccess()) { + span.setStatus(StatusCode.ERROR, context.getErrorMessage()); + } + + span.end(); + } finally { + holder.scope.close(); + } + + log.debug("OpenTelemetryObservationHelper#endInterceptorSpan - reason=็ป“ๆŸInterceptor Span, " + + "spanKey={}, success={}", spanKey, context.isSuccess()); + } + + // ==================== React Phase Span ==================== + + /** + * ๅผ€ๅง‹ React Phase Span + * + * @param spanKey ๅ”ฏไธ€ๆ ‡่ฏ† + * @param context React้˜ถๆฎต่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + * @return ๅˆ›ๅปบ็š„ Span + */ + public Span startReactPhaseSpan(String spanKey, ReactPhaseObservationContext context) { + String nodeTypeName = context.getNodeType() != null + ? context.getNodeType().name().toLowerCase() + : "unknown"; + String spanName = "codeact.react." + nodeTypeName; + + SpanKind kind = context.getNodeType() == ReactPhaseObservationContext.NodeType.LLM + ? SpanKind.CLIENT + : SpanKind.INTERNAL; + + Span span = tracer.spanBuilder(spanName) + .setSpanKind(kind) + .setAllAttributes(context.toAttributes()) + .startSpan(); + + Scope scope = span.makeCurrent(); + activeSpans.put(spanKey, new SpanHolder(span, scope)); + + log.debug("OpenTelemetryObservationHelper#startReactPhaseSpan - reason=ๅผ€ๅง‹React Phase Span, " + + "spanKey={}, nodeType={}", spanKey, nodeTypeName); + + return span; + } + + /** + * ็ป“ๆŸ React Phase Span + * + * @param spanKey ๅ”ฏไธ€ๆ ‡่ฏ† + * @param context ๆ›ดๆ–ฐๅŽ็š„ไธŠไธ‹ๆ–‡ + * @param error ๅผ‚ๅธธ๏ผˆๅฏ้€‰๏ผ‰ + */ + public void endReactPhaseSpan(String spanKey, ReactPhaseObservationContext context, Throwable error) { + SpanHolder holder = activeSpans.remove(spanKey); + if (holder == null) { + log.warn("OpenTelemetryObservationHelper#endReactPhaseSpan - reason=ๆœชๆ‰พๅˆฐSpan, spanKey={}", spanKey); + return; + } + + try { + Span span = holder.span; + + // ๆทปๅŠ ็ป“ๆŸๆ—ถ็š„ๅฑžๆ€ง + if (context.getDurationMs() > 0) { + span.setAttribute("duration.ms", context.getDurationMs()); + } + span.setAttribute("codeact.react.success", context.isSuccess()); + + // Token usage for LLM nodes + if (context.getInputTokens() > 0) { + span.setAttribute("gen_ai.usage.input_tokens", (long) context.getInputTokens()); + } + if (context.getOutputTokens() > 0) { + span.setAttribute("gen_ai.usage.output_tokens", (long) context.getOutputTokens()); + } + if (context.getFinishReason() != null) { + span.setAttribute("gen_ai.response.finish_reasons", context.getFinishReason()); + } + + if (error != null) { + span.setStatus(StatusCode.ERROR, error.getMessage()); + span.recordException(error); + } else if (!context.isSuccess()) { + span.setStatus(StatusCode.ERROR, context.getErrorMessage()); + } + + span.end(); + } finally { + holder.scope.close(); + } + + log.debug("OpenTelemetryObservationHelper#endReactPhaseSpan - reason=็ป“ๆŸReact Phase Span, " + + "spanKey={}, success={}", spanKey, context.isSuccess()); + } + + // ==================== Generic Span Operations ==================== + + /** + * ๅœจๆŒ‡ๅฎš็š„ Span ไฝœ็”จๅŸŸๅ†…ๆ‰ง่กŒๆ“ไฝœ + * + * @param spanName Spanๅ็งฐ + * @param attributes Spanๅฑžๆ€ง + * @param action ่ฆๆ‰ง่กŒ็š„ๆ“ไฝœ + * @param ่ฟ”ๅ›žๅ€ผ็ฑปๅž‹ + * @return ๆ“ไฝœ็ป“ๆžœ + */ + public T withSpan(String spanName, Attributes attributes, Supplier action) { + Span span = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAllAttributes(attributes) + .startSpan(); + + try (Scope ignored = span.makeCurrent()) { + T result = action.get(); + return result; + } catch (Exception e) { + span.setStatus(StatusCode.ERROR, e.getMessage()); + span.recordException(e); + throw e; + } finally { + span.end(); + } + } + + /** + * ๅœจๆŒ‡ๅฎš็š„ Span ไฝœ็”จๅŸŸๅ†…ๆ‰ง่กŒๆ“ไฝœ๏ผˆๆ— ่ฟ”ๅ›žๅ€ผ๏ผ‰ + * + * @param spanName Spanๅ็งฐ + * @param attributes Spanๅฑžๆ€ง + * @param action ่ฆๆ‰ง่กŒ็š„ๆ“ไฝœ + */ + public void withSpanVoid(String spanName, Attributes attributes, Runnable action) { + Span span = tracer.spanBuilder(spanName) + .setSpanKind(SpanKind.INTERNAL) + .setAllAttributes(attributes) + .startSpan(); + + try (Scope ignored = span.makeCurrent()) { + action.run(); + } catch (Exception e) { + span.setStatus(StatusCode.ERROR, e.getMessage()); + span.recordException(e); + throw e; + } finally { + span.end(); + } + } + + /** + * ่Žทๅ–ๅฝ“ๅ‰ๆดป่ทƒ็š„ Span ๆ•ฐ้‡ + */ + public int getActiveSpanCount() { + return activeSpans.size(); + } + + /** + * ๆธ…็†ๆŒ‡ๅฎš session ็›ธๅ…ณ็š„ๆ‰€ๆœ‰ Span + * + * @param sessionIdPrefix sessionๅ‰็ผ€ + */ + public void cleanupSession(String sessionIdPrefix) { + activeSpans.entrySet().removeIf(entry -> { + if (entry.getKey().startsWith(sessionIdPrefix)) { + SpanHolder holder = entry.getValue(); + try { + holder.span.end(); + } finally { + holder.scope.close(); + } + log.debug("OpenTelemetryObservationHelper#cleanupSession - reason=ๆธ…็†Span, spanKey={}", + entry.getKey()); + return true; + } + return false; + }); + } + + /** + * Span ๆŒๆœ‰่€…๏ผŒๅŒ…ๅซ Span ๅ’Œๅฏนๅบ”็š„ Scope + */ + private static class SpanHolder { + final Span span; + final Scope scope; + + SpanHolder(Span span, Scope scope) { + this.span = span; + this.scope = scope; + } + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeGenerationObservationContext.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeGenerationObservationContext.java new file mode 100644 index 0000000..d933ce9 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeGenerationObservationContext.java @@ -0,0 +1,185 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation.context; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +import static com.alibaba.assistant.agent.core.observation.CodeactObservationDocumentation.CodeGenerationAttributes; + +/** + * Codeact ไปฃ็ ็”Ÿๆˆ่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + *

    + * ๅญ˜ๅ‚จไปฃ็ ็”Ÿๆˆ่ฟ‡็จ‹ไธญ็š„่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒๅŒ…ๆ‹ฌๆจกๅž‹ใ€ๅ‡ฝๆ•ฐๅใ€Tokenไฝฟ็”จๆƒ…ๅ†ต็ญ‰ใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation.Context ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class CodeGenerationObservationContext { + + private String functionName; + private String language; + private String modelName; + private int codeLines; + private int inputTokens; + private int outputTokens; + private long durationMs; + private boolean success; + private String errorType; + private String errorMessage; + + public CodeGenerationObservationContext() { + } + + public CodeGenerationObservationContext(String language, String modelName) { + this.language = language; + this.modelName = modelName; + } + + // Getters and Setters + + public String getFunctionName() { + return functionName; + } + + public CodeGenerationObservationContext setFunctionName(String functionName) { + this.functionName = functionName; + return this; + } + + public String getLanguage() { + return language; + } + + public CodeGenerationObservationContext setLanguage(String language) { + this.language = language; + return this; + } + + public String getModelName() { + return modelName; + } + + public CodeGenerationObservationContext setModelName(String modelName) { + this.modelName = modelName; + return this; + } + + public int getCodeLines() { + return codeLines; + } + + public CodeGenerationObservationContext setCodeLines(int codeLines) { + this.codeLines = codeLines; + return this; + } + + public int getInputTokens() { + return inputTokens; + } + + public CodeGenerationObservationContext setInputTokens(int inputTokens) { + this.inputTokens = inputTokens; + return this; + } + + public int getOutputTokens() { + return outputTokens; + } + + public CodeGenerationObservationContext setOutputTokens(int outputTokens) { + this.outputTokens = outputTokens; + return this; + } + + public long getDurationMs() { + return durationMs; + } + + public CodeGenerationObservationContext setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public CodeGenerationObservationContext setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public CodeGenerationObservationContext setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public CodeGenerationObservationContext setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + /** + * ๆž„ๅปบ OpenTelemetry Attributes + * + * @return Attributes + */ + public Attributes toAttributes() { + AttributesBuilder builder = Attributes.builder(); + + if (language != null) { + builder.put(CodeGenerationAttributes.LANGUAGE, language); + } + if (modelName != null) { + builder.put(CodeGenerationAttributes.MODEL_NAME, modelName); + } + builder.put(CodeGenerationAttributes.SUCCESS, success); + if (functionName != null) { + builder.put(CodeGenerationAttributes.FUNCTION_NAME, functionName); + } + builder.put(CodeGenerationAttributes.CODE_LINES, (long) codeLines); + builder.put(CodeGenerationAttributes.INPUT_TOKENS, (long) inputTokens); + builder.put(CodeGenerationAttributes.OUTPUT_TOKENS, (long) outputTokens); + builder.put(CodeGenerationAttributes.DURATION_MS, durationMs); + + return builder.build(); + } + + @Override + public String toString() { + return "CodeGenerationObservationContext{" + + "functionName='" + functionName + '\'' + + ", language='" + language + '\'' + + ", modelName='" + modelName + '\'' + + ", codeLines=" + codeLines + + ", inputTokens=" + inputTokens + + ", outputTokens=" + outputTokens + + ", durationMs=" + durationMs + + ", success=" + success + + '}'; + } +} diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeactExecutionObservationContext.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeactExecutionObservationContext.java new file mode 100644 index 0000000..0a0ee2e --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeactExecutionObservationContext.java @@ -0,0 +1,178 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation.context; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +import static com.alibaba.assistant.agent.core.observation.CodeactObservationDocumentation.ExecutionAttributes; + +/** + * Codeact ไปฃ็ ๆ‰ง่กŒ่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + *

    + * ๅญ˜ๅ‚จไปฃ็ ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ็š„่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒๅŒ…ๆ‹ฌๅ‡ฝๆ•ฐๅใ€ๅ‚ๆ•ฐใ€็ป“ๆžœ็ญ‰ใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation.Context ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class CodeactExecutionObservationContext { + + private String functionName; + private String language; + private String arguments; + private int argumentsLength; + private String result; + private int resultLength; + private long durationMs; + private boolean success; + private String errorType; + private String errorMessage; + + public CodeactExecutionObservationContext() { + } + + public CodeactExecutionObservationContext(String functionName, String language) { + this.functionName = functionName; + this.language = language; + } + + // Getters and Setters + + public String getFunctionName() { + return functionName; + } + + public CodeactExecutionObservationContext setFunctionName(String functionName) { + this.functionName = functionName; + return this; + } + + public String getLanguage() { + return language; + } + + public CodeactExecutionObservationContext setLanguage(String language) { + this.language = language; + return this; + } + + public String getArguments() { + return arguments; + } + + public CodeactExecutionObservationContext setArguments(String arguments) { + this.arguments = arguments; + this.argumentsLength = arguments != null ? arguments.length() : 0; + return this; + } + + public int getArgumentsLength() { + return argumentsLength; + } + + public String getResult() { + return result; + } + + public CodeactExecutionObservationContext setResult(String result) { + this.result = result; + this.resultLength = result != null ? result.length() : 0; + return this; + } + + public int getResultLength() { + return resultLength; + } + + public long getDurationMs() { + return durationMs; + } + + public CodeactExecutionObservationContext setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public CodeactExecutionObservationContext setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public CodeactExecutionObservationContext setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public CodeactExecutionObservationContext setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + /** + * ๆž„ๅปบ OpenTelemetry Attributes + * + * @return Attributes + */ + public Attributes toAttributes() { + AttributesBuilder builder = Attributes.builder(); + + if (language != null) { + builder.put(ExecutionAttributes.LANGUAGE, language); + } + builder.put(ExecutionAttributes.SUCCESS, success); + if (functionName != null) { + builder.put(ExecutionAttributes.FUNCTION_NAME, functionName); + } + builder.put(ExecutionAttributes.ARGUMENTS_LENGTH, (long) argumentsLength); + builder.put(ExecutionAttributes.RESULT_LENGTH, (long) resultLength); + builder.put(ExecutionAttributes.DURATION_MS, durationMs); + if (errorType != null) { + builder.put(ExecutionAttributes.ERROR_TYPE, errorType); + } + if (errorMessage != null) { + builder.put(ExecutionAttributes.ERROR_MESSAGE, errorMessage); + } + + return builder.build(); + } + + @Override + public String toString() { + return "CodeactExecutionObservationContext{" + + "functionName='" + functionName + '\'' + + ", language='" + language + '\'' + + ", argumentsLength=" + argumentsLength + + ", resultLength=" + resultLength + + ", durationMs=" + durationMs + + ", success=" + success + + ", errorType='" + errorType + '\'' + + '}'; + } +} diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeactToolCallObservationContext.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeactToolCallObservationContext.java new file mode 100644 index 0000000..f999605 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/CodeactToolCallObservationContext.java @@ -0,0 +1,190 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation.context; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +import static com.alibaba.assistant.agent.core.observation.CodeactObservationDocumentation.ToolCallAttributes; +import static com.alibaba.assistant.agent.core.observation.CodeactObservationDocumentation.GenAIAttributes; + +/** + * Codeact ๅทฅๅ…ท่ฐƒ็”จ่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + *

    + * ๅญ˜ๅ‚จๅœจไปฃ็ ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ่ฐƒ็”จๅทฅๅ…ท็š„่ง‚ๆต‹ๆ•ฐๆฎใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation.Context ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class CodeactToolCallObservationContext { + + private String sessionId; + private String toolName; + private String arguments; + private int argumentsLength; + private String result; + private int resultLength; + private long durationMs; + private boolean success; + private String errorType; + private String errorMessage; + + public CodeactToolCallObservationContext() { + } + + public CodeactToolCallObservationContext(String toolName) { + this.toolName = toolName; + } + + // Getters and Setters + + public String getSessionId() { + return sessionId; + } + + public CodeactToolCallObservationContext setSessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + public String getToolName() { + return toolName; + } + + public CodeactToolCallObservationContext setToolName(String toolName) { + this.toolName = toolName; + return this; + } + + public String getArguments() { + return arguments; + } + + public CodeactToolCallObservationContext setArguments(String arguments) { + this.arguments = arguments; + this.argumentsLength = arguments != null ? arguments.length() : 0; + return this; + } + + public int getArgumentsLength() { + return argumentsLength; + } + + public String getResult() { + return result; + } + + public CodeactToolCallObservationContext setResult(String result) { + this.result = result; + this.resultLength = result != null ? result.length() : 0; + return this; + } + + public int getResultLength() { + return resultLength; + } + + public long getDurationMs() { + return durationMs; + } + + public CodeactToolCallObservationContext setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public CodeactToolCallObservationContext setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public CodeactToolCallObservationContext setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public CodeactToolCallObservationContext setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + /** + * ๆž„ๅปบ OpenTelemetry Attributes + * + * @return Attributes + */ + public Attributes toAttributes() { + AttributesBuilder builder = Attributes.builder(); + + if (sessionId != null) { + builder.put(GenAIAttributes.CONVERSATION_ID, sessionId); + } + if (toolName != null) { + builder.put(ToolCallAttributes.TOOL_NAME, toolName); + } + builder.put(ToolCallAttributes.SUCCESS, success); + if (arguments != null) { + builder.put(ToolCallAttributes.ARGUMENTS, truncate(arguments, 500)); + } + builder.put(ToolCallAttributes.ARGUMENTS_LENGTH, (long) argumentsLength); + builder.put(ToolCallAttributes.RESULT_LENGTH, (long) resultLength); + builder.put(ToolCallAttributes.DURATION_MS, durationMs); + if (errorType != null) { + builder.put(ToolCallAttributes.ERROR_TYPE, errorType); + } + + return builder.build(); + } + + /** + * ๆˆชๆ–ญๅญ—็ฌฆไธฒ + */ + private String truncate(String str, int maxLength) { + if (str == null) { + return null; + } + if (str.length() <= maxLength) { + return str; + } + return str.substring(0, maxLength) + "...[truncated]"; + } + + @Override + public String toString() { + return "CodeactToolCallObservationContext{" + + "toolName='" + toolName + '\'' + + ", argumentsLength=" + argumentsLength + + ", resultLength=" + resultLength + + ", durationMs=" + durationMs + + ", success=" + success + + ", errorType='" + errorType + '\'' + + '}'; + } +} diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/HookObservationContext.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/HookObservationContext.java new file mode 100644 index 0000000..99cd345 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/HookObservationContext.java @@ -0,0 +1,267 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation.context; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +import java.util.HashMap; +import java.util.Map; + +/** + * Hook ่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + *

    + * ๅญ˜ๅ‚จ Hook ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ็š„่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒๆ”ฏๆŒ่‡ชๅฎšไน‰ๆ•ฐๆฎๆณจๅ†Œใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation.Context ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class HookObservationContext { + + // ๆ ‡ๅ‡†ๅฑžๆ€ง้”ฎๅฎšไน‰ + public static final AttributeKey HOOK_NAME = AttributeKey.stringKey("codeact.hook.name"); + public static final AttributeKey HOOK_POSITION = AttributeKey.stringKey("codeact.hook.position"); + public static final AttributeKey AGENT_NAME = AttributeKey.stringKey("gen_ai.agent.name"); + public static final AttributeKey SESSION_ID = AttributeKey.stringKey("gen_ai.conversation.id"); + public static final AttributeKey DURATION_MS = AttributeKey.longKey("duration.ms"); + public static final AttributeKey SUCCESS = AttributeKey.booleanKey("codeact.hook.success"); + public static final AttributeKey ERROR_TYPE = AttributeKey.stringKey("error.type"); + public static final AttributeKey ERROR_MESSAGE = AttributeKey.stringKey("error.message"); + + private String hookName; + private String hookPosition; + private String agentName; + private String sessionId; + private long durationMs; + private boolean success = true; + private String errorType; + private String errorMessage; + + /** + * ่‡ชๅฎšไน‰ๆ•ฐๆฎๅญ˜ๅ‚จ๏ผŒๅ…่ฎธHookๅœจๆ‰ง่กŒ่ฟ‡็จ‹ไธญๆณจๅ†Œๅฎšๅˆถๆ•ฐๆฎ + */ + private final Map customData = new HashMap<>(); + + public HookObservationContext() { + } + + public HookObservationContext(String hookName, String hookPosition) { + this.hookName = hookName; + this.hookPosition = hookPosition; + } + + // ==================== Getters and Setters ==================== + + public String getHookName() { + return hookName; + } + + public HookObservationContext setHookName(String hookName) { + this.hookName = hookName; + return this; + } + + public String getHookPosition() { + return hookPosition; + } + + public HookObservationContext setHookPosition(String hookPosition) { + this.hookPosition = hookPosition; + return this; + } + + public String getAgentName() { + return agentName; + } + + public HookObservationContext setAgentName(String agentName) { + this.agentName = agentName; + return this; + } + + public String getSessionId() { + return sessionId; + } + + public HookObservationContext setSessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + public long getDurationMs() { + return durationMs; + } + + public HookObservationContext setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public HookObservationContext setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public HookObservationContext setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public HookObservationContext setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + // ==================== Custom Data Methods ==================== + + /** + * ๆณจๅ†Œ่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @param value ๆ•ฐๆฎๅ€ผ + * @return this + */ + public HookObservationContext putCustomData(String key, Object value) { + this.customData.put(key, value); + return this; + } + + /** + * ๆ‰น้‡ๆณจๅ†Œ่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param data ๆ•ฐๆฎMap + * @return this + */ + public HookObservationContext putAllCustomData(Map data) { + if (data != null) { + this.customData.putAll(data); + } + return this; + } + + /** + * ่Žทๅ–่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @return ๆ•ฐๆฎๅ€ผ๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจ่ฟ”ๅ›žnull + */ + @SuppressWarnings("unchecked") + public T getCustomData(String key) { + return (T) this.customData.get(key); + } + + /** + * ่Žทๅ–ๆ‰€ๆœ‰่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @return ่‡ชๅฎšไน‰ๆ•ฐๆฎ็š„ไธๅฏๅ˜่ง†ๅ›พ + */ + public Map getAllCustomData() { + return Map.copyOf(customData); + } + + /** + * ๆฃ€ๆŸฅๆ˜ฏๅฆๅญ˜ๅœจๆŒ‡ๅฎš้”ฎ็š„่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @return ๆ˜ฏๅฆๅญ˜ๅœจ + */ + public boolean hasCustomData(String key) { + return customData.containsKey(key); + } + + @Override + public String toString() { + return "HookObservationContext{" + + "hookName='" + hookName + '\'' + + ", hookPosition='" + hookPosition + '\'' + + ", agentName='" + agentName + '\'' + + ", sessionId='" + sessionId + '\'' + + ", durationMs=" + durationMs + + ", success=" + success + + ", errorType='" + errorType + '\'' + + ", customDataKeys=" + customData.keySet() + + '}'; + } + + /** + * ๅฐ†ไธŠไธ‹ๆ–‡่ฝฌๆขไธบ OpenTelemetry Attributes + * + * @return Attributes ๅฏน่ฑก + */ + public Attributes toAttributes() { + AttributesBuilder builder = Attributes.builder(); + + if (hookName != null) { + builder.put(HOOK_NAME, hookName); + } + if (hookPosition != null) { + builder.put(HOOK_POSITION, hookPosition); + } + if (agentName != null) { + builder.put(AGENT_NAME, agentName); + } + if (sessionId != null) { + builder.put(SESSION_ID, sessionId); + } + if (durationMs > 0) { + builder.put(DURATION_MS, durationMs); + } + builder.put(SUCCESS, success); + if (errorType != null) { + builder.put(ERROR_TYPE, errorType); + } + if (errorMessage != null) { + builder.put(ERROR_MESSAGE, errorMessage); + } + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + for (Map.Entry entry : customData.entrySet()) { + if (entry.getValue() != null) { + String key = "codeact.hook.custom." + entry.getKey(); + Object value = entry.getValue(); + if (value instanceof String) { + builder.put(AttributeKey.stringKey(key), (String) value); + } else if (value instanceof Long) { + builder.put(AttributeKey.longKey(key), (Long) value); + } else if (value instanceof Double) { + builder.put(AttributeKey.doubleKey(key), (Double) value); + } else if (value instanceof Boolean) { + builder.put(AttributeKey.booleanKey(key), (Boolean) value); + } else { + builder.put(AttributeKey.stringKey(key), String.valueOf(value)); + } + } + } + + return builder.build(); + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/InterceptorObservationContext.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/InterceptorObservationContext.java new file mode 100644 index 0000000..646221c --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/InterceptorObservationContext.java @@ -0,0 +1,389 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation.context; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +import java.util.HashMap; +import java.util.Map; + +/** + * Interceptor ่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + *

    + * ๅญ˜ๅ‚จ Interceptor ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ็š„่ง‚ๆต‹ๆ•ฐๆฎ๏ผŒๆ”ฏๆŒ่‡ชๅฎšไน‰ๆ•ฐๆฎๆณจๅ†Œใ€‚ + * ้€‚็”จไบŽ ModelInterceptor ๅ’Œ ToolInterceptorใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation.Context ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class InterceptorObservationContext { + + // ๆ ‡ๅ‡†ๅฑžๆ€ง้”ฎๅฎšไน‰ + public static final AttributeKey INTERCEPTOR_NAME_KEY = AttributeKey.stringKey("codeact.interceptor.name"); + public static final AttributeKey INTERCEPTOR_TYPE_KEY = AttributeKey.stringKey("codeact.interceptor.type"); + public static final AttributeKey AGENT_NAME_KEY = AttributeKey.stringKey("gen_ai.agent.name"); + public static final AttributeKey SESSION_ID_KEY = AttributeKey.stringKey("gen_ai.conversation.id"); + public static final AttributeKey DURATION_MS_KEY = AttributeKey.longKey("duration.ms"); + public static final AttributeKey SUCCESS_KEY = AttributeKey.booleanKey("codeact.interceptor.success"); + public static final AttributeKey MODEL_NAME_KEY = AttributeKey.stringKey("gen_ai.response.model"); + public static final AttributeKey INPUT_TOKENS_KEY = AttributeKey.longKey("gen_ai.usage.input_tokens"); + public static final AttributeKey OUTPUT_TOKENS_KEY = AttributeKey.longKey("gen_ai.usage.output_tokens"); + public static final AttributeKey TOOL_NAME_KEY = AttributeKey.stringKey("gen_ai.tool.name"); + + /** + * ๆ‹ฆๆˆชๅ™จ็ฑปๅž‹ๆžšไธพ + */ + public enum InterceptorType { + MODEL, + TOOL + } + + private String interceptorName; + private InterceptorType interceptorType; + private String agentName; + private String sessionId; + private long durationMs; + private boolean success = true; + private String errorType; + private String errorMessage; + + // Model Interceptor specific fields + private String modelName; + private int inputTokens; + private int outputTokens; + private int messageCount; + + // Tool Interceptor specific fields + private String toolName; + private String toolArguments; + private int toolArgumentsLength; + private String toolResult; + private int toolResultLength; + + /** + * ่‡ชๅฎšไน‰ๆ•ฐๆฎๅญ˜ๅ‚จ๏ผŒๅ…่ฎธInterceptorๅœจๆ‰ง่กŒ่ฟ‡็จ‹ไธญๆณจๅ†Œๅฎšๅˆถๆ•ฐๆฎ + */ + private final Map customData = new HashMap<>(); + + public InterceptorObservationContext() { + } + + public InterceptorObservationContext(String interceptorName, InterceptorType interceptorType) { + this.interceptorName = interceptorName; + this.interceptorType = interceptorType; + } + + // ==================== Common Getters and Setters ==================== + + public String getInterceptorName() { + return interceptorName; + } + + public InterceptorObservationContext setInterceptorName(String interceptorName) { + this.interceptorName = interceptorName; + return this; + } + + public InterceptorType getInterceptorType() { + return interceptorType; + } + + public InterceptorObservationContext setInterceptorType(InterceptorType interceptorType) { + this.interceptorType = interceptorType; + return this; + } + + public String getAgentName() { + return agentName; + } + + public InterceptorObservationContext setAgentName(String agentName) { + this.agentName = agentName; + return this; + } + + public String getSessionId() { + return sessionId; + } + + public InterceptorObservationContext setSessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + public long getDurationMs() { + return durationMs; + } + + public InterceptorObservationContext setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public InterceptorObservationContext setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public InterceptorObservationContext setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public InterceptorObservationContext setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + // ==================== Model Interceptor Getters and Setters ==================== + + public String getModelName() { + return modelName; + } + + public InterceptorObservationContext setModelName(String modelName) { + this.modelName = modelName; + return this; + } + + public int getInputTokens() { + return inputTokens; + } + + public InterceptorObservationContext setInputTokens(int inputTokens) { + this.inputTokens = inputTokens; + return this; + } + + public int getOutputTokens() { + return outputTokens; + } + + public InterceptorObservationContext setOutputTokens(int outputTokens) { + this.outputTokens = outputTokens; + return this; + } + + public int getMessageCount() { + return messageCount; + } + + public InterceptorObservationContext setMessageCount(int messageCount) { + this.messageCount = messageCount; + return this; + } + + // ==================== Tool Interceptor Getters and Setters ==================== + + public String getToolName() { + return toolName; + } + + public InterceptorObservationContext setToolName(String toolName) { + this.toolName = toolName; + return this; + } + + public String getToolArguments() { + return toolArguments; + } + + public InterceptorObservationContext setToolArguments(String toolArguments) { + this.toolArguments = toolArguments; + this.toolArgumentsLength = toolArguments != null ? toolArguments.length() : 0; + return this; + } + + public int getToolArgumentsLength() { + return toolArgumentsLength; + } + + public String getToolResult() { + return toolResult; + } + + public InterceptorObservationContext setToolResult(String toolResult) { + this.toolResult = toolResult; + this.toolResultLength = toolResult != null ? toolResult.length() : 0; + return this; + } + + public int getToolResultLength() { + return toolResultLength; + } + + // ==================== Custom Data Methods ==================== + + /** + * ๆณจๅ†Œ่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @param value ๆ•ฐๆฎๅ€ผ + * @return this + */ + public InterceptorObservationContext putCustomData(String key, Object value) { + this.customData.put(key, value); + return this; + } + + /** + * ๆ‰น้‡ๆณจๅ†Œ่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param data ๆ•ฐๆฎMap + * @return this + */ + public InterceptorObservationContext putAllCustomData(Map data) { + if (data != null) { + this.customData.putAll(data); + } + return this; + } + + /** + * ่Žทๅ–่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @return ๆ•ฐๆฎๅ€ผ๏ผŒๅฆ‚ๆžœไธๅญ˜ๅœจ่ฟ”ๅ›žnull + */ + @SuppressWarnings("unchecked") + public T getCustomData(String key) { + return (T) this.customData.get(key); + } + + /** + * ่Žทๅ–ๆ‰€ๆœ‰่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @return ่‡ชๅฎšไน‰ๆ•ฐๆฎ็š„ไธๅฏๅ˜่ง†ๅ›พ + */ + public Map getAllCustomData() { + return Map.copyOf(customData); + } + + /** + * ๆฃ€ๆŸฅๆ˜ฏๅฆๅญ˜ๅœจๆŒ‡ๅฎš้”ฎ็š„่‡ชๅฎšไน‰ๆ•ฐๆฎ + * + * @param key ๆ•ฐๆฎ้”ฎ + * @return ๆ˜ฏๅฆๅญ˜ๅœจ + */ + public boolean hasCustomData(String key) { + return customData.containsKey(key); + } + + @Override + public String toString() { + return "InterceptorObservationContext{" + + "interceptorName='" + interceptorName + '\'' + + ", interceptorType=" + interceptorType + + ", agentName='" + agentName + '\'' + + ", sessionId='" + sessionId + '\'' + + ", durationMs=" + durationMs + + ", success=" + success + + ", modelName='" + modelName + '\'' + + ", toolName='" + toolName + '\'' + + ", customDataKeys=" + customData.keySet() + + '}'; + } + + /** + * ๅฐ†ไธŠไธ‹ๆ–‡่ฝฌๆขไธบ OpenTelemetry Attributes + * + * @return Attributes ๅฏน่ฑก + */ + public Attributes toAttributes() { + AttributesBuilder builder = Attributes.builder(); + + if (interceptorName != null) { + builder.put(INTERCEPTOR_NAME_KEY, interceptorName); + } + if (interceptorType != null) { + builder.put(INTERCEPTOR_TYPE_KEY, interceptorType.name()); + } + if (agentName != null) { + builder.put(AGENT_NAME_KEY, agentName); + } + if (sessionId != null) { + builder.put(SESSION_ID_KEY, sessionId); + } + if (durationMs > 0) { + builder.put(DURATION_MS_KEY, durationMs); + } + builder.put(SUCCESS_KEY, success); + + // Model specific + if (modelName != null) { + builder.put(MODEL_NAME_KEY, modelName); + } + if (inputTokens > 0) { + builder.put(INPUT_TOKENS_KEY, (long) inputTokens); + } + if (outputTokens > 0) { + builder.put(OUTPUT_TOKENS_KEY, (long) outputTokens); + } + + // Tool specific + if (toolName != null) { + builder.put(TOOL_NAME_KEY, toolName); + } + + // Error info + if (errorType != null) { + builder.put(AttributeKey.stringKey("error.type"), errorType); + } + if (errorMessage != null) { + builder.put(AttributeKey.stringKey("error.message"), errorMessage); + } + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + for (Map.Entry entry : customData.entrySet()) { + if (entry.getValue() != null) { + String key = "codeact.interceptor.custom." + entry.getKey(); + Object value = entry.getValue(); + if (value instanceof String) { + builder.put(AttributeKey.stringKey(key), (String) value); + } else if (value instanceof Long) { + builder.put(AttributeKey.longKey(key), (Long) value); + } else if (value instanceof Integer) { + builder.put(AttributeKey.longKey(key), ((Integer) value).longValue()); + } else if (value instanceof Double) { + builder.put(AttributeKey.doubleKey(key), (Double) value); + } else if (value instanceof Boolean) { + builder.put(AttributeKey.booleanKey(key), (Boolean) value); + } else { + builder.put(AttributeKey.stringKey(key), String.valueOf(value)); + } + } + } + + return builder.build(); + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/ReactPhaseObservationContext.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/ReactPhaseObservationContext.java new file mode 100644 index 0000000..d0801d1 --- /dev/null +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/observation/context/ReactPhaseObservationContext.java @@ -0,0 +1,521 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.core.observation.context; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * React ้˜ถๆฎต่ง‚ๆต‹ไธŠไธ‹ๆ–‡ + *

    + * ๅญ˜ๅ‚จ React ้˜ถๆฎต๏ผˆLlmNode ๅ’Œ ToolNode๏ผ‰ๆ‰ง่กŒ่ฟ‡็จ‹ไธญ็š„่ง‚ๆต‹ๆ•ฐๆฎใ€‚ + *

    + * ๅทฒไปŽ Micrometer Observation.Context ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class ReactPhaseObservationContext { + + // ๆ ‡ๅ‡†ๅฑžๆ€ง้”ฎๅฎšไน‰ + public static final AttributeKey SESSION_ID_KEY = AttributeKey.stringKey("gen_ai.conversation.id"); + public static final AttributeKey AGENT_NAME_KEY = AttributeKey.stringKey("gen_ai.agent.name"); + public static final AttributeKey ITERATION_KEY = AttributeKey.longKey("codeact.react.iteration"); + public static final AttributeKey NODE_TYPE_KEY = AttributeKey.stringKey("codeact.react.node_type"); + public static final AttributeKey NODE_ID_KEY = AttributeKey.stringKey("codeact.react.node_id"); + public static final AttributeKey DURATION_MS_KEY = AttributeKey.longKey("duration.ms"); + public static final AttributeKey SUCCESS_KEY = AttributeKey.booleanKey("codeact.react.success"); + public static final AttributeKey MODEL_NAME_KEY = AttributeKey.stringKey("gen_ai.response.model"); + public static final AttributeKey INPUT_TOKENS_KEY = AttributeKey.longKey("gen_ai.usage.input_tokens"); + public static final AttributeKey OUTPUT_TOKENS_KEY = AttributeKey.longKey("gen_ai.usage.output_tokens"); + public static final AttributeKey FINISH_REASON_KEY = AttributeKey.stringKey("gen_ai.response.finish_reasons"); + public static final AttributeKey TOOL_CALLS_COUNT_KEY = AttributeKey.longKey("codeact.react.tool_calls_count"); + + /** + * Node ็ฑปๅž‹ๆžšไธพ + */ + public enum NodeType { + LLM, + TOOL, + UNKNOWN + } + + private String sessionId; + private String agentName; + private int iteration; + private NodeType nodeType; + private String nodeId; + private long durationMs; + private boolean success = true; + private String errorType; + private String errorMessage; + + // LlmNode specific fields + private String modelName; + private int inputTokens; + private int outputTokens; + private int promptMessageCount; + private int responseMessageCount; + private String finishReason; + + /** + * LLM ๅฏ่ฐƒ็”จ็š„ๅทฅๅ…ทๅ็งฐๅˆ—่กจ + */ + private List availableToolNames = new ArrayList<>(); + + /** + * LLM ่พ“ๅ…ฅๆถˆๆฏๅ†…ๅฎน๏ผˆๆ‘˜่ฆ๏ผ‰ + */ + private String inputSummary; + + /** + * LLM ่พ“ๅ‡บๆถˆๆฏๅ†…ๅฎน๏ผˆๆ‘˜่ฆ๏ผ‰ + */ + private String outputSummary; + + // ToolNode specific fields + private List toolCalls = new ArrayList<>(); + + /** + * ่‡ชๅฎšไน‰ๆ•ฐๆฎๅญ˜ๅ‚จ + */ + private final Map customData = new HashMap<>(); + + public ReactPhaseObservationContext() { + } + + public ReactPhaseObservationContext(String sessionId, NodeType nodeType, String nodeId) { + this.sessionId = sessionId; + this.nodeType = nodeType; + this.nodeId = nodeId; + } + + // ==================== Common Getters and Setters ==================== + + public String getSessionId() { + return sessionId; + } + + public ReactPhaseObservationContext setSessionId(String sessionId) { + this.sessionId = sessionId; + return this; + } + + public String getAgentName() { + return agentName; + } + + public ReactPhaseObservationContext setAgentName(String agentName) { + this.agentName = agentName; + return this; + } + + public int getIteration() { + return iteration; + } + + public ReactPhaseObservationContext setIteration(int iteration) { + this.iteration = iteration; + return this; + } + + public NodeType getNodeType() { + return nodeType; + } + + public ReactPhaseObservationContext setNodeType(NodeType nodeType) { + this.nodeType = nodeType; + return this; + } + + public String getNodeId() { + return nodeId; + } + + public ReactPhaseObservationContext setNodeId(String nodeId) { + this.nodeId = nodeId; + return this; + } + + public long getDurationMs() { + return durationMs; + } + + public ReactPhaseObservationContext setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public ReactPhaseObservationContext setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public ReactPhaseObservationContext setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public ReactPhaseObservationContext setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + // ==================== LlmNode Getters and Setters ==================== + + public String getModelName() { + return modelName; + } + + public ReactPhaseObservationContext setModelName(String modelName) { + this.modelName = modelName; + return this; + } + + public int getInputTokens() { + return inputTokens; + } + + public ReactPhaseObservationContext setInputTokens(int inputTokens) { + this.inputTokens = inputTokens; + return this; + } + + public int getOutputTokens() { + return outputTokens; + } + + public ReactPhaseObservationContext setOutputTokens(int outputTokens) { + this.outputTokens = outputTokens; + return this; + } + + public int getPromptMessageCount() { + return promptMessageCount; + } + + public ReactPhaseObservationContext setPromptMessageCount(int promptMessageCount) { + this.promptMessageCount = promptMessageCount; + return this; + } + + public int getResponseMessageCount() { + return responseMessageCount; + } + + public ReactPhaseObservationContext setResponseMessageCount(int responseMessageCount) { + this.responseMessageCount = responseMessageCount; + return this; + } + + public String getFinishReason() { + return finishReason; + } + + public ReactPhaseObservationContext setFinishReason(String finishReason) { + this.finishReason = finishReason; + return this; + } + + public List getAvailableToolNames() { + return availableToolNames; + } + + public ReactPhaseObservationContext setAvailableToolNames(List availableToolNames) { + this.availableToolNames = availableToolNames != null ? availableToolNames : new ArrayList<>(); + return this; + } + + public String getInputSummary() { + return inputSummary; + } + + public ReactPhaseObservationContext setInputSummary(String inputSummary) { + this.inputSummary = inputSummary; + return this; + } + + public String getOutputSummary() { + return outputSummary; + } + + public ReactPhaseObservationContext setOutputSummary(String outputSummary) { + this.outputSummary = outputSummary; + return this; + } + + // ==================== ToolNode Getters and Setters ==================== + + public List getToolCalls() { + return toolCalls; + } + + public ReactPhaseObservationContext setToolCalls(List toolCalls) { + this.toolCalls = toolCalls; + return this; + } + + public ReactPhaseObservationContext addToolCall(ToolCallInfo toolCall) { + this.toolCalls.add(toolCall); + return this; + } + + // ==================== Custom Data Methods ==================== + + public ReactPhaseObservationContext putCustomData(String key, Object value) { + this.customData.put(key, value); + return this; + } + + public ReactPhaseObservationContext putAllCustomData(Map data) { + if (data != null) { + this.customData.putAll(data); + } + return this; + } + + @SuppressWarnings("unchecked") + public T getCustomData(String key) { + return (T) this.customData.get(key); + } + + public Map getAllCustomData() { + return Map.copyOf(customData); + } + + // ==================== Tool Call Info ==================== + + /** + * ๅทฅๅ…ท่ฐƒ็”จไฟกๆฏ + */ + public static class ToolCallInfo { + private String toolName; + private String arguments; + private int argumentsLength; + private String result; + private int resultLength; + private long durationMs; + private boolean success; + private String errorType; + private String errorMessage; + + public ToolCallInfo() { + } + + public ToolCallInfo(String toolName) { + this.toolName = toolName; + } + + // Getters and Setters + + public String getToolName() { + return toolName; + } + + public ToolCallInfo setToolName(String toolName) { + this.toolName = toolName; + return this; + } + + public String getArguments() { + return arguments; + } + + public ToolCallInfo setArguments(String arguments) { + this.arguments = arguments; + this.argumentsLength = arguments != null ? arguments.length() : 0; + return this; + } + + public int getArgumentsLength() { + return argumentsLength; + } + + public String getResult() { + return result; + } + + public ToolCallInfo setResult(String result) { + this.result = result; + this.resultLength = result != null ? result.length() : 0; + return this; + } + + public int getResultLength() { + return resultLength; + } + + public long getDurationMs() { + return durationMs; + } + + public ToolCallInfo setDurationMs(long durationMs) { + this.durationMs = durationMs; + return this; + } + + public boolean isSuccess() { + return success; + } + + public ToolCallInfo setSuccess(boolean success) { + this.success = success; + return this; + } + + public String getErrorType() { + return errorType; + } + + public ToolCallInfo setErrorType(String errorType) { + this.errorType = errorType; + return this; + } + + public String getErrorMessage() { + return errorMessage; + } + + public ToolCallInfo setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + @Override + public String toString() { + return "ToolCallInfo{" + + "toolName='" + toolName + '\'' + + ", argumentsLength=" + argumentsLength + + ", resultLength=" + resultLength + + ", durationMs=" + durationMs + + ", success=" + success + + '}'; + } + } + + @Override + public String toString() { + return "ReactPhaseObservationContext{" + + "sessionId='" + sessionId + '\'' + + ", agentName='" + agentName + '\'' + + ", iteration=" + iteration + + ", nodeType=" + nodeType + + ", nodeId='" + nodeId + '\'' + + ", durationMs=" + durationMs + + ", success=" + success + + ", modelName='" + modelName + '\'' + + ", toolCallsCount=" + toolCalls.size() + + '}'; + } + + /** + * ๅฐ†ไธŠไธ‹ๆ–‡่ฝฌๆขไธบ OpenTelemetry Attributes + * + * @return Attributes ๅฏน่ฑก + */ + public Attributes toAttributes() { + AttributesBuilder builder = Attributes.builder(); + + if (sessionId != null) { + builder.put(SESSION_ID_KEY, sessionId); + } + if (agentName != null) { + builder.put(AGENT_NAME_KEY, agentName); + } + if (iteration > 0) { + builder.put(ITERATION_KEY, (long) iteration); + } + if (nodeType != null) { + builder.put(NODE_TYPE_KEY, nodeType.name()); + } + if (nodeId != null) { + builder.put(NODE_ID_KEY, nodeId); + } + if (durationMs > 0) { + builder.put(DURATION_MS_KEY, durationMs); + } + builder.put(SUCCESS_KEY, success); + + // LLM specific + if (modelName != null) { + builder.put(MODEL_NAME_KEY, modelName); + } + if (inputTokens > 0) { + builder.put(INPUT_TOKENS_KEY, (long) inputTokens); + } + if (outputTokens > 0) { + builder.put(OUTPUT_TOKENS_KEY, (long) outputTokens); + } + if (finishReason != null) { + builder.put(FINISH_REASON_KEY, finishReason); + } + + // Tool calls + if (!toolCalls.isEmpty()) { + builder.put(TOOL_CALLS_COUNT_KEY, (long) toolCalls.size()); + } + + // Error info + if (errorType != null) { + builder.put(AttributeKey.stringKey("error.type"), errorType); + } + if (errorMessage != null) { + builder.put(AttributeKey.stringKey("error.message"), errorMessage); + } + + // ๆทปๅŠ ่‡ชๅฎšไน‰ๆ•ฐๆฎ + for (Map.Entry entry : customData.entrySet()) { + if (entry.getValue() != null) { + String key = "codeact.react.custom." + entry.getKey(); + Object value = entry.getValue(); + if (value instanceof String) { + builder.put(AttributeKey.stringKey(key), (String) value); + } else if (value instanceof Long) { + builder.put(AttributeKey.longKey(key), (Long) value); + } else if (value instanceof Integer) { + builder.put(AttributeKey.longKey(key), ((Integer) value).longValue()); + } else if (value instanceof Double) { + builder.put(AttributeKey.doubleKey(key), (Double) value); + } else if (value instanceof Boolean) { + builder.put(AttributeKey.booleanKey(key), (Boolean) value); + } else { + builder.put(AttributeKey.stringKey(key), String.valueOf(value)); + } + } + } + + return builder.build(); + } +} + diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/ToolRegistryBridge.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/ToolRegistryBridge.java index e06853a..e9e2f89 100644 --- a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/ToolRegistryBridge.java +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/ToolRegistryBridge.java @@ -16,11 +16,15 @@ package com.alibaba.assistant.agent.core.tool; import com.alibaba.assistant.agent.common.tools.CodeactTool; +import com.alibaba.assistant.agent.core.model.ToolCallRecord; import com.alibaba.assistant.agent.core.tool.schema.ReturnSchemaRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ToolContext; +import java.util.ArrayList; +import java.util.List; + /** * ToolRegistry Bridge - ไพ› Python ่ฐƒ็”จ็š„ Java ๅฏน่ฑกใ€‚ * @@ -38,6 +42,11 @@ public class ToolRegistryBridge { private final ToolContext toolContext; + /** + * ๅทฅๅ…ท่ฐƒ็”จ่ฟฝ่ธช่ฎฐๅฝ• + */ + private final List callTrace = new ArrayList<>(); + /** * ๆž„้€ ๅ‡ฝๆ•ฐใ€‚ * @param registry ๅทฅๅ…ทๆณจๅ†Œ่กจ @@ -61,6 +70,9 @@ public String callTool(String toolName, String argsJson) { toolContext != null, toolContext != null && toolContext.getContext() != null ? toolContext.getContext().keySet() : "null"); + // ่ฎฐๅฝ•ๅทฅๅ…ท่ฐƒ็”จๅˆฐ่ฟฝ่ธชๅˆ—่กจ + recordToolCall(toolName); + try { // ไปŽๆณจๅ†Œ่กจ่Žทๅ–ๅทฅๅ…ท CodeactTool tool = registry.getTool(toolName) @@ -118,5 +130,45 @@ private void observeReturnSchema(String toolName, String resultJson, boolean suc } } + /** + * ่ฎฐๅฝ•ๅทฅๅ…ท่ฐƒ็”จใ€‚ + * @param toolName ๅทฅๅ…ทๅ็งฐ + */ + private void recordToolCall(String toolName) { + // ่Žทๅ–ๅทฅๅ…ท็š„targetClassNameๆฅๆž„ๅปบๅฎŒๆ•ด็š„ๅทฅๅ…ทๆ ‡่ฏ† + String toolIdentifier = toolName; + try { + CodeactTool tool = registry.getTool(toolName).orElse(null); + if (tool != null && tool.getCodeactMetadata() != null) { + String targetClassName = tool.getCodeactMetadata().targetClassName(); + if (targetClassName != null && !targetClassName.isEmpty()) { + toolIdentifier = targetClassName + "." + toolName; + } + } + } catch (Exception e) { + logger.warn("ToolRegistryBridge#recordToolCall - reason=่Žทๅ–ๅทฅๅ…ทๅ…ƒๆ•ฐๆฎๅคฑ่ดฅ, toolName={}", toolName); + } + + ToolCallRecord record = new ToolCallRecord(callTrace.size() + 1, toolIdentifier); + callTrace.add(record); + logger.info("ToolRegistryBridge#recordToolCall - reason=่ฎฐๅฝ•ๅทฅๅ…ท่ฐƒ็”จ, order={}, tool={}", record.getOrder(), record.getTool()); + } + + /** + * ่Žทๅ–ๅทฅๅ…ท่ฐƒ็”จ่ฟฝ่ธช่ฎฐๅฝ•ใ€‚ + * @return ๅทฅๅ…ท่ฐƒ็”จ่ฎฐๅฝ•ๅˆ—่กจ + */ + public List getCallTrace() { + return new ArrayList<>(callTrace); + } + + /** + * ๆธ…็ฉบๅทฅๅ…ท่ฐƒ็”จ่ฟฝ่ธช่ฎฐๅฝ•ใ€‚ + */ + public void clearCallTrace() { + callTrace.clear(); + logger.debug("ToolRegistryBridge#clearCallTrace - reason=ๆธ…็ฉบ่ฐƒ็”จ่ฟฝ่ธช่ฎฐๅฝ•"); + } + } diff --git a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/view/PythonToolViewRenderer.java b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/view/PythonToolViewRenderer.java index 7e42acf..2767314 100644 --- a/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/view/PythonToolViewRenderer.java +++ b/assistant-agent-core/src/main/java/com/alibaba/assistant/agent/core/tool/view/PythonToolViewRenderer.java @@ -124,31 +124,8 @@ public String renderClassStub(String className, String classDescription, Listไผ˜ๅ…ˆไปŽ codeInvocationTemplate ไธญๆๅ–๏ผŒไธŽ GraalCodeExecutor.generatePythonMethod ไฟๆŒไธ€่‡ดใ€‚ + * ่ฟ™็กฎไฟไบ† LLM ็”Ÿๆˆ็š„ไปฃ็ ไธญ็š„ๆ–นๆณ•ๅไธŽๅฎž้™…ๆณจๅ…ฅๅˆฐ Python ็Žฏๅขƒไธญ็š„ๆ–นๆณ•ๅไธ€่‡ดใ€‚ + * + * @param tool CodeactTool ๅฎžไพ‹ + * @return ๆ–นๆณ•ๅ + */ + private String extractMethodName(CodeactTool tool) { + // ่Žทๅ– ParameterTree ๅˆคๆ–ญๆ˜ฏๅฆๆœ‰็ป“ๆž„ๅŒ–ๅ‚ๆ•ฐ + ParameterTree parameterTree = tool.getParameterTree(); + + // ๅฆ‚ๆžœๆฒกๆœ‰็ป“ๆž„ๅŒ–ๅ‚ๆ•ฐ๏ผŒๅฐ่ฏ•ไปŽ codeInvocationTemplate ๆๅ–ๆ–นๆณ•ๅ + // ่ฟ™ไธŽ GraalCodeExecutor.generatePythonMethod ็š„้€ป่พ‘ไธ€่‡ด + if (parameterTree == null || !parameterTree.hasParameters()) { + String invocationTemplate = tool.getCodeactMetadata() != null + ? tool.getCodeactMetadata().codeInvocationTemplate() + : null; + + if (invocationTemplate != null && invocationTemplate.contains("(")) { + int parenIndex = invocationTemplate.indexOf('('); + String extractedName = invocationTemplate.substring(0, parenIndex).trim(); + if (!extractedName.isEmpty()) { + logger.debug("PythonToolViewRenderer#extractMethodName - reason=ไปŽcodeInvocationTemplateๆๅ–ๆ–นๆณ•ๅ, " + + "methodName={}, toolClass={}", extractedName, tool.getClass().getSimpleName()); + return extractedName; + } + } + } + + // ๅ›ž้€€ๅˆฐ ToolDefinition.name() + String methodName = null; + if (tool.getToolDefinition() != null) { + methodName = tool.getToolDefinition().name(); + logger.debug("PythonToolViewRenderer#extractMethodName - reason=ไปŽToolDefinition่Žทๅ–ๅ็งฐ, name={}, toolClass={}", + methodName, tool.getClass().getSimpleName()); + } + + // ๅฆ‚ๆžœ่ฟ˜ๆ˜ฏ็ฉบ๏ผŒๅฐ่ฏ• getName() + if (methodName == null || methodName.isEmpty()) { + methodName = tool.getName(); + logger.debug("PythonToolViewRenderer#extractMethodName - reason=ๅฐ่ฏ•getName, name={}", methodName); + } + + // ๆœ€ๅŽ็š„ไฟๅบ• + if (methodName == null || methodName.isEmpty()) { + methodName = "unknown_method"; + logger.warn("PythonToolViewRenderer#extractMethodName - reason=ๅทฅๅ…ทๅไธบ็ฉบไฝฟ็”จ้ป˜่ฎคๅ็งฐ, toolClass={}", + tool.getClass().getSimpleName()); + } + + return methodName; + } + } diff --git a/assistant-agent-evaluation/pom.xml b/assistant-agent-evaluation/pom.xml index 2a84b46..e9f0be9 100644 --- a/assistant-agent-evaluation/pom.xml +++ b/assistant-agent-evaluation/pom.xml @@ -6,7 +6,7 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-evaluation @@ -50,6 +50,16 @@ slf4j-api + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-context + + com.alibaba.cloud.ai spring-ai-alibaba-dashscope diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/DefaultEvaluationService.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/DefaultEvaluationService.java index 23bbe15..de5bf39 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/DefaultEvaluationService.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/DefaultEvaluationService.java @@ -19,6 +19,10 @@ import com.alibaba.assistant.agent.evaluation.model.EvaluationContext; import com.alibaba.assistant.agent.evaluation.model.EvaluationResult; import com.alibaba.assistant.agent.evaluation.model.EvaluationSuite; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; + import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -58,24 +62,69 @@ public DefaultEvaluationService(GraphBasedEvaluationExecutor executor, ExecutorS @Override public EvaluationResult evaluate(EvaluationSuite suite, EvaluationContext context) { - logger.info("Starting evaluation for suite: {}", suite.getName()); + return evaluate(suite, context, null); + } + + @Override + public EvaluationResult evaluate(EvaluationSuite suite, EvaluationContext context, + Span parentSpan) { + logger.info("DefaultEvaluationService#evaluate - reason=ๅผ€ๅง‹่ฏ„ไผฐ, suite={}, hasParent={}", + suite.getName(), parentSpan != null); + + // ่ฎพ็ฝฎ parent Span ๅˆฐ ThreadLocal + if (parentSpan != null) { + com.alibaba.assistant.agent.evaluation.observation.EvaluationObservationLifecycleListener + .setParentSpan(parentSpan); + } try { - // Execute evaluation - EvaluationResult result = executor.execute(suite, context); + // Execute evaluation with parent span + EvaluationResult result = executor.execute(suite, context, parentSpan); - logger.info("Evaluation completed for suite: {}", suite.getName()); + logger.info("DefaultEvaluationService#evaluate - reason=่ฏ„ไผฐๅฎŒๆˆ, suite={}", suite.getName()); return result; } catch (Exception e) { - logger.error("Error executing evaluation for suite: {}", suite.getName(), e); + logger.error("DefaultEvaluationService#evaluate - reason=่ฏ„ไผฐๅคฑ่ดฅ, suite={}", suite.getName(), e); throw new RuntimeException("Evaluation execution failed", e); + } finally { + // ๆธ…็† ThreadLocal + if (parentSpan != null) { + com.alibaba.assistant.agent.evaluation.observation.EvaluationObservationLifecycleListener + .clearParentSpan(); + } } } @Override public CompletableFuture evaluateAsync(EvaluationSuite suite, EvaluationContext context) { - return CompletableFuture.supplyAsync(() -> evaluate(suite, context), asyncExecutor); + return evaluateAsync(suite, context, null); + } + + @Override + public CompletableFuture evaluateAsync(EvaluationSuite suite, EvaluationContext context, + Span parentSpan) { + // ๆ•่Žทๅฝ“ๅ‰็š„ parent Span ๅ’Œ Context๏ผŒๅœจๅผ‚ๆญฅๆ‰ง่กŒๆ—ถไผ ้€’ + final Span capturedParent = parentSpan; + final Context capturedContext = parentSpan != null ? Context.current().with(parentSpan) : Context.current(); + + return CompletableFuture.supplyAsync(() -> { + // ๅœจๅผ‚ๆญฅ็บฟ็จ‹ไธญๆขๅค trace context + try (Scope ignored = capturedContext.makeCurrent()) { + // ่ฎพ็ฝฎ parent Span ๅˆฐ ThreadLocal๏ผŒไพ› EvaluationObservationLifecycleListener ไฝฟ็”จ + if (capturedParent != null) { + com.alibaba.assistant.agent.evaluation.observation.EvaluationObservationLifecycleListener + .setParentSpan(capturedParent); + } + try { + return executor.execute(suite, context, capturedParent); + } finally { + // ๆธ…็† ThreadLocal + com.alibaba.assistant.agent.evaluation.observation.EvaluationObservationLifecycleListener + .clearParentSpan(); + } + } + }, asyncExecutor); } @Override @@ -93,7 +142,7 @@ public void registerSuite(EvaluationSuite suite) { throw new IllegalArgumentException("Suite ID cannot be null or empty"); } suiteRegistry.put(suite.getId(), suite); - logger.info("Registered evaluation suite: {}", suite.getId()); + logger.info("DefaultEvaluationService#registerSuite - reason=ๆณจๅ†Œ่ฏ„ไผฐๅฅ—ไปถ, suiteId={}", suite.getId()); } /** diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/EvaluationService.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/EvaluationService.java index c68c0f4..c20e6ea 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/EvaluationService.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/EvaluationService.java @@ -18,6 +18,7 @@ import com.alibaba.assistant.agent.evaluation.model.EvaluationContext; import com.alibaba.assistant.agent.evaluation.model.EvaluationResult; import com.alibaba.assistant.agent.evaluation.model.EvaluationSuite; +import io.opentelemetry.api.trace.Span; import java.util.concurrent.CompletableFuture; @@ -38,6 +39,20 @@ public interface EvaluationService { */ EvaluationResult evaluate(EvaluationSuite suite, EvaluationContext context); + /** + * Execute evaluation synchronously with parent Span for tracing + * + * @param suite Evaluation suite to execute + * @param context Evaluation context + * @param parentSpan Parent span for establishing span hierarchy + * @return Evaluation result + */ + default EvaluationResult evaluate(EvaluationSuite suite, EvaluationContext context, + Span parentSpan) { + // Default implementation ignores parentSpan for backward compatibility + return evaluate(suite, context); + } + /** * Execute evaluation asynchronously * @@ -47,6 +62,20 @@ public interface EvaluationService { */ CompletableFuture evaluateAsync(EvaluationSuite suite, EvaluationContext context); + /** + * Execute evaluation asynchronously with parent Span for tracing + * + * @param suite Evaluation suite to execute + * @param context Evaluation context + * @param parentSpan Parent span for establishing span hierarchy + * @return CompletableFuture of evaluation result + */ + default CompletableFuture evaluateAsync(EvaluationSuite suite, EvaluationContext context, + Span parentSpan) { + // Default implementation ignores parentSpan for backward compatibility + return evaluateAsync(suite, context); + } + /** * Load a pre-configured evaluation suite by ID * diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/builder/EvaluationSuiteBuilder.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/builder/EvaluationSuiteBuilder.java index afd392e..8131576 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/builder/EvaluationSuiteBuilder.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/builder/EvaluationSuiteBuilder.java @@ -21,10 +21,14 @@ import com.alibaba.assistant.agent.evaluation.model.EvaluationCriterion; import com.alibaba.assistant.agent.evaluation.model.EvaluationSuite; import com.alibaba.cloud.ai.graph.CompiledGraph; +import com.alibaba.cloud.ai.graph.CompileConfig; +import com.alibaba.cloud.ai.graph.GraphLifecycleListener; import com.alibaba.cloud.ai.graph.KeyStrategy; import com.alibaba.cloud.ai.graph.StateGraph; import com.alibaba.cloud.ai.graph.exception.GraphStateException; import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import java.util.ArrayList; import java.util.Collections; @@ -47,6 +51,9 @@ public class EvaluationSuiteBuilder { private final EvaluatorRegistry evaluatorRegistry; private final BatchAggregationStrategyRegistry aggregationStrategyRegistry; private ExecutorService executorService; + private final List lifecycleListeners = new ArrayList<>(); + private Tracer tracer; + private Span parentSpan; public EvaluationSuiteBuilder(String id, EvaluatorRegistry evaluatorRegistry) { this(id, evaluatorRegistry, new BatchAggregationStrategyRegistry(), null); @@ -80,6 +87,54 @@ public EvaluationSuiteBuilder executorService(ExecutorService executorService) { return this; } + /** + * ๆทปๅŠ  GraphLifecycleListener ็”จไบŽๅฏ่ง‚ๆต‹ๆ€ง + * + * @param listener GraphLifecycleListener + * @return this + */ + public EvaluationSuiteBuilder lifecycleListener(GraphLifecycleListener listener) { + if (listener != null) { + this.lifecycleListeners.add(listener); + } + return this; + } + + /** + * ๆทปๅŠ ๅคšไธช GraphLifecycleListeners + * + * @param listeners GraphLifecycleListeners + * @return this + */ + public EvaluationSuiteBuilder lifecycleListeners(List listeners) { + if (listeners != null) { + this.lifecycleListeners.addAll(listeners); + } + return this; + } + + /** + * ่ฎพ็ฝฎ Tracer ็”จไบŽๅฏ่ง‚ๆต‹ๆ€ง + * + * @param tracer Tracer + * @return this + */ + public EvaluationSuiteBuilder tracer(Tracer tracer) { + this.tracer = tracer; + return this; + } + + /** + * ่ฎพ็ฝฎ parent Span๏ผŒๆ‰€ๆœ‰่ฏ„ไผฐ้กน็š„ span ้ƒฝไผš็ปงๆ‰ฟๆญค็ˆถ span + * + * @param parentSpan ็ˆถ Span + * @return this + */ + public EvaluationSuiteBuilder parentSpan(Span parentSpan) { + this.parentSpan = parentSpan; + return this; + } + public EvaluationSuiteBuilder name(String name) { suite.setName(name); @@ -237,8 +292,20 @@ public EvaluationSuite build() { stateGraph.addEdge(previousJoinNode, StateGraph.END); } - // Compile graph; keep representation inside suite - CompiledGraph compiled = stateGraph.compile(); + // Build CompileConfig with lifecycleListeners for observability + CompileConfig.Builder configBuilder = CompileConfig.builder(); + + // Add ObservationRegistry if available + + // Add all lifecycleListeners + for (GraphLifecycleListener listener : lifecycleListeners) { + configBuilder.withLifecycleListener(listener); + } + + CompileConfig compileConfig = configBuilder.build(); + + // Compile graph with config + CompiledGraph compiled = stateGraph.compile(compileConfig); suite.setCompiledGraph(compiled); } catch (GraphStateException e) { diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/evaluator/LLMBasedEvaluator.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/evaluator/LLMBasedEvaluator.java index a80cba6..9304e0f 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/evaluator/LLMBasedEvaluator.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/evaluator/LLMBasedEvaluator.java @@ -65,6 +65,9 @@ public CriterionResult evaluate(CriterionExecutionContext executionContext) { // Build prompt String promptText = buildPrompt(executionContext); + // Store prompt for observability + result.setRawPrompt(promptText); + logger.debug("Evaluating criterion {} with LLM, prompt: {}", executionContext.getCriterion().getName(), promptText); diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/CriterionEvaluationAction.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/CriterionEvaluationAction.java index 5939f86..aa6a7ff 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/CriterionEvaluationAction.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/CriterionEvaluationAction.java @@ -132,6 +132,11 @@ public Map apply(OverAllState state) { updates.put(criterion.getName() + "_value", result.getValue()); } + // ๆณจๅ†Œ็ป“ๆžœๅˆฐ้™ๆ€ๅญ˜ๅ‚จ๏ผŒไพ› EvaluationObservationLifecycleListener ่ฏปๅ– + // ๅ› ไธบ after() ๅ›ž่ฐƒไธญ็š„ state ๅฏ่ƒฝๆ˜ฏๅฟซ็…ง๏ผŒไธๅŒ…ๅซๅˆšๅ†™ๅ…ฅ็š„ๆ•ฐๆฎ + com.alibaba.assistant.agent.evaluation.observation.EvaluationObservationLifecycleListener + .registerCriterionResult(criterion.getName(), result); + logger.info("Criterion {} completed with result: {}", criterion.getName(), result.getValue()); } catch (Exception e) { diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/GraphBasedEvaluationExecutor.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/GraphBasedEvaluationExecutor.java index 96a046f..84d46d9 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/GraphBasedEvaluationExecutor.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/executor/GraphBasedEvaluationExecutor.java @@ -20,10 +20,12 @@ import com.alibaba.assistant.agent.evaluation.model.EvaluationResult; import com.alibaba.assistant.agent.evaluation.model.EvaluationCriterion; import com.alibaba.assistant.agent.evaluation.model.EvaluationSuite; +import com.alibaba.assistant.agent.evaluation.observation.EvaluationObservationLifecycleListener; import com.alibaba.cloud.ai.graph.CompiledGraph; import com.alibaba.cloud.ai.graph.NodeOutput; import com.alibaba.cloud.ai.graph.RunnableConfig; import com.alibaba.cloud.ai.graph.StateGraph; +import io.opentelemetry.api.trace.Span; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,6 +75,19 @@ public ExecutorService getExecutorService() { * Execute evaluation for a suite using its compiled graph. */ public EvaluationResult execute(EvaluationSuite suite, EvaluationContext context) { + return execute(suite, context, null); + } + + /** + * Execute evaluation for a suite using its compiled graph with parent Span. + * + * @param suite Evaluation suite to execute + * @param context Evaluation context + * @param parentSpan Parent span for establishing span hierarchy + * @return Evaluation result + */ + public EvaluationResult execute(EvaluationSuite suite, EvaluationContext context, + Span parentSpan) { EvaluationResult result = new EvaluationResult(); result.setSuiteId(suite.getId()); result.setSuiteName(suite.getName()); @@ -90,6 +105,13 @@ public EvaluationResult execute(EvaluationSuite suite, EvaluationContext context initialData.put("suite", suite); initialData.put("evaluationContext", context); + // ๅฐ† parent Span ๅญ˜ๅ…ฅ ThreadLocal๏ผŒ่€Œไธๆ˜ฏ initialData + // ๅ› ไธบ Span ๅฏน่ฑกไธ่ƒฝ่ขซๅบๅˆ—ๅŒ– + if (parentSpan != null) { + EvaluationObservationLifecycleListener.setParentSpan(parentSpan); + logger.debug("GraphBasedEvaluationExecutor#execute - reason=่ฎพ็ฝฎparentSpanๅˆฐThreadLocal"); + } + // Use CompiledGraph.invokeAndGetOutput to execute the graph // This follows graph-core best practices and ensures we get the final state // Important: graph-core uses ParallelNode for fan-out edges (e.g. multiple START children, @@ -102,6 +124,7 @@ public EvaluationResult execute(EvaluationSuite suite, EvaluationContext context for (String nodeId : parallelNodeIds) { configBuilder.addParallelNodeExecutor(nodeId, executorService); } + RunnableConfig config = configBuilder.build(); Optional outputOpt = compiledGraph.invokeAndGetOutput(initialData, config); @@ -136,6 +159,8 @@ public EvaluationResult execute(EvaluationSuite suite, EvaluationContext context } finally { result.setEndTimeMillis(System.currentTimeMillis()); + // ๆธ…็† ThreadLocal๏ผŒ้ฟๅ…ๅ†…ๅญ˜ๆณ„ๆผ + EvaluationObservationLifecycleListener.clearParentSpan(); } return result; @@ -218,7 +243,7 @@ private EvaluationResult.EvaluationStatistics calculateStatistics( */ public void shutdown() { if (shouldShutdownExecutor && executorService != null) { - logger.info("Shutting down ExecutorService"); + logger.info("GraphBasedEvaluationExecutor#shutdown - reason=ๅ…ณ้—ญExecutorService"); executorService.shutdown(); } } diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/model/CriterionResult.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/model/CriterionResult.java index 5487b77..f26eb0b 100644 --- a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/model/CriterionResult.java +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/model/CriterionResult.java @@ -49,6 +49,11 @@ public class CriterionResult { */ private String rawResponse; + /** + * Raw prompt sent to LLM (for observability) + */ + private String rawPrompt; + /** * Error message if status is ERROR */ @@ -111,6 +116,14 @@ public void setRawResponse(String rawResponse) { this.rawResponse = rawResponse; } + public String getRawPrompt() { + return rawPrompt; + } + + public void setRawPrompt(String rawPrompt) { + this.rawPrompt = rawPrompt; + } + public String getErrorMessage() { return errorMessage; } diff --git a/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/observation/EvaluationObservationLifecycleListener.java b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/observation/EvaluationObservationLifecycleListener.java new file mode 100644 index 0000000..7417bf4 --- /dev/null +++ b/assistant-agent-evaluation/src/main/java/com/alibaba/assistant/agent/evaluation/observation/EvaluationObservationLifecycleListener.java @@ -0,0 +1,363 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.assistant.agent.evaluation.observation; + +import com.alibaba.assistant.agent.evaluation.model.CriterionResult; +import com.alibaba.cloud.ai.graph.GraphLifecycleListener; +import com.alibaba.cloud.ai.graph.RunnableConfig; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ่ฏ„ไผฐ Graph ็š„ๅฏ่ง‚ๆต‹ๆ€ง็”Ÿๅ‘ฝๅ‘จๆœŸ็›‘ๅฌๅ™จ + *

    + * ไธบๆฏไธช่ฏ„ไผฐ้กน๏ผˆcriterion๏ผ‰ๅˆ›ๅปบ็‹ฌ็ซ‹็š„ OpenTelemetry Span๏ผŒ่ฎฐๅฝ•๏ผš + *

      + *
    • ่ฏ„ไผฐ้กนๅ็งฐ
    • + *
    • LLM ่พ“ๅ…ฅ๏ผˆprompt๏ผ‰
    • + *
    • LLM ่พ“ๅ‡บ๏ผˆresponse๏ผ‰
    • + *
    • ่ฏ„ไผฐ็ป“ๆžœ
    • + *
    • ๆ‰ง่กŒๆ—ถ้•ฟ
    • + *
    + *

    + * ๅทฒไปŽ Micrometer Observation ่ฟ็งปๅˆฐ OpenTelemetry ๅŽŸ็”Ÿ APIใ€‚ + * + * @author Assistant Agent Team + * @since 1.0.0 + */ +public class EvaluationObservationLifecycleListener implements GraphLifecycleListener { + + private static final Logger log = LoggerFactory.getLogger(EvaluationObservationLifecycleListener.class); + + /** + * ๅœจ RunnableConfig metadata ไธญๅญ˜ๅ‚จ parent Span ็š„ key + * @deprecated ไฝฟ็”จ ThreadLocal ไปฃๆ›ฟ๏ผŒๅ› ไธบ Span ๅฏน่ฑกไธ่ƒฝ่ขซๅบๅˆ—ๅŒ– + */ + @Deprecated + public static final String PARENT_SPAN_KEY = "_evaluation_parent_span_"; + + /** + * InheritableThreadLocal ๅญ˜ๅ‚จ parent Span + * ไฝฟ็”จ InheritableThreadLocal ็กฎไฟๅญ็บฟ็จ‹๏ผˆๅผ‚ๆญฅๆ‰ง่กŒ๏ผ‰่ƒฝ็ปงๆ‰ฟ parent + * ๅ› ไธบ Span ๅฏน่ฑกๅŒ…ๅซๅคๆ‚็š„ๅผ•็”จ้“พ๏ผŒไธ่ƒฝๆ”พๅ…ฅไผš่ขซๅบๅˆ—ๅŒ–็š„ Map ไธญ + */ + private static final InheritableThreadLocal PARENT_SPAN_HOLDER = new InheritableThreadLocal<>(); + + /** + * ่ฎพ็ฝฎ parent Span๏ผˆ็”ฑ GraphBasedEvaluationExecutor ่ฐƒ็”จ๏ผ‰ + */ + public static void setParentSpan(Span parentSpan) { + PARENT_SPAN_HOLDER.set(parentSpan); + } + + /** + * ่Žทๅ– parent Span + */ + public static Span getParentSpanFromThreadLocal() { + return PARENT_SPAN_HOLDER.get(); + } + + /** + * ๆธ…็† parent Span๏ผˆๆ‰ง่กŒๅฎŒๆˆๅŽ็”ฑ GraphBasedEvaluationExecutor ่ฐƒ็”จ๏ผ‰ + */ + public static void clearParentSpan() { + PARENT_SPAN_HOLDER.remove(); + } + + private final Tracer tracer; + + /** + * ้ป˜่ฎค็š„ parent Span๏ผˆๅœจๆž„้€ ๆ—ถ่ฎพ็ฝฎ๏ผ‰ + */ + private final Span defaultParentSpan; + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ Span + */ + private final ConcurrentHashMap nodeSpans = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ Scope + */ + private final ConcurrentHashMap nodeScopes = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ๅผ€ๅง‹ๆ—ถ้—ด + */ + private final ConcurrentHashMap nodeStartTimes = new ConcurrentHashMap<>(); + + /** + * ๅญ˜ๅ‚จๆฏไธช่Š‚็‚น็š„ parent Scope๏ผˆ็”จไบŽๆญฃ็กฎ็š„็ˆถๅญๅ…ณ็ณป๏ผ‰ + */ + private final ConcurrentHashMap parentScopes = new ConcurrentHashMap<>(); + + /** + * ้™ๆ€ๅญ˜ๅ‚จ criterion ็ป“ๆžœ๏ผˆๆŒ‰ nodeKey ๅˆ†็ป„๏ผ‰ + * ๅ› ไธบ after() ไธญ็š„ state ๅฏ่ƒฝๆ˜ฏๅฟซ็…ง๏ผŒไธๅŒ…ๅซๅˆšๅˆšๅ†™ๅ…ฅ็š„็ป“ๆžœ + */ + private static final ConcurrentHashMap CRITERION_RESULT_STORE = new ConcurrentHashMap<>(); + + /** + * ๆณจๅ†Œ criterion ็ป“ๆžœ๏ผˆ็”ฑ CriterionEvaluationAction ่ฐƒ็”จ๏ผ‰ + * + * @param criterionName criterion ๅ็งฐ + * @param result ็ป“ๆžœ + */ + public static void registerCriterionResult(String criterionName, CriterionResult result) { + if (criterionName != null && result != null) { + CRITERION_RESULT_STORE.put(criterionName, result); + log.debug("EvaluationObservationLifecycleListener#registerCriterionResult - " + + "reason=ๆณจๅ†Œ็ป“ๆžœ, criterionName={}, status={}", criterionName, result.getStatus()); + } + } + + /** + * ่Žทๅ–ๅนถๆธ…้™ค criterion ็ป“ๆžœ + * + * @param criterionName criterion ๅ็งฐ + * @return ็ป“ๆžœ + */ + public static CriterionResult getAndClearCriterionResult(String criterionName) { + return CRITERION_RESULT_STORE.remove(criterionName); + } + + public EvaluationObservationLifecycleListener(Tracer tracer) { + this(tracer, null); + } + + /** + * ๆž„้€ ๅ‡ฝๆ•ฐ๏ผŒๆ”ฏๆŒ่ฎพ็ฝฎ parent Span + * + * @param tracer OpenTelemetry Tracer + * @param parentSpan ็ˆถ Span๏ผŒๆ‰€ๆœ‰่ฏ„ไผฐ้กน็š„ span ้ƒฝไผš็ปงๆ‰ฟๆญค็ˆถ span + */ + public EvaluationObservationLifecycleListener(Tracer tracer, Span parentSpan) { + this.tracer = tracer; + this.defaultParentSpan = parentSpan; + log.info("EvaluationObservationLifecycleListener# - reason=ๅˆๅง‹ๅŒ–ๅฎŒๆˆ, hasTracer={}, hasParent={}", + tracer != null, parentSpan != null); + } + + @Override + public void onStart(String nodeId, Map state, RunnableConfig config) { + // ่ฏ„ไผฐ Graph ๅผ€ๅง‹ๆ—ถไธ้œ€่ฆ็‰นๆฎŠๅค„็† + if (nodeId != null && nodeId.equalsIgnoreCase("__start__")) { + log.debug("EvaluationObservationLifecycleListener#onStart - reason=่ฏ„ไผฐGraphๅผ€ๅง‹"); + } + } + + @Override + public void onComplete(String nodeId, Map state, RunnableConfig config) { + // ่ฏ„ไผฐ Graph ๅฎŒๆˆๆ—ถไธ้œ€่ฆ็‰นๆฎŠๅค„็† + if (nodeId != null && nodeId.equalsIgnoreCase("__end__")) { + log.debug("EvaluationObservationLifecycleListener#onComplete - reason=่ฏ„ไผฐGraphๅฎŒๆˆ"); + } + } + + @Override + public void onError(String nodeId, Map state, Throwable ex, RunnableConfig config) { + log.error("EvaluationObservationLifecycleListener#onError - reason=่ฏ„ไผฐๆ‰ง่กŒๅ‡บ้”™, nodeId={}", nodeId, ex); + + // ๅœๆญขๅฏนๅบ”่Š‚็‚น็š„ Span + String nodeKey = getNodeKey(nodeId, config); + stopSpan(nodeKey, ex); + } + + @Override + public void before(String nodeId, Map state, RunnableConfig config, Long curTime) { + // ่ทณ่ฟ‡ๆฑ‡่š่Š‚็‚นๅ’Œ็ณป็ปŸ่Š‚็‚น + if (nodeId == null || nodeId.startsWith("join_") || nodeId.startsWith("__")) { + return; + } + + if (tracer == null) { + return; + } + + String nodeKey = getNodeKey(nodeId, config); + nodeStartTimes.put(nodeKey, System.currentTimeMillis()); + + // ่Žทๅ– parent Span + Span parentSpan = getParentSpan(state); + + // ๅ…ณ้”ฎ๏ผšๅฆ‚ๆžœๆœ‰ parent๏ผŒ้œ€่ฆๅ…ˆๅฐ†ๅ…ถ่ฎพ็ฝฎไธบๅฝ“ๅ‰ context + Context parentContext = Context.current(); + if (parentSpan != null) { + parentContext = parentContext.with(parentSpan); + } + + // ๅˆ›ๅปบ criterion ่ฏ„ไผฐ็š„ Span + Span span = tracer.spanBuilder("codeact.evaluation.criterion") + .setParent(parentContext) + .setSpanKind(SpanKind.INTERNAL) + .setAttribute("gen_ai.span_kind_name", "EVALUATOR") + .setAttribute("gen_ai.operation.name", "evaluate") + .setAttribute("codeact.evaluation.criterion_name", nodeId) + .startSpan(); + + // ๆ‰“ๅผ€ๅฝ“ๅ‰ span ็š„ scope + Scope scope = span.makeCurrent(); + + nodeSpans.put(nodeKey, span); + nodeScopes.put(nodeKey, scope); + + log.debug("EvaluationObservationLifecycleListener#before - reason=่ฏ„ไผฐ้กนๅผ€ๅง‹ๆ‰ง่กŒ, criterionName={}", nodeId); + } + + /** + * ่Žทๅ– parent Span + * ไผ˜ๅ…ˆไปŽ ThreadLocal ไธญ่Žทๅ–๏ผŒๅ…ถๆฌกไฝฟ็”จๆž„้€ ๆ—ถไผ ๅ…ฅ็š„้ป˜่ฎคๅ€ผ + */ + private Span getParentSpan(Map state) { + // ๅ…ˆๅฐ่ฏ•ไปŽ ThreadLocal ไธญ่Žทๅ– + Span fromThreadLocal = PARENT_SPAN_HOLDER.get(); + if (fromThreadLocal != null) { + return fromThreadLocal; + } + // ไฝฟ็”จ้ป˜่ฎค็š„ parent Span + return defaultParentSpan; + } + + @Override + public void after(String nodeId, Map state, RunnableConfig config, Long curTime) { + // ่ทณ่ฟ‡ๆฑ‡่š่Š‚็‚นๅ’Œ็ณป็ปŸ่Š‚็‚น + if (nodeId == null || nodeId.startsWith("join_") || nodeId.startsWith("__")) { + return; + } + + String nodeKey = getNodeKey(nodeId, config); + + // ่ฎก็ฎ—ๆ‰ง่กŒๆ—ถ้•ฟ + Long startTime = nodeStartTimes.remove(nodeKey); + long durationMs = startTime != null ? System.currentTimeMillis() - startTime : 0; + + // ๅ…ˆๅ…ณ้—ญๅฝ“ๅ‰ span ็š„ scope + Scope scope = nodeScopes.remove(nodeKey); + if (scope != null) { + scope.close(); + } + + // ่Žทๅ–ๅนถๅœๆญข Span + Span span = nodeSpans.remove(nodeKey); + if (span != null) { + span.setAttribute("duration.ms", durationMs); + + // ไผ˜ๅ…ˆไปŽ้™ๆ€ๅญ˜ๅ‚จ่Žทๅ–็ป“ๆžœ๏ผˆ่งฃๅ†ณ state ๅฟซ็…ง้—ฎ้ข˜๏ผ‰ + CriterionResult result = getAndClearCriterionResult(nodeId); + + // ๅฆ‚ๆžœ้™ๆ€ๅญ˜ๅ‚จๆฒกๆœ‰๏ผŒๅฐ่ฏ•ไปŽ state ่Žทๅ– + if (result == null) { + String resultKey = nodeId + "_result"; + Object resultObj = state.get(resultKey); + if (resultObj instanceof CriterionResult) { + result = (CriterionResult) resultObj; + } + } + + if (result != null) { + // ่ฎฐๅฝ•่ฏ„ไผฐ็ป“ๆžœ + span.setAttribute("codeact.evaluation.status", + result.getStatus() != null ? result.getStatus().name() : "UNKNOWN"); + + if (result.getValue() != null) { + span.setAttribute("codeact.evaluation.value", + truncate(result.getValue().toString(), 500)); + } + + if (result.getReason() != null) { + span.setAttribute("codeact.evaluation.reason", + truncate(result.getReason(), 500)); + } + + if (result.getRawPrompt() != null) { + // LLM ่พ“ๅ…ฅ + span.setAttribute("gen_ai.input.messages", + truncate(result.getRawPrompt(), 2000)); + } + + if (result.getRawResponse() != null) { + // LLM ่พ“ๅ‡บ + span.setAttribute("gen_ai.output.messages", + truncate(result.getRawResponse(), 2000)); + } + + if (result.getErrorMessage() != null) { + span.setAttribute("codeact.evaluation.error", + truncate(result.getErrorMessage(), 500)); + } + + log.info("EvaluationObservationLifecycleListener#after - reason=่ฏ„ไผฐ้กนๆ‰ง่กŒๅฎŒๆˆ, " + + "criterionName={}, status={}, durationMs={}", + nodeId, result.getStatus(), durationMs); + } else { + log.warn("EvaluationObservationLifecycleListener#after - reason=่ฏ„ไผฐ็ป“ๆžœๆœชๆ‰พๅˆฐ, criterionName={}", nodeId); + } + + span.end(); + } + } + + /** + * ็”Ÿๆˆ่Š‚็‚นๅ”ฏไธ€ๆ ‡่ฏ† + */ + private String getNodeKey(String nodeId, RunnableConfig config) { + String threadId = config != null ? config.threadId().orElse("default") : "default"; + return threadId + ":" + nodeId; + } + + /** + * ๅœๆญข Span๏ผˆๅ‡บ้”™ๆ—ถ่ฐƒ็”จ๏ผ‰ + */ + private void stopSpan(String nodeKey, Throwable error) { + Scope scope = nodeScopes.remove(nodeKey); + if (scope != null) { + scope.close(); + } + + Span span = nodeSpans.remove(nodeKey); + if (span != null) { + if (error != null) { + span.setStatus(StatusCode.ERROR, error.getMessage()); + span.recordException(error); + span.setAttribute("error.type", error.getClass().getSimpleName()); + } + span.end(); + } + } + + /** + * ๆˆชๆ–ญๅญ—็ฌฆไธฒ + */ + private String truncate(String str, int maxLength) { + if (str == null) { + return "null"; + } + if (str.length() <= maxLength) { + return str; + } + return str.substring(0, maxLength) + "...[truncated]"; + } +} + diff --git a/assistant-agent-extensions/pom.xml b/assistant-agent-extensions/pom.xml index 42ccf7a..2159e75 100644 --- a/assistant-agent-extensions/pom.xml +++ b/assistant-agent-extensions/pom.xml @@ -6,7 +6,7 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-extensions diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java index e8f2f1c..aadac5a 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java @@ -147,8 +147,8 @@ private CodeactTool createToolFromCallback(ToolCallback callback, ObjectMapper o String inputSchema = definition.inputSchema(); // ็ฑปๅ๏ผšไฝฟ็”จ้…็ฝฎ็š„ server ๅ็งฐๆˆ–้ป˜่ฎคๅ‰็ผ€ - // ๅฝ’ไธ€ๅŒ–ๅŽ็š„็ฑปๅ๏ผˆๅฆ‚ my-mcp-server -> my_mcp_server_tools๏ผ‰ - String targetClassName = nameNormalizer.normalizeClassName(defaultTargetClassNamePrefix) + "_tools"; + // ๅฝ’ไธ€ๅŒ–ๅŽ็š„็ฑปๅ๏ผˆๅฆ‚ my-mcp-server -> my_mcp_server๏ผ‰ + String targetClassName = nameNormalizer.normalizeClassName(defaultTargetClassNamePrefix); String targetClassDescription = defaultTargetClassDescription; // ไผ˜ๅ…ˆๆฃ€ๆŸฅๆ˜ฏๅฆๅฎž็Žฐไบ† McpServerAwareToolCallback ๆŽฅๅฃ @@ -157,7 +157,7 @@ private CodeactTool createToolFromCallback(ToolCallback callback, ObjectMapper o String displayName = serverAwareCallback.getServerDisplayName(); if (serverName != null && !serverName.isEmpty()) { - targetClassName = nameNormalizer.normalizeClassName(serverName) + "_tools"; + targetClassName = nameNormalizer.normalizeClassName(serverName); targetClassDescription = displayName != null ? displayName : serverName; // ไฝฟ็”จๅŽŸๅง‹ๅทฅๅ…ทๅ่€Œไธๆ˜ฏ ToolDefinition ไธญ็š„ๅ็งฐ toolName = serverAwareCallback.getToolName(); @@ -171,7 +171,7 @@ else if (!serverSpecs.isEmpty()) { for (Map.Entry entry : serverSpecs.entrySet()) { McpServerSpec spec = entry.getValue(); // ไฝฟ็”จ serverSpec ไธญ็š„ๅ็งฐ - targetClassName = nameNormalizer.normalizeClassName(spec.getServerName()) + "_tools"; + targetClassName = nameNormalizer.normalizeClassName(spec.getServerName()); targetClassDescription = spec.getDescription(); break; // ็›ฎๅ‰ๅชๆ”ฏๆŒๅ•ไธช server๏ผŒไฝฟ็”จ็ฌฌไธ€ไธช } diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpServerAwareToolCallback.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpServerAwareToolCallback.java index 5a4eafb..da12dc8 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpServerAwareToolCallback.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpServerAwareToolCallback.java @@ -38,7 +38,7 @@ public interface McpServerAwareToolCallback extends ToolCallback { * ่Žทๅ– MCP Server ๅ็งฐใ€‚ * *

    ๆญคๅ็งฐๅฐ†็”จไบŽ็”Ÿๆˆ Python ็ฑปๅ๏ผˆ็ป่ฟ‡ๅฝ’ไธ€ๅŒ–ๅค„็†๏ผ‰ใ€‚ - * ไพ‹ๅฆ‚ "aone-app-center" ไผš่ขซ่ฝฌๆขไธบ "AoneAppCenter" ็ฑปใ€‚ + * ไพ‹ๅฆ‚ "server-center" ไผš่ขซ่ฝฌๆขไธบ "ServerCenter" ็ฑปใ€‚ * * @return MCP Server ๅ็งฐ๏ผŒไธ่ƒฝไธบ null */ diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/naming/NameNormalizer.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/naming/NameNormalizer.java index bd480b7..4a5009f 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/naming/NameNormalizer.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/naming/NameNormalizer.java @@ -120,15 +120,15 @@ else if (c == '_' || c == '-' || c == ' ' || c == '.' || c == 'ยท') { // ๅธธ่งๅˆ†้š”็ฌฆ่ฝฌไธบไธ‹ๅˆ’็บฟ sb.append('_'); } + else if (Pinyin.isChinese(c)) { + // ไธญๆ–‡ๅญ—็ฌฆ่ฝฌไธบๆ‹ผ้Ÿณ๏ผˆไผ˜ๅ…ˆไฝฟ็”จ Pinyin ๅบ“็š„ๅˆคๆ–ญ๏ผŒๆ›ดๅ‡†็กฎ๏ผ‰ + String pinyin = Pinyin.toPinyin(c).toLowerCase(); + sb.append(pinyin); + logger.debug("NameNormalizer#normalizeToIdentifier - reason=ไธญๆ–‡่ฝฌๆ‹ผ้Ÿณ, char={}, pinyin={}", c, pinyin); + } else if (isChinese(c)) { - // ไธญๆ–‡ๅญ—็ฌฆ่ฝฌไธบๆ‹ผ้Ÿณ - String pinyin = toPinyin(c); - if (pinyin != null && !pinyin.isEmpty()) { - sb.append(pinyin); - } - else { - sb.append('_'); - } + // ๅ…ถไป– CJK ็›ธๅ…ณๅญ—็ฌฆ๏ผˆๆ ‡็‚น็ญ‰๏ผ‰่ฝฌไธบไธ‹ๅˆ’็บฟ + sb.append('_'); } else { // ๅ…ถไป–็‰นๆฎŠๅญ—็ฌฆ่ฝฌไธบไธ‹ๅˆ’็บฟ @@ -171,15 +171,6 @@ private boolean isChinese(char c) { || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION; } - /** - * ๅฐ†ๅ•ไธชไธญๆ–‡ๅญ—็ฌฆ่ฝฌๆขไธบๆ‹ผ้Ÿณใ€‚ - */ - private String toPinyin(char c) { - if (Pinyin.isChinese(c)) { - return Pinyin.toPinyin(c).toLowerCase(); - } - return null; - } /** * ็กฎไฟๅ็งฐๅ”ฏไธ€ใ€‚ diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/config/ExperienceExtensionAutoConfiguration.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/config/ExperienceExtensionAutoConfiguration.java index 16fe7f7..7b6101d 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/config/ExperienceExtensionAutoConfiguration.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/config/ExperienceExtensionAutoConfiguration.java @@ -4,6 +4,7 @@ import com.alibaba.assistant.agent.extension.experience.hook.CommonSenseExperienceModelHook; import com.alibaba.assistant.agent.extension.experience.hook.FastIntentReactHook; import com.alibaba.assistant.agent.extension.experience.hook.ReactExperienceAgentHook; +import com.alibaba.assistant.agent.extension.experience.tool.CommonSenseInjectionTool; import com.alibaba.assistant.agent.extension.experience.fastintent.FastIntentService; import com.alibaba.assistant.agent.extension.experience.internal.InMemoryExperienceProvider; import com.alibaba.assistant.agent.extension.experience.internal.InMemoryExperienceRepository; @@ -126,4 +127,21 @@ public CommonSenseExperienceModelHook commonSenseExperienceModelHook(ExperienceP log.info("ExperienceExtensionAutoConfiguration#commonSenseExperienceModelHook - reason=creating common sense prompt model hook bean"); return new CommonSenseExperienceModelHook(experienceProvider, properties); } + + /** + * ้…็ฝฎๅธธ่ฏ†็ป้ชŒๆณจๅ…ฅๅ‡ๅทฅๅ…ท + * + *

    ่ฟ™ไธชๅทฅๅ…ทไธŽ CommonSenseExperienceModelHook ้…ๅฅ—ไฝฟ็”จใ€‚ + * Hook ไผšๆณจๅ…ฅ AssistantMessage + ToolResponseMessage ้…ๅฏนๆฅๆจกๆ‹Ÿๅทฅๅ…ท่ฐƒ็”จ๏ผŒ + * ๆณจๅ†Œ่ฟ™ไธชๅทฅๅ…ทๅฏไปฅ่ฎฉ ReactAgent ็š„่ทฏ็”ฑ้€ป่พ‘ๆญฃ็กฎ่ฏ†ๅˆซๅ’Œๅค„็†ใ€‚ + */ + @Bean + @ConditionalOnProperty(prefix = "spring.ai.alibaba.codeact.extension.experience", + name = "common-experience-enabled", + havingValue = "true", + matchIfMissing = true) + public CommonSenseInjectionTool commonSenseInjectionTool() { + log.info("ExperienceExtensionAutoConfiguration#commonSenseInjectionTool - reason=creating common sense injection tool bean"); + return new CommonSenseInjectionTool(); + } } diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/hook/FastIntentReactHook.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/hook/FastIntentReactHook.java index 940b294..5798770 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/hook/FastIntentReactHook.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/hook/FastIntentReactHook.java @@ -124,9 +124,8 @@ public CompletableFuture> beforeAgent(OverAllState state, Ru Optional bestOpt = fastIntentService.selectBestMatch(experiences, ctx); if (bestOpt.isEmpty()) { log.debug("FastIntentReactHook#beforeAgent - reason=no matched experience"); - return CompletableFuture.completedFuture(Map.of( - "jump_to", JumpTo.model - )); + // ไธ่ฎพ็ฝฎ jump_to๏ผŒ่ฎฉๆญฃๅธธๆต็จ‹็ปง็ปญ๏ผˆๅฆๅˆ™ไผšๅฏผ่‡ดๆ— ้™ๅพช็Žฏๅˆฐ model๏ผ‰ + return CompletableFuture.completedFuture(Map.of()); } Experience best = bestOpt.get(); diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/tool/CommonSenseInjectionTool.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/tool/CommonSenseInjectionTool.java new file mode 100644 index 0000000..e979903 --- /dev/null +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/experience/tool/CommonSenseInjectionTool.java @@ -0,0 +1,36 @@ +package com.alibaba.assistant.agent.extension.experience.tool; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.tool.annotation.Tool; + +/** + * ๅธธ่ฏ†็ป้ชŒๆณจๅ…ฅๅ‡ๅทฅๅ…ท + * + *

    ่ฟ™ไธชๅทฅๅ…ทไธๆ‰ง่กŒไปปไฝ•ๅฎž้™…ๆ“ไฝœ๏ผŒไป…็”จไบŽๆ”ฏๆŒ CommonSenseExperienceModelHook + * ้€š่ฟ‡ AssistantMessage + ToolResponseMessage ้…ๅฏนๆ–นๅผๆณจๅ…ฅๅธธ่ฏ†็ป้ชŒใ€‚ + * + *

    ๆณจๅ†Œ่ฟ™ไธชๅทฅๅ…ทๅฏไปฅ่ฎฉ ReactAgent ็š„่ทฏ็”ฑ้€ป่พ‘ๆญฃ็กฎ่ฏ†ๅˆซๅ’Œๅค„็†็ป้ชŒๆณจๅ…ฅใ€‚ + * + * @author Assistant Agent Team + */ +public class CommonSenseInjectionTool { + + private static final Logger log = LoggerFactory.getLogger(CommonSenseInjectionTool.class); + + /** + * ๅธธ่ฏ†็ป้ชŒๆณจๅ…ฅๆ–นๆณ• - ่ฟ™ๆ˜ฏไธ€ไธชๅ ไฝๅทฅๅ…ท๏ผŒๅฎž้™…ไธไผš่ขซ่ฐƒ็”จ + * + *

    Hook ไผš้ข„ๅ…ˆๆž„้€  AssistantMessage(toolCall) + ToolResponseMessage ้…ๅฏน๏ผŒ + * ๆจกๆ‹Ÿๅทฒ็ปๅฎŒๆˆ็š„ๅทฅๅ…ท่ฐƒ็”จ๏ผŒๆ‰€ไปฅ่ฟ™ไธชๆ–นๆณ•ไธไผš่ขซ็œŸๆญฃๆ‰ง่กŒใ€‚ + * + * @return ็ฉบๅญ—็ฌฆไธฒ๏ผˆๅฎž้™…ไธไผš่ขซ่ฐƒ็”จ๏ผ‰ + */ + @Tool(name = "common_sense_injection", + description = "ๅ†…้ƒจๅทฅๅ…ท๏ผš็”จไบŽๆณจๅ…ฅๅธธ่ฏ†็ป้ชŒๅˆฐๅฏน่ฏไธŠไธ‹ๆ–‡ใ€‚ๆญคๅทฅๅ…ท็”ฑ็ณป็ปŸ่‡ชๅŠจ่ฐƒ็”จ๏ผŒๆ— ้œ€็”จๆˆทๆ‰‹ๅŠจ่งฆๅ‘ใ€‚") + public String inject() { + log.warn("CommonSenseInjectionTool#inject - reason=ๆญคๅทฅๅ…ทไธๅบ”่ขซ็›ดๆŽฅ่ฐƒ็”จ๏ผŒไป…ไฝœไธบๅ ไฝๅทฅๅ…ทๅญ˜ๅœจ"); + return ""; + } +} + diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/config/LearningExtensionAutoConfiguration.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/config/LearningExtensionAutoConfiguration.java index 87eea42..c88df68 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/config/LearningExtensionAutoConfiguration.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/config/LearningExtensionAutoConfiguration.java @@ -41,6 +41,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ChatModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -48,6 +50,7 @@ import org.springframework.context.annotation.Configuration; import java.util.List; +import java.util.concurrent.ExecutorService; /** * ๅญฆไน ๆจกๅ—่‡ชๅŠจ้…็ฝฎ็ฑป @@ -69,13 +72,30 @@ public LearningExtensionAutoConfiguration() { /** * ้…็ฝฎๅผ‚ๆญฅๅญฆไน ๅค„็†ๅ™จ + *

    + * ๅฆ‚ๆžœๆไพ›ไบ†ๅไธบ "learningAsyncExecutor" ็š„ ExecutorService Bean๏ผŒ + * ๅˆ™ไฝฟ็”จ่ฏฅ่‡ชๅฎšไน‰ ExecutorService๏ผˆๆ”ฏๆŒ trace ไธŠไธ‹ๆ–‡ไผ ้€’็ญ‰็‰นๆ€ง๏ผ‰ใ€‚ + * ๅฆๅˆ™ไฝฟ็”จ้ป˜่ฎค็š„็บฟ็จ‹ๆฑ ้…็ฝฎใ€‚ + * + * @param properties ๅญฆไน ๆ‰ฉๅฑ•้…็ฝฎ + * @param customExecutor ๅฏ้€‰็š„่‡ชๅฎšไน‰ ExecutorService๏ผˆๅไธบ "learningAsyncExecutor"๏ผ‰ + * @return AsyncLearningHandler */ @Bean @ConditionalOnMissingBean - public AsyncLearningHandler asyncLearningHandler(LearningExtensionProperties properties) { + public AsyncLearningHandler asyncLearningHandler( + LearningExtensionProperties properties, + @Autowired(required = false) @Qualifier("learningAsyncExecutor") ExecutorService customExecutor) { + if (customExecutor != null) { + log.info( + "LearningExtensionAutoConfiguration#asyncLearningHandler - reason=creating async learning handler with custom executor, executorType={}", + customExecutor.getClass().getSimpleName()); + return new AsyncLearningHandler(customExecutor); + } + LearningExtensionProperties.AsyncConfig asyncConfig = properties.getAsync(); log.info( - "LearningExtensionAutoConfiguration#asyncLearningHandler - reason=creating async learning handler, threadPoolSize={}, queueCapacity={}", + "LearningExtensionAutoConfiguration#asyncLearningHandler - reason=creating async learning handler with default executor, threadPoolSize={}, queueCapacity={}", asyncConfig.getThreadPoolSize(), asyncConfig.getQueueCapacity()); return new AsyncLearningHandler(asyncConfig.getThreadPoolSize(), asyncConfig.getQueueCapacity()); } diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/hook/AfterAgentLearningHook.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/hook/AfterAgentLearningHook.java index 01e24d3..fa5948c 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/hook/AfterAgentLearningHook.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/hook/AfterAgentLearningHook.java @@ -18,6 +18,10 @@ import com.alibaba.assistant.agent.common.hook.AgentPhase; import com.alibaba.assistant.agent.common.hook.HookPhases; +import com.alibaba.assistant.agent.common.hook.AgentPhase; +import com.alibaba.assistant.agent.common.hook.HookPhases; +import com.alibaba.assistant.agent.core.observation.HookObservationHelper; +import com.alibaba.assistant.agent.core.observation.ObservationState; import com.alibaba.assistant.agent.extension.learning.internal.DefaultLearningContext; import com.alibaba.assistant.agent.extension.learning.internal.DefaultLearningTask; import com.alibaba.assistant.agent.extension.learning.model.LearningContext; @@ -71,8 +75,13 @@ public CompletableFuture> afterAgent(OverAllState state, Run try { log.info("AfterAgentLearningHook#afterAgent - reason=agent execution completed, starting learning process"); + // ๆณจๅ†Œ่‡ชๅฎšไน‰่ง‚ๆต‹ๆ•ฐๆฎๅˆฐ ObservationState + registerObservationData(state, "hook.input.learningType", learningType); + registerObservationData(state, "hook.input.triggerSource", LearningTriggerSource.AFTER_AGENT.name()); + // 1. ไปŽstateไธญๆๅ–ๅฏน่ฏๅކๅฒ List conversationHistory = extractConversationHistory(state); + registerObservationData(state, "hook.input.conversationHistorySize", conversationHistory.size()); // 2. ๆž„ๅปบๅญฆไน ไธŠไธ‹ๆ–‡ LearningContext context = DefaultLearningContext.builder() @@ -92,36 +101,52 @@ public CompletableFuture> afterAgent(OverAllState state, Run // 4. ๅˆคๆ–ญๆ˜ฏๅฆๅบ”่ฏฅ่งฆๅ‘ๅญฆไน  if (!learningStrategy.shouldTriggerLearning(triggerContext)) { log.info("AfterAgentLearningHook#afterAgent - reason=strategy decided not to trigger learning"); + registerObservationData(state, "hook.output.status", "NOT_TRIGGERED"); + registerObservationData(state, "hook.output.triggered", false); return CompletableFuture.completedFuture(Map.of()); } - // 4. ๆž„ๅปบๅญฆไน ไปปๅŠก + // ๆณจๅ†Œๅญฆไน ่งฆๅ‘ไฟกๆฏ + registerObservationData(state, "hook.output.triggered", true); + + // 5. ๆž„ๅปบๅญฆไน ไปปๅŠก LearningTask task = DefaultLearningTask.builder() .learningType(learningType) .triggerSource(LearningTriggerSource.AFTER_AGENT) .context(context) .build(); - // 5. ๆ‰ง่กŒๅญฆไน ๏ผˆๅผ‚ๆญฅๆˆ–ๅŒๆญฅ๏ผ‰ + registerObservationData(state, "hook.output.taskId", task.getId()); + + // 6. ๆ‰ง่กŒๅญฆไน ๏ผˆๅผ‚ๆญฅๆˆ–ๅŒๆญฅ๏ผ‰ if (learningStrategy.shouldExecuteAsync(task)) { log.info( "AfterAgentLearningHook#afterAgent - reason=executing learning asynchronously, taskId={}", task.getId()); + registerObservationData(state, "hook.output.executionMode", "ASYNC"); + learningExecutor.executeAsync(task).exceptionally(ex -> { log.error( "AfterAgentLearningHook#afterAgent - reason=async learning execution failed, taskId={}", task.getId(), ex); return null; }); + registerObservationData(state, "hook.output.status", "ASYNC_SUBMITTED"); } else { log.debug("AfterAgentLearningHook#afterAgent - reason=executing learning synchronously, taskId={}", task.getId()); + registerObservationData(state, "hook.output.executionMode", "SYNC"); + LearningResult result = learningExecutor.execute(task); if (!result.isSuccess()) { log.warn( "AfterAgentLearningHook#afterAgent - reason=learning execution failed, taskId={}, failureReason={}", task.getId(), result.getFailureReason()); + registerObservationData(state, "hook.output.status", "FAILED"); + registerObservationData(state, "hook.output.failureReason", truncate(result.getFailureReason(), 200)); + } else { + registerObservationData(state, "hook.output.status", "SUCCESS"); } } @@ -129,6 +154,8 @@ public CompletableFuture> afterAgent(OverAllState state, Run catch (Exception e) { // ๅญฆไน ๅคฑ่ดฅไธๅฝฑๅ“ไธปๆต็จ‹ log.error("AfterAgentLearningHook#afterAgent - reason=learning hook failed", e); + registerObservationData(state, "hook.output.status", "ERROR"); + registerObservationData(state, "hook.output.errorType", e.getClass().getSimpleName()); } return CompletableFuture.completedFuture(Map.of()); @@ -154,5 +181,42 @@ private List extractConversationHistory(OverAllState state) { } } -} + /** + * ๆณจๅ†Œ่‡ชๅฎšไน‰่ง‚ๆต‹ๆ•ฐๆฎๅˆฐ ObservationState + *

    + * ่ฟ™ไบ›ๆ•ฐๆฎไผš่ขซ CodeactAgentObservationLifecycleListener ๆ”ถ้›†ๅนถ่ฎฐๅฝ•ๅˆฐ Observation ไธญใ€‚ + * + * @param state OverAllState + * @param key ๆ•ฐๆฎ้”ฎ๏ผŒๅปบ่ฎฎไฝฟ็”จ "hook." ๅ‰็ผ€ + * @param value ๆ•ฐๆฎๅ€ผ + */ + private void registerObservationData(OverAllState state, String key, Object value) { + try { + // ๅฐ่ฏ•่Žทๅ– ObservationState + Object obsStateObj = state.value("_observation_state_").orElse(null); + if (obsStateObj != null) { + // ไฝฟ็”จๅๅฐ„่ฐƒ็”จ put ๆ–นๆณ•๏ผŒ้ฟๅ…็›ดๆŽฅไพ่ต– ObservationState ็ฑป + java.lang.reflect.Method putMethod = obsStateObj.getClass().getMethod("put", String.class, Object.class); + putMethod.invoke(obsStateObj, key, value); + log.debug("AfterAgentLearningHook#registerObservationData - reason=ๆณจๅ†Œ่ง‚ๆต‹ๆ•ฐๆฎๆˆๅŠŸ, key={}", key); + } + } catch (Exception e) { + // ้™้ป˜ๅคฑ่ดฅ๏ผŒไธๅฝฑๅ“ไธปๆต็จ‹ + log.debug("AfterAgentLearningHook#registerObservationData - reason=ๆณจๅ†Œ่ง‚ๆต‹ๆ•ฐๆฎๅคฑ่ดฅ, key={}, error={}", key, e.getMessage()); + } + } + /** + * ๆˆชๆ–ญๅญ—็ฌฆไธฒ + */ + private String truncate(String str, int maxLength) { + if (str == null) { + return "null"; + } + if (str.length() <= maxLength) { + return str; + } + return str.substring(0, maxLength) + "..."; + } + +} diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/internal/AsyncLearningHandler.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/internal/AsyncLearningHandler.java index f75e347..2075f6d 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/internal/AsyncLearningHandler.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/learning/internal/AsyncLearningHandler.java @@ -24,6 +24,8 @@ /** * ๅผ‚ๆญฅๅญฆไน ๅค„็†ๅ™จ * ็ฎก็†ๅผ‚ๆญฅๅญฆไน ไปปๅŠก็š„ๆ‰ง่กŒ๏ผŒๅŒ…ๆ‹ฌ็บฟ็จ‹ๆฑ ็ฎก็†ๅ’ŒไปปๅŠกๆ‹’็ป็ญ–็•ฅ + *

    + * ๆ”ฏๆŒ่‡ชๅฎšไน‰ ExecutorService ๆณจๅ…ฅ๏ผŒๅ…่ฎธ่ฐƒ็”จๆ–นไผ ๅ…ฅๆ”ฏๆŒ trace ไธŠไธ‹ๆ–‡ไผ ้€’็š„็บฟ็จ‹ๆฑ ใ€‚ * * @author Assistant Agent Team * @since 1.0.0 @@ -33,7 +35,14 @@ public class AsyncLearningHandler { private static final Logger log = LoggerFactory.getLogger(AsyncLearningHandler.class); private final ExecutorService executorService; + private final boolean externalExecutor; + /** + * ไฝฟ็”จ้ป˜่ฎค็บฟ็จ‹ๆฑ ้…็ฝฎๅˆ›ๅปบ AsyncLearningHandler + * + * @param threadPoolSize ็บฟ็จ‹ๆฑ ๅคงๅฐ + * @param queueCapacity ้˜Ÿๅˆ—ๅฎน้‡ + */ public AsyncLearningHandler(int threadPoolSize, int queueCapacity) { this.executorService = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueCapacity), new ThreadFactory() { @@ -44,12 +53,30 @@ public Thread newThread(Runnable r) { return new Thread(r, "learning-async-" + (count++)); } }, new ThreadPoolExecutor.CallerRunsPolicy()); + this.externalExecutor = false; log.info( - "AsyncLearningHandler#constructor - reason=async learning handler initialized, threadPoolSize={}, queueCapacity={}", + "AsyncLearningHandler#constructor - reason=async learning handler initialized with default executor, threadPoolSize={}, queueCapacity={}", threadPoolSize, queueCapacity); } + /** + * ไฝฟ็”จ่‡ชๅฎšไน‰ ExecutorService ๅˆ›ๅปบ AsyncLearningHandler + *

    + * ๅ…่ฎธ่ฐƒ็”จๆ–นไผ ๅ…ฅๆ”ฏๆŒ trace ไธŠไธ‹ๆ–‡ไผ ้€’็š„็บฟ็จ‹ๆฑ ๏ผˆๅฆ‚ TraceAwareExecutorService๏ผ‰๏ผŒ + * ็กฎไฟๅผ‚ๆญฅไปปๅŠก็š„ traceId ไธŽ็ˆถ็บฟ็จ‹ไฟๆŒไธ€่‡ดใ€‚ + * + * @param executorService ่‡ชๅฎšไน‰็š„ ExecutorService + */ + public AsyncLearningHandler(ExecutorService executorService) { + this.executorService = executorService; + this.externalExecutor = true; + + log.info( + "AsyncLearningHandler#constructor - reason=async learning handler initialized with custom executor, executorType={}", + executorService.getClass().getSimpleName()); + } + /** * ๅผ‚ๆญฅๆ‰ง่กŒไปปๅŠก * @param task ไปปๅŠก @@ -70,8 +97,16 @@ public CompletableFuture executeAsync(Callable task) { /** * ๅ…ณ้—ญ็บฟ็จ‹ๆฑ  + *

    + * ๆณจๆ„๏ผšๅฆ‚ๆžœไฝฟ็”จ็š„ๆ˜ฏๅค–้ƒจไผ ๅ…ฅ็š„ ExecutorService๏ผŒๅˆ™ไธไผšๅ…ณ้—ญๅฎƒ๏ผŒ + * ็”ฑๅค–้ƒจ่ฐƒ็”จๆ–น่ดŸ่ดฃ็ฎก็†ๅ…ถ็”Ÿๅ‘ฝๅ‘จๆœŸใ€‚ */ public void shutdown() { + if (externalExecutor) { + log.info("AsyncLearningHandler#shutdown - reason=skipping shutdown for external executor"); + return; + } + log.info("AsyncLearningHandler#shutdown - reason=shutting down async learning handler"); executorService.shutdown(); try { diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/BaseReplyCodeactTool.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/BaseReplyCodeactTool.java index f2d8c17..8ef3582 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/BaseReplyCodeactTool.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/BaseReplyCodeactTool.java @@ -106,13 +106,14 @@ public BaseReplyCodeactTool(String toolName, String description, ReplyChannelDef @Override public String call(String toolInput) { + log.debug("BaseReplyCodeactTool#call(ๅ•ๅ‚ๆ•ฐ) - reason=ๆก†ๆžถ่ฐƒ็”จไบ†ๅ•ๅ‚ๆ•ฐ็‰ˆๆœฌ, toolName={}", toolName); return call(toolInput, null); } @Override public String call(String toolInput, ToolContext toolContext) { - log.debug("BaseReplyCodeactTool#call - reason=ๅผ€ๅง‹ๆ‰ง่กŒๅ›žๅคๅทฅๅ…ท, toolName={}, toolInput={}", toolName, - toolInput); + log.debug("BaseReplyCodeactTool#call - reason=ๅผ€ๅง‹ๆ‰ง่กŒๅ›žๅคๅทฅๅ…ท, toolName={}, hasToolContext={}", + toolName, toolContext != null); try { // ่งฃๆž JSON ่พ“ๅ…ฅๅ‚ๆ•ฐ @@ -480,15 +481,6 @@ private ChannelExecutionContext buildExecutionContext(ToolContext toolContext) { ToolContextHelper.getFromMetadata(toolContext, "user_id").ifPresent(builder::userId); ToolContextHelper.getFromMetadata(toolContext, "trace_id").ifPresent(builder::traceId); - // ไปŽ metadata ่Žทๅ–ๆ‰€ๆœ‰ๆ‰ฉๅฑ•ๅญ—ๆฎต๏ผŒๆ”พๅ…ฅ extensions - // ่ฟ™ๆ ทไธšๅŠกๆ–นๅฏไปฅ้€š่ฟ‡ extensions ไผ ้€’่‡ชๅฎšไน‰ๆ•ฐๆฎ - ToolContextHelper.getAllMetadata(toolContext).ifPresent(metadata -> { - for (Map.Entry entry : metadata.entrySet()) { - String key = entry.getKey(); - builder.extension(key, entry.getValue()); - } - }); - return builder.build(); } diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/ReplyCodeactToolFactory.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/ReplyCodeactToolFactory.java index 436a19e..98c040d 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/ReplyCodeactToolFactory.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/reply/tools/ReplyCodeactToolFactory.java @@ -110,12 +110,69 @@ public ReplyCodeactTool createTool(ReplyToolConfig config) { /** * ๆž„ๅปบๅ‚ๆ•ฐๆจกๅผใ€‚ + *

    ไผ˜ๅ…ˆไฝฟ็”จ้…็ฝฎไธญ็š„ๅ‚ๆ•ฐๅฎšไน‰๏ผŒๅฆ‚ๆžœ้…็ฝฎไธญๆฒกๆœ‰๏ผŒๅˆ™ไปŽๆธ ้“ๅฎšไน‰ไธญ่Žทๅ–ใ€‚ */ private ParameterSchema buildParameterSchema(ReplyToolConfig config, ReplyChannelDefinition channel) { - // ไปŽ้…็ฝฎๅ’Œๆธ ้“ๅฎšไน‰ไธญๆž„ๅปบๅ‚ๆ•ฐๆจกๅผ - return ParameterSchema.builder() - .parameter("message", ParameterSchema.ParameterType.STRING, true, "่ฆๅ‘้€็š„ๆถˆๆฏๅ†…ๅฎน") - .build(); + // 1. ไผ˜ๅ…ˆไฝฟ็”จ้…็ฝฎไธญ็š„ๅ‚ๆ•ฐๅฎšไน‰ + if (config.getParameters() != null && !config.getParameters().isEmpty()) { + ParameterSchema.Builder builder = ParameterSchema.builder(); + for (ReplyToolConfig.ParameterConfig paramConfig : config.getParameters()) { + ParameterSchema.ParameterType type = convertToParameterType(paramConfig.getType()); + ParameterSchema.ParameterDef def = new ParameterSchema.ParameterDef( + paramConfig.getName(), + type, + paramConfig.isRequired(), + paramConfig.getDescription() + ); + def.setDefaultValue(paramConfig.getDefaultValue()); + def.setEnumValues(paramConfig.getEnumValues()); + builder.parameter(def); + } + log.debug("ReplyCodeactToolFactory#buildParameterSchema - reason=ไฝฟ็”จ้…็ฝฎไธญ็š„ๅ‚ๆ•ฐๅฎšไน‰, toolName={}, paramCount={}", + config.getToolName(), config.getParameters().size()); + return builder.build(); + } + + // 2. ๅฆ‚ๆžœ้…็ฝฎไธญๆฒกๆœ‰ๅ‚ๆ•ฐๅฎšไน‰๏ผŒไปŽๆธ ้“ๅฎšไน‰ไธญ่Žทๅ– + ParameterSchema channelSchema = channel.getSupportedParameters(); + if (channelSchema != null) { + log.debug("ReplyCodeactToolFactory#buildParameterSchema - reason=ไฝฟ็”จๆธ ้“ๅฎšไน‰็š„ๅ‚ๆ•ฐ, toolName={}, channelCode={}", + config.getToolName(), config.getChannelCode()); + return channelSchema; + } + + // 3. ๅฆ‚ๆžœ้ƒฝๆฒกๆœ‰๏ผŒ่ฟ”ๅ›ž็ฉบ็š„ๅ‚ๆ•ฐๆจกๅผ + log.warn("ReplyCodeactToolFactory#buildParameterSchema - reason=ๆœชๆ‰พๅˆฐๅ‚ๆ•ฐๅฎšไน‰ๅฐ†ไฝฟ็”จ็ฉบๅ‚ๆ•ฐๆจกๅผ, toolName={}, channelCode={}", + config.getToolName(), config.getChannelCode()); + return ParameterSchema.builder().build(); + } + + /** + * ๅฐ†ๅญ—็ฌฆไธฒ็ฑปๅž‹่ฝฌๆขไธบ ParameterTypeใ€‚ + */ + private ParameterSchema.ParameterType convertToParameterType(String type) { + if (type == null) { + return ParameterSchema.ParameterType.STRING; + } + String lowerType = type.toLowerCase(); + switch (lowerType) { + case "integer": + case "int": + case "long": + return ParameterSchema.ParameterType.INTEGER; + case "boolean": + case "bool": + return ParameterSchema.ParameterType.BOOLEAN; + case "array": + case "list": + return ParameterSchema.ParameterType.ARRAY; + case "object": + case "map": + return ParameterSchema.ParameterType.OBJECT; + case "string": + default: + return ParameterSchema.ParameterType.STRING; + } } /** diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/search/tools/UnifiedSearchCodeactTool.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/search/tools/UnifiedSearchCodeactTool.java index 5467c99..12b4394 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/search/tools/UnifiedSearchCodeactTool.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/search/tools/UnifiedSearchCodeactTool.java @@ -37,13 +37,7 @@ import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.tool.definition.ToolDefinition; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; /** diff --git a/assistant-agent-prompt-builder/pom.xml b/assistant-agent-prompt-builder/pom.xml index 3d8d8b4..15657d8 100644 --- a/assistant-agent-prompt-builder/pom.xml +++ b/assistant-agent-prompt-builder/pom.xml @@ -6,7 +6,7 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-prompt-builder diff --git a/assistant-agent-start/pom.xml b/assistant-agent-start/pom.xml index 3b18432..96ea8c6 100644 --- a/assistant-agent-start/pom.xml +++ b/assistant-agent-start/pom.xml @@ -6,7 +6,7 @@ com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 assistant-agent-start @@ -43,6 +43,13 @@ python-community pom + + + + org.junit.jupiter + junit-jupiter + test + diff --git a/assistant-agent-start/src/main/java/com/alibaba/assistant/agent/start/config/ExperienceEvaluationCriterionProvider.java b/assistant-agent-start/src/main/java/com/alibaba/assistant/agent/start/config/ExperienceEvaluationCriterionProvider.java index 705b99e..239f1ba 100644 --- a/assistant-agent-start/src/main/java/com/alibaba/assistant/agent/start/config/ExperienceEvaluationCriterionProvider.java +++ b/assistant-agent-start/src/main/java/com/alibaba/assistant/agent/start/config/ExperienceEvaluationCriterionProvider.java @@ -72,7 +72,7 @@ public List getReactPhaseCriteria() { .reasoningPolicy(ReasoningPolicy.NONE) .evaluatorType(EvaluatorType.LLM_BASED) .evaluatorRef("llm-based") - .contextBindings("context.userInput") + .contextBindings("context.input.userInput") .build(); // 2. ๆจก็ณŠ็จ‹ๅบฆๅˆคๆ–ญ Criterion @@ -97,7 +97,7 @@ public List getReactPhaseCriteria() { .reasoningPolicy(ReasoningPolicy.NONE) .evaluatorType(EvaluatorType.LLM_BASED) .evaluatorRef("llm-based") - .contextBindings("userInput") + .contextBindings("context.input.userInput") .build(); EvaluationCriterion reactExperienceRetrieval = EvaluationCriterionBuilder @@ -107,7 +107,7 @@ public List getReactPhaseCriteria() { .evaluatorType(EvaluatorType.RULE_BASED) .evaluatorRef("react_experience_evaluator") .dependsOn("enhanced_user_input") - .contextBindings("userInput") + .contextBindings("context.input.userInput") .build(); return List.of(enhancedUserInput, isFuzzy, reactExperienceRetrieval); @@ -133,7 +133,7 @@ public List getCodeActPhaseCriteria() { .reasoningPolicy(ReasoningPolicy.NONE) .evaluatorType(EvaluatorType.LLM_BASED) .evaluatorRef("llm-based") - .contextBindings("userInput") + .contextBindings("context.input.userInput") .build(); // 2. purpose ๅˆคๆ–ญ Criterion @@ -159,7 +159,7 @@ public List getCodeActPhaseCriteria() { .reasoningPolicy(ReasoningPolicy.NONE) .evaluatorType(EvaluatorType.LLM_BASED) .evaluatorRef("llm-based") - .contextBindings("userInput") + .contextBindings("context.input.userInput") .build(); EvaluationCriterion codeactExperienceRetrieval = EvaluationCriterionBuilder @@ -169,7 +169,7 @@ public List getCodeActPhaseCriteria() { .evaluatorType(EvaluatorType.RULE_BASED) .evaluatorRef("codeact_experience_evaluator") .dependsOn("enhanced_user_input") - .contextBindings("userInput") + .contextBindings("context.input.userInput") .build(); return List.of(enhancedUserInput, purpose, codeactExperienceRetrieval); diff --git a/pom.xml b/pom.xml index 2b54164..7fd349b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.alibaba.agent.assistant assistant-agent - 0.1.3-SNAPSHOT + 0.1.3 pom assistant-agent-autoconfigure @@ -24,8 +24,9 @@ 24.2.1 3.4.8 1.1.0.0 - 1.1.0.0 + 1.1.2.0 1.1.0 + 1.35.0 @@ -120,6 +121,15 @@ pom import + + + + io.opentelemetry + opentelemetry-bom + ${opentelemetry.version} + pom + import +