mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 20:18:08 +00:00
This PR adds the following two features 1. Prompt suggestions to get started with Copilot Chat 2. June events for each action  --------- Co-authored-by: Pranav <pranavrajs@gmail.com>
138 lines
4.0 KiB
Ruby
138 lines
4.0 KiB
Ruby
require 'openai'
|
|
class Captain::Agent
|
|
attr_reader :name, :tools, :prompt, :persona, :goal, :secrets
|
|
|
|
def initialize(name:, config:)
|
|
@name = name
|
|
@prompt = construct_prompt(config)
|
|
@tools = prepare_tools(config[:tools] || [])
|
|
@messages = config[:messages] || []
|
|
@max_iterations = config[:max_iterations] || 10
|
|
@llm = Captain::LlmService.new(api_key: config[:secrets][:OPENAI_API_KEY])
|
|
@logger = Rails.logger
|
|
|
|
@logger.info(@prompt)
|
|
end
|
|
|
|
def execute(input, context)
|
|
setup_messages(input, context)
|
|
result = {}
|
|
@max_iterations.times do |iteration|
|
|
push_to_messages(role: 'system', content: 'Provide a final answer') if iteration == @max_iterations - 1
|
|
|
|
result = @llm.call(@messages, functions)
|
|
handle_llm_result(result)
|
|
|
|
break if result[:stop]
|
|
end
|
|
|
|
result[:output]
|
|
end
|
|
|
|
def register_tool(tool)
|
|
@tools << tool
|
|
end
|
|
|
|
private
|
|
|
|
def setup_messages(input, context)
|
|
if @messages.empty?
|
|
push_to_messages({ role: 'system', content: @prompt })
|
|
push_to_messages({ role: 'assistant', content: context }) if context.present?
|
|
end
|
|
push_to_messages({ role: 'user', content: input })
|
|
end
|
|
|
|
def handle_llm_result(result)
|
|
if result[:tool_call]
|
|
tool_result = execute_tool(result[:tool_call])
|
|
push_to_messages({ role: 'assistant', content: tool_result })
|
|
else
|
|
push_to_messages({ role: 'assistant', content: result[:output] })
|
|
end
|
|
result[:output]
|
|
end
|
|
|
|
def execute_tool(tool_call)
|
|
function_name = tool_call['function']['name']
|
|
arguments = JSON.parse(tool_call['function']['arguments'])
|
|
|
|
tool = @tools.find { |t| t.name == function_name }
|
|
tool.execute(arguments, {})
|
|
rescue StandardError => e
|
|
"Tool execution failed: #{e.message}"
|
|
end
|
|
|
|
def construct_prompt(config)
|
|
return config[:prompt] if config[:prompt]
|
|
|
|
<<~PROMPT
|
|
Persona: #{config[:persona]}
|
|
Objective: #{config[:goal]}
|
|
|
|
Guidelines:
|
|
- Persistently work towards achieving the stated objective without deviation.
|
|
- Use only the provided tools to complete the task. Avoid inventing or assuming function names.
|
|
- Set `'stop': true` once the objective is fully achieved.
|
|
- DO NOT return tool usage as the final result.
|
|
- If sufficient information is available to deliver result, compile and present it to the user.
|
|
- Always return a final result and ENSURE the final result is formatted in Markdown.
|
|
|
|
Output Structure:
|
|
|
|
1. **Tool Usage:**
|
|
- If a relevant function is identified, call it directly without unnecessary explanations.
|
|
|
|
2. **Final Answer:**
|
|
When ready to provide a complete response, follow this JSON format:
|
|
|
|
```json
|
|
{
|
|
"thought_process": "Explain the reasoning and steps taken to arrive at the final result.",
|
|
"result": "Provide the complete response in clear, structured text.",
|
|
"stop": true
|
|
}
|
|
PROMPT
|
|
end
|
|
|
|
def prepare_tools(tools = [])
|
|
tools.map do |_, tool|
|
|
Captain::Tool.new(
|
|
name: tool['name'],
|
|
config: {
|
|
description: tool['description'],
|
|
properties: tool['properties'],
|
|
secrets: tool['secrets'],
|
|
implementation: tool['implementation']
|
|
}
|
|
)
|
|
end
|
|
end
|
|
|
|
def functions
|
|
@tools.map do |tool|
|
|
properties = {}
|
|
tool.properties.each do |property_name, property_details|
|
|
properties[property_name] = {
|
|
type: property_details[:type],
|
|
description: property_details[:description]
|
|
}
|
|
end
|
|
required = tool.properties.select { |_, details| details[:required] == true }.keys
|
|
{
|
|
type: 'function',
|
|
function: {
|
|
name: tool.name,
|
|
description: tool.description,
|
|
parameters: { type: 'object', properties: properties, required: required }
|
|
}
|
|
}
|
|
end
|
|
end
|
|
|
|
def push_to_messages(message)
|
|
@logger.info("\n\n\nMessage: #{message}\n\n\n")
|
|
@messages << message
|
|
end
|
|
end
|