Customer sentiment analysis
Analyzing sentiment from customer service chat logs is vital for improving service quality. By monitoring emotional changes in real time, companies can quickly identify high-risk sessions, such as those with persistent negative sentiment. This allows them to trigger alerts and assign senior customer service representatives to intervene. This improves the customer experience and reduces churn. This topic describes how to use the pgml plugin in AnalyticDB for PostgreSQL V7.0 to perform customer sentiment analysis.
Prerequisites
You have an AnalyticDB for PostgreSQL V7.0 instance with minor engine version v7.2.1.0 or later.
The instance Master has the following specifications: 8 CUs or higher.
The instance compute nodes (Segments) have the following specifications: 8-core 32 GB or higher.
NoteYou can view the minor version on the Basic Information page of an instance in the AnalyticDB for PostgreSQL console. If your instance does not meet the required versions, update the minor version of the instance.
To learn how to change the configurations of the Master and compute nodes, see Master Resource Management and Compute Node Upgrade/Downgrade.
The instance uses the storage-elastic resource type.
The pgml extension is installed.
NoteIf the extension is installed, pgml appears in the schema list of the database. If it is not installed, submit a ticket to contact technical support for assistance with the installation. An instance restart is required. To uninstall the extension, you must also submit a ticket to contact technical support.
Procedure
Step 1: Create business tables
In the target database where the pgml plugin is installed, create a schema, create business tables, and insert test data.
-- Create a schema
CREATE SCHEMA testschema;
-- Create a table for raw chat records
CREATE TABLE testschema.chat_records (
session_id VARCHAR(40),
dialog_time TIMESTAMP,
role VARCHAR(10), -- Role: customer/service
dialog_text TEXT
);
-- Create a table to record inference results
CREATE TABLE testschema.sentiment_raw_results (
session_id character varying(40),
raw_response text
);
-- Write test data
INSERT INTO testschema.chat_records VALUES
('S001', '2024-05-01 09:30:00', 'customer', 'Your product quality is terrible! It broke after only three days!'),
('S001', '2024-05-01 09:31:00', 'service', 'We are very sorry for the inconvenience. We can arrange a free replacement.'),
('S002', '2024-05-01 10:15:00', 'customer', 'Thank you for your patient guidance. The problem is perfectly solved.'),
('S003', '2024-05-01 11:20:00', 'customer', 'I need to check the logistics information for my order.');Step 2: Use the model to perform inference on a single piece of data
Download the DeepSeek model to the Master.
NoteCurrently, only the DeepSeek-R1-Distill-Qwen-7B model is supported. This model is free of charge.
SELECT pgml.download_built_in_model('deepseek-ai/DeepSeek-R1-Distill-Qwen-7B');Use the model to perform inference on a single piece of data.
SELECT pgml.transform( task => '{ "task": "text-generation", "model": "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", "load_in_8bit": false, "device_map": "auto" }'::JSONB, inputs => ARRAY[ 'Analyze the customer sentiment in the following chat record. "Positive" means the user is very satisfied. "Negative" means there is a high risk of churn. "Neutral" means no emotional tendency is shown. Do not provide the thought process or reasons. Return only the result. For example: # User message: Hello, I would like to ask about your plans. # Sentiment: Neutral Now, rate based on the following input: # User message: '||'Thank you for your patient guidance. The problem is perfectly solved.'||' # Sentiment: </think>' ], args => '{ "max_new_tokens": 10, "temperature": 0.1 }'::JSONB ) AS response;Parameter description
Parameter name
Description
Sub-parameter
Example value
task
Specifies the pre-trained model and task type to use.
task: The task type.
task => '{ "task": "text-generation", "model": "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", "load_in_8bit": false, "device_map": "auto" }'::JSONBmodel: The model name.
load_in_8bit: A parameter related to model loading and 8-bit quantization. Valid values:
false: Disables 8-bit quantization.
true: Enables 8-bit quantization.
device_map: A parameter related to model loading and device allocation. The value
autoindicates that the framework automatically allocates different parts of the model to available devices to optimize memory usage and compute efficiency. This setting is ideal for large models or multi-device scenarios.args
Passes a set of configuration options to a logical unit, such as a function, stored procedure, API, or model.
max_new_tokens: Limits the number of tokens the model generates.
args => '{ "max_new_tokens": 10, "temperature": 0.1 }'::JSONBtemperature: The temperature coefficient, which controls the randomness of the generation.
A smaller value produces more deterministic and stable results. This is suitable for scenarios that require stable and consistent results, such as Q&A systems and code generation.
A larger value produces more random and diverse results. This is suitable for scenarios that require diverse and creative content, such as dialogue generation and story writing.
inputs
The text to classify.
None
ARRAY['The product quality is very good']
Step 3: Use the model to process historical chat records
Download the DeepSeek model to the compute nodes (Segments).
SELECT pgml.download_built_in_model('deepseek-ai/DeepSeek-R1-Distill-Qwen-7B') FROM gp_dist_random('gp_id');Process the historical chat records.
WITH model_output AS ( SELECT session_id, pgml.transform( task => '{ "task": "text-generation", "model": "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", "load_in_8bit": false, "device_map": "auto" }'::JSONB, inputs => ARRAY[ 'Analyze the customer sentiment in the following chat record. "Positive" means the user is very satisfied. "Negative" means there is a high risk of churn. "Neutral" means no emotional tendency is shown. Do not provide the thought process or reasons. Return only the result. For example: # User message: Hello, I would like to ask about your plans. # Sentiment: Neutral Now, rate based on the following input: # User message: '|| string_agg(dialog_text, '; ')||' # Sentiment: </think>' ], args => '{ "max_new_tokens": 10, "temperature": 0.1 }'::JSONB ) AS response FROM chat_records WHERE role = 'customer' -- Analyze only customer messages GROUP BY session_id ) INSERT INTO testschema.sentiment_raw_results SELECT session_id, response->0->0->>'generated_text' AS raw_response FROM model_output;Parameter description
Parameter name
Description
Sub-parameter
Example value
task
Specifies the pre-trained model and task type to use.
task: The task type.
task => '{ "task": "text-generation", "model": "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", "load_in_8bit": false, "device_map": "auto" }'::JSONBmodel: The model name.
load_in_8bit: A parameter related to model loading and 8-bit quantization. Valid values:
false: Disables 8-bit quantization.
true: Enables 8-bit quantization.
device_map: A parameter related to model loading and device allocation. The value
autoindicates that the framework automatically allocates different parts of the model to available devices to optimize memory usage and compute efficiency. This setting is ideal for large models or multi-device scenarios.args
Passes a set of configuration options to a logical unit, such as a function, stored procedure, API, or model.
max_new_tokens: Limits the number of tokens the model generates.
args => '{ "max_new_tokens": 10, "temperature": 0.1 }'::JSONBtemperature: The temperature coefficient, which controls the randomness of the generation.
A smaller value produces more deterministic and stable results. This is suitable for scenarios that require stable and consistent results, such as Q&A systems and code generation.
A larger value produces more random and diverse results. This is suitable for scenarios that require diverse and creative content, such as dialogue generation and story writing.
inputs
The text to classify.
None
ARRAY['The product quality is very good']
Query results
SELECT * FROM testschema.sentiment_raw_results limit 1;The following result is returned.
session_id | raw_response ------------+---------------------------------------------------------------------------------------------------------------------------------------------- S002 | Analyze the customer sentiment in the following chat record. "Positive" means the user is very satisfied. "Negative" means there is a high risk of churn. "Neutral" means no emotional tendency is shown. Do not provide the thought process or reasons. Return only the result. | For example: | # User message: Hello, I would like to ask about your plans. | # Sentiment: Neutral | | Now, rate based on the following input: | # User message: | Thank you for your patient guidance. The problem is perfectly solved. | # Sentiment: </think> | Positive (1 row)
Step 4: Convert to quantitative labels
Convert the results into risk levels to enable business follow-up.
WITH sentiment_results AS(
SELECT split_part(
raw_response,
'</think>',
(length(raw_response) - length(replace(raw_response, '</think>', '')))/8 + 1
) AS raw_label,
session_id
FROM testschema.sentiment_raw_results
)
SELECT
r.session_id,
r.dialog_time,
r.dialog_text,
CASE
WHEN s.raw_label~'Negative' THEN 'High risk (priority follow-up required)'
WHEN s.raw_label~'Positive' THEN 'Low risk (archive and monitor)'
ELSE 'Medium risk (routine handling)'
END AS risk_level
FROM testschema.chat_records r
JOIN sentiment_results s ON r.session_id = s.session_id
WHERE r.role = 'customer';The following result is returned.
session_id | dialog_time | dialog_text | risk_level
----------------+-----------------------+-----------------------------------+-------------------
S001 | 2024-05-01 09:30:00 | Your product quality is terrible! It broke after only three days! | High risk (priority follow-up required)
S003 | 2024-05-01 11:20:00 | I need to check the logistics information for my order. | Medium risk (routine handling)
S002 | 2024-05-01 10:15:00 | Thank you for your patient guidance. The problem is perfectly solved. | Low risk (archive and monitor)
(3 rows)