smart_agent 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +259 -45
- data/lib/smart_agent/agent.rb +56 -42
- data/lib/smart_agent/mcp_client.rb +14 -17
- data/lib/smart_agent/result.rb +8 -4
- data/lib/smart_agent/tool.rb +9 -0
- data/lib/smart_agent/version.rb +1 -1
- data/lib/smart_agent.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92f316229977853df082c594f3dfe42677dcb1d92e6e0ffe7e8c12dbf09db3cd
|
4
|
+
data.tar.gz: 42890f035e21b320b205b62f9de845cd7d2d0f492c13967e9147f52822af4702
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a837125205d17f4b9c3c3a3417c014f718c406a874fbc88a7339d59ee2dee1d5fd01fd4eb13654a28349bbbda97f1acc71b8b03b7a3ef14f21afe5f1dbbdf11
|
7
|
+
data.tar.gz: e721b313294d3f149cf986e9d4a3140a83fb68368542b527f5c2205fc1ff850c3a4f8982f08185bb0ac28fe681b21e95c604b557d81f19cd7a4e87b7eccc9111
|
data/README.md
CHANGED
@@ -1,89 +1,303 @@
|
|
1
1
|
# SmartAgent Framework
|
2
2
|
|
3
|
-
|
3
|
+
[](https://www.ruby-lang.org/)
|
4
|
+
[](./LICENSE)
|
5
|
+
[](./lib/smart_agent/version.rb)
|
4
6
|
|
5
|
-
|
7
|
+
**An intelligent agent framework for Ruby with MCP support, tool calling, and multi-LLM integration**
|
6
8
|
|
7
|
-
|
8
|
-
- Built-in utility tools:
|
9
|
-
- Weather query (get_weather)
|
10
|
-
- Math calculations (get_sum)
|
11
|
-
- Code generation and execution (get_code)
|
12
|
-
- Integrated with OpenDigger MCP service
|
13
|
-
- Supports natural language interaction in both English and Chinese
|
14
|
-
- Extensible tool system
|
9
|
+
## 🚀 Overview
|
15
10
|
|
16
|
-
|
11
|
+
SmartAgent is a powerful Ruby framework for building intelligent agents that can interact with various AI models, execute tools, and integrate with external services through the Model Context Protocol (MCP). It provides a declarative DSL for defining agents, tools, and workflows.
|
17
12
|
|
18
|
-
|
13
|
+
## ✨ Key Features
|
19
14
|
|
20
|
-
|
21
|
-
|
15
|
+
### 🤖 **Intelligent Agent System**
|
16
|
+
- **Agent Definition**: Create custom agents with specific behaviors and capabilities
|
17
|
+
- **Event-Driven Architecture**: Handle reasoning, content, and tool call events with custom callbacks
|
18
|
+
- **Multi-Agent Support**: Build and manage multiple specialized agents
|
19
|
+
|
20
|
+
### 🔧 **Tool Integration**
|
21
|
+
- **Built-in Tools**: Weather queries, web search, code generation, and mathematical calculations
|
22
|
+
- **Custom Tools**: Easy-to-define tools with parameter validation and type checking
|
23
|
+
- **Tool Groups**: Organize related tools for better management
|
24
|
+
|
25
|
+
### 🌐 **MCP (Model Context Protocol) Support**
|
26
|
+
- **Multiple MCP Servers**: Connect to various MCP-compatible services
|
27
|
+
- **Protocol Types**: Support for both STDIO and SSE (Server-Sent Events) connections
|
28
|
+
- **Service Integration**: OpenDigger, PostgreSQL, geographic services, and more
|
29
|
+
|
30
|
+
### 🎯 **Multi-LLM Backend Support**
|
31
|
+
- **Multiple Providers**: OpenAI, DeepSeek, SiliconFlow, Qwen, Ollama, and more
|
32
|
+
- **Flexible Configuration**: Easy switching between different AI models
|
33
|
+
- **Streaming Support**: Real-time response streaming with event callbacks
|
34
|
+
|
35
|
+
### 📝 **Advanced Prompt System**
|
36
|
+
- **Template Engine**: ERB-based templates for dynamic prompt generation
|
37
|
+
- **Worker System**: Specialized workers for different AI tasks
|
38
|
+
- **History Management**: Conversation context and memory management
|
39
|
+
|
40
|
+
## 📦 Installation
|
41
|
+
|
42
|
+
### Prerequisites
|
43
|
+
- Ruby 3.2.0 or higher
|
44
|
+
- Bundler gem manager
|
45
|
+
|
46
|
+
### Installation
|
47
|
+
Add this line to your application's Gemfile:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
gem 'smart_agent'
|
22
51
|
```
|
23
52
|
|
24
|
-
|
53
|
+
And then execute:
|
54
|
+
```bash
|
55
|
+
$ bundle install
|
56
|
+
```
|
25
57
|
|
58
|
+
Or install it yourself as:
|
26
59
|
```bash
|
27
|
-
|
60
|
+
$ gem install smart_agent
|
28
61
|
```
|
29
62
|
|
30
|
-
|
63
|
+
### Configuration
|
64
|
+
1. **Configure LLM providers** in `config/llm_config.yml`:
|
65
|
+
```yaml
|
66
|
+
llms:
|
67
|
+
deepseek:
|
68
|
+
adapter: openai
|
69
|
+
url: https://api.deepseek.com
|
70
|
+
api_key: ENV["DEEPSEEK_API_KEY"]
|
71
|
+
default_model: deepseek-reasoner
|
72
|
+
# Add other providers...
|
73
|
+
```
|
31
74
|
|
32
|
-
|
33
|
-
|
75
|
+
2. **Set up agent configuration** in `config/agent.yml`:
|
76
|
+
```yaml
|
77
|
+
logger_file: "./log/agent.log"
|
78
|
+
engine_config: "./config/llm_config.yml"
|
79
|
+
agent_path: "./agents"
|
80
|
+
tools_path: "./agents/tools"
|
81
|
+
mcp_path: "./agents/mcps"
|
34
82
|
```
|
35
83
|
|
36
|
-
## Usage
|
84
|
+
## 🛠️ Usage
|
37
85
|
|
38
|
-
### Basic
|
86
|
+
### Basic Agent Creation
|
39
87
|
|
40
88
|
```ruby
|
41
89
|
require 'smart_agent'
|
42
90
|
|
43
|
-
|
91
|
+
# Initialize the engine
|
92
|
+
engine = SmartAgent::Engine.new("./config/agent.yml")
|
93
|
+
|
94
|
+
# Define a smart agent
|
95
|
+
SmartAgent.define :smart_bot do
|
96
|
+
call_tool = true
|
97
|
+
while call_tool
|
98
|
+
result = call_worker(:smart_bot, params, with_tools: true, with_history: true)
|
99
|
+
if result.call_tools
|
100
|
+
call_tools(result)
|
101
|
+
params[:text] = "please continue"
|
102
|
+
else
|
103
|
+
call_tool = false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
result.response
|
107
|
+
end
|
108
|
+
|
109
|
+
# Build and configure the agent
|
110
|
+
agent = engine.build_agent(:smart_bot,
|
111
|
+
tools: [:get_weather, :search, :get_code],
|
112
|
+
mcp_servers: [:opendigger, :postgres]
|
113
|
+
)
|
44
114
|
|
45
|
-
#
|
46
|
-
|
115
|
+
# Add event handlers
|
116
|
+
agent.on_reasoning do |reasoning_content|
|
117
|
+
print reasoning_content.dig("choices", 0, "delta", "reasoning_content")
|
118
|
+
end
|
47
119
|
|
48
|
-
|
49
|
-
|
120
|
+
agent.on_content do |content|
|
121
|
+
print content.dig("choices", 0, "delta", "content")
|
122
|
+
end
|
50
123
|
|
51
|
-
#
|
52
|
-
|
124
|
+
# Use the agent
|
125
|
+
response = agent.please("What's the weather like in Shanghai tomorrow?")
|
126
|
+
puts response
|
53
127
|
```
|
54
128
|
|
55
|
-
### Custom
|
129
|
+
### Custom Tool Definition
|
56
130
|
|
57
131
|
```ruby
|
58
|
-
SmartAgent::Tool.define :
|
59
|
-
|
60
|
-
param_define :
|
132
|
+
SmartAgent::Tool.define :custom_calculator do
|
133
|
+
desc "Perform mathematical calculations"
|
134
|
+
param_define :expression, "Mathematical expression to evaluate", :string
|
135
|
+
param_define :precision, "Number of decimal places", :integer
|
61
136
|
|
62
|
-
|
63
|
-
|
64
|
-
"
|
137
|
+
tool_proc do
|
138
|
+
expression = input_params["expression"]
|
139
|
+
precision = input_params["precision"] || 2
|
140
|
+
|
141
|
+
begin
|
142
|
+
result = eval(expression)
|
143
|
+
result.round(precision)
|
144
|
+
rescue => e
|
145
|
+
"Error: #{e.message}"
|
146
|
+
end
|
65
147
|
end
|
66
148
|
end
|
67
149
|
```
|
68
150
|
|
69
|
-
|
70
|
-
|
71
|
-
Supports getting GitHub project metrics via OpenDigger MCP service:
|
151
|
+
### MCP Server Integration
|
72
152
|
|
73
153
|
```ruby
|
74
|
-
|
154
|
+
# Define MCP servers
|
75
155
|
SmartAgent::MCPClient.define :opendigger do
|
76
156
|
type :stdio
|
77
|
-
command "node
|
157
|
+
command "node /path/to/open-digger-mcp-server/dist/index.js"
|
158
|
+
end
|
159
|
+
|
160
|
+
SmartAgent::MCPClient.define :postgres do
|
161
|
+
type :stdio
|
162
|
+
command "node /path/to/postgres-mcp-server/dist/index.js postgres://user:pass@localhost/db"
|
163
|
+
end
|
164
|
+
|
165
|
+
SmartAgent::MCPClient.define :web_service do
|
166
|
+
type :sse
|
167
|
+
url "https://api.example.com/mcp/sse"
|
168
|
+
end
|
169
|
+
|
170
|
+
# Use with agent
|
171
|
+
agent = engine.build_agent(:research_bot, mcp_servers: [:opendigger, :postgres])
|
172
|
+
```
|
173
|
+
|
174
|
+
### Advanced Features
|
175
|
+
|
176
|
+
#### Stream Processing with Events
|
177
|
+
```ruby
|
178
|
+
agent.on_reasoning do |chunk|
|
179
|
+
# Handle reasoning content in real-time
|
180
|
+
print chunk.dig("choices", 0, "delta", "reasoning_content")
|
78
181
|
end
|
79
182
|
|
80
|
-
|
183
|
+
agent.on_tool_call do |event|
|
184
|
+
case event[:status]
|
185
|
+
when :start
|
186
|
+
puts "🔧 Starting tool execution..."
|
187
|
+
when :end
|
188
|
+
puts "✅ Tool execution completed"
|
189
|
+
else
|
190
|
+
print event[:content] if event[:content]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
#### Custom Workers
|
196
|
+
```ruby
|
197
|
+
SmartPrompt.define_worker :code_analyzer do
|
198
|
+
use "deepseek"
|
199
|
+
model "deepseek-chat"
|
200
|
+
sys_msg "You are an expert code analyzer."
|
201
|
+
|
202
|
+
prompt :analyze_template, {
|
203
|
+
code: params[:code],
|
204
|
+
language: params[:language]
|
205
|
+
}
|
206
|
+
|
207
|
+
send_msg
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
## 🏗️ Architecture
|
212
|
+
|
213
|
+
### Core Components
|
214
|
+
|
215
|
+
1. **SmartAgent::Engine**
|
216
|
+
- Configuration management
|
217
|
+
- Agent lifecycle management
|
218
|
+
- Tool and MCP server loading
|
219
|
+
|
220
|
+
2. **SmartAgent::Agent**
|
221
|
+
- Agent behavior definition
|
222
|
+
- Tool calling coordination
|
223
|
+
- Event handling system
|
224
|
+
|
225
|
+
3. **SmartAgent::Tool**
|
226
|
+
- Custom tool definition
|
227
|
+
- Parameter validation
|
228
|
+
- Function execution
|
229
|
+
|
230
|
+
4. **SmartAgent::MCPClient**
|
231
|
+
- MCP protocol implementation
|
232
|
+
- External service integration
|
233
|
+
- Multi-protocol support (STDIO/SSE)
|
234
|
+
|
235
|
+
5. **SmartAgent::Result**
|
236
|
+
- Response processing
|
237
|
+
- Tool call detection
|
238
|
+
- Content extraction
|
239
|
+
|
240
|
+
### Directory Structure
|
81
241
|
```
|
242
|
+
├── lib/smart_agent/ # Core framework code
|
243
|
+
├── config/ # Configuration files
|
244
|
+
├── templates/ # Prompt templates
|
245
|
+
├── workers/ # Specialized AI workers
|
246
|
+
├── agents/ # Agent definitions (auto-loaded)
|
247
|
+
├── agents/tools/ # Custom tools (auto-loaded)
|
248
|
+
└── agents/mcps/ # MCP server definitions (auto-loaded)
|
249
|
+
```
|
250
|
+
|
251
|
+
## 🔧 Configuration
|
252
|
+
|
253
|
+
### Supported LLM Providers
|
254
|
+
- **OpenAI Compatible**: DeepSeek, SiliconFlow, Gitee AI
|
255
|
+
- **Local Solutions**: Ollama, llama.cpp
|
256
|
+
- **Cloud Services**: Alibaba Cloud DashScope
|
257
|
+
|
258
|
+
### Environment Variables
|
259
|
+
```bash
|
260
|
+
export DEEPSEEK_API_KEY="your_deepseek_key"
|
261
|
+
export OPENAI_API_KEY="your_openai_key"
|
262
|
+
export SERPER_API_KEY="your_serper_key" # For web search
|
263
|
+
```
|
264
|
+
|
265
|
+
## 🎯 Use Cases
|
266
|
+
|
267
|
+
- **Research Assistants**: Integrate with academic databases and search engines
|
268
|
+
- **Code Analysis Tools**: Generate, analyze, and execute code dynamically
|
269
|
+
- **Data Analytics**: Connect to databases and perform complex queries
|
270
|
+
- **Content Creation**: Multi-modal content generation with tool assistance
|
271
|
+
- **API Integration**: Bridge different services through MCP protocol
|
272
|
+
|
273
|
+
## 🤝 Contributing
|
274
|
+
|
275
|
+
We welcome contributions! Please:
|
276
|
+
|
277
|
+
1. Fork the repository
|
278
|
+
2. Create a feature branch
|
279
|
+
3. Add tests for new functionality
|
280
|
+
4. Ensure all tests pass
|
281
|
+
5. Submit a pull request
|
282
|
+
|
283
|
+
### Development Setup
|
284
|
+
```bash
|
285
|
+
git clone https://github.com/zhuangbiaowei/smart_agent.git
|
286
|
+
cd smart_agent
|
287
|
+
bundle install
|
288
|
+
ruby test.rb # Run example tests
|
289
|
+
```
|
290
|
+
|
291
|
+
## 📄 License
|
292
|
+
|
293
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
82
294
|
|
83
|
-
##
|
295
|
+
## 🙏 Acknowledgments
|
84
296
|
|
85
|
-
|
297
|
+
- Built on top of the SmartPrompt framework
|
298
|
+
- Supports the Model Context Protocol (MCP)
|
299
|
+
- Integrates with various AI model providers
|
86
300
|
|
87
|
-
|
301
|
+
---
|
88
302
|
|
89
|
-
|
303
|
+
**⭐ Star this repository if you find it useful!**
|
data/lib/smart_agent/agent.rb
CHANGED
@@ -10,6 +10,10 @@ module SmartAgent
|
|
10
10
|
@code = self.class.agents[name]
|
11
11
|
end
|
12
12
|
|
13
|
+
def name
|
14
|
+
@name
|
15
|
+
end
|
16
|
+
|
13
17
|
def on_reasoning(&block)
|
14
18
|
@reasoning_event_proc = block
|
15
19
|
end
|
@@ -66,7 +70,8 @@ module SmartAgent
|
|
66
70
|
end
|
67
71
|
|
68
72
|
def call_worker(name, params, with_tools: true, with_history: false)
|
69
|
-
SmartAgent.logger.info
|
73
|
+
SmartAgent.logger.info("Call Worker name is: #{name}")
|
74
|
+
SmartAgent.logger.info("Call Worker params is: #{params}")
|
70
75
|
if with_tools
|
71
76
|
simple_tools = []
|
72
77
|
if @agent.tools
|
@@ -85,46 +90,15 @@ module SmartAgent
|
|
85
90
|
params[:with_history] = with_history
|
86
91
|
ret = nil
|
87
92
|
if @agent.on_event
|
88
|
-
|
89
|
-
tool_calls = []
|
90
|
-
result = SmartAgent.prompt_engine.call_worker_by_stream(name, params) do |chunk, _bytesize|
|
91
|
-
if full_result.empty?
|
92
|
-
full_result["id"] = chunk["id"]
|
93
|
-
full_result["object"] = chunk["object"]
|
94
|
-
full_result["created"] = chunk["created"]
|
95
|
-
full_result["model"] = chunk["model"]
|
96
|
-
full_result["choices"] = [{
|
97
|
-
"index" => 0,
|
98
|
-
"message" => {
|
99
|
-
"role" => "assistant",
|
100
|
-
"content" => "",
|
101
|
-
"reasoning_content" => "",
|
102
|
-
"tool_calls" => [],
|
103
|
-
},
|
104
|
-
}]
|
105
|
-
full_result["usage"] = chunk["usage"]
|
106
|
-
full_result["system_fingerprint"] = chunk["system_fingerprint"]
|
107
|
-
end
|
93
|
+
SmartAgent.prompt_engine.call_worker_by_stream(name, params) do |chunk, _bytesize|
|
108
94
|
if chunk.dig("choices", 0, "delta", "reasoning_content")
|
109
|
-
full_result["choices"][0]["message"]["reasoning_content"] += chunk.dig("choices", 0, "delta", "reasoning_content")
|
110
95
|
@agent.processor(:reasoning).call(chunk) if @agent.processor(:reasoning)
|
111
96
|
end
|
112
97
|
if chunk.dig("choices", 0, "delta", "content")
|
113
|
-
full_result["choices"][0]["message"]["content"] += chunk.dig("choices", 0, "delta", "content")
|
114
98
|
@agent.processor(:content).call(chunk) if @agent.processor(:content)
|
115
99
|
end
|
116
|
-
if chunk_tool_calls = chunk.dig("choices", 0, "delta", "tool_calls")
|
117
|
-
chunk_tool_calls.each do |tool_call|
|
118
|
-
if tool_calls.size > tool_call["index"]
|
119
|
-
tool_calls[tool_call["index"]]["function"]["arguments"] += tool_call["function"]["arguments"]
|
120
|
-
else
|
121
|
-
tool_calls << tool_call
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
100
|
end
|
126
|
-
|
127
|
-
result = full_result
|
101
|
+
result = SmartAgent.prompt_engine.stream_response
|
128
102
|
else
|
129
103
|
result = SmartAgent.prompt_engine.call_worker(name, params)
|
130
104
|
end
|
@@ -132,6 +106,42 @@ module SmartAgent
|
|
132
106
|
return ret
|
133
107
|
end
|
134
108
|
|
109
|
+
def safe_parse(input)
|
110
|
+
# 保存原始输入用于调试
|
111
|
+
original_input = input.dup
|
112
|
+
|
113
|
+
# 步骤1: 清理输入
|
114
|
+
cleaned = input.strip
|
115
|
+
|
116
|
+
# 步骤2: 处理外层引号(如果存在)
|
117
|
+
if cleaned.start_with?('"') && cleaned.end_with?('"')
|
118
|
+
cleaned = cleaned[1...-1]
|
119
|
+
end
|
120
|
+
|
121
|
+
# 步骤3: 反转义双重转义字符
|
122
|
+
# 关键:只处理需要反转义的字符,保持JSON合法性
|
123
|
+
cleaned = cleaned
|
124
|
+
.gsub(/\\"/, '"') # 反转义引号
|
125
|
+
.gsub(/\\\\/, '\\') # 反转义反斜杠
|
126
|
+
# 不反转义\n, \t, \r等,因为它们是JSON合法的转义序列
|
127
|
+
|
128
|
+
# 步骤4: 尝试解析
|
129
|
+
begin
|
130
|
+
return JSON.parse(cleaned)
|
131
|
+
rescue JSON::ParserError => e
|
132
|
+
# 如果清理后失败,尝试原始输入
|
133
|
+
begin
|
134
|
+
return JSON.parse(original_input)
|
135
|
+
rescue JSON::ParserError
|
136
|
+
puts "Failed to parse JSON: #{e.message}"
|
137
|
+
puts "Original: #{original_input}"
|
138
|
+
puts "Cleaned: #{cleaned}"
|
139
|
+
# 返回原始字符串以便后续处理
|
140
|
+
return original_input
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
135
145
|
def call_tools(result)
|
136
146
|
@agent.processor(:tool).call({ :status => :start }) if @agent.processor(:tool)
|
137
147
|
SmartAgent.logger.info("call tools: " + result.to_s)
|
@@ -139,23 +149,27 @@ module SmartAgent
|
|
139
149
|
result.call_tools.each do |tool|
|
140
150
|
tool_call_id = tool["id"]
|
141
151
|
tool_name = tool["function"]["name"].to_sym
|
142
|
-
params =
|
152
|
+
params = safe_parse(tool["function"]["arguments"])
|
143
153
|
if Tool.find_tool(tool_name)
|
144
154
|
@agent.processor(:tool).call({ :content => "ToolName is `#{tool_name}`\n" }) if @agent.processor(:tool)
|
145
155
|
@agent.processor(:tool).call({ :content => "params is `#{params}`\n" }) if @agent.processor(:tool)
|
146
156
|
tool_result = Tool.find_tool(tool_name).call(params)
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
157
|
+
if tool_result
|
158
|
+
SmartAgent.prompt_engine.history_messages << { "role" => "assistant", "content" => "", "tool_calls" => [tool] } #result.response.dig("choices", 0, "message")
|
159
|
+
SmartAgent.prompt_engine.history_messages << { "role" => "tool", "tool_call_id" => tool_call_id, "content" => tool_result.to_s.force_encoding("UTF-8") }
|
160
|
+
results << tool_result
|
161
|
+
end
|
151
162
|
end
|
152
163
|
if server_name = MCPClient.find_server_by_tool_name(tool_name)
|
153
164
|
@agent.processor(:tool).call({ :content => "MCP Server is `#{server_name}`, ToolName is `#{tool_name}`\n" }) if @agent.processor(:tool)
|
154
165
|
@agent.processor(:tool).call({ :content => "params is `#{params}`\n" }) if @agent.processor(:tool)
|
155
166
|
tool_result = MCPClient.new(server_name).call(tool_name, params)
|
156
|
-
|
157
|
-
|
158
|
-
|
167
|
+
if tool_result
|
168
|
+
@agent.processor(:tool).call({ :content => tool_result })
|
169
|
+
SmartAgent.prompt_engine.history_messages << { "role" => "assistant", "content" => "", "tool_calls" => [tool] } # result.response.dig("choices", 0, "message")
|
170
|
+
SmartAgent.prompt_engine.history_messages << { "role" => "tool", "tool_call_id" => tool_call_id, "content" => tool_result.to_s }
|
171
|
+
results << tool_result
|
172
|
+
end
|
159
173
|
end
|
160
174
|
@agent.processor(:tool).call({ :content => " ... done\n" }) if @agent.processor(:tool)
|
161
175
|
end
|
@@ -4,19 +4,19 @@ module SmartAgent
|
|
4
4
|
SmartAgent.logger.info "Create mcp server's name is #{name}"
|
5
5
|
@name = name
|
6
6
|
@code = self.class.servers[name]
|
7
|
-
end
|
8
|
-
|
9
|
-
def to_json
|
10
7
|
@context = MCPContext.new
|
11
8
|
@context.instance_eval(&@code)
|
12
9
|
command_path = @context.command_path
|
13
10
|
if @context.mcp_type == :stdio
|
14
|
-
client = MCP::StdioClient.new(command_path)
|
11
|
+
@client = MCP::StdioClient.new(command_path)
|
15
12
|
else
|
16
|
-
client = MCP::SSEClient.new(command_path)
|
13
|
+
@client = MCP::SSEClient.new(command_path)
|
17
14
|
end
|
18
|
-
client.start
|
19
|
-
|
15
|
+
@client.start
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_json
|
19
|
+
mcp_server_json = @client.list_tools
|
20
20
|
if mcp_server_json
|
21
21
|
mcp_server_json["tools"].each do |tool|
|
22
22
|
MCPClient.set_server(tool["name"].to_sym, @name)
|
@@ -26,16 +26,7 @@ module SmartAgent
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def call(tool_name, params)
|
29
|
-
@
|
30
|
-
@context.instance_eval(&@code)
|
31
|
-
command_path = @context.command_path
|
32
|
-
if @context.mcp_type == :stdio
|
33
|
-
client = MCP::StdioClient.new(command_path)
|
34
|
-
else
|
35
|
-
client = MCP::SSEClient.new(command_path)
|
36
|
-
end
|
37
|
-
client.start
|
38
|
-
client.call_method(
|
29
|
+
@client.call_method(
|
39
30
|
{
|
40
31
|
"name": tool_name.to_s,
|
41
32
|
"arguments": params,
|
@@ -43,6 +34,10 @@ module SmartAgent
|
|
43
34
|
)
|
44
35
|
end
|
45
36
|
|
37
|
+
def close
|
38
|
+
@client.stop
|
39
|
+
end
|
40
|
+
|
46
41
|
class << self
|
47
42
|
def servers
|
48
43
|
@servers ||= {}
|
@@ -54,6 +49,8 @@ module SmartAgent
|
|
54
49
|
|
55
50
|
def define(name, &block)
|
56
51
|
servers[name] = block
|
52
|
+
client = MCPClient.new(name)
|
53
|
+
client.to_json
|
57
54
|
end
|
58
55
|
|
59
56
|
def set_server(tool_name, server_name)
|
data/lib/smart_agent/result.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module SmartAgent
|
2
2
|
class Result
|
3
3
|
def initialize(response)
|
4
|
-
SmartAgent.logger.info("response is:" + response.to_s)
|
4
|
+
#SmartAgent.logger.info("response is:" + response.to_s)
|
5
5
|
@response = response
|
6
6
|
end
|
7
7
|
|
@@ -10,10 +10,14 @@ module SmartAgent
|
|
10
10
|
return false
|
11
11
|
else
|
12
12
|
tool_calls = @response.dig("choices", 0, "message", "tool_calls")
|
13
|
-
if tool_calls
|
14
|
-
|
13
|
+
if tool_calls
|
14
|
+
unless tool_calls.empty?
|
15
|
+
return tool_calls
|
16
|
+
else
|
17
|
+
return false
|
18
|
+
end
|
15
19
|
else
|
16
|
-
return
|
20
|
+
return false
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
data/lib/smart_agent/tool.rb
CHANGED
@@ -90,6 +90,15 @@ module SmartAgent
|
|
90
90
|
SmartAgent.prompt_engine.call_worker(name, params)
|
91
91
|
end
|
92
92
|
|
93
|
+
def call_tool(name, params)
|
94
|
+
if Tool.find_tool(name)
|
95
|
+
return Tool.find_tool(name).call(params)
|
96
|
+
end
|
97
|
+
if server_name = MCPClient.find_server_by_tool_name(name)
|
98
|
+
return MCPClient.new(server_name).call(name, params)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
93
102
|
def tool_proc(&block)
|
94
103
|
@proc = block
|
95
104
|
end
|
data/lib/smart_agent/version.rb
CHANGED
data/lib/smart_agent.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zhuang Biaowei
|
@@ -23,6 +23,20 @@ dependencies:
|
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: mcp-sdk.rb
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
26
40
|
- !ruby/object:Gem::Dependency
|
27
41
|
name: rspec
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
85
|
- !ruby/object:Gem::Version
|
72
86
|
version: '0'
|
73
87
|
requirements: []
|
74
|
-
rubygems_version: 3.
|
88
|
+
rubygems_version: 3.7.1
|
75
89
|
specification_version: 4
|
76
90
|
summary: Intelligent agent framework with DSL and MCP integration
|
77
91
|
test_files: []
|