基于 E:\openkitty 项目源码(347个 Go 源文件,616 个 Go 文件含测试)深度分析 覆盖 17 大模块,从架构设计到源码实现,一文读懂 AI 数字员工平台
📖 阅读导引
OpenKitty 是一个用 Go 语言编写的 AI 数字员工平台,借鉴了 Claude Code 的架构思想,在单仓库(monorepo)中实现了 Agent 编排、Pipeline 工作流、记忆系统、多渠道通信等完整能力。
本文从源码出发,系统梳理了 120 个核心问题,按模块组织,适合:
🧑💻 想深入理解 OpenKitty 架构的开发者 🔍 准备贡献代码的技术同学 📚 学习 AI Agent 系统设计的读者
🗺️ 架构全景
┌─────────────────────────────────────────────────────────┐│ GatewayService ││ (HTTP/WS 入口 · 渠道分发 · 会话管理) │├─────────────────────────────────────────────────────────┤│ Channel 抽象层 ││ ┌──────────┬──────────┬──────────┬──────────┐ ││ │ 飞书 │ 钉钉 │ AG-UI │ 终端 │ ... ││ └──────────┴──────────┴──────────┴──────────┘ │├─────────────────────────────────────────────────────────┤│ Agent 工厂 → LLMAgent → Generate() 核心循环 ││ │ ││ ├── plan_task (计划编排) ││ ├── sub_agent (子代理并行) ││ └── Pipeline (工作流引擎) │├─────────────────────────────────────────────────────────┤│ 基础设施层 ││ ┌────────┬────────┬────────┬────────┬────────┐ ││ │ 记忆 │ 工具 │ RAG │ Hook │ 上下文 │ ││ │ 系统 │ 系统 │ 搜索 │ 扩展 │ 管理 │ ││ └────────┴────────┴────────┴────────┴────────┘ │└─────────────────────────────────────────────────────────┘
一、架构与设计理念
Q1: OpenKitty 的整体架构是什么?
OpenKitty 采用单仓库 Go 项目架构,所有代码在 package main 下。核心设计理念:
- Gateway 模式
: GatewayService(~11500 行)作为中央调度器 - Channel 抽象
:统一抽象飞书、钉钉、终端等多种交互渠道 - Agent 工厂模式
: AgentFactory负责创建和注入各类 Agent 实例 - Pipeline 编排
:步骤化的工作流执行引擎 - 声明式配置
:通过 openkitty-config.yaml声明式定义
Q2: 借鉴了 Claude Code 的哪些设计?
代码注释中明确标注了 "Claude Code 借鉴":
coordinator_engine.go | |
SystemCompactBoundaryMessage | |
partitionToolCalls |
二、Agent Generate 核心流程
Q6: Agent.Generate() 的完整执行流程
LLMAgent.Generate(ctx, input) 是整个 Agent 系统的核心方法,位于 mac.go:753:
阶段一:初始化
创建 MemoryEvent,记录 AgentName、UserQuestion、StartTime构建 System Prompt(~2000+ token) 初始化迭代计数器、Token 追踪器
阶段二:主循环
每轮迭代:发送 messages → 接收 LLM 响应 → 解析工具调用 工具调用分区(只读工具并行、写工具串行) Reflection Checkpoint(每 10 轮)、Rethink(每 15 轮)
阶段三:终止
自然结束(LLM 返回纯文本无工具调用) 达到 maxIterations 用户中断或超时
Q8: plan_task 和 ActivePlan 的工作原理
三个组件协作实现计划-执行模式:
- plan_task 工具
:LLM 调用创建计划,steps 转为 []PlanStep - ActivePlan 状态机
: pending → in_progress → completed/skipped - 注入机制
:每轮迭代将当前步骤注入 System Prompt
Q12: Reflection 和 Rethink 的区别
三、Pipeline 管道系统
Q16: Pipeline 系统的核心架构
PipelineService(pipeline.go,~5621 行)是工作流执行引擎:
- Step 定义
:每个步骤包含 Name、Prompt、Tools、MaxIterations、Approval - 黑名单机制
: pipelineBlacklist永久禁止 delete_file、shell 等危险工具 - 条件执行
:支持 Condition(前置条件)和 SkipOnError - 状态追踪
:running → completed / failed
Q17: Pipeline 的持久化恢复
PipelineDurable 检查点模型:
每步完成后写入检查点(step_name、status、output) 中断恢复时从最后 completed 步骤继续 支持重试失败步骤
Q18: 进化引擎(PipelineEvolver)
基于统计指标的规则引擎:
minRuns=5 触发分析 5 种建议类型:步骤合并、工具优化、Prompt 调整、超时调整、并行化 分析指标:AvgDuration、AvgToolCalls、FailureRate、ToolUsage、ToolErrors
四、Agent 系统
Q21: Agent 的加载和发现机制
AgentLoader(agent_loader.go):
- 目录扫描
:从 .openkitty/agents/发现 Agent 定义 - 格式支持
:YAML 和 JSON - 热加载
:autoReload 模式下监控文件变更 - 最后修改追踪
:基于文件修改时间的增量加载
Q22: LLMAgent 的核心执行循环
主循环位于 mac.go,核心逻辑:
构建请求(System Prompt + 历史消息 + 当前输入) 调用 LLM API(支持流式) 解析响应(文本 or 工具调用) 执行工具(并行/串行分区) 将结果追加到消息历史 检查终止条件
Q25: SwarmTool 和 SubAgentTool 的区别
五、记忆系统
Q26: 记忆系统的核心数据结构
MemoryEvent(memory.go)是核心记忆表:
- 标识信息
:AgentName、AgentType、SessionID、TaskID - 内容
:Role(user/assistant/system/tool)、Content、ToolName、ToolResult - Token 统计
:PromptTokens、CompletionTokens、TotalTokens - 状态
:Status(success/failed)、ErrorType、ErrorMessage
Q27: 记忆衰减算法
基于 Ebbinghaus 遗忘曲线的衰减策略:
近期记忆权重高,远期记忆权重低 支持按重要性(importance)调整衰减速度 检索时综合相关性分数 + 时间衰减权重
Q29: 会话记忆的自动提取
SessionMemory 双阈值自动提取:
- 重要性阈值
:超过阈值的记忆自动提取 - 频率阈值
:多次出现的主题自动归纳 输出为 session-memory.md文件
六、工具系统
Q31: 工具接口定义
type Tool interface {Name() stringDescription() stringParameters() map[string]interface{} // JSON SchemaExecute(ctx context.Context, params map[string]interface{}) (string, error)RiskLevel() RiskLevelIsReadOnly() bool}
Q32: 工具编排器的并发分区策略
ToolOrchestrator.partitionToolCalls:
只读工具 → 并行执行(goroutine + WaitGroup) 写工具 → 串行执行(按调用顺序) 混合 → 先并行执行只读组,再串行执行写组
Q34: 安全分级设计
七、RAG 与搜索系统
Q36: RAG 搜索系统架构
基于 bleve(Go 原生全文搜索引擎):
- 分词
:中文分词 + stopTokens 过滤 - 多格式支持
:Markdown、纯文本、代码文件 - 增量索引
:文件变更时自动更新 - 混合检索
:BM25 全文搜索 + 向量相似度
八、LLM 调用与模型管理
Q41: 重试策略
llmCallPolicy:
默认超时:300 秒/次 默认尝试次数:2 次(含初始请求) 安全边界:最小 15 秒,最大 300 秒超时;最大 3 次尝试
Q42: 模型回退链
FallbackChain:主模型失败 → 自动切换到备用模型 → 继续失败则返回错误
Q45: 熔断器状态机
CLOSED → (失败次数达阈值) → OPEN → (冷却时间到) → HALF_OPEN│┌──────────────────┤│ 成功 → CLOSED │ 失败 → OPEN
九、Hook 与扩展系统
Q46: Hook 生命周期事件
Q48: 技能(Skill)系统
通过 use_skill 工具动态加载技能:
内置技能预加载到运行时上下文 自定义技能从 .openkitty/skills/发现支持 Python 脚本、Shell 命令、内置功能三种类型
十、上下文管理
Q51: 6 层防御体系
ContextManager(context_compressor.go):
第1层:Token 预算 ──→ 70% 警告,阈值时自动续接第2层:API 微压缩 ──→ 输入超阈值时清除旧工具结果第3层:滑动窗口 ──→ 消息数量超限时丢弃最旧消息第4层:自动压缩 ──→ 接近上下文窗口时触发 LLM 摘要第5层:手动压缩 ──→ 用户主动触发 /compact第6层:紧急截断 ──→ 最后手段,强制截断最早消息
十一、测试与质量保证
Q56: 测试策略
- 616 个 Go 文件,其中 269 个测试文件
,测试覆盖率约 44% 单元测试:每个核心模块都有对应的 _test.go集成测试:Agent 协作、Pipeline 端到端测试 终端测试: action_receipt_test.go验证工具执行回执
十二、渠道与通信
Q64: 飞书渠道的完整生命周期
FeishuChannel(feishu.go,~1195 行):
初始化:Lark SDK 创建客户端,配置 AppID/AppSecret 事件订阅: dispatcher注册消息事件处理器WebSocket 长连接: larkws实现实时通信审批卡片: ApprovalCard机制,敏感操作需用户确认
十三、数据存储与持久化
Q74: 数据库架构
十四、高级特性
Q84: Agent 协作(AE Collaboration)
ae_collaboration.go 实现三种协作模式:
十五、部署与运维
Q96: 构建和部署流程
- 构建
: go build -o openkitty .生成单一二进制 - 交叉编译
:支持 Linux/macOS/Windows - 配置外置
:二进制与配置文件分离 - 静态资源嵌入
:前端资源编译进二进制 - Docker 支持
:可容器化部署 - systemd 集成
:支持 Linux 服务化
十六、实战场景题
Q106: 如何实现一个"代码审查 Agent"?
- Agent 定义
:创建 YAML 配置,Role 设定为代码审查专家 - 工具授权
:read_file、grep、glob、code_check、git - 技能绑定
:加载代码审查相关技能 - Pipeline 定义
:检出代码 → 静态分析 → 逻辑审查 → 生成报告
Q107: 如何设计"每日新闻摘要"Pipeline?
Step 1: 采集 → social_trending 多平台抓取Step 2: 筛选 → LLM 过滤高价值内容Step 3: 摘要 → 生成结构化摘要Step 4: 发布 → 推送到飞书/钉钉/邮件
十七、Pipeline 与 Sub-Agent 源码实现(深度篇)
111. Pipeline 是怎么跑起来的?
源码位置:pipeline.go(5621 行),核心结构体 PipelineExecutor(L2263)
type PipelineExecutor struct {agentFactory *AgentFactorygateway *GatewayServiceexecutions map[string]*PipelineExecutionpipelines map[string]PipelineprofessionalAgents *ProfessionalAgentRegistry}
Pipeline 的执行不是「一个 Agent 跑完所有步骤」,而是每个 Step 独立启动一个 Agent,通过 pipeline_vars 传递上下文。
112. Pipeline 的持久化恢复
PipelineDurable 检查点模型:
每步完成后写入检查点(step_name、status、output) 中断恢复时从最后 completed 步骤继续 支持重试失败步骤
113. Pipeline 的进化引擎
基于统计指标的规则引擎:
minRuns=5 触发分析 5 种建议类型:步骤合并、工具优化、Prompt 调整、超时调整、并行化
114. sub_agent 的完整生命周期
- 创建
:移除递归工具(sub_agent 不能再 spawn sub_agent) - 执行
:独立 Agent 实例,10 分钟超时 - 清理
:结果返回后释放资源
115. parallel_sub_agent 的并发模型
goroutine + WaitGroup,最多 5 并发,结果有序收集。
116. Pipeline vs sub_agent 本质区别
117. 什么时候用哪个?
118. 两者能组合使用吗?
可以。Pipeline Step 内部的 Agent 仍然可以 spawn sub_agents:
Pipeline Step 1 → Agent A(可 spawn sub_agents)Pipeline Step 2 → Agent B(可 spawn sub_agents)Pipeline Step 3 → Agent C(可 spawn sub_agents)
119. Pipeline 的审批机制
三层防御:
- 黑名单
:永久禁止危险工具 - 步骤审批
:Approval 标记的步骤需人工确认 - 超时拒绝
:超时未审批自动拒绝
120. 子 Agent 为什么不能递归 spawn?
三个原因:防组合爆炸、防失控、防资源耗尽。
121. Function Calling 如何保证稳定生成 JSON 结构?
OpenKitty 的函数调用 JSON 稳定性依赖四层防御:
第一层:System Prompt 约束。 在每个工具定义的 JSON Schema 中,parameters 字段严格声明类型(string/number/boolean/object/array)、required 字段、enum 约束。LLM 在生成 tool call 时,System Prompt 中的这些 Schema 会引导它输出符合规范的 JSON。
第二层:多策略解析(main.go parseWriteFileArgs)。 即使 LLM 输出的 JSON 有瑕疵,框架也有 4 层解析策略兜底:
json.Unmarshal | ||
\" 替换为 " 后再解析 | ||
strconv.Unquote | ||
parseManually |
策略4 的 parseManually 通过 extractFieldSmart(正则匹配 "path":)、extractBoolFieldSmart(匹配 "append":)、extractContentField → extractQuotedString(逐字符扫描双引号字符串,处理 \" 转义)来逐字段提取,即使 JSON 整体结构损坏也能恢复关键字段。
第三层:Tool Registry 校验。 解析后的参数会经过 tools.go 中注册的 Tool Schema 校验,required 字段缺失或类型不匹配会直接报错返回给 LLM,让 LLM 重试。
第四层:内容净化(write.go)。 即使 content 字段被 LLM 包了 markdown 代码块(```...```),WriteFileTool.Execute 也会自动剥离;HTML 文件还会提取 <html>...</html> 之间的内容并解码实体。
122. 大模型输出特别长时,如何保证断点续传?
OpenKitty 采用分块写入 + append 模式策略,核心实现在 write.* 的 WriteFileTool.Execute:
机制一:分块写入(Chunked Write)。 System Prompt 明确指示:当 content 超过约 6000 字符时,LLM 应分多次调用 write_file,每次设置 append=true。首次调用 append=false 创建文件,后续调用 append=true 追加内容。底层实现是标准 os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)。
机制二:JSON 结构增量构建。 对于 JSON 文件,System Prompt 指示:先写一个小的合法骨架(如 {"items":[]}),然后用 json_patch 工具通过 RFC 6901 JSON Pointer 操作(add/replace/remove)逐步添加结构。这避免了拼接无效 JSON 片段的问题。
机制三:格式感知路由。detectFormat 根据扩展名路由到不同 writer。append 模式仅对 FormatText、FormatMarkdown、default 分支生效;JSON/YAML/CSV/HTML/DOCX/XLSX/PDF 不支持 append——这些结构化格式走各自的专用 writer(如 writeJSONFile),保证格式完整性。
机制四:LLM 自主分片。 由于每次 tool call 的 content 参数有 token 限制,LLM 被训练为自动检测内容长度,超过阈值就切分。框架不强制分片大小,而是信任 LLM 的判断——如果某次写入失败,LLM 看到错误后会自行调整策略。
夜雨聆风