deepagents 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/LICENSE +21 -0
- data/README.md +237 -0
- data/Rakefile +17 -0
- data/examples/langchain_integration.rb +58 -0
- data/examples/research_agent.rb +180 -0
- data/lib/deepagents/deepagentsrb/errors.rb +80 -0
- data/lib/deepagents/deepagentsrb/graph.rb +213 -0
- data/lib/deepagents/deepagentsrb/models.rb +167 -0
- data/lib/deepagents/deepagentsrb/state.rb +139 -0
- data/lib/deepagents/deepagentsrb/sub_agent.rb +125 -0
- data/lib/deepagents/deepagentsrb/tools.rb +205 -0
- data/lib/deepagents/deepagentsrb/version.rb +3 -0
- data/lib/deepagents/errors.rb +80 -0
- data/lib/deepagents/graph.rb +207 -0
- data/lib/deepagents/models.rb +217 -0
- data/lib/deepagents/state.rb +139 -0
- data/lib/deepagents/sub_agent.rb +130 -0
- data/lib/deepagents/tools.rb +152 -0
- data/lib/deepagents/version.rb +3 -0
- data/lib/deepagents.rb +61 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c68db07095761fd89fad4e2d5c27427ae659d94eaf9d2ebc8f4d5b7f2e243727
|
4
|
+
data.tar.gz: aca823c82b3ae695af8e2f75e5450a10d55a94e064fb59ec901a48007a2c9ccf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 69998915abd938c0de71ef6f0248a52708fa3f0680e076f0421cfd4a6305980da9456a7646fdcc8f969256de7da20ad17121314b2b653cfc88b25ded2fad27ac
|
7
|
+
data.tar.gz: 5fdf4e47952dcfb7b83e8e6c3c02c369491a219b07d16dbf544f12b761e8a313577db66956f78d9c929d53971e30e5f0a66745227afac55ff0f470cd983f109c
|
data/.rspec
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Chris
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# š§ š¤ DeepAgents for Ruby
|
2
|
+
|
3
|
+
Using an LLM to call tools in a loop is the simplest form of an agent. This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks. Applications like "Deep Research", "Manus", and "Claude Code" have gotten around this limitation by implementing a combination of four things: a **planning tool**, **sub agents**, access to a **file system**, and a **detailed prompt**.
|
4
|
+
|
5
|
+
`deepagents` is a Ruby gem that implements these in a general purpose way so that you can easily create a Deep Agent for your application.
|
6
|
+
|
7
|
+
**Acknowledgements: This project was inspired by the Python [deepagents](https://github.com/langchain-ai/deepagents) library and is a Ruby implementation of its functionality.**
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'deepagents'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
$ bundle install
|
21
|
+
```
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ gem install deepagents
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
### Basic Usage
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'deepagents'
|
35
|
+
require 'anthropic'
|
36
|
+
|
37
|
+
# Initialize the Anthropic client
|
38
|
+
anthropic_client = Anthropic::Client.new(api_key: ENV['ANTHROPIC_API_KEY'])
|
39
|
+
|
40
|
+
# Define a search tool
|
41
|
+
def internet_search(query, max_results: 5, topic: 'general', include_raw_content: false)
|
42
|
+
# In a real implementation, this would call a search API
|
43
|
+
# For example, you could use a Ruby wrapper for Tavily or another search API
|
44
|
+
"Search results for: #{query}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a tool object
|
48
|
+
search_tool = DeepAgents::Tool.new(
|
49
|
+
"internet_search",
|
50
|
+
"Run a web search"
|
51
|
+
) do |query, max_results: 5, topic: 'general', include_raw_content: false|
|
52
|
+
internet_search(query, max_results: max_results, topic: topic, include_raw_content: include_raw_content)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Prompt prefix to steer the agent to be an expert researcher
|
56
|
+
research_instructions = <<~INSTRUCTIONS
|
57
|
+
You are an expert researcher. Your job is to conduct thorough research, and then write a polished report.
|
58
|
+
|
59
|
+
You have access to a few tools.
|
60
|
+
|
61
|
+
## `internet_search`
|
62
|
+
|
63
|
+
Use this to run an internet search for a given query. You can specify the number of results, the topic, and whether raw content should be included.
|
64
|
+
INSTRUCTIONS
|
65
|
+
|
66
|
+
# Create the agent using built-in model adapters
|
67
|
+
agent = DeepAgents.create_deep_agent(
|
68
|
+
[search_tool],
|
69
|
+
research_instructions,
|
70
|
+
model: DeepAgents.claude_model() # or DeepAgents.openai_model()
|
71
|
+
)
|
72
|
+
|
73
|
+
# Invoke the agent
|
74
|
+
result = agent.invoke({
|
75
|
+
messages: [{role: "user", content: "what is Ruby on Rails?"}]
|
76
|
+
})
|
77
|
+
|
78
|
+
# Access the agent's response
|
79
|
+
puts result.messages.last[:content]
|
80
|
+
```
|
81
|
+
|
82
|
+
### Using with langchainrb and langgraph_rb
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
require 'deepagents'
|
86
|
+
require 'langchainrb'
|
87
|
+
require 'langgraph_rb'
|
88
|
+
|
89
|
+
# Create a search tool using langchainrb's tool system
|
90
|
+
search_tool = Langchain::Tool.new(
|
91
|
+
name: "internet_search",
|
92
|
+
description: "Run a web search",
|
93
|
+
function: ->(query:, max_results: 5) {
|
94
|
+
# In a real implementation, this would call a search API
|
95
|
+
"Search results for: #{query}"
|
96
|
+
}
|
97
|
+
)
|
98
|
+
|
99
|
+
# Convert to DeepAgents tool
|
100
|
+
deep_search_tool = DeepAgents::Tool.from_langchain(search_tool)
|
101
|
+
|
102
|
+
# Create a deep agent with langchainrb integration
|
103
|
+
agent = DeepAgents.create_deep_agent(
|
104
|
+
[deep_search_tool],
|
105
|
+
"You are an expert researcher. Your job is to conduct thorough research.",
|
106
|
+
model: "claude-3-sonnet-20240229" # Will automatically create appropriate model adapter
|
107
|
+
)
|
108
|
+
|
109
|
+
# Run the agent
|
110
|
+
result = agent.run("What is Ruby on Rails?")
|
111
|
+
puts result
|
112
|
+
```
|
113
|
+
|
114
|
+
## Creating a custom deep agent
|
115
|
+
|
116
|
+
There are three parameters you can pass to `create_deep_agent` to create your own custom deep agent.
|
117
|
+
|
118
|
+
### `tools` (Required)
|
119
|
+
|
120
|
+
The first argument to `create_deep_agent` is `tools`.
|
121
|
+
This should be an array of tool objects created using `DeepAgents::Tool.new`.
|
122
|
+
The agent (and any subagents) will have access to these tools.
|
123
|
+
|
124
|
+
### `instructions` (Required)
|
125
|
+
|
126
|
+
The second argument to `create_deep_agent` is `instructions`.
|
127
|
+
This will serve as part of the prompt of the deep agent.
|
128
|
+
Note that there is a built-in system prompt as well, so this is not the *entire* prompt the agent will see.
|
129
|
+
|
130
|
+
### `model` (Optional)
|
131
|
+
|
132
|
+
By default, `deepagents` uses a simple placeholder model. You should provide your own model implementation that interfaces with an LLM API like Anthropic's Claude or OpenAI.
|
133
|
+
|
134
|
+
Your model class should implement a `generate` method that takes a prompt and messages array and returns a response string.
|
135
|
+
|
136
|
+
### `subagents` (Optional)
|
137
|
+
|
138
|
+
A keyword parameter to `create_deep_agent` is `subagents`.
|
139
|
+
This can be used to specify any custom subagents this deep agent will have access to.
|
140
|
+
|
141
|
+
`subagents` should be an array of `DeepAgents::SubAgent` objects, where each has:
|
142
|
+
|
143
|
+
- **name**: The name of the subagent, and how the main agent will call the subagent
|
144
|
+
- **description**: The description of the subagent that is shown to the main agent
|
145
|
+
- **prompt**: The prompt used for the subagent
|
146
|
+
- **tools**: The list of tools that the subagent has access to (optional)
|
147
|
+
|
148
|
+
To use it looks like:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
research_sub_agent = DeepAgents::SubAgent.new(
|
152
|
+
name: "research-agent",
|
153
|
+
description: "Used to research more in depth questions",
|
154
|
+
prompt: sub_research_prompt
|
155
|
+
)
|
156
|
+
|
157
|
+
agent = DeepAgents.create_deep_agent(
|
158
|
+
tools,
|
159
|
+
prompt,
|
160
|
+
subagents: [research_sub_agent]
|
161
|
+
)
|
162
|
+
```
|
163
|
+
|
164
|
+
## Integration with langchainrb and langgraph_rb
|
165
|
+
|
166
|
+
DeepAgents now integrates with the popular Ruby gems `langchainrb` and `langgraph_rb` to provide enhanced functionality and compatibility with the broader LLM ecosystem.
|
167
|
+
|
168
|
+
### langchainrb Integration
|
169
|
+
|
170
|
+
The `langchainrb` integration provides:
|
171
|
+
|
172
|
+
- **Model Compatibility**: All DeepAgents model adapters can be converted to langchainrb models using the `to_langchain_model` method
|
173
|
+
- **Tool Interoperability**: Convert between DeepAgents tools and langchainrb tools
|
174
|
+
- **Vector Database Access**: Leverage langchainrb's vector database integrations for RAG applications
|
175
|
+
- **Prompt Management**: Use langchainrb's prompt templates with DeepAgents
|
176
|
+
|
177
|
+
### langgraph_rb Integration
|
178
|
+
|
179
|
+
The `langgraph_rb` integration enables:
|
180
|
+
|
181
|
+
- **Stateful Agents**: Build agents that maintain state across interactions
|
182
|
+
- **Directed Graphs**: Create complex agent workflows with directed graph structures
|
183
|
+
- **Multi-Actor Systems**: Coordinate multiple agents working together
|
184
|
+
- **Checkpointing**: Save and restore agent state
|
185
|
+
|
186
|
+
## Deep Agent Details
|
187
|
+
|
188
|
+
The below components are built into `deepagents` and help make it work for deep tasks off-the-shelf.
|
189
|
+
|
190
|
+
### Planning Tool
|
191
|
+
|
192
|
+
`deepagents` comes with a built-in planning tool. This planning tool is very simple and is based on ClaudeCode's TodoWrite tool.
|
193
|
+
This tool doesn't actually do anything - it is just a way for the agent to come up with a plan, and then have that in the context to help keep it on track.
|
194
|
+
|
195
|
+
### File System Tools
|
196
|
+
|
197
|
+
`deepagents` comes with four built-in file system tools: `ls`, `edit_file`, `read_file`, `write_file`.
|
198
|
+
These do not actually use a file system - rather, they mock out a file system using the agent's state object.
|
199
|
+
|
200
|
+
Right now the "file system" will only be one level deep (no sub directories).
|
201
|
+
|
202
|
+
These files can be passed in (and also retrieved) by using the `files` key in the agent's state.
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
agent = DeepAgents.create_deep_agent(...)
|
206
|
+
|
207
|
+
result = agent.invoke({
|
208
|
+
messages: [...],
|
209
|
+
# Pass in files to the agent using this key
|
210
|
+
# files: {"foo.txt" => "foo", ...}
|
211
|
+
})
|
212
|
+
|
213
|
+
# Access any files afterwards like this
|
214
|
+
result.files
|
215
|
+
```
|
216
|
+
|
217
|
+
### Sub Agents
|
218
|
+
|
219
|
+
`deepagents` comes with the built-in ability to call sub agents.
|
220
|
+
It has access to a `general-purpose` subagent at all times - this is a subagent with the same instructions as the main agent and all the tools that it has access to.
|
221
|
+
You can also specify custom sub agents with their own instructions and tools.
|
222
|
+
|
223
|
+
Sub agents are useful for "context quarantine" (to help not pollute the overall context of the main agent) as well as custom instructions.
|
224
|
+
|
225
|
+
## Development
|
226
|
+
|
227
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
228
|
+
|
229
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
230
|
+
|
231
|
+
## Contributing
|
232
|
+
|
233
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/cdaviis/deepagents/blob/main/CODE_OF_CONDUCT.md).
|
234
|
+
|
235
|
+
## License
|
236
|
+
|
237
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc "Run tests with coverage"
|
9
|
+
task :coverage do
|
10
|
+
ENV['COVERAGE'] = 'true'
|
11
|
+
Rake::Task["spec"].execute
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Run the example research agent"
|
15
|
+
task :example do
|
16
|
+
ruby "examples/research_agent.rb"
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "deepagents"
|
5
|
+
require "langchainrb"
|
6
|
+
require "langgraph_rb"
|
7
|
+
|
8
|
+
# Example showing integration between DeepAgents, langchainrb, and langgraph_rb
|
9
|
+
|
10
|
+
# 1. Create a langchainrb tool
|
11
|
+
search_tool = Langchain::Tool.new(
|
12
|
+
name: "internet_search",
|
13
|
+
description: "Search the internet for information",
|
14
|
+
function: ->(query:) {
|
15
|
+
puts "Searching for: #{query}"
|
16
|
+
"Here are some results for '#{query}': [Example search results]"
|
17
|
+
}
|
18
|
+
)
|
19
|
+
|
20
|
+
# 2. Create a DeepAgents tool
|
21
|
+
calculator_tool = DeepAgents::Tool.new(
|
22
|
+
"calculator",
|
23
|
+
"Perform mathematical calculations"
|
24
|
+
) do |expression|
|
25
|
+
begin
|
26
|
+
eval(expression).to_s
|
27
|
+
rescue => e
|
28
|
+
"Error: #{e.message}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# 3. Create a Claude model using DeepAgents
|
33
|
+
claude_model = DeepAgents.claude_model(model: "claude-3-sonnet-20240229")
|
34
|
+
|
35
|
+
# 4. Create a research sub-agent
|
36
|
+
research_agent = DeepAgents::SubAgent.new(
|
37
|
+
name: "research-agent",
|
38
|
+
description: "Used for in-depth research on specific topics",
|
39
|
+
prompt: "You are a specialized research agent. Your task is to provide detailed information on specific topics."
|
40
|
+
)
|
41
|
+
|
42
|
+
# 5. Create a deep agent with both tools and the sub-agent
|
43
|
+
agent = DeepAgents.create_deep_agent(
|
44
|
+
[calculator_tool],
|
45
|
+
"You are a helpful assistant that can perform calculations and search for information.",
|
46
|
+
model: claude_model,
|
47
|
+
subagents: [research_agent]
|
48
|
+
)
|
49
|
+
|
50
|
+
# 6. Run the agent with a user query
|
51
|
+
if ARGV.length > 0
|
52
|
+
result = agent.run(ARGV.join(" "))
|
53
|
+
puts "Agent response:"
|
54
|
+
puts result
|
55
|
+
else
|
56
|
+
puts "Please provide a query as a command-line argument."
|
57
|
+
puts "Example: ruby langchain_integration.rb 'What is 42 * 73?'"
|
58
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Add the lib directory to the load path
|
5
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
6
|
+
|
7
|
+
require 'deepagents'
|
8
|
+
|
9
|
+
# Load environment variables from .env file if present
|
10
|
+
begin
|
11
|
+
require 'dotenv'
|
12
|
+
Dotenv.load
|
13
|
+
rescue LoadError
|
14
|
+
# dotenv gem not installed, skipping
|
15
|
+
puts "Note: dotenv gem not installed. Using environment variables directly."
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define a search tool (in a real implementation, this would use a search API)
|
19
|
+
class MockInternetSearch
|
20
|
+
def initialize
|
21
|
+
# Mock search results
|
22
|
+
@results = {
|
23
|
+
"ruby gem" => "Ruby gems are packages of code that can be downloaded and used in Ruby projects. They are distributed via RubyGems.org.",
|
24
|
+
"anthropic claude" => "Claude is a family of AI assistants created by Anthropic. Claude excels at thoughtful dialogue and complex reasoning.",
|
25
|
+
"deep agent" => "Deep agents are AI systems that can perform complex tasks by breaking them down into smaller steps and using tools to accomplish goals."
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def search(query)
|
30
|
+
# Find the most relevant result
|
31
|
+
best_match = @results.keys.max_by do |key|
|
32
|
+
# Simple relevance scoring based on word overlap
|
33
|
+
(query.downcase.split & key.downcase.split).size
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return the result or a default message
|
37
|
+
@results[best_match] || "No results found for '#{query}'."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a search tool using the mock internet search
|
42
|
+
search_engine = MockInternetSearch.new
|
43
|
+
search_tool = DeepAgents::Tool.new(
|
44
|
+
"search",
|
45
|
+
"Search the internet for information"
|
46
|
+
) do |query, state:, tool_call_id:, **kwargs|
|
47
|
+
begin
|
48
|
+
raise ArgumentError, "Query cannot be empty" if query.nil? || query.strip.empty?
|
49
|
+
result = search_engine.search(query)
|
50
|
+
DeepAgents::ToolMessage.new(
|
51
|
+
"Search results for '#{query}':\n#{result}",
|
52
|
+
tool_call_id: tool_call_id
|
53
|
+
)
|
54
|
+
rescue => e
|
55
|
+
DeepAgents::ToolMessage.new(
|
56
|
+
"Error searching for '#{query}': #{e.message}",
|
57
|
+
tool_call_id: tool_call_id
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Define the agent's instructions
|
63
|
+
instructions = <<~INSTRUCTIONS
|
64
|
+
You are a research assistant that helps users find information and write reports.
|
65
|
+
|
66
|
+
When given a research topic:
|
67
|
+
1. Break down the topic into key questions to investigate
|
68
|
+
2. Use the search tool to find information about each question
|
69
|
+
3. Synthesize the information into a coherent report
|
70
|
+
4. Use the write_file tool to save your report
|
71
|
+
|
72
|
+
Be thorough but concise in your research.
|
73
|
+
Always cite your sources.
|
74
|
+
INSTRUCTIONS
|
75
|
+
|
76
|
+
# Create a research sub-agent for more focused research
|
77
|
+
sub_research_prompt = <<~SUB_PROMPT
|
78
|
+
You are a specialized research assistant. Your job is to dive deep into specific topics and extract detailed information.
|
79
|
+
|
80
|
+
When given a research task:
|
81
|
+
1. Break it down into specific questions
|
82
|
+
2. Search for information on each question
|
83
|
+
3. Organize the information in a structured way
|
84
|
+
4. Return a detailed analysis
|
85
|
+
SUB_PROMPT
|
86
|
+
|
87
|
+
research_sub_agent = DeepAgents::SubAgent.new(
|
88
|
+
name: "research-agent",
|
89
|
+
description: "Used to research more in depth questions",
|
90
|
+
prompt: sub_research_prompt
|
91
|
+
)
|
92
|
+
|
93
|
+
# Create the agent with proper error handling
|
94
|
+
begin
|
95
|
+
puts "Creating research agent..."
|
96
|
+
|
97
|
+
# Try to use the real API if keys are available
|
98
|
+
if ENV['ANTHROPIC_API_KEY']
|
99
|
+
puts "Using Anthropic Claude API"
|
100
|
+
model = DeepAgents.claude_model
|
101
|
+
elsif ENV['OPENAI_API_KEY']
|
102
|
+
puts "Using OpenAI API"
|
103
|
+
model = DeepAgents.openai_model
|
104
|
+
else
|
105
|
+
# Fall back to the mock model
|
106
|
+
puts "Warning: No API keys found. Using mock model."
|
107
|
+
model = DeepAgents::Models::MockModel.new({
|
108
|
+
"ruby gems" => <<~RESPONSE
|
109
|
+
I'll research Ruby gems for you.
|
110
|
+
|
111
|
+
I'll break this down into steps:
|
112
|
+
|
113
|
+
```tool_call
|
114
|
+
{"name": "write_todos", "arguments": {"todos": [{"content": "Search for Ruby gem information", "status": "pending"}, {"content": "Synthesize findings into a report", "status": "pending"}, {"content": "Save report to a file", "status": "pending"}]}}
|
115
|
+
```
|
116
|
+
|
117
|
+
Let me search for information about Ruby gems.
|
118
|
+
|
119
|
+
```tool_call
|
120
|
+
{"name": "search", "arguments": {"query": "ruby gem"}}
|
121
|
+
```
|
122
|
+
|
123
|
+
Now I'll write a report based on this information.
|
124
|
+
|
125
|
+
```tool_call
|
126
|
+
{"name": "write_todos", "arguments": {"todos": [{"content": "Search for Ruby gem information", "status": "completed"}, {"content": "Synthesize findings into a report", "status": "in_progress"}, {"content": "Save report to a file", "status": "pending"}]}}
|
127
|
+
```
|
128
|
+
|
129
|
+
```tool_call
|
130
|
+
{"name": "write_file", "arguments": {"file_path": "/ruby_gems_report.md", "content": "# Ruby Gems Report\n\nRuby gems are packages of code that can be downloaded and used in Ruby projects. They are distributed via RubyGems.org.\n\n## Key Points\n\n- Ruby gems are reusable code packages\n- They are distributed through RubyGems.org\n- They make it easy to share and use code across projects\n\n## Sources\n\n- Internet search results"}}
|
131
|
+
```
|
132
|
+
|
133
|
+
```tool_call
|
134
|
+
{"name": "write_todos", "arguments": {"todos": [{"content": "Search for Ruby gem information", "status": "completed"}, {"content": "Synthesize findings into a report", "status": "completed"}, {"content": "Save report to a file", "status": "completed"}]}}
|
135
|
+
```
|
136
|
+
|
137
|
+
I've completed the research on Ruby gems and saved a report to `/ruby_gems_report.md`. The report includes information about what Ruby gems are, how they're distributed, and their purpose in the Ruby ecosystem.
|
138
|
+
RESPONSE
|
139
|
+
})
|
140
|
+
end
|
141
|
+
|
142
|
+
agent = DeepAgents.create_deep_agent(
|
143
|
+
[search_tool],
|
144
|
+
instructions,
|
145
|
+
model: model,
|
146
|
+
subagents: [research_sub_agent]
|
147
|
+
)
|
148
|
+
|
149
|
+
# Run the agent with a research topic
|
150
|
+
puts "\nš¤ Running research agent..."
|
151
|
+
result = agent.invoke(messages: [{role: "user", content: "Please research Ruby gems and write a report."}])
|
152
|
+
|
153
|
+
# Print the final state
|
154
|
+
puts "\nš Final State:\n"
|
155
|
+
puts "Messages:"
|
156
|
+
result.messages.each do |message|
|
157
|
+
puts "#{message[:role]}: #{message[:content].split('\n').first}..."
|
158
|
+
end
|
159
|
+
|
160
|
+
puts "\nš Files:"
|
161
|
+
result.files.each do |path, content|
|
162
|
+
puts "#{path}:\n#{content}\n"
|
163
|
+
end
|
164
|
+
|
165
|
+
puts "\nš Todos:"
|
166
|
+
result.todos.each do |todo|
|
167
|
+
puts "- [#{todo.status == 'completed' ? 'x' : ' '}] #{todo.content}"
|
168
|
+
end
|
169
|
+
rescue DeepAgents::CredentialsError => e
|
170
|
+
puts "\nā API Credential Error: #{e.message}"
|
171
|
+
puts "Please set the appropriate API key in your environment variables."
|
172
|
+
rescue DeepAgents::ModelError => e
|
173
|
+
puts "\nā Model Error: #{e.message}"
|
174
|
+
puts "There was an issue with the LLM model."
|
175
|
+
rescue DeepAgents::Error => e
|
176
|
+
puts "\nā DeepAgents Error: #{e.message}"
|
177
|
+
rescue => e
|
178
|
+
puts "\nā Unexpected Error: #{e.message}"
|
179
|
+
puts e.backtrace.join("\n")
|
180
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module DeepAgentsRb
|
2
|
+
# Base error class for all DeepAgentsRb errors
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# Error raised when there's an issue with the state
|
6
|
+
class StateError < Error; end
|
7
|
+
|
8
|
+
# Error raised when there's an issue with a tool
|
9
|
+
class ToolError < Error
|
10
|
+
attr_reader :tool_name
|
11
|
+
|
12
|
+
def initialize(tool_name, message)
|
13
|
+
@tool_name = tool_name
|
14
|
+
super("Error in tool '#{tool_name}': #{message}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Error raised when a tool is not found
|
19
|
+
class ToolNotFoundError < ToolError
|
20
|
+
def initialize(tool_name)
|
21
|
+
super(tool_name, "Tool not found")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Error raised when there's an issue with a file operation
|
26
|
+
class FileError < Error; end
|
27
|
+
|
28
|
+
# Error raised when a file is not found
|
29
|
+
class FileNotFoundError < FileError
|
30
|
+
attr_reader :path
|
31
|
+
|
32
|
+
def initialize(path)
|
33
|
+
@path = path
|
34
|
+
super("File not found: #{path}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Error raised when there's an issue with a model
|
39
|
+
class ModelError < Error; end
|
40
|
+
|
41
|
+
# Error raised when there's an issue with API credentials
|
42
|
+
class CredentialsError < ModelError
|
43
|
+
def initialize(model_name)
|
44
|
+
super("Missing API credentials for #{model_name}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Error raised when there's an issue with a sub-agent
|
49
|
+
class SubAgentError < Error; end
|
50
|
+
|
51
|
+
# Error raised when a sub-agent is not found
|
52
|
+
class SubAgentNotFoundError < SubAgentError
|
53
|
+
attr_reader :agent_name
|
54
|
+
|
55
|
+
def initialize(agent_name)
|
56
|
+
@agent_name = agent_name
|
57
|
+
super("Sub-agent not found: #{agent_name}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Error raised when there's an issue with parsing a tool call
|
62
|
+
class ToolCallParseError < Error
|
63
|
+
attr_reader :raw_content
|
64
|
+
|
65
|
+
def initialize(message, raw_content = nil)
|
66
|
+
@raw_content = raw_content
|
67
|
+
super(message)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Error raised when there's an issue with the LLM response
|
72
|
+
class LLMResponseError < Error; end
|
73
|
+
|
74
|
+
# Error raised when the maximum number of iterations is reached
|
75
|
+
class MaxIterationsError < Error
|
76
|
+
def initialize(iterations)
|
77
|
+
super("Maximum number of iterations (#{iterations}) reached")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|