Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 72 additions & 47 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,37 @@ Messages are the fundamental way agents communicate. Each message has:
- System's response to agent-requested tool calls
- Contains the output of the action execution

### The `with` Method Pattern

The `with` method is a **class method** that allows you to pass parameters to agents before calling actions. This follows the Rails pattern and returns a `Generation` object that can be executed.

#### Important: `with` is a CLASS Method

```ruby
# CORRECT - with is called on the class
response = MyAgent.with(param: value).my_action.generate_now

# INCORRECT - with is NOT an instance method
agent = MyAgent.new
response = agent.with(param: value).my_action # This will error!
```

#### How it Works

1. `with` returns a `Parameterized::Agent` object
2. Calling an action on it returns a `Generation` object
3. Call `generate_now` or `generate_later` to execute

```ruby
# Step by step
generation = TranslationAgent.with(message: "Hello", locale: "es").translate
# generation is now a Generation object
response = generation.generate_now # Execute the generation

# Or chain it all together
response = TranslationAgent.with(message: "Hello", locale: "es").translate.generate_now
```

### Actions: The Bridge Between Rails and AI

Actions in ActiveAgent serve multiple purposes:
Expand Down Expand Up @@ -318,8 +349,8 @@ class MessagesController < ApplicationController
response = agent.generate(prompt: params[:message])

render json: {
reply: response.content,
actions_taken: response.requested_actions
reply: response.message.content,
actions_taken: response.prompt.requested_actions
}
end
end
Expand All @@ -342,9 +373,13 @@ end
Translate: <%= @text %>
To: <%= @target %>

# Usage in controller
agent = TranslationAgent.new
response = agent.with(message: "Hello", locale: "es").translate
# Usage in controller - Note: `with` is a CLASS method
# It returns a Generation object that can be executed
response = TranslationAgent.with(message: "Hello", locale: "es").translate.generate_now

# Alternative: store the generation for later execution
generation = TranslationAgent.with(message: "Hello", locale: "es").translate
response = generation.generate_now # Execute when ready
```

#### 3. **Background Generation**
Expand All @@ -361,8 +396,7 @@ class DataAnalysisAgent < ApplicationAgent
end

# In your controller
agent = DataAnalysisAgent.new
generation = agent.with(dataset: large_data).generate_later
generation = DataAnalysisAgent.with(dataset: large_data).generate_later

# Check status later
generation.finished? # => true/false
Expand Down Expand Up @@ -499,20 +533,18 @@ Add AI capabilities to ActiveRecord models:
```ruby
class Article < ApplicationRecord
def generate_summary
agent = ContentAgent.new
agent.with(
ContentAgent.with(
title: title,
content: content,
max_length: 200
).summarize
).summarize.generate_now
end

def translate_to(locale)
agent = TranslationAgent.new
agent.with(
TranslationAgent.with(
message: content,
locale: locale
).translate
).translate.generate_now
end
end
```
Expand All @@ -539,8 +571,7 @@ module AgentHelpers
end

def generate_with_agent(agent_class, action, params = {})
agent = agent_class.new
agent.with(params).public_send(action)
agent_class.with(params).public_send(action).generate_now
end
end
```
Expand Down Expand Up @@ -725,16 +756,14 @@ class BlogPostsController < ApplicationController
end

def generate
agent = BlogWriterAgent.new

# Generate a blog post
response = agent.with(
response = BlogWriterAgent.with(
topic: params[:topic],
style: params[:style],
length: params[:length]
).write_post
).write_post.generate_now

