
1. 项目概述为什么乌克兰语RAG是个值得深挖的“硬骨头”最近在折腾一个挺有意思的项目面向乌克兰语的智能RAG系统。乍一听可能有人觉得这不就是给RAG检索增强生成换个语言吗把英文的向量模型、分词器换成乌克兰语的不就完事了但真正上手后才发现这里面的水比想象中深得多。它远不止是简单的“语言替换”而是一个涉及多语言混杂处理、小语种资源稀缺、以及复杂代理决策逻辑的综合工程挑战。为什么是乌克兰语这背后有几个现实的考量。首先乌克兰语属于西斯拉夫语系其语法如七格变位、构词和字符集西里尔字母但包含ґ、і、ї等特有字母与英语差异巨大直接套用为英语优化的现成方案效果会大打折扣。其次互联网上高质量、结构化的乌克兰语文本数据相对稀缺这对构建可靠的检索知识库提出了更高要求。更重要的是在实际应用场景中用户查询和文档内容常常是乌克兰语、俄语、甚至英语混杂的状态系统必须具备强大的语言识别、归一化和跨语言检索能力才能给出精准的答案。所以这个项目的核心目标是构建一个不仅能“听懂”纯乌克兰语还能妥善处理语言混合输入并在此基础上通过智能的代理机制来动态决策检索、重排、生成乃至调用外部工具流程的系统。它不是一个简单的问答机器人而是一个具备一定“思考”和“调度”能力的智能体。接下来我就把自己在搭建这个系统过程中关于检索增强和代理机制的核心设计思路、踩过的坑以及实战心得毫无保留地分享出来。2. 核心架构设计从“检索-生成”到“感知-决策-执行”的演进传统的RAG流水线可以概括为“索引-检索-生成”三步走。但对于一个面向复杂现实场景的智能系统尤其是处理乌克兰语这种特定语言时我们需要一个更健壮、更灵活的架构。我将其演进为一种基于代理的感知-决策-执行循环。2.1 整体架构蓝图整个系统的核心不再是单一的流水线而是一个由协调代理驱动的动态工作流。下图描绘了其核心交互逻辑graph TD A[用户输入br混合语言问题] -- B(语言感知与br查询理解代理); B -- C{决策点br问题类型与需求}; C --|简单事实查询| D[向量检索模块]; C --|需要复杂推理/计算| E[工具调用代理br计算、搜索API]; C --|需要多步信息整合| F[子问题分解与br规划代理]; D -- G[召回结果]; E -- H[工具执行结果]; F -- F1[子问题1] -- D; F -- F2[子问题2] -- E; G -- I[结果重排序与br融合模块]; H -- I; I -- J[生成代理]; J -- K[最终答案输出]; B -- L[乌克兰语专用br文本处理管道]; L -- M[知识库br向量存储]; D -- M;这个架构的关键在于代理作为核心调度者它根据对用户输入的理解感知来决定调用哪些能力决策并组织执行执行。例如对于“请比较基辅和利沃夫的人口并计算总和”这个问题代理可能会1识别出需要事实检索两个城市的人口2识别出需要计算工具3规划先并行检索两个数据再调用计算工具进行求和。2.2 为什么选择代理机制而非固定流程在乌克兰语场景下固定流程的弊端被放大。比如用户问“Що таке ‘державний суверенітет’ А також знайдіть останні новини на цю тему.”什么是“国家主权”并请查找关于这个话题的最新新闻。这是一个典型的混合型请求前半部分是概念定义可从静态知识库检索后半部分是实时信息需调用搜索引擎API。固定流程要么只能处理前者要么需要用户拆分成两个问题。代理机制的优势立刻显现意图分解代理能理解这是一个复合问题自动将其拆解为“定义查询”和“实时新闻查询”两个子任务。动态路由针对“定义查询”路由到向量检索模块针对“实时新闻查询”路由到网络搜索工具调用模块。结果综合代理负责将来自静态知识库的定义和来自搜索引擎的新闻摘要进行整合组织成连贯的答案。这种灵活性对于处理语言本身可能就不规范、需求多样的真实用户查询至关重要。3. 乌克兰语文本处理检索效果的基石检索增强检索是第一步也是最容易“失之毫厘谬以千里”的一步。乌克兰语文本处理需要一套量身定制的方案。3.1 分词与嵌入模型选型英文RAG中我们可能不假思索地选用sentence-transformers的all-MiniLM-L6-v2。但对于乌克兰语这几乎是无效的。因为通用多语言模型在低资源语言上的表现通常远逊于英语。我的选型与理由嵌入模型我选择了专门针对乌克兰语优化的intfloat/multilingual-e5-large的乌克兰语版本或者社区训练的sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2并确保其在乌克兰语语料上微调过。更佳的选择是使用在乌克兰语维基百科、新闻语料上训练过的专用模型例如ukr-sbert。这能确保相似语义的乌克兰语句子在向量空间中是靠近的。注意直接使用多语言模型时务必检查其支持的语言列表中是否明确包含乌克兰语uk并最好在少量乌克兰语相似性任务上做快速验证。分词器这是关键陷阱。许多Transformer模型使用基于SentencePiece或BPE的分词器对西里尔字母的处理可能将一个单词拆分成不直观的片段。例如乌克兰语单词“державний”国家的可能被拆成“дер”, “жа”, “вний”这样的子词虽然对模型理解影响不大但对于精确匹配、关键词高亮或后续的一些处理可能带来困扰。我们需要接受这种“子词”化是当前技术的常态但在构建知识库时要确保文本清洗和分块策略与之适配。3.2 文本分块策略优化分块Chunking策略直接影响检索精度。乌克兰语文本有其特点长复合句常见由于复杂的格变化和从句结构句子可能很长。形态丰富一个词的变体很多。我采用的混合分块策略递归字符分割作为基础设置一个适中的块大小如512字符。但单纯按字符分割容易切断一个完整的句子或意群。叠加语义分割利用乌克兰语的分句模型如spacy的乌克兰语支持或nltk尝试在句末标点处进行分割。将递归分割的块与语义分割的边界进行对齐优先保证句子的完整性。重叠设置设置较大的重叠区如150字符。因为乌克兰语中关键信息可能分散在相邻的句子中足够的重叠能减少检索时因分割不当导致的核心信息丢失。实操心得不要盲目追求“语义完整”而使用过大的分块。过大的块虽然包含了上下文但也会在检索时引入更多噪声降低答案的精确度。我的经验是对于百科、文档类知识块大小在300-600词约500-1000字符之间重叠在15%-20%是一个不错的起点需要根据实际检索效果微调。3.3 处理语言混合问题这是现实场景中的最大挑战。用户可能输入“Як працюєblockchainтехнологія?”区块链技术如何工作其中混入了英文术语。解决方案语言检测在查询入口处使用轻量级语言检测库如langdetect快速识别查询中各个片段的语言。对于混合查询可以标记出不同语言的部分。查询翻译/扩展对于嵌入模型一种策略是将非乌克兰语的关键词如blockchain翻译成乌克兰语блокчейн生成一个“双语”查询向量。另一种更鲁棒的策略是进行查询扩展利用大语言模型LLM将原始混合查询重写为几个纯乌克兰语的、同义的查询。例如将上述查询扩展为“Як працює технологія блокчейн?”、“Принцип роботи блокчейну”、“Що таке блокчейн та як він функціонує?”。用这组查询去检索能大大提高召回相关乌克兰语文档的概率。知识库侧处理在构建知识库时对于包含大量外来词或混合语的文档可以考虑额外添加一个“术语翻译对照表”作为元数据或者在嵌入前对文档进行轻微的同义词替换扩展需谨慎避免引入错误。4. 智能代理机制的设计与实现代理是系统的大脑。我设计了一个分层代理系统核心是一个主协调代理它可以根据任务类型调用不同的技能或子代理。4.1 代理的核心循环代理遵循经典的ReAct范式Reason思考-Act行动-Observe观察并循环进行。思考分析当前用户问题、历史对话和可用工具决定下一步做什么。行动执行决定可能是调用检索工具、调用计算器、调用网络搜索或者直接生成答案。观察获取行动的结果检索到的文档、计算出的数字、搜索到的摘要。循环基于观察结果再次思考直到问题被解决或达到步骤限制。4.2 工具集的构建代理的能力取决于它拥有什么工具。我为乌克兰语RAG系统装备了以下核心工具向量知识库检索工具最核心的工具。输入是乌克兰语或混合查询输出是相关的文本片段及其来源。我为其设计了丰富的元数据过滤接口例如按文档类型、日期、语言过滤。网络搜索工具用于获取最新信息。集成如SerpAPI或自定义的新闻网站爬虫需遵守robots.txt。关键点在于搜索结果的摘要需要被翻译或处理成乌克兰语以便与本地知识库内容融合。计算器工具处理涉及数字计算的问题。这是一个确定性工具能保证计算结果的绝对准确。子问题分解工具严格来说这不是一个“行动”工具而是主代理在“思考”阶段可以调用的内部能力。当遇到复杂问题时代理可以调用一个LLM来将问题分解成一系列顺序或并行的子问题然后逐一解决。4.3 提示工程让代理“理解”乌克兰语任务代理的“思考”能力由LLM驱动而引导LLM的关键就是系统提示词。我的系统提示词模板包含以下几个关键部分你是一个专业的乌克兰语信息助手精通乌克兰语并能处理其中可能夹杂的俄语或英语术语。 你的核心能力包括 1. 从乌克兰语知识库中检索信息。 2. 使用网络搜索获取最新资讯。 3. 进行精确的数学计算。 4. 将复杂问题分解为简单步骤。 请严格遵循以下流程 1. **分析问题**判断用户问题的语言和核心意图。是事实查询、比较、计算还是需要最新信息 2. **制定计划**如果需要多步先列出步骤。例如“第一步检索A的定义第二步检索B的定义第三步比较异同。” 3. **选择工具**根据计划选择工具。检索知识库用vector_search找最新消息用web_search计算用calculator。 4. **执行与整合**使用工具获取信息并用自己的话乌克兰语组织成连贯、准确的答案。所有事实陈述必须基于工具返回的证据。 特别注意 - 如果用户问题包含非乌克兰语词汇请理解其含义并在检索或搜索时考虑其乌克兰语对应词。 - 优先使用知识库中的信息除非问题明确要求最新数据或知识库中找不到。 - 你的最终答案必须使用乌克兰语。这个提示词明确了代理的角色、能力、工作流程和语言要求是代理行为符合预期的保障。4.4 实现示例基于LangChain的代理搭建我使用LangChain作为代理框架的实现基础因为它提供了良好的抽象和工具集成能力。from langchain.agents import AgentExecutor, create_react_agent from langchain_core.prompts import ChatPromptTemplate from langchain_community.llms import OllamaLLM # 假设使用本地Ollama部署的LLaMA模型 from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.tools import Tool from langchain_community.utilities import SerpAPIWrapper # 1. 初始化组件 # LLM - 使用支持乌克兰语的模型例如通过Ollama运行的llama3或专门微调过的模型 llm OllamaLLM(modelllama3, base_urlhttp://localhost:11434) # 嵌入模型 - 乌克兰语专用 embeddings HuggingFaceEmbeddings(model_nameukr-sbert-model) # 加载已有的乌克兰语向量库 vectorstore Chroma(persist_directory./ukr_db, embedding_functionembeddings) retriever vectorstore.as_retriever(search_kwargs{k: 4}) # 2. 定义工具 def vector_search(query: str) - str: 在乌克兰语知识库中搜索相关信息。 docs retriever.get_relevant_documents(query) return \n\n.join([f来源 {i1}: {doc.page_content} for i, doc in enumerate(docs)]) search SerpAPIWrapper(serpapi_api_keyyour_key) # 实际使用时替换为你的key tools [ Tool( nameUkrainian_Knowledge_Base, funcvector_search, description当需要从乌克兰语知识库中查找事实、定义、解释或历史信息时使用此工具。输入应为乌克兰语查询。 ), Tool( nameWeb_Search, funcsearch.run, description当问题涉及最新新闻、实时数据或知识库中找不到的非常见信息时使用此工具。输入可以是乌克兰语或英语关键词。 ), # 可以添加更多工具... ] # 3. 创建代理提示词 system_prompt 你是一个智能乌克兰语助手... # 此处填入上文设计的完整提示词 prompt ChatPromptTemplate.from_messages([ (system, system_prompt), (human, {input}), (placeholder, {agent_scratchpad}), ]) # 4. 创建并运行代理 agent create_react_agent(llmllm, toolstools, promptprompt) agent_executor AgentExecutor(agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue) # 执行查询 result agent_executor.invoke({ input: Хто такий Тарас Шевченко? Коли він народився та які його найвідоміші твори? }) print(result[output])在这个示例中代理会根据问题“塔拉斯·舍甫琴科是谁他何时出生他最著名的作品是什么”自动判断这是一个纯粹的事实查询从而调用Ukrainian_Knowledge_Base工具从本地向量库中检索信息并生成答案。5. 检索增强流程的深度优化有了强大的代理和扎实的文本处理基础检索增强流程本身还有巨大的优化空间。5.1 混合检索策略单一的向量检索有时不够精确尤其是对于包含具体名称、日期、数字的查询。我采用了混合检索密集检索使用上述乌克兰语嵌入模型进行语义搜索召回相关文档。稀疏检索同时使用基于关键词的检索如BM25。对于乌克兰语需要配置支持西里尔字母分词的分析器如Elasticsearch中的ukrainian分析器。混合检索能同时保证语义相关性和关键词匹配度。融合排序将密集检索和稀疏检索的结果列表使用倒数排序融合或更复杂的学习排序方法进行重排得到最终的召回列表。5.2 重排序模型的应用从检索器召回的可能有10-20个文档块但并非所有都与生成答案直接相关。引入一个重排序模型对召回的文档进行精细打分只将Top-K个最相关的文档送入生成阶段。对于乌克兰语可以尝试多语言重排序模型如cross-encoder/ms-marco-MiniLM-L-6-v2并在乌克兰语数据上微调以更好地理解查询与文档的相关性。5.3 上下文窗口的智能管理LLM的上下文长度有限。当代理经过多轮工具调用积累了大量的检索结果和历史对话时需要智能地管理上下文。选择性记忆只将与当前思考最相关的工具调用结果和历史对话保留在上下文中。摘要压缩对于较长的检索文档可以让LLM先对其进行摘要再将摘要送入生成上下文。结构化历史将对话历史、工具调用记录以结构化的格式如JSON存储在需要时让LLM快速回顾要点而不是塞入全部原始文本。6. 评估与迭代如何知道系统真的“智能”了构建完成后评估至关重要。我采用多维度评估检索效果评估命中率对于一组标准问题Top-K个检索结果中包含正确答案的比例。平均排序正确答案在检索结果列表中的平均位置。语言鲁棒性测试使用混合语言查询、带错别字的乌克兰语查询进行测试看检索效果是否稳定。生成答案评估事实准确性答案中的事实是否与检索到的文档一致这是RAG的底线。相关性答案是否直接回应了问题流畅性与语言质量生成的乌克兰语是否自然、流畅、符合语法幻觉率答案中是否出现了知识库中没有的信息代理决策评估工具调用准确率代理是否在正确的时机调用了正确的工具问题分解合理性对于复杂问题分解出的子问题是否逻辑清晰、可解决多轮对话连贯性在连续对话中代理是否能记住上下文并做出合理回应评估需要构建一个包含问题-标准答案-参考文档的测试集。初期可以手动评估后期可以引入LLM作为裁判进行自动评估例如用GPT-4评估答案的相关性和流畅性。7. 生产级部署的考量与挑战将实验系统推向生产环境会面临新的挑战。7.1 性能与延迟嵌入模型推理乌克兰语专用模型可能比通用小模型更大需要GPU加速或使用量化版本。向量数据库考虑使用Milvus、Qdrant或Weaviate等高性能向量数据库支持大规模数据和高并发检索。LLM调用代理的每一步“思考”都需要调用LLM这是延迟的主要来源。可以考虑对常见问题类型进行缓存或使用更小、更快的模型进行初步的意图分类和路由。7.2 知识库的持续更新乌克兰语世界的信息也在更新。需要建立知识库的增量更新机制。流式处理监控数据源如特定新闻网站、官方公报将新内容自动经过清洗、分块、嵌入增量添加到向量数据库。版本管理对知识库进行版本快照以便在更新引入错误时可以回滚。去重与质量过滤在入库前进行严格的内容去重和质量审核避免垃圾信息污染知识库。7.3 安全与可控性输入过滤对用户输入进行严格的恶意内容、敏感词过滤。输出审查在最终答案返回前可以增加一层安全审查确保生成的内容不包含有害信息或严重的幻觉。工具权限控制限制网络搜索工具访问的域名计算器工具设置超时和计算复杂度限制防止滥用。构建一个面向乌克兰语的智能RAG系统是一次从技术到工程的全方位挑战。它要求我们不仅深入理解RAG和代理的技术细节更要深入理解目标语言的特性和用户的实际场景。从选择合适的嵌入模型开始到设计能处理语言混合的文本管道再到构建一个能动态规划、调用工具的智能代理每一步都需要反复试验和调优。这个过程让我深刻体会到真正的“智能”不在于模型的参数有多大而在于系统设计能否精准地理解问题、高效地利用工具、并可靠地交付结果。目前这个系统仍在迭代中特别是在处理高度复杂的、需要多步深度推理的乌克兰语问题上还有很长的路要走。下一步我计划引入更复杂的反思机制让代理能评估自己答案的质量并在不满意时自动调整策略这或许能让它离真正的“智能”更近一步。