类低代码平台的Agent开发实践(上)——文档助手

最近在开发仿真平台的Agent,其场景和开发思路和大多数低代码平台都是一样的。我将Agent功能分为两大块:文档助手和智能生成。

本文介绍文档助手agent的开发。

文档助手

低码类平台常常有非常多的配置,也具有复杂的操作手册和各式案例,是智能文档助手的最佳应用场景。

技术选型

项目中rag部分的关键技术选型是chromadb。

ts 复制代码
import { Chroma } from "@langchain/community/vectorstores/chroma";

function getVectorStore(): Promise<Chroma> {
  if (vectorStore) {
    return vectorStore;
  }

  vectorStore = new Chroma(embeddings, {
    collectionName: COLLECTION_NAME,
    url: `http://${config.chromaHost}:${config.chromaPort}`,
  });

  return vectorStore;
}

chromadb有内置的embedding模型(all-MiniLM-L6-v2),不过我选择Qwen3-Embedding-8B,该处理中文更好。

另外如果知识库中有大量图片的,建议使用Qwen3-VL-Embedding-8B。(这个模型跑起来比较难,要么调阿里云的api,要么就得找个好点的机子本地部署。ollama是没有办法运行这种多模态模型的。)

typescript 复制代码
import { OpenAIEmbeddings } from "@langchain/openai";
import { config } from "../config/index.js";

export const embeddings = new OpenAIEmbeddings({
  model: config.embeddingModel,
  batchSize: 8,
  timeout: 300_000,
  configuration: {
    baseURL: config.embeddingBaseUrl,
  },
});

rag流程

rag的标准流程是载入文档 → 分词 → 嵌入 → 向量存储 → 检索

载入文档

typescript 复制代码
function loadDocuments(): Promise<Document[]> {
  const files = await getFiles(DOCS_PATH);
  const documents: Document[] = [];

  for (const file of files) {
    const rawContent = await readFile(file, 'utf-8');


    //如果不是纯文本,比如excel/html,需要将其转为纯文本
    const text = convert(rawContent);

    documents.push(
      new Document({
        pageContent: text,
        metadata: {
          source: file,
          title,
        },
      })
    );
  }

  return documents;
}

分词

分词工具建议使用RecursiveCharacterTextSplitter(@langchain/textsplitters)。

其代码实现很简单:

typescript 复制代码
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import type { Document } from '@langchain/core/documents';

const CHUNK_SIZE = 1000;
const CHUNK_OVERLAP = 200;

export function createSplitter(): RecursiveCharacterTextSplitter {
  return new RecursiveCharacterTextSplitter({
    chunkSize: CHUNK_SIZE,
    chunkOverlap: CHUNK_OVERLAP,
  });
}

export async function splitDocuments(docs: Document[]): Promise<Document[]> {
  const splitter = createSplitter();
  return splitter.splitDocuments(docs);
}

这里简单介绍下它的原理------分级降级,尽力而为

  1. 按优先级切分 :它首先会尝试用一个较高的分隔符(比如段落分隔符 \n\n)来切分文本,希望保持段落完整。
  2. 检查块大小 :如果切分出来的某个块仍然超过了预设的 chunk_size,它不会强行保留这个超长的块,而是进入下一步。
  3. 递归降级处理 :如果高优先级的分隔符切出的块过长,它会自动"降级",对这个过长的块使用优先级次之的分隔符(比如句号 .)再次尝试切分。
  4. 循环直至完成 :这个过程会一直递归下去,直到所有的块都符合大小要求。如果到最后使用最小分隔符(如单个字符 "")切分后块还是过大,它最终也只能产生一个超长块,并给出警告

基于以上原理,我们可以额外添加中文标点

typescript 复制代码
export function createSplitter(): RecursiveCharacterTextSplitter {
  return new RecursiveCharacterTextSplitter({
    separators: [
      "\n\n", // 段落
      "\n", // 换行
      "。",
      "!",
      "?",
      ";", // 中文句子结束符
      ",",
      "、", // 中文逗号/顿号
      " ", // 空格
      "", // 字符
    ],
    chunkSize: CHUNK_SIZE,
    chunkOverlap: CHUNK_OVERLAP,
  });
}

嵌入和存储

Chroma.from_documents() 方法是一个将向量化(Embedding)存储(Storage) 两个核心步骤合二为一的便捷方法。 调用它时,会在内部自动执行以下步骤:

  1. 调用嵌入模型 :它会调用你通过 embedding 参数传入的嵌入模型,将 documents 列表中的每一个 Document 的文本内容转换成对应的向量(Embeddings)。
  2. 存储到数据库 :它会创建一个 Chroma 数据库的连接,并将上一步生成的 向量 与对应的 文档原文、元数据 一起,存储到 Chroma 中,形成一个完整的向量数据库。
typescript 复制代码
 const store = await Chroma.fromDocuments(docs, embeddings, {
    collectionName: COLLECTION_NAME,
    url: `http://${config.chromaHost}:${config.chromaPort}`,
  }).catch((error) => {
    console.error("Error creating vector store:", error);
    throw error;
  });

这一步根据材料和模型的不同,耗时可能会很长。文档更新,最好能够增量embedding。

查询

查询时也需要将原始字符串转为向量才行,不过similaritySearch会自动完成这一步。

ts 复制代码
   const llm = createLLM(0.3);

  const docs: Document[] = await vectorStore.similaritySearch(query, 5);
  const contextDocs: string[] = docs.map((doc: Document) => doc.pageContent);
  // 源文档,可以用作连接
  const sources: string[] = [
    ...new Set(docs.map((doc: Document) => doc.metadata.source as string)),
  ];
  // 向量数据库查找出来的内容塞到llm的promot中 
  const prompt = buildPrompt(
    query,
    contextDocs,
    projectContext,
    conversationHistory,
  );

  const response = await llm.invoke(prompt);
相关推荐
测试开发技术17 分钟前
AI 测试提效 | 告别手工写脚本,分享我的 Playwright + Skill 批量生成 UI 自动化脚本方案
自动化测试·人工智能·ui·自动化·agent·skill·ai测试
喜欢的名字被抢了1 小时前
langgraph教程系列-05-给 agent 装上手脚 - 工具调用
agent·教程·langgraph
leeyi1 小时前
Embedder 接口 + 缓存层源码:一个把 key 撑大 3 倍的实现(第71篇-E57)
aigc·agent·ai编程
JaydenAI1 小时前
[Agent的评估-07]整合MEAI针对自然语言处理相关的评估器
ai·nlp·agent·evaluation·maf
CZW1 小时前
RAG系统核心解析:文档向量化与检索的实践探索
agent
code_371491 小时前
Agent 设计及实现 demo
agent
极客密码3 小时前
DeepSeek V4-Flash 正式版发布:Agent 基准、价格与 Codex 接入保姆级教程
agent·ai编程·deepseek
jsl_jsl_jsl5 小时前
claudecode学习 第 3 章 · Agent 循环与工具协议
agent
TrisighT5 小时前
一张路由表少写一行,装好的 tri-loop 就成了死 skill:我把 tri-intent 的 27 个落点扒了一遍
aigc·agent·ai编程
5pat54OfCg5 小时前
OpenCode 源码拆解(一):Agent 怎么活下来?——进程生命周期的 3 个设计模式
agent