@generated_content = response.content
@generated_content = response.message.content
@post = BlogPost.new(
title: extract_title(@generated_content),
content: @generated_content,
Expand All @@ -746,14 +775,13 @@ class BlogPostsController < ApplicationController

def edit_with_ai
@post = BlogPost.find(params[:id])
agent = BlogWriterAgent.new

response = agent.with(
response = BlogWriterAgent.with(
content: @post.content,
instructions: params[:instructions]
).edit_post
).edit_post.generate_now

@post.content = response.content
@post.content = response.message.content
render :edit
end

Expand All @@ -772,33 +800,32 @@ end
class BlogGenerationService
def initialize(user)
@user = user
@agent = BlogWriterAgent.new
end

def generate_weekly_posts(topics)
topics.map do |topic|
response = @agent.with(
response = BlogWriterAgent.with(
topic: topic,
style: "professional",
length: 800
).write_post
).write_post.generate_now

BlogPost.create!(
title: extract_title(response.content),
content: response.content,
title: extract_title(response.message.content),
content: response.message.content,
author: @user,
status: "draft"
)
end
end

def improve_seo(post)
response = @agent.with(
response = BlogWriterAgent.with(
content: post.content,
instructions: "Improve SEO by adding relevant keywords, meta description, and improving headings"
).edit_post
).edit_post.generate_now

post.update!(content: response.content)
post.update!(content: response.message.content)
end
end
```
Expand All @@ -816,14 +843,14 @@ class BlogWriterAgentTest < ActiveSupport::TestCase

test "writes blog post about Rails" do
VCR.use_cassette("blog_writer_rails_post") do
response = @agent.with(
response = BlogWriterAgent.with(
topic: "Getting Started with Rails 7",
style: "technical",
length: 600
).write_post
).write_post.generate_now

assert response.content.include?("Rails")
assert response.content.length > 400
assert response.message.content.include?("Rails")
assert response.message.content.length > 400

# Generate documentation example
doc_example_output(response)
Expand All @@ -834,13 +861,13 @@ class BlogWriterAgentTest < ActiveSupport::TestCase
original = "Rails is framework. It make web app easy."

VCR.use_cassette("blog_writer_edit_grammar") do
response = @agent.with(
response = BlogWriterAgent.with(
content: original,
instructions: "Fix grammar and improve clarity"
).edit_post
).edit_post.generate_now

assert response.content != original
assert response.content.include?("Rails")
assert response.message.content != original
assert response.message.content.include?("Rails")
end
end
end
Expand Down Expand Up @@ -923,11 +950,10 @@ Chain multiple agents together for complex tasks:
class DocumentProcessingService
def process_document(file_path)
# Extract content
extractor = DataExtractionAgent.new
extracted_data = extractor.with(
extracted_data = DataExtractionAgent.with(
file_path: file_path,
schema: :document_schema
).extract
).extract.generate_now

# Summarize content
summarizer = SummaryAgent.new
Expand Down Expand Up @@ -1152,12 +1178,11 @@ class ComplexAgentTest < ActiveSupport::TestCase
test "multi-agent document processing" do
VCR.use_cassette("complex_document_flow") do
# Step 1: Extract data
extractor = DataExtractionAgent.new
extracted = extractor.with(
extracted = DataExtractionAgent.with(
content: file_fixture("report.pdf").read
).extract
).extract.generate_now

assert extracted.content.present?
assert extracted.message.content.present?

# Step 2: Analyze with context
analyzer = AnalysisAgent.new
Expand Down
6 changes: 6 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ export default defineConfig({
{ text: 'Generation Provider', link: '/docs/framework/generation-provider' },
]
},
{
text: 'Generation Providers',
items: [
{ text: 'OpenRouter', link: '/docs/generation-providers/open-router-provider' },
]
},
{
text: 'Action Prompt',
items: [
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/action-prompt/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ApplicationAgent.new.prompt(

These Prompt objects contain the context Messages and available Actions. These actions are the interface that agents can use to interact with tools through text and JSON views or interact with users through text and HTML views.

Actions can be used to render Prompt objects with `:assistant` Messages back to a user or `:tool` Messages to provide the result of an action back to the Agent.
Actions can be used to render Prompt objects with `:assistant` [Messages](/docs/action-prompt/messages) back to a user or `:tool` Messages to provide the result of an action back to the Agent.

## Defining Actions
You can define actions in your agent class that can be used to interact with the agent.
Expand Down Expand Up @@ -213,7 +213,7 @@ Tool schema definitions are also view templates that can be rendered to the agen

## Tool Calling Example

Here's an example of how agents handle tool calls using the support agent:
Here's an example of how agents handle tool calls using the support agent. [See complete tool calling workflows →](/docs/action-prompt/tool-calling)

<<< @/../test/agents/support_agent_test.rb#support_agent_tool_call{ruby:line-numbers}

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/action-prompt/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ Agent performed actions result in `:tool` message. These messages are used to re

## Building Message Context

Messages form the conversation history that provides context for the agent:
Messages form the conversation history that provides context for the agent. [Learn how messages flow through generation →](/docs/active-agent/generation)

<<< @/../test/agents/messages_examples_test.rb#message_context{ruby:line-numbers}
4 changes: 2 additions & 2 deletions docs/docs/action-prompt/tool-calling.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ When an error occurs, the agent receives the error message and can provide appro

## Tool Schemas

Define tool schemas using JSON views to describe parameters:
Define tool schemas using JSON views to describe parameters. [Learn more about tool implementation →](/docs/action-prompt/tools)

<<< @/../test/dummy/app/views/calculator_agent/add.json.jbuilder {ruby:line-numbers}

Expand All @@ -90,4 +90,4 @@ The tool calling flow is handled by the `perform_generation` method:
5. **Continuation**: Generation continues with `continue_generation`
6. **Completion**: The process repeats until no more tools are requested

This creates a natural conversation flow where the agent can gather information through tools before providing a final answer.
This creates a natural conversation flow where the agent can gather information through tools before providing a final answer. [Understanding the complete generation cycle →](/docs/active-agent/generation)
4 changes: 3 additions & 1 deletion docs/docs/action-prompt/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ Tools are defined as methods in your agent class. The tool's JSON schema is defi

### Tool Schema Definition

<<< @/../test/dummy/app/views/support_agent/get_cat_image.json.erb {erb}
<<< @/../test/dummy/app/views/support_agent/get_cat_image.json.erb {erb}

[See how tools are called and executed in multi-turn conversations →](/docs/action-prompt/tool-calling)
2 changes: 1 addition & 1 deletion docs/docs/active-agent/callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ You can apply around_generation callbacks conditionally using `:only` and `:exce
<<< @/../test/callbacks_test.rb#around_generation_conditions {ruby:line-numbers}

This pattern is useful for:
- **Performance monitoring**: Track generation times for specific actions
- **Performance monitoring**: Track generation times for specific actions. [For long-running tasks, consider queued generation →](/docs/active-agent/queued-generation)
- **Caching**: Cache LLM responses for expensive operations
- **Rate limiting**: Implement custom rate limiting logic
- **Debugging**: Log detailed information about specific generations
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/active-agent/generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ The prompt generation cycle is similar to the request-response cycle of Action C
3. **Generation Response**: The generation provider processes the request and returns a response, which is then passed back to the agent.
4. **Response Handling**: The agent processes the response, which can be sent back to the user or used for further processing.
5. **Action Execution**: If the response includes actions, the agent executes them and updates the context accordingly.
6. **Updated Context**: The context is updated with the new messages, actions, and parameters, and the cycle continues.
6. **Updated Context**: The context is updated with the new messages, actions, and parameters, and the cycle continues. [Customize generation behavior with callbacks →](/docs/active-agent/callbacks)
## Prompt Context
Action Prompt renders prompt context objects that represent the contextual data and runtime parameters for the generation process. Prompt context objects contain messages, actions, and params that are passed in the request to the agent's generation provider. The context object is responsible for managing the contextual history and providing the necessary information for prompt and response cycles.
Loading