首页 / 文章 / 实用说明:作为向量存储的 PostgreSQL + pgvector 与 SQL Server 2025

实用说明:作为向量存储的 PostgreSQL + pgvector 与 SQL Server 2025

《实用笔记》操作指南:将 PostgreSQL + pgvector 与 SQL Server 2025 作为向量存储使用——面向采用该架构的团队提供的契约规范、检查清单及可直接嵌入的代码片段。

4064 词

以下内容围绕《PostgreSQL + pgvector与SQL Server 2025作为RAG的向量存储——实践者指南》整理出一条实用路径。重点在于约定规范、检查项以及可直接使用的代码占位符,而非激励性表述。

2025年的向量数据库格局

在处理“向量数据库格局”这一阶段时,首先需明确约定:所需输入、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续代码修改的规范性。 将此阶段视为输入与验证后输出之间的约定。为相关成果命名,定义成功检查标准,杜绝无声的半完成状态。 在调整提示词之前,先使用固定问题集测试召回率。仅仅更换提示词往往无法改善较差的检索效果。

-- Find the most relevant chunks, but only from documents
-- belonging to enterprise-tier customers — a single SQL query
SELECT c.content, 1 - (c.embedding <=> query_vec) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC
LIMIT 5;

全栈架构

在完成全栈开发阶段时,首先需列出相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 在调整提示词之前,先使用固定的问题集测试检索效果。仅仅更换提示词往往无法改善较差的检索性能。

第一部分 — PostgreSQL 18 + pgvector

在完成 PostgreSQL 18 的第一阶段时,首先列出相关规范:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将配置信息与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中管理,这样操作人员无需查看全部代码即可进行审计。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词很难解决检索效果不佳的问题。

在 Windows 上安装 pgvector

在完成“在 Windows 上安装 pgvector”这一环节时,首先需明确相关要求:所需的输入参数、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改不会偏离原有设计。

set "PGROOT=C:\Program Files\PostgreSQL\18"
cd %TEMP%
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
cd pgvector
nmake /F Makefile.win
nmake /F Makefile.win install
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres --name pgvector pgvector/pgvector:pg18
conda install -c conda-forge pgvector

在 pgAdmin 中创建表

在pgAdmin的创建表阶段工作时,首先需明确相关规范:所需输入、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 建议采用小型、可测试的单元而非庞大的脚本。当某一步骤失败时,故障应指向单一责任模块,而非复杂的流程链。 在调整提示词之前,先使用固定的问题集测试召回率。仅仅更换提示词很难解决检索效果不佳的问题。 在pgAdmin的创建表阶段工作时,首先需明确相关规范:所需输入、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果之外,还需记录执行时间以及令牌或查询的成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外费用。

-- Enable the pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Main chunks table with VECTOR(768) column
CREATE TABLE IF NOT EXISTS rag_chunks (
    id           UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
    source       TEXT        NOT NULL,
    chunk_index  INTEGER     NOT NULL,
    content      TEXT        NOT NULL,
    file_hash    TEXT,
    ingested_at  TIMESTAMPTZ DEFAULT NOW(),
    embedding    vector(768)  -- pgvector native type
);

-- HNSW index for approximate cosine similarity search
CREATE INDEX IF NOT EXISTS idx_rag_chunks_embedding
    ON rag_chunks USING hnsw (embedding vector_cosine_ops);

-- Source filter index
CREATE INDEX IF NOT EXISTS idx_rag_chunks_source
    ON rag_chunks (source);

-- Staleness registry
CREATE TABLE IF NOT EXISTS rag_staleness (
    doc_name    TEXT        PRIMARY KEY,
    file_hash   TEXT        NOT NULL,
    chunk_count INTEGER,
    ingested_at TIMESTAMPTZ DEFAULT NOW(),
    version     INTEGER     DEFAULT 1
);

-- CDC chunk registry
CREATE TABLE IF NOT EXISTS rag_chunk_registry (
    doc_name    TEXT NOT NULL,
    chunk_hash  TEXT NOT NULL,
    chunk_id    TEXT NOT NULL,
    PRIMARY KEY (doc_name, chunk_hash)
);

-- Conversation sessions
CREATE TABLE IF NOT EXISTS rag_sessions (
    session_id  TEXT        PRIMARY KEY,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    updated_at  TIMESTAMPTZ DEFAULT NOW(),
    model       TEXT,
    embed_model TEXT,
    turn_count  INTEGER     DEFAULT 0
);

