
去年有跟小伙伴们分享过小董宝在做一个「AI 老板」,通过将上千份会议纪要 "喂" 给大模型,从而让公司的小伙伴能够“直接”和“老板”对话。2025 最狠办公革命:我造了个「AI 老板」
先看看这套系统目前的运行成效:

截至目前,这套系统已有 126 位用户,累计被咨询 1771 次,大幅降低了公司在需求分析、信息判断、决策沟通等方面的成本。


上次分享的文章被老板看后,反馈说技术深度不够,今天给大家分享一些纯干货。
做过知识库的同学都知道,知识库本质就是 AI 的记忆层。
底层流程看起来非常简单:
文档切块 → 向量存储 → 相似性检索 → 拼接生成但现实很残酷:
有的人的 AI 回答精准可靠,有的人的 AI 却答非所问、逻辑混乱。
问题很大程度就出在文档切块这一步。
而这件事,远比你想象的要难。
传统方案一般是:
1、按固定字数切
2、进阶一点:按段落切
3、再进阶:按语义段落切
我最开始直接选了看起来最高级的方案:语义切块。
把整篇文本丢给大模型,配合自研的断点续切算法,让 AI 自动识别语义边界,把相关内容归为一组。
效果确实比前两种好很多,但有一个致命问题:AI 会随机漏掉内容。
整体分片完整率大概只能稳定在 95% 左右。
我试了大量方案、换了多款模型,问题依然存在。
顿悟时刻:这不就是一家公司吗?
"复杂任务之所以失控,是因为一个人承担了太多不同性质的工作。"
1、读懂整篇上万字的文章
2、识别话题转换点
3、把所有段落都分配到某个组(这步经常出错)
4、给每组起标题、提关键词
5、检查有没有遗漏
那如果我把这些职能拆开,交给不同的 "专员" 呢?
整体架构:一条内容生产流水线
原始会议纪要(上万字)│▼┌─────────────┐│ 录入员 │ 清洗文本,逐段编号└──────┬──────┘│ [(1,"..."), (2,"..."), ..., (89,"...")]▼┌─────────────┐│ 排期员 │ 按 Token 数分批,控制 AI 调用规模└──────┬──────┘│ [batch1, batch2, batch3]│▼ ┌──────────── feedback(上一轮审稿意见)──────────┐┌─────────────┐ ││ 策划编辑 │ AI 识别语义边界,只输出起始编号 │└──────┬──────┘ ││ [1, 8, 23, 45, 67, 78] │▼ │┌─────────────┐ ││ 归档员 │ 程序化连续分组,数学上保证全覆盖 │└──────┬──────┘ ││ [group1, group2, ..., group6] │▼ │┌─────────────┐ ││ 文案编辑 │ AI 生成 topic / keywords / synonyms │└──────┬──────┘ ││ │▼ │┌─────────────┐ 不通过 ││ 审稿编辑 │ ──────────────────────────────────────────▶ ┘└──────┬──────┘ 最多迭代 5 轮│ 通过▼┌─────────────┐│ 核稿员 │ 双重完整性校验:段落覆盖率 + 字符差值=0└──────┬──────┘│▼输出 JSON 文件
class BaseAgent:name = "基础智能体(BaseAgent)"def run(self, **kwargs):raise NotImplementedError(f"{self.name}.run() 未实现")def _log(self, msg: str, level: str = "info"):getattr(logger, level)(f"[{self.name}] {msg}")
八位员工,各司其职
🗂️ 录入员 PreprocessAgent
class PreprocessAgent(BaseAgent):name = "录入员(PreprocessAgent)"def run(self, text: str) -> list:text = text.replace('\r\n', '\n').replace('\r', '\n')lines = []for line in text.split('\n'):line = line.replace('\u200d', '').strip() # 去除零宽字符if line:lines.append(line)return list(enumerate(lines, start=1))# 输出:[(1, "张总:..."), (2, "首先是..."), ...]
📅 排期员 BatchingAgent
class BatchingAgent(BaseAgent):name = "排期员(BatchingAgent)"def __init__(self, max_tokens: int = 4000):self.max_tokens = max_tokensdef run(self, numbered_paras: list) -> list:batches, current, current_tokens = [], [], 0for idx, para in numbered_paras:t = len(_ENCODER.encode(f"[{idx}] {para}\n"))if current_tokens + t > self.max_tokens and current:batches.append(current)current, current_tokens = [], 0current.append((idx, para))current_tokens += tif current:batches.append(current)return batches
🎯 策划编辑 BoundaryDetectionAgent
这是整个方案最核心的创新,值得多花篇幅讲清楚。
设计决策:AI 只报起点,程序做分组
{"group1": [1,2,3,4,5,6,7], "group2": [8,9,10,...], ...}[1, 8, 23, 45, 67, 78]Prompt 设计
## 系统角色与能力您是一位专业的会议纪要语义分析助手,专注于识别会议纪要中语义模块的起始位置。## 输入格式- `[编号] 段落内容` — 当前批次需要分析的段落- `[编号][context] 段落内容` — 上一批次末尾的参考段落,仅供理解上下文,编号不计入输出## 任务找出当前批次(非 context)段落中,每个新语义模块的起始段落编号。## Goals- 识别会议纪要中话题、议题或讨论焦点的自然转换点- 确保每个语义模块内容完整,不在发言中途切断## Constrains1. 第一个非 context 段落的编号必须包含在输出中2. context 段落的编号绝对不能出现在输出中3. 只有当话题/议题/讨论焦点发生明显转换时,才输出新的起始编号4. 同一发言人的连续发言通常属于同一模块,不应切断## Workflow1. 通读所有段落(包括 context),理解整体话题脉络2. 识别非 context 段落中话题切换的位置3. 将第一个非 context 段落编号加入结果4. 对每个明确的话题切换点,将其起始编号加入结果5. 自检:确认所有输出编号均为非 context 段落编号## OutputFormat只输出一个 JSON 整数数组,不输出任何其他内容:[1, 28, 56]
跨批 Overlap Context
def run(self, batches: list, feedback: dict = None) -> list:all_boundaries = set()prev_tail = []for batch_no, batch in enumerate(batches, start=1):context_indices = {idx for idx, _ in prev_tail}full_batch = prev_tail + batch # context + 正文boundaries = self._detect_batch(full_batch, context_indices, feedback)all_boundaries.update(boundaries)prev_tail = batch[-self.overlap:] # 保留末尾5段作为下批context
[38][context] 王经理:所以Q3我们计划冲到……[39][context] 李总监:这个目标有点激进……[40][context] 王经理:但是有几个有利条件……[41] 张总:好,我们听一下有哪些有利条件 ← 本批次正文[42] 王经理:第一,竞争对手在Q2出现了……
Feedback 注入
if feedback and feedback.get("issues"):issues_text = "\n".join(f"- 模块 {i['semantics_index']}:{i['issue']}"for i in feedback["issues"])prompt = (f"【上次分片存在以下问题,本次请据此重新划分边界】\n"f"{issues_text}\n整体建议:{feedback.get('suggestion', '')}\n\n"+ prompt)
📁 归档员 GroupingAgent
class GroupingAgent(BaseAgent):name = "归档员(GroupingAgent)"def run(self, numbered_paras: list, boundaries: list) -> list:boundary_set = set(boundaries)groups, current_group = [], []for idx, para in numbered_paras:if idx in boundary_set and current_group:groups.append(current_group)current_group = [(idx, para)]else:current_group.append((idx, para))if current_group:groups.append(current_group)return groups
✍️ 文案编辑 MetadataAgent
Prompt 设计
## 系统角色与能力您是一位专业的会议纪要语义分片助手,专注于为已划分的语义模块生成准确的元数据。## Goals- 为每个模块提炼核心主题(topic),要求准确、简洁、能区分不同模块- 提取关键词(keywords),帮助提高检索效率- 生成同义词扩展(synonyms),提升检索灵活性## Constrains1. topic:必须准确描述该模块实际内容,不能过于宽泛(如"会议讨论")2. keywords:选取最能代表该模块内容的词汇,3-5个3. synonyms:列出 topic 的其他可能表达方式或关键词变体,2-4个4. 按模块顺序输出,数量与输入模块数严格一致5. 只输出 JSON 数组,不输出任何其他内容## OutputFormat[{"topic": "本模块核心主题(15字以内)","keywords": ["关键词1", "关键词2", "关键词3"],"synonyms": ["同义表达1", "同义表达2"]}]
核心实现
def run(self, groups: list) -> list:chunks = []for start in range(0, len(groups), self.batch_size):batch_groups = [(start+i+1, groups[start+i])for i in range(min(self.batch_size, len(groups)-start))]prompt = "\n\n".join(f"=== 模块 {idx} ===\n" + "\n".join(p for _, p in group)for idx, group in batch_groups)for attempt in range(1, self.retry + 1):raw = self.ai_client.get_answer(system_prompt=METADATA_SYSTEM_PROMPT,user_prompt=prompt,json_response=True)result = json.loads(raw)if isinstance(result, list) and len(result) == len(batch_groups):meta_list = resultbreak# 降级:元数据失败则置空,不中断流程...
🔍 核稿员 CompletenessValidationAgent
def run(self, chunks: list, numbered_paras: list) -> dict:all_paras = [para for _, para in numbered_paras]covered = [p for chunk in chunks for p in chunk.get("paragraphs", [])]missing = set(all_paras) - set(covered)char_diff = sum(len(p) for p in covered) - sum(len(p) for p in all_paras)passed = not missing and char_diff == 0return {"passed": passed,"total_paragraphs": len(all_paras),"missing_count": len(missing),"original_chars": sum(len(p) for p in all_paras),"covered_chars": sum(len(p) for p in covered),"char_diff": char_diff,}
两道关卡缺一不可:
1、段落集合差值为空 → 无遗漏
2、字符总量差值为零 → 无内容损耗
📋 审稿编辑 SemanticValidationAgent
关键细节:发送完整段落,不截断。
Prompt 设计
## 系统角色与能力您是一位专业的内容质量审核助手,专注于审核会议纪要语义分片结果的准确性和合理性。## Goals- 验证每个模块的 topic 是否准确反映了段落实际内容- 识别分片边界是否存在发言被拆断或语义相关内容被割裂的问题- 发现归类错误和明显的语义不一致问题## 审查维度1. topic 准确性:topic 是否准确描述了对应段落的核心内容2. 边界合理性:边界处是否在发言中途切断,或将语义紧密相关的内容拆分到不同模块3. 归类合理性:段落是否被归入了语义不相关的模块4. 模块完整性:每个模块是否包含完整的议题讨论## OutputFormat通过时:{"passed": true,"issues": [],"suggestion": "OK"}有问题时:{"passed": false,"issues": [{"semantics_index": 3, "issue": "具体问题描述"}],"suggestion": "整体改进建议"}
👔 总编辑 OrchestratorAgent
for iteration in range(1, self.max_iterations + 1):self._log(f"── 第 {iteration}/{self.max_iterations} 轮 ──")boundaries = self.boundary_agent.run(batches, feedback=feedback)groups = self.grouping_agent.run(numbered_paras, boundaries)final_chunks = self.metadata_agent.run(groups)if self.skip_semantic_validate:breaksemantic_report = self.semantic_agent.run(final_chunks)if semantic_report.get("passed") or not semantic_report.get("issues"):self._log(f"语义校验通过(第 {iteration} 轮)")breakfeedback = semantic_report # 退稿意见传给下一轮策划编辑
统一配置入口:
orchestrator_config = {"max_tokens_per_batch": 4000, # 每批 token 上限"overlap": 5, # 跨批 context 段落数"metadata_batch_size": 5, # 元数据生成每批组数"retry": 3, # AI 调用失败重试次数"max_iterations": 5, # 语义反馈最大迭代轮次"skip_semantic_validate": False, # 快速模式:跳过语义校验}
三个让我想拍桌子的设计决策
决策一:让 AI"只报起点"
决策二:feedback 要进入分片,不能只进入标注
决策三:完整性校验必须包含字符数差值
这套系统跑起来是什么感觉
[录入员(PreprocessAgent)] 开始预处理,文本长度=18432[录入员(PreprocessAgent)] 完成:127 个段落[排期员(BatchingAgent)] 完成:4 批,max_tokens=4000[总编辑(OrchestratorAgent)] ── 第 1/5 轮 ──[策划编辑(BoundaryDetectionAgent)] 批次 1/4 | 段落 [1~38] | context=无[策划编辑(BoundaryDetectionAgent)] 批次 2/4 | 段落 [39~76] | context=[34,35,36,37,38][策划编辑(BoundaryDetectionAgent)] 批次 3/4 | 段落 [77~110]| context=[72,73,74,75,76][策划编辑(BoundaryDetectionAgent)] 完成:14 个边界点[归档员(GroupingAgent)] 完成:14 个语义组 | 段落分布=[9,8,7,11,...][文案编辑(MetadataAgent)] 元数据生成:模块 1~5/14[文案编辑(MetadataAgent)] 完成:14 个语义块[审稿编辑(SemanticValidationAgent)] 发现 2 处问题[总编辑(OrchestratorAgent)] 第 1 轮发现 2 处问题:[3,9],准备重新分片[总编辑(OrchestratorAgent)] ── 第 2/5 轮 ──[策划编辑(BoundaryDetectionAgent)] 批次 1/4 | 注入 feedback:2 处问题[审稿编辑(SemanticValidationAgent)] 通过[总编辑(OrchestratorAgent)] 语义校验通过(第 2 轮)[核稿员(CompletenessValidationAgent)] 通过:127 个段落,18432 字符,差值=0[总编辑(OrchestratorAgent)] 处理完成 | 语义块=14 | 轮次=2 | 完整性=✓ | 语义=✓
这件事让我想到了更大的问题
很多时候,问题的解法不是一个更强的 AI,而是更好的协作架构。
我们在构建的,究竟是一个更强的员工,还是一家更好的公司?
最后
段落遗漏率:从~5% 降至0%
字符损耗率:0%
语义校验平均收敛轮次:1.6 轮
夜雨聆风