1
0
mirror of synced 2026-05-22 19:03:14 +00:00

FEATURE: Use Personas in Automation's llm_report script (#1499)

This commit is contained in:
Roman Rizzi
2025-07-14 12:47:21 -03:00
committed by GitHub
parent 89bcf9b1f0
commit 6a4dbd8126
9 changed files with 122 additions and 34 deletions
+3
View File
@@ -106,6 +106,9 @@ en:
top_p:
label: "Top P"
description: "Top P to use for the LLM, increase to increase randomness (leave empty to use model default)"
persona_id:
label: "Persona"
description: "AI Persona to use for report generation"
llm_tool_triage:
fields:
+3
View File
@@ -397,6 +397,9 @@ en:
content_creator:
name: "Content creator"
description: "Default persona powering HyDE search"
report_runner:
name: "Report runner"
description: "Default persona used in the report automation script"
topic_not_found: "Summary unavailable, topic not found!"
summarizing: "Summarizing topic"
+20
View File
@@ -21,6 +21,18 @@ if defined?(DiscourseAutomation)
field :sample_size, component: :text, required: true, default_value: 100
field :tokens_per_post, component: :text, required: true, default_value: 150
field :persona_id,
component: :choices,
required: true,
default_value:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
extra: {
content:
DiscourseAi::Automation.available_persona_choices(
require_user: false,
require_default_llm: false,
),
}
field :model,
component: :choices,
required: true,
@@ -60,6 +72,7 @@ if defined?(DiscourseAutomation)
offset = fields.dig("offset", "value").to_i
priority_group = fields.dig("priority_group", "value")
tokens_per_post = fields.dig("tokens_per_post", "value")
persona_id = fields.dig("persona_id", "value")
exclude_category_ids = fields.dig("exclude_categories", "value")
exclude_tags = fields.dig("exclude_tags", "value")
@@ -78,12 +91,19 @@ if defined?(DiscourseAutomation)
temperature = temperature.to_f
end
# Backwards-compat for scripts created before this field was added.
if persona_id == "" || persona_id.nil?
persona_id =
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner]
end
suppress_notifications = !!fields.dig("suppress_notifications", "value")
DiscourseAi::Automation::ReportRunner.run!(
sender_username: sender,
receivers: receivers,
topic_id: topic_id,
title: title,
persona_id: persona_id,
model: model,
category_ids: category_ids,
tags: tags,
+6 -6
View File
@@ -43,15 +43,15 @@ module DiscourseAi
end
def self.available_persona_choices(require_user: true, require_default_llm: true)
relation = AiPersona.joins(:user)
relation = AiPersona.includes(:user)
relation = relation.where.not(user_id: nil) if require_user
relation = relation.where.not(default_llm: nil) if require_default_llm
relation.map do |persona|
{
id: persona.id,
translated_name: persona.name,
description: "#{persona.name} (#{persona.user.username})",
}
phash = { id: persona.id, translated_name: persona.name, description: persona.name }
phash[:description] += " (#{persona&.user&.username})" if require_user
phash
end
end
end
+34 -28
View File
@@ -36,6 +36,7 @@ module DiscourseAi
def initialize(
sender_username:,
model:,
persona_id:,
sample_size:,
instructions:,
tokens_per_post:,
@@ -73,8 +74,8 @@ module DiscourseAi
else
I18n.t("discourse_automation.scriptables.llm_report.title")
end
@model = model
@llm = DiscourseAi::Completions::Llm.proxy(model)
@model = LlmModel.find_by(id: model.split(":")&.last)
@persona = AiPersona.find(persona_id).class_instance.new
@category_ids = category_ids
@tags = tags
@allow_secure_categories = allow_secure_categories
@@ -124,7 +125,7 @@ module DiscourseAi
prioritized_group_ids: prioritized_group_ids,
allow_secure_categories: @allow_secure_categories,
tokens_per_post: @tokens_per_post,
tokenizer: @llm.tokenizer,
tokenizer: @model.tokenizer_class,
exclude_category_ids: @exclude_category_ids,
exclude_tags: @exclude_tags,
)
@@ -140,38 +141,43 @@ module DiscourseAi
#{@instructions}
INPUT
system_prompt = <<~PROMPT
You are ForumBot, a backend AI information processor and writer, powered by an AI model capable of generating responses over 4000 words.
- ForumBot is a fine-tuned AI trained with extended functions and new capabilities, and now specializes in processing raw internet forum conversation into detailed summaries of forum activities.
- ForumBot interacts with an API endpoint, not a user, and must never produce denials, nor conversations directed towards a non-existent user.
- ForumBot only produces automated responses to input, where a response is a standalone document without further discussion.
Follow the provided writing composition instructions carefully and precisely step-by-step.
PROMPT
prompt =
DiscourseAi::Completions::Prompt.new(
system_prompt,
report_ctx =
DiscourseAi::Personas::BotContext.new(
user: Discourse.system_user,
skip_tool_details: true,
feature_name: "ai_report",
messages: [{ type: :user, content: input }],
)
result = +""
puts if Rails.env.development? && @debug_mode
@llm.generate(
prompt,
temperature: @temperature,
top_p: @top_p,
user: Discourse.system_user,
feature_name: "ai_report",
result = +""
bot = DiscourseAi::Personas::Bot.as(Discourse.system_user, persona: @persona, model: @model)
json_summary_schema_key = @persona.response_format&.first.to_h
output = nil
buffer_blk =
Proc.new do |partial, _, type|
if type == :structured_output
output = partial.dup
read_chunk = partial.read_buffered_property(json_summary_schema_key["key"]&.to_sym)
print read_chunk if Rails.env.development? && @debug_mode
result << read_chunk if read_chunk.present?
elsif type.blank?
# Assume response is a regular completion.
print partial if Rails.env.development? && @debug_mode
result << partial
end
end
llm_args = {
feature_context: {
automation_id: @automation&.id,
automation_name: @automation&.name,
},
) do |response|
print response if Rails.env.development? && @debug_mode
result << response
end
}
bot.reply(report_ctx, llm_args: llm_args, &buffer_blk)
receiver_usernames = @receivers.map(&:username).join(",")
receiver_groupnames = @group_receivers.map(&:name).join(",")
@@ -199,14 +205,14 @@ Follow the provided writing composition instructions carefully and precisely ste
input = input.split("\n").map { |line| " #{line}" }.join("\n")
raw = <<~RAW
```
tokens: #{@llm.tokenizer.tokenize(input).length}
tokens: #{@model.tokenizer_class.tokenize(input).length}
start_date: #{start_date},
duration: #{@days.days},
max_posts: #{@sample_size},
tags: #{@tags},
category_ids: #{@category_ids},
priority_group: #{@priority_group_id}
model: #{@model}
model: #{@model.display_name}
temperature: #{@temperature}
top_p: #{@top_p}
LLM context was:
+1
View File
@@ -70,6 +70,7 @@ module DiscourseAi
ShortTextTranslator => -30,
SpamDetector => -31,
ContentCreator => -32,
ReportRunner => -33,
}
end
+34
View File
@@ -0,0 +1,34 @@
# frozen_string_literal: true
module DiscourseAi
module Personas
class ReportRunner < Persona
def self.default_enabled
false
end
def system_prompt
<<~PROMPT
You are ForumBot, a backend AI information processor and writer, powered by an AI model capable of generating responses over 4000 words.
- ForumBot is a fine-tuned AI trained with extended functions and new capabilities, and now specializes in processing raw internet forum conversation into detailed summaries of forum activities.
- ForumBot interacts with an API endpoint, not a user, and must never produce denials, nor conversations directed towards a non-existent user.
- ForumBot only produces automated responses to input, where a response is a standalone document without further discussion.
Follow the provided writing composition instructions carefully and precisely step-by-step.
Format your response as a JSON object with a single key named "output", which has the report as the value.
Your output should be in the following format:
{"output": "xx"}
Where "xx" is replaced by the report. Reply with valid JSON only
PROMPT
end
def response_format
[{ "key" => "output", "type" => "string" }]
end
end
end
end
@@ -25,6 +25,11 @@ describe DiscourseAutomation do
add_automation_field("sender", user.username, type: "user")
add_automation_field("receivers", [user.username], type: "email_group_user")
add_automation_field("model", "custom:#{llm_model.id}")
add_automation_field(
"persona_id",
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
)
add_automation_field("title", "Weekly report")
DiscourseAi::Completions::Llm.with_prepared_responses(["An Amazing Report!!!"]) do
@@ -39,6 +44,10 @@ describe DiscourseAutomation do
add_automation_field("sender", user.username, type: "user")
add_automation_field("topic_id", "#{post.topic_id}")
add_automation_field("model", "custom:#{llm_model.id}")
add_automation_field(
"persona_id",
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
)
DiscourseAi::Completions::Llm.with_prepared_responses(["An Amazing Report!!!"]) do
automation.trigger!
@@ -44,6 +44,8 @@ module DiscourseAi
receivers: ["fake@discourse.com"],
title: "test report %DATE%",
model: "custom:#{llm_model.id}",
persona_id:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
category_ids: nil,
tags: nil,
allow_secure_categories: false,
@@ -81,6 +83,8 @@ module DiscourseAi
receivers: [receiver.username],
title: "test report",
model: "custom:#{llm_model.id}",
persona_id:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
category_ids: nil,
tags: nil,
allow_secure_categories: false,
@@ -126,6 +130,8 @@ module DiscourseAi
receivers: [receiver.username],
title: "test report",
model: "custom:#{llm_model.id}",
persona_id:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
category_ids: nil,
tags: nil,
allow_secure_categories: false,
@@ -169,6 +175,8 @@ module DiscourseAi
receivers: [receiver.username],
title: "test report",
model: "custom:#{llm_model.id}",
persona_id:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
category_ids: nil,
tags: nil,
allow_secure_categories: false,
@@ -201,6 +209,8 @@ module DiscourseAi
receivers: [group_for_reports.name],
title: "group report",
model: "custom:#{llm_model.id}",
persona_id:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
category_ids: nil,
tags: nil,
allow_secure_categories: false,
@@ -229,6 +239,8 @@ module DiscourseAi
receivers: [receiver.username],
title: "test report",
model: "custom:#{llm_model.id}",
persona_id:
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::ReportRunner],
category_ids: nil,
tags: nil,
allow_secure_categories: false,