AnalyticDB for PostgreSQL (ADB-PG) provides persistent, semantically searchable memory for large language model (LLM) applications built on Spring AI. Unlike conversation history, which stores every message in sequence, long-term memory uses vector similarity search to surface the most relevant facts from past interactions—regardless of when they occurred or how many messages have passed.
The spring-ai-alibaba component handles this end to end: it extracts structured facts from raw conversations using a configured LLM, stores those facts as vectors in ADB-PG, and retrieves them at query time to give the model persistent context across sessions.
Prerequisites
Before you begin, ensure that you have:
An AnalyticDB for PostgreSQL instance with vector search engine optimization enabled. To create an instance, see Create an instance
A Spring Boot 3.x project
An API key for a supported model service such as Qwen. To get one, see Get an API key
How it works
A request flows through three layers:
Advisor layer —
AdbpgChatMemoryAdvisorintercepts eachChatClientcall, retrieves relevant memories from ADB-PG using similarity search, and injects them into the prompt context.Memory service — The server-side component extracts structured facts from new conversation messages using the configured LLM and stores them as vectors via the embedding model.
Storage layer — ADB-PG persists the vector embeddings and metadata, enabling filtered retrieval by user, agent, time range, or custom attributes.
The fact extraction step is what distinguishes long-term memory from a simple message store. A raw message such as "I'm travelling to San Francisco" is stored as a structured fact: "User plans to travel to San Francisco". This normalized representation improves retrieval accuracy across sessions.
Set up the integration
Step 1: Add dependencies
The dependency packages are not yet available on Maven Central. To get them, contact Alibaba Cloud support on DingTalk (ID: 1rr-h38w9hwtt5).Add the following to your pom.xml:
<properties>
<!-- Replace with the version you received -->
<spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>
</properties>
<dependencies>
<!-- Core starter for ADB-PG long-term memory -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-adbpg</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Auto-configuration for ADB-PG long-term memory -->
<dependency>
<groupId>com.alibaba.cloud.ai.autoconfigure.memory.adbpg</groupId>
<artifactId>spring-ai-alibaba-autoconfigure-memory-adbpg</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
</dependencies>Step 2: Configure the connection
Add the following to your application.yml. The auto-configuration initializes all required beans when the server node is present.
spring:
ai:
dashscope:
api-key: sk-xxxx # Your DashScope API key
alibaba:
adbpg:
# Server-side: controls how memories are stored and processed in ADB-PG
server: # This node must exist to trigger auto-configuration
vector-store:
provider: adbpg
config:
port: 3000 # Internal port for the memory service
dbname: postgres
user: test_user
password: xxxx
# Must match the vector dimensions of the embedding model below
embedding-model-dims: 1024
llm:
provider: qwen # LLM used for fact extraction
config:
api-key: ${spring.ai.dashscope.api-key}
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
model: qwen3-32b # Other options: qwen-plus, qwen-max
embedder:
provider: openai # Uses DashScope's OpenAI-compatible endpoint
config:
api-key: ${spring.ai.dashscope.api-key}
model: text-embedding-v4
openai-base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
embedding-dims: 1024 # Must match embedding-model-dims above
# Optional: override the default fact extraction prompt
custom-fact-extraction-prompt: classpath:/prompts/custom_fact_extraction_prompt.st
# Client-side: JDBC connection for your application to read/write memories
client:
url: jdbc:postgresql://gp-xxxx-master.gpdb.rds.aliyuncs.com:5432/postgres
username: test_user
password: xxxxKey constraints:
embedding-model-dimsandembedding-dimsmust be identical. A mismatch causes vector storage errors.The
servernode must exist in the config. Removing it disables auto-configuration.The
embedder.provideris set toopenaibecause DashScope exposes an OpenAI-compatible API. It does not call OpenAI.
Step 3: Initialize the controller
Wire AdbpgChatMemoryAdvisor into your ChatClient at construction time. The advisor automatically retrieves relevant memories before each call and injects them into the prompt context.
import com.alibaba.cloud.ai.memory.adbpg.advisor.AdbpgChatMemoryAdvisor;
import com.alibaba.cloud.ai.memory.adbpg.core.AdbpgServiceClient;
import com.alibaba.cloud.ai.memory.adbpg.model.AdbpgServerRequest;
public class ADBPGMemoryController {
private static final Logger logger = LoggerFactory.getLogger(ADBPGMemoryController.class);
private final ChatClient chatClient; // Spring AI interface for LLM calls
private final VectorStore store; // Spring AI vector store interface
private final AdbpgServiceClient adbpgServiceClient; // Client for direct memory operations
public ADBPGMemoryController(VectorStore store, AdbpgServiceClient adbpgServiceClient, ChatClient.Builder builder) {
this.store = store;
this.adbpgServiceClient = adbpgServiceClient;
this.chatClient = builder
.defaultAdvisors(
AdbpgChatMemoryAdvisor.builder(store).build()
)
.build();
}
public String call(@RequestParam(value = "message", defaultValue = "Recommend some fun games for me") String message,
@RequestParam(value = "user_id", defaultValue = "miao") String userId) {
return chatClient.prompt(message)
.advisors(a -> a.params(Map.of(USER_ID, userId)))
.call().content();
}
}Manage memories directly
Use adbpgServiceClient when you need direct control over memory operations—for example, to pre-populate memories, apply custom filters, or clean up stale data.
Add memory
Pass one or more conversation messages to addMemory. The service extracts structured facts from the messages and stores them in ADB-PG.
// Basic: add messages for a user
adbpgServiceClient.addMemory(
AdbpgServerRequest.MemoryCreate.builder()
.userId("test")
.messages(List.of(
new AdbpgServerRequest.Message("user", "I'm travelling to San Francisco"),
new AdbpgServerRequest.Message("assistant", "That's great! I'm going to Dubai next month.")
))
.build()
);
// With agent isolation and expiration metadata
adbpgServiceClient.addMemory(
AdbpgServerRequest.MemoryCreate.builder()
.userId("test")
.agentId("agent2") // Isolates memories per agent (see table below)
.messages(List.of(
new AdbpgServerRequest.Message("user", "I'm travelling to San Francisco"),
new AdbpgServerRequest.Message("assistant", "That's great! I'm going to Dubai next month.")
))
.metadata(Map.of("expiration_date", "2026-01-01")) // Custom attribute for filtered queries
.build()
);After fact extraction, the raw message "I'm travelling to San Francisco" is stored as a structured fact such as "User plans to travel to San Francisco". This normalized representation improves retrieval accuracy for future similarity searches.
`MemoryCreate` fields:
| Field | Type | Required | When to use |
|---|---|---|---|
messages | List<Message> | Yes | The conversation turn to store |
userId | String | Yes | Scopes memories to a specific user |
agentId | String | No | Use when multiple agents share the same user space and need isolated memory pools |
runId | String | No | Use to tag memories from a specific session or workflow run for later tracing |
metadata | Map<String, Object> | No | Add custom attributes (e.g., expiration_date, topic) to enable filtered retrieval |
Retrieve memory
Use store.similaritySearch to fetch memories relevant to a query or scoped to a specific user or agent.
// Retrieve all memories for an agent
List<Document> documents = store.similaritySearch(
AdbpgServerRequest.SearchRequest.builder()
.userId("test")
.agentId("agent2")
.build()
);
logger.info("Agent long-term memory: {}", documents);
// Retrieve by semantic query with metadata filters
documents = store.similaritySearch(
AdbpgServerRequest.SearchRequest.builder()
.query("What do you know about me?")
.userId("test")
.filters(Map.of(
"AND",
Arrays.asList(
Map.of("created_at", Map.of("gte", "2025-07-29", "lte", "2025-11-30")),
Map.of("user_id", "test")
)
))
.build()
);`SearchRequest` fields:
| Field | Type | Required | When to use |
|---|---|---|---|
query | String | No | Provide a natural language query to retrieve semantically similar memories. Omit to return all memories in scope. |
userId | String | No | Limits results to a specific user. Omit only when querying across all users. |
agentId | String | No | Limits results to memories stored under a specific agent |
runId | String | No | Limits results to a specific session or run |
filters | Map<String, Object> | No | Applies structured conditions on metadata fields (e.g., date ranges, custom tags) |
limits | Integer | No | Caps the number of results returned. Defaults to the server configuration when omitted. |
Delete memory
Delete individual memories by ID, or delete all memories for a user.
// Delete a specific memory by ID
adbpgServiceClient.deleteMemory(documents.stream().findFirst().get().getId());
// Delete all memories for a user (pass null for agentId and runId to match all)
adbpgServiceClient.deleteAllMemories("test", null, null);When to use each approach
| Approach | How it works | Best for |
|---|---|---|
AdbpgChatMemoryAdvisor | Automatically retrieves and injects memories on each ChatClient call | Standard conversational AI where memory should be transparent to the application |
adbpgServiceClient directly | Explicit add, retrieve, and delete operations | Scenarios requiring custom filters, pre-populated memories, scheduled cleanup, or multi-agent memory isolation |
What's next
To customize fact extraction, create a prompt template at
classpath:/prompts/custom_fact_extraction_prompt.stand reference it incustom-fact-extraction-prompt.To scope memories across multiple agents for the same user, set a unique
agentIdon eachMemoryCreateandSearchRequest.To manage memory retention, add an
expiration_datefield tometadataand filter by it during retrieval.