-- Conversation turns
CREATE TABLE IF NOT EXISTS rag_turns (
    id          SERIAL      PRIMARY KEY,
    session_id  TEXT        REFERENCES rag_sessions(session_id) ON DELETE CASCADE,
    role        TEXT        NOT NULL CHECK (role IN ('user','assistant')),
    content     TEXT        NOT NULL,
    sources     TEXT[],
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_rag_turns_session
    ON rag_turns (session_id, created_at);

Python依赖项

将“Python依赖项”阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份完美的运行示例、一个故障案例以及回滚说明。 配置信息应置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放,这样操作人员无需查看整个系统结构即可进行审计。 在讲解循环逻辑之前,先锁定解释器和依赖项的版本。笔记本电脑与持续集成环境之间的差异是API演示中最常见的隐性故障原因。

uv add google-genai pypdf pgvector psycopg2-binary python-dotenv huggingface_hub

连接设置(单元2)

将“连接设置单元2”阶段视为可测量的界面时,其效果最佳。在扩大范围之前,需记录一个成功的案例、一个失败案例以及回滚说明。同时记录正常流程与恢复流程的文档。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续需要补充的内容。应将分块策略与检索策略分开,当质量指标发生变化时,修改其中一项不应强制要求重新编写另一项。

import psycopg2
from pgvector.psycopg2 import register_vector

PG_HOST     = "localhost"
PG_PORT     = 5432
PG_DB       = "postgres"
PG_USER     = "postgres"
PG_PASSWORD = os.environ.get("PG_PASSWORD", "postgres")

def get_pg_conn():
    """Returns a fresh PostgreSQL connection with pgvector registered."""
    conn = psycopg2.connect(
        host=PG_HOST, port=PG_PORT,
        dbname=PG_DB, user=PG_USER, password=PG_PASSWORD
    )
    register_vector(conn)   # tells psycopg2 how to handle vector type
    return conn

存储嵌入向量(单元6)

将“存储嵌入模型”第6阶段视为可度量的对象来处理时效果最佳。在扩大范围之前,先记录一个理想案例、一个故障案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某一步骤出现故障时,故障应指向单一责任主体,而非复杂的流程链。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

import numpy as np

def store_in_postgres(chunks, embeddings, doc_name) -> int:
    conn = get_pg_conn()
    cur  = conn.cursor()
    for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
        cur.execute("""
            INSERT INTO rag_chunks (source, chunk_index, content, embedding)
            VALUES (%s, %s, %s, %s)
        """, (doc_name, i, chunk, np.array(emb)))  # np.array → pgvector handles serialization
    conn.commit()
    conn.close()
    return len(chunks)

将“存储嵌入模型”第6阶段视为可度量的对象来处理时效果最佳。在扩大范围之前,先记录一个理想案例、一个故障案例以及回滚说明。 除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

基于余弦相似度的检索(第6阶段续)

在“基于余弦相似度的检索”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测其中的隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审核。 需注明实际作为答案依据的段落。若没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

def retrieve_context(query: str) -> list[dict]:
    query_embedding = embed_query(query)
    conn = get_pg_conn()
    cur  = conn.cursor()

    cur.execute("""
        SELECT
            content,
            source,
            chunk_index,
            ingested_at,
            1 - (embedding <=> %s) AS cosine_score  -- <=> is cosine distance
        FROM rag_chunks
        ORDER BY embedding <=> %s                    -- sort ascending (smallest distance first)
        LIMIT %s
    """, (np.array(query_embedding), np.array(query_embedding), TOP_K))
    rows = cur.fetchall()
    conn.close()
    return [{"text": r[0], "source": r[1], "chunk_index": r[2],
             "ingested_at": str(r[3]) if r[3] else "",
             "score": round(float(r[4]), 4)} for r in rows]

使用 pgvector 的 CDC —— 插入或更新模式

对于使用 pgvector Upsert 功能的 CDC,修改代码之前需先定义输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理都属于产品功能的一部分,而非后续需要补充的内容。 必须引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

cur.execute("""
    INSERT INTO rag_staleness (doc_name, file_hash, chunk_count, version)
    VALUES (%s, %s, %s, 1)
    ON CONFLICT (doc_name) DO UPDATE SET
        file_hash   = EXCLUDED.file_hash,
        chunk_count = EXCLUDED.chunk_count,
        ingested_at = NOW(),
        version     = rag_staleness.version + 1;
""", (doc_name, file_hash, chunk_count))

第二部分 — SQL Server 2025(原生向量功能)

在第二阶段的 SQL Server 处理中,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测其隐藏状态。相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能指向具体的责任主体,而非复杂的流程链。必须引用实际作为答案依据的段落;没有引用的话,操作人员就无法区分是虚假信息还是索引缺失所致。

为何 SQL Server 2025 不需要扩展功能

在“Why SQL Server 2025”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏的状态。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并杜绝无声的半完成状态。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分幻觉内容与索引缺失问题。

在SSMS中的设置

在SSMS阶段的设置中,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏的状态。除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解这些成本信息,可避免在从演示环境过渡到共享环境时出现意外费用。必须注明实际作为答案依据的段落;如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

-- Batch 1: Run this first — must commit before vector objects are recognized
ALTER DATABASE SCOPED CONFIGURATION SET PREVIEW_FEATURES = ON;
-- Batch 2: Create all tables
CREATE TABLE rag_chunks (
    id           INT              IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    source       NVARCHAR(500)    NOT NULL,
    chunk_index  INT              NOT NULL,
    content      NVARCHAR(MAX)    NOT NULL,
    file_hash    NVARCHAR(64),
    ingested_at  DATETIME2        DEFAULT GETUTCDATE(),
    embedding    VECTOR(768)      -- native SQL Server 2025 type
);

CREATE INDEX idx_rag_source ON rag_chunks(source);

CREATE TABLE rag_staleness (
    doc_name    NVARCHAR(500)   PRIMARY KEY,
    file_hash   NVARCHAR(64)    NOT NULL,
    chunk_count INT,
    ingested_at DATETIME2       DEFAULT GETUTCDATE(),
    version     INT             DEFAULT 1
);

CREATE TABLE rag_chunk_registry (
    doc_name    NVARCHAR(500)   NOT NULL,
    chunk_hash  NVARCHAR(64)    NOT NULL,
    chunk_id    NVARCHAR(64)    NOT NULL,
    PRIMARY KEY (doc_name, chunk_hash)
);

CREATE TABLE rag_sessions (
    session_id  NVARCHAR(100)   PRIMARY KEY,
    created_at  DATETIME2       DEFAULT GETUTCDATE(),
    updated_at  DATETIME2       DEFAULT GETUTCDATE(),
    model       NVARCHAR(200),
    embed_model NVARCHAR(200),
    turn_count  INT             DEFAULT 0
);

CREATE TABLE rag_turns (
    id          INT             IDENTITY(1,1) PRIMARY KEY,
    session_id  NVARCHAR(100)   NOT NULL REFERENCES rag_sessions(session_id),
    role        NVARCHAR(20)    NOT NULL CHECK (role IN ('user','assistant')),
    content     NVARCHAR(MAX)   NOT NULL,
    sources     NVARCHAR(MAX),
    created_at  DATETIME2       DEFAULT GETUTCDATE()
);

CREATE INDEX idx_rag_turns_session ON rag_turns(session_id, created_at);
-- Batch 3: Must run AFTER Batch 2 commits
-- Cannot run inside a transaction — this is a known SQL Server 2025 preview constraint
CREATE VECTOR INDEX idx_rag_embedding
    ON rag_chunks(embedding)
    WITH (METRIC = 'COSINE');

Python依赖项

在 Python 依赖阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审核。 将客户端构建与消息处理循环分开,这样在更换提供方时无需重写对话状态机。

uv add google-genai pypdf pyodbc python-dotenv huggingface_hub fpdf2

连接设置(单元 2)

在“连接设置单元2”阶段,修改代码之前需明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的内容。 必须引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

import pyodbc

SQL_SERVER   = "YOUR_SERVER_NAME"   # from SSMS title bar
SQL_DATABASE = "local_rag"
SQL_CONN_STR = (
    f"DRIVER={{ODBC Driver 17 for SQL Server}};"
    f"SERVER={SQL_SERVER};"
    f"DATABASE={SQL_DATABASE};"
    f"Trusted_Connection=yes;"    # Windows Authentication - no password needed
)

def get_conn():
    return pyodbc.connect(SQL_CONN_STR)

def get_conn_autocommit():
    """Required for CREATE/DROP VECTOR INDEX - cannot run inside a transaction."""
    return pyodbc.connect(SQL_CONN_STR, autocommit=True)

嵌入向量存储(单元6)

在“存储嵌入向量”第6阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 优先选择小型、可测试的单元,而非冗长的脚本。当某一步骤失败时,故障应指向单一责任主体,而非复杂的流程链。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。 在“存储嵌入向量”第6阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境切换到共享环境时出现意外费用。

...

import json

def vec_to_json(embedding: list[float]) -> str:
    return json.dumps(embedding)   # '[0.12, 0.34, ...]'

def store_in_sqlserver(chunks, embeddings, doc_name) -> int:
    drop_vector_index()    # must drop before any INSERT

    conn = get_conn()
    cur  = conn.cursor()

    sql = """
        INSERT INTO rag_chunks (source, chunk_index, content, embedding)
        VALUES (?, ?, ?, CAST(? AS VECTOR(768)))
    """
    for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
        cur.setinputsizes([
            (_pyodbc.SQL_WVARCHAR, 500, 0),
            _pyodbc.SQL_INTEGER,
            (_pyodbc.SQL_WVARCHAR, 0,   0),
            (_pyodbc.SQL_VARCHAR,  0,   0),   # ← must be VARCHAR, not NTEXT
        ])
        cur.execute(sql, (doc_name, i, chunk, vec_to_json(emb)))

    conn.commit()
    conn.close()
    create_vector_index()   # recreate after all inserts
    return len(chunks)

使用VECTOR_DISTANCE进行检索(第6单元续)

在处理“使用VECTOR_DISTANCE进行检索”这一单元时,首先列出相关要求:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续代码修改的规范性。 将配置信息置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放,以便操作人员无需查看整个系统结构即可进行审计。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词很难改善较差的检索效果。

def retrieve_context(query: str) -> list[dict]:
    query_embedding = embed_query(query)
    query_vec_json  = vec_to_json(query_embedding)

    conn = get_conn()
    cur  = conn.cursor()

    cur.setinputsizes([(_pyodbc.SQL_VARCHAR, 0, 0)])   # force VARCHAR for vector param

    cur.execute(f"""
        SELECT TOP ({TOP_K})
            content, source, chunk_index, ingested_at,
            VECTOR_DISTANCE('cosine', embedding, CAST(? AS VECTOR(768))) AS distance
        FROM rag_chunks
        ORDER BY distance ASC;
    """, (query_vec_json,))

    rows = cur.fetchall()
    conn.close()
    return [{"text": r[0], "source": r[1], "chunk_index": r[2],
             "ingested_at": str(r[3]) if r[3] else "",
             "score": round(1 - float(r[4]), 4)} for r in rows]

SQL Server 2025向量功能中的常见问题——我们遇到的所有问题

在处理 SQL Server 2025 Vector 阶段时,首先需明确相关规范:所需的输入参数、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 同时记录正常流程与故障恢复流程。重试机制、人工审核环节以及错误消息处理都是产品功能的一部分,而非后续需要补充的内容。 在调整提示词之前,需先用固定的问题集来衡量检索效果。仅仅更换提示词很难解决检索能力不足的问题。

常见错误 1 — CREATE 语句中出现未知对象类型 ‘VECTOR’

在处理“陷阱1:未知对象”阶段时,首先需明确接口规范:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 优先选择小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应指向单一责任点,而非复杂的流程链。 在调整提示词之前,先使用固定的问题集测试召回率。仅仅更换提示词很难改善较差的检索效果。 在处理“陷阱1:未知对象”阶段时,首先需明确接口规范:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

Msg 343, Level 15: Unknown object type 'VECTOR' used in CREATE, DROP, or ALTER statement.

陷阱2——主键必须是单个4字节的INT列

在将“陷阱2”中的主键阶段视为可度量的指标时,效果最佳。在扩大范围之前,需记录一份理想的处理方案、一个失败案例以及回滚说明。 配置应置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 分块策略与检索策略应分开处理。当质量指标发生变化时,修改其中一项不应强制要求重新编写另一项。

Msg 42217: Table must have a clustered primary key on a single 4 byte INT column to create a vector index.
-- ❌ Does not work with vector index
id  UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID()

-- ✅ Required
id  INT IDENTITY(1,1) PRIMARY KEY CLUSTERED

陷阱3——向量索引存在时无法执行INSERT/DELETE/UPDATE操作

“Gotcha 3:Cannot INSERT stage”这一问题在被视为可度量的界面时最容易解决。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。同时将正常流程和恢复流程都记录下来。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的内容。应将分块策略与检索策略分开处理;当质量指标发生变化时,修改其中一项不应强制要求重新编写另一项。

Msg 42231: Data modification statement failed because table 'rag_chunks' has a vector index on it.
def drop_vector_index():
    conn = get_conn_autocommit()   # autocommit required
    conn.cursor().execute("""
        IF EXISTS (
            SELECT 1 FROM sys.indexes
            WHERE name = 'idx_rag_embedding'
            AND object_id = OBJECT_ID('rag_chunks')
        )
        DROP INDEX idx_rag_embedding ON rag_chunks;
    """)
    conn.close()

def create_vector_index():
    conn = get_conn_autocommit()   # autocommit required
    conn.cursor().execute("""
        CREATE VECTOR INDEX idx_rag_embedding
            ON rag_chunks(embedding)
            WITH (METRIC = 'COSINE');
    """)
    conn.close()

Gotcha 4 — CREATE VECTOR INDEX无法在事务中运行

在将“CREATE VECTOR”阶段视为可度量的对象时,其效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个故障案例以及回滚说明。 相较于庞大的脚本,应优先选择小型且可测试的单元。当某个步骤出现故障时,故障点应指向单一责任模块,而非复杂的流程链。 应将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。 在将“CREATE VECTOR”阶段视为可度量的对象时,其效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个故障案例以及回滚说明。 除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

Msg 574: CREATE VECTOR INDEX statement cannot be used inside a user transaction.
# ❌ Fails — implicit transaction
conn = pyodbc.connect(SQL_CONN_STR)
conn.cursor().execute("CREATE VECTOR INDEX ...")

# ✅ Works - no transaction wrapper
conn = pyodbc.connect(SQL_CONN_STR, autocommit=True)
conn.cursor().execute("CREATE VECTOR INDEX ...")

陷阱5——不允许直接将ntext转换为向量

在处理陷阱5的直接转换阶段时,应在修改代码之前明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个操作人员可以审核的位置,无需查看整个系统结构。 需引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

Msg 529: Explicit conversion from data type ntext to vector is not allowed.
cur.setinputsizes([
    (_pyodbc.SQL_WVARCHAR, 500, 0),  # source — Unicode fine
    _pyodbc.SQL_INTEGER,              # chunk_index
    (_pyodbc.SQL_WVARCHAR, 0,   0),  # content — Unicode fine
    (_pyodbc.SQL_VARCHAR,  0,   0),  # embedding ← must be ASCII VARCHAR
])
cur.execute(sql, (doc_name, i, chunk, vec_to_json(emb)))

陷阱6——VECTOR_SEARCH不支持?参数占位符

对于Gotcha 6 VECTORSEARCH流程,修改代码之前需先明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 必须引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

Msg 102: Incorrect syntax near '('.
-- ❌ VECTOR_SEARCH with parameter placeholder — fails
FROM VECTOR_SEARCH(
    TABLE = rag_chunks USING VECTOR INDEX idx_rag_embedding,
    SIMILAR_TO = CAST(? AS VECTOR(768)),  -- pyodbc cannot pass ? here
    ...
)

-- ✅ VECTOR_DISTANCE - fully parameterized, GA, works perfectly
SELECT TOP (5)
    content,
    VECTOR_DISTANCE('cosine', embedding, CAST(? AS VECTOR(768))) AS distance
FROM rag_chunks
ORDER BY distance ASC;

PostgreSQL与SQL Server——对比分析

在 PostgreSQL 对比 SQL Server 的阶段中,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏的状态。相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任主体,而非复杂的流程链。必须引用实际作为答案依据的段落;没有引用的话,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

两种数据库具备而 ChromaDB 不具有的功能

在“两个数据库的贡献”阶段,应在修改代码之前明确输入内容、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉还是索引缺失所致。

-- PostgreSQL: Find chunks from documents belonging to a specific customer
SELECT c.content, 1 - (c.embedding <=> query_vec) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC
LIMIT 5;
-- SQL Server: Same query, T-SQL syntax
SELECT TOP 5
    c.content,
    1 - VECTOR_DISTANCE('cosine', c.embedding, CAST(? AS VECTOR(768))) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC;

结论

在结论阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况可以避免在从演示环境过渡到共享环境时出现意外费用。必须注明支撑答案的具体内容段落;没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

操作检查清单