大模型基础架构设计
Agent
1. ReAct 模式(推理+行动循环)
想一步做一步,配合 Agent 工作流 Thought → Action → Observation 循环。
2. Function Calling(工具调用能力)
LLM 自己决定调什么工具,工具描述 + 参数 Schema。
3. 记忆系统(Memory)
大模型历史对话:
- 每次历史对话进表
- 超过10条总结摘要(MQ 异步),靠大模型能力
- 大量对话:查最近5条,携带以前的总结摘要
- 数据库设计:对话ID关联,高并发场景需要根据用户ID分库分表,高并发读写分离
- 根据用户ID存入 Redis,对话ID切换需要重新往 Redis 写历史对话,或者只是清除 Redis 内容等用户提问了再重新写入 Redis
- (Redis 存的是加工后的产物:摘要 + 最近消息),每次从 Redis 里查
长期记忆:
- 每次对话结束后,Agent 自动总结关键信息写入记忆文件
- 下次启动时召回相关片段注入上下文
Skill 文档:
- 调用某个 Skill 会把 Skill 加入记忆
4. 规划能力(Planning)
先计划再执行 Plan-then-Execute
typescript
private async think(mode: 'planner' | 'validator' | 'reflector', input: any): Promise<any> {
const prompts = {
planner: '你是一个规划专家。将任务拆解成可执行的步骤。',
validator: '你是一个计划审核专家。检查计划是否合理。',
reflector: '你是一个反思专家。分析执行结果,找出改进点。'
};
return await this.model.invoke([
new SystemMessage(prompts[mode]),
new HumanMessage(JSON.stringify(input))
]);
}
5. 自我反思(Self-Reflection)
错了能自己改,评估 → 反思 → 重试。
执行完后加一个 Agent 或者工具,让它去验证结果对不对,之后反思有问题重新改重新反思,没问题直接回答结果。
6. 多 Agent 协作
分工协作 Orchestrator + Specialist
单个 Agent 不能加太多的 Tools 和 Skill。Tool 和 Skill 太多,大部分工具跟当前问题完全无关,但 LLM 不知道哪些无关,它要逐个考虑。这就导致了:
- 选错工具(用了不相干的工具)
- 多调用(调了不必要的工具)
- 反应慢(思考时间变长)
- 幻觉(自己编了个工具名)
创建多个 Agent,主 Agent 分配给那些 Agent 继续执行。
可以使用 MQ:主 Agent 运行之后查看是否发送给 MQ,其他 Agent 获取 MQ 的消息,自行判断是否处理 MQ 中的消息。
各个 Agent:
- 有依赖 → 串行等待
- 无依赖 → 并行执行,获取到全部回答让 LLM 整合
- 不确定 → 先并行,再根据结果决策
7. 安全与对齐
知道什么不能做,前置约束 + 后置校验,权限控制,不能让 Agent 乱操作(例如删库)。
8. 选举策略
策略1:多数投票法(Majority Voting)
原理:多个 Agent 给出答案,选出现次数最多的那个。
策略2:加权评分法(Weighted Scoring)
原理:从多个维度给每个提案打分,加权求和后选最高分。
策略3:擂台赛(Tournament Style)
原理:两两 PK,胜者晋级,直到选出冠军。
策略4:专家评审法(Expert Review)
原理:由一个或多个专门的"评审 Agent"对所有提案进行评分。
策略5:贝叶斯融合(Bayesian Fusion)
原理:基于历史表现,给每个 Agent 分配可信度权重,加权融合结果。
9. 沙箱
- OpenClaw:使用 Docker,能配置是否开启沙箱
- Cursor / Trae:原生机制 + 自研编排
10. 意图分类器
路由规则或者 Embedding 小模型,分类,选择那些 Tools。
11. 多 Agent
因为每个 Agent 最好不要超过 10-15 个 Tools,工具调用过多需要分 Agent。
多 Agent 能串联和并联。
生产环境:并联串联结合使用,JS 用 Promise.all,Java 用 CompletableFuture。
多 Agent 之间结构化通信:context.setData / getData。
结构化通信对比直接把第一个 Agent 的文本传递给下一个 Agent 的优势:
- 准确性更高(不会解析错):上一个 Agent 如果返回对话方式有变化,后边的 Agent 可能解析出错
- 减少大模型的 Token 消耗:只传关键字,大对话节约 Token
- 下游 Agent 不需要重复理解:一次解析多处应用,不然上一个 Agent 返回大量文本,后边的 Agent 每次都全部解析
- 可以精确控制传递的内容:信息隔离,安全性更好,关注当前 Agent 需要关注的信息
结构化传递的核心优势是准确性和效率:数据不会被误解,Token 消耗更低,下游 Agent 不需要重复解析。自然语言传递的优势是灵活性和简单性:能处理意外信息,实现更简单。生产环境中建议混合使用:关键数据用结构化传递确保准确,上下文和创意内容用自然语言保持灵活性。
12. Agent 的错误恢复与重试
Agent 的错误恢复与重试的核心就三点:
① 用指数退避 + 抖动来控制重试节奏,避免雪崩
② 根据错误类型选择不同的策略(超时重试、限流等待、格式错误直接抛)
③ 重试耗尽后要有降级方案(缓存、备选 Agent、默认值),不能让整个流程因为一个 Agent 失败而终止
13. Agent 超时控制
CompletableFuture 设置超时时间,超时失败。
14. Agent 日志 & 监控
15. Agent 热更新 & 动态注册
使用 Nacos 自动注册服务,配合数据库:新增一个 Agent,表里加条数据。主 Agent 服务每次查找 Agent 都去表里查有哪些 Agent,Agent 作用是什么。
16.流式输出
SSE
知识库
数据库表设计
Chunks 分片:
chunks:文本 + 向量(JSON),数据主库,查询的最终数据源 ✅ 必须chunks_vec:向量(BLOB),加速向量余弦距离计算 ❌ 可选,装 sqlite-vec 才有chunks_fts:文本 FTS5 索引,加速 BM25 关键词匹配 ❌ 可选,FTS5 通常内置
三张表协同工作:混合搜索时,向量搜索走 chunks_vec,关键词搜索走 chunks_fts,最后从 chunks 取 text 返回结果。
Agent 告诉模型有知识库。
查询使用混合搜索(向量 0.7 + BM25 0.3),历史对话根据时间权重衰减。
OpenClaw:使用 SQLite 扩展存储的向量信息,使用 JS 做余弦相似度之后去库里对比。
chunkMarkdown(400 tokens/块,80 重叠):类去做分片。
chunks 表字段:
sql
chunks (
id TEXT PRIMARY KEY,
path TEXT NOT NULL, -- 来源文件路径
source TEXT NOT NULL, -- 来源类型:memory / sessions
start_line INTEGER NOT NULL,
end_line INTEGER NOT NULL,
hash TEXT NOT NULL,
model TEXT NOT NULL,
text TEXT NOT NULL, -- 分片内容
embedding TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
id 和 hash 都是 SHA256 算出来的。hash = chunk 文本内容的 SHA256。文本不变,hash 就不变。
会话要存储到 chunks,source = memory。
RAG 检索流程
用户问题
↓
检索模块:从知识库中找到 Top-K 相关片段
↓
拼接:问题 + 检索到的知识 → Prompt
↓
生成模块:大模型基于给定资料回答
在 RAG(检索增强生成)系统中,分片(Chunking) 是决定检索质量和生成效果的最关键环节之一。分片策略直接决定了向量数据库中存储单元的语义完整性和上下文丰富度。以下是 2026 年主流的 7 种知识库分片方法,按从简单到复杂、从规则驱动到智能驱动的顺序详解:
1. 固定长度分片(Fixed-Size Chunking)
最基础的暴力切分方法,按预设的字符数 或 Token 数 (如 512 tokens)一刀切,通常配合 Overlap(重叠,如 50 tokens) 防止边界语义断裂。
- 优点:速度极快,实现简单,行为可预测,适合日志、邮件等无结构文本。
- 缺点:极易切断句子或段落,导致语义不完整,Embedding 表达力差。
- 场景:快速原型验证、大规模杂乱语料预处理。
2. 递归字符分割(Recursive Character Splitting)
目前 LangChain/LlamaIndex 等框架的默认策略,也是生产环境的通用基线。它定义了分隔符优先级层次,优先按结构分割,超限再降级:
- 优先按
\n\n(段落)切 - 再按
\n(换行)切 - 再按
.(句子)切 - 最后按空格或字符兜底切
- 优点:在保持语义边界(段落/句子)和控制块大小之间取得了最佳平衡。
- 场景 :80% 的混合格式长文档、通用知识库。
3. 文档结构感知分片(Structure-Aware Splitting)
针对 Markdown、HTML、PDF、Word 等带格式的文档,解析其层级结构(标题 #、列表、表格、代码块)进行切分。
- 实现 :识别
H1/H2/H3标题边界,将"标题 + 下属内容"作为一个 Chunks;表格/代码块作为独立单元不切断。 - 优点:保留文档逻辑结构,检索时可带回层级元数据(如"来自第三章第二节"),极大提升精准度。
- 场景:技术文档、API 手册、政策法规、带目录的报告。
4. 语义分片(Semantic Chunking)
不再依赖长度或标点,而是利用 Embedding 模型计算相邻句子/段落的向量相似度。当相似度骤降(话题转换)时切分。
-
流程:句子级 Embedding → 计算余弦相似度 → 设定阈值(Breakpoint Percentile)→ 相似度低处切分。
-
优点:块内主题高度聚焦,语义连贯性最好。
-
缺点:计算成本高(需大量 Embedding 调用),块大小不均,可能产生过碎片段(<50 tokens)导致 LLM 上下文不足。
-
场景:长篇叙事、散文、研究报告、法律合同(主题切换频繁)。
文本
↓
Embedding 模型
↓
向量(如 1024 维)
↓
存向量库 / 查向量库
5. 父子分片 / 层次化分片(Parent-Child / Hierarchical Chunking)
一种双层索引架构,解决"检索要小(精准)但生成要大(完整上下文)"的矛盾:
- 子块(Child,小如 128 tokens):用于向量化存储和检索,保证 Embedding 聚焦,提高召回精度。
- 父块(Parent,大如 1024 tokens):检索命中子块后,回溯找到对应的父块完整内容传给 LLM 生成。
- 优点:兼顾检索精度与生成质量,避免小切片上下文缺失。
- 场景:企业级复杂知识库、技术手册、需完整引用的合同。
6. Agentic 分片(Agentic Chunking)
2026 年兴起的前沿范式,利用 LLM Agent 代替规则或简单相似度来决定边界。Agent 会阅读文本,理解"用户可能怎么问",从而主动规划出最利于检索的块(例如把"格式说明"、"操作步骤"、"时效说明"拆成不同块,而非按篇幅切)。
- 优点:分片质量天花板,接近人类认知,能处理跨段落逻辑和复杂结构(表格/代码/例外条款)。
- 缺点:索引成本极高(每文档需 LLM 调用),速度慢。
- 场景:高价值法律/医疗文档、复杂企业级政策库(通常配合缓存使用)。
7. 延迟分片与上下文检索(Late Chunking & Contextual Retrieval)
- Late Chunking :传统是先切再 Embedding;它是先对整篇文档用长上下文模型 Embedding,然后在向量空间对 token 表示进行均值池化切分。确保每个块的向量都继承了全文语境,解决"代词指代不明"问题。
- Contextual Retrieval(Anthropic) :索引时让 LLM 为每个块生成一句上下文摘要(如"这是关于 XX 政策的退货条款"),拼接到原文再 Embedding,大幅降低检索歧义。
- 场景:高精度问答、指代复杂的长文档、与 Reranker 配合使用。
8. 代码感知分片(AST-Based / Code-Aware)
针对代码库的特殊分片,基于抽象语法树(AST) 解析,按函数、类、方法边界切分,绝不切断函数体或注释对。
- 场景:代码检索、API 文档、技术仓库 RAG。
选型决策矩阵(2026 实践建议)
| 策略 | 语义完整性 | 成本/速度 | 推荐场景 |
|---|---|---|---|
| 递归分割 | ⭐⭐⭐⭐ | 低/快 | 通用首选基线(未知文档类型先用它) |
| 结构感知 | ⭐⭐⭐⭐ | 低/快 | Markdown/PDF/HTML 结构化文档 |
| 父子分片 | ⭐⭐⭐⭐⭐ | 中/中 | 生产级高精度问答(需完整上下文) |
| 语义分片 | ⭐⭐⭐⭐⭐ | 高/慢 | 纯文本、主题频繁切换的长篇论述 |
| Agentic | ⭐⭐⭐⭐⭐ | 极高/极慢 | 高价值复杂文档(法律/医疗/金融) |
| Late/Contextual | ⭐⭐⭐⭐⭐ | 高/中 | 指代复杂、需消歧义的检索任务 |
核心经验 :不要对所有文档用一种策略。生产系统通常做路由分发 ------根据文件类型(.md 走结构感知,.py 走 AST,.pdf 走递归+重叠)调用不同分片器。
文档版本管理
老文档和新文档上传时先选分类,做重名加版本处理并且设置老版本为非活跃。如果没有重名,靠大模型能力去对比有没有相似性。之后用户手动确定新上传的文档是否要覆盖或者是其他文档的升级版本,老版本设置为非活跃。查询只查活跃版本。
示例流程:
-
首次上传 "2024年退货政策_v1.docx"
→ 系统创建文档家族:RETURN_POLICY
→ 创建版本:v1
-
再次上传 "2025年退货政策_最终版.docx"
→ 用户选择归属:RETURN_POLICY(或系统自动匹配)
→ 系统创建版本:v2
→ 标记 v1 为 deprecated
→ 删除 v1 的向量数据
-
检索时:
→ 只查 status = 'active' 的版本
→ 不管文件名是什么,只认家族 ID
以上即为完整的格式整理版。