乐于分享
好东西不私藏

从零构建一个 AI Agent:分享我做 PulseBot 的全过程

从零构建一个 AI Agent:分享我做 PulseBot 的全过程

突然火爆的AI Agent

2025 年初,Andrej Karpathy 随手发了一条推文,造了个词叫 “vibe coding”——大意是你只要用自然语言告诉 AI 你想要什么,代码就自己长出来了。这个概念不到一年时间,在美国开发者中的采用率已经超过 90%。Claude Code 从 2025 年 2 月上线,8 个月内就超过了 GitHub Copilot 和 Cursor,成为使用量最大的 AI 编程工具。OpenClaw(被OpenAI收入麾下的开源个人 Agent 框架)更夸张,60 天拿下超过 24.7 万 GitHub stars,成为 GitHub 历史上增速最快的开源项目。Gartner 预测到 2026 年底,全球 60% 的新代码将由 AI 生成。

这些数字说明:AI 已经不再只是扮演”辅助打野”的角色,它正在变成写代码的“主力中单”。而 Agent——也就是能自主决策、调用工具、持续工作的 AI 程序——是这场变化中最让人兴奋的方向。

但我做 PulseBot 的动机,并不是盲目”追热点”。

在 Timeplus 做了好几年流处理引擎(Proton),一直在想一个问题:Agent 跑起来之后,那些交互记录、工具调用、成本消耗,都去了哪里?答案往往是——消失了。大多数 Agent 框架把消息放在内存里,进程一停,什么都不剩。这对实验性玩具来说无所谓,但如果你想把 Agent 用到生产环境里,这就是一个很大的问题。

所以 PulseBot 的出发点很简单:既然流处理引擎天然擅长持久化、实时查询和事件驱动,为什么不直接用它来做 Agent 的基础设施?

一个 Agent 到底由哪些部分组成?

在动手写代码之前,我想先聊聊 Agent 的基本构成,因为很多初学者会把 Agent 和”聊天机器人”搞混。

一个 Agent,不管框架怎么包装,本质上就是四个东西:

LLM(大语言模型)——Agent 的”大脑”。它负责理解用户的意图、做出决策、生成回复。PulseBot 支持接入 Anthropic Claude、OpenAI、Ollama、OpenRouter 和 NVIDIA 的模型,选择哪个取决于你的场景和预算。

Agent Loop(智能体循环)——这是 Agent 区别于普通聊天机器人的关键。聊天机器人是一问一答,问完就结束了。Agent Loop 是一个持续运转的循环:接收输入 → 思考 → 调用工具 → 观察结果 → 继续思考 → 输出回复。这个循环可以跑多轮,中间可能会调用好几个工具,直到 Agent 觉得任务完成了才停下来。

Memory(记忆)——这又分两层。短期记忆就是当前对话的上下文,比如你说了什么、Agent 回了什么,这些信息会一直保留在对话窗口里。长期记忆则是跨会话的——你上周告诉 Agent 你用的是 PostgreSQL,下次再聊数据库的时候,它应该还记得这件事。没有长期记忆的 Agent,每次对话都像第一次见面,用起来会很痛苦。

Tool(工具)——Agent 的”手脚”。LLM 本身只能生成文字,但通过 Tool Calling,它可以读写文件、运行命令、查询数据库、发送消息、调用 API。工具的丰富程度直接决定了 Agent 能做多少事情。

这四个部分,每一个都有很多实现方式。下面我会用 PulseBot 的实际设计来说明,我是怎么把它们组合到一起的。

PulseBot 的架构:让流成为一切的基座

PulseBot 的整体架构可以用一句话概括:通信层就是可观测层,就是持久化层。

传统的 Agent 框架通常会这样做:用一个消息队列来传递消息,用一个数据库来存储状态,用一个监控系统来记录日志,用一个调度器来执行定时任务。这意味着你需要维护四套独立的基础设施,它们之间的数据是割裂的。

PulseBot 的做法不同——所有数据都流过 Timeplus 的流(Stream)。用户的输入是一条事件,Agent 的回复是一条事件,工具的调用是一条事件,LLM 的花费是一条事件,定时任务的触发还是一条事件。这些事件全部写入 Timeplus 的流里,被持久化存储,可以用标准 SQL 随时查询。

这里简单解释一下 Timeplus 的"流"(Stream)是什么,因为它和传统数据库里的"表"有本质区别。

在传统数据库中,表是静态的——数据写进去之后就安静地躺在那里,你执行一条 SELECT,它返回结果,查询就结束了。Timeplus 的流不是这样。流是一个持续增长的、无界的事件序列。你对一个流执行 SELECT,查询不会结束——它会一直运行着,每当有新事件写入这条流,查询就立刻输出新的结果。这种行为在 Timeplus 里叫做"流式查询"(Streaming Query),它回答的不是"过去发生了什么",而是"现在正在发生什么"。

当然,如果你确实需要查历史数据,Timeplus 也支持。只要用 table() 函数把流包一层——比如 SELECT * FROM table(messages)——查询就变成了传统的有界查询,返回流中已有的全部历史数据,查完即止。

所以 Timeplus 的流同时扮演了两个角色:它既是一个实时的消息通道(新事件到达时立刻可查),又是一个持久化的存储(历史事件随时可回溯)。这也是为什么 PulseBot 能用流来同时解决消息传递、状态存储和实时监控这三个看似不同的问题——因为在 Timeplus 的世界里,它们本来就是同一个东西。

整个框架维护了 9 条 Timeplus 流,各有分工:

  • messages 是所有对话的中心枢纽,用户输入、Agent 回复、工具调用的结果都在这里。

  • llm_logs 记录每次 LLM 调用的细节——用了哪个模型、花了多少 token、延迟多久、第一个 token 什么时候出来、调用是否成功。

  • tool_logs 记录每次工具执行的参数、耗时和结果。

  • memory 存放向量化的长期记忆,带有重要性评分。

  • events 是系统的”神经系统”,承载心跳、通道连接、技能加载、告警、任务通知等信号

  • task_triggers 是定时任务的审计日志。

  • 还有三条 kanban 相关的流(kanban、kanban_projects、kanban_agents),用于多 Agent 协作。

部署上,Docker Compose 启动三个服务就够了:Timeplus(流数据库,跑在 8123/3218/8463 端口)、PulseBot Agent(消息处理循环)、PulseBot API(FastAPI 提供 REST 和 WebSocket 接口,跑在 8000 端口)。流会在第一次启动时自动创建,不需要任何手动建表操作。

为什么选 Proton(Timeplus 的开源流处理引擎)作为底座?因为 Proton 是一个单体 C++ 二进制文件,体积不到 500MB,却能达到每秒 9000 万事件、端到端延迟 4 毫秒的处理能力。对于 Agent 这种消息量不大但对延迟敏感的场景来说,它完全够用,而且不需要额外部署 Kafka 或者 Redis。

Agent Loop:

六步循环的设计与提示词工程

PulseBot 的 Agent Loop 分成六个步骤,每一步都和 Timeplus 的流深度绑定。

第一步,监听。 Agent 持续监听 messages 流上的 user_input 事件。注意,这里不是轮询(polling),而是流驱动的——有新消息到达时才触发处理,没消息的时候 Agent 不消耗计算资源。

第二步,构建上下文。 Agent 从 messages 流中拉取最近的对话历史(短期记忆),同时通过向量搜索从 memory 流中检索最相关的长期记忆。这两部分合在一起,构成了这次 LLM 调用的上下文。

第三步,调用 LLM。 把构建好的上下文连同所有可用的工具定义一起发给大模型,让它做推理。

第四步,执行工具。 如果 LLM 的回复里包含了工具调用请求(tool call),Agent 就执行对应的工具,并把每次执行的参数、耗时和结果写入 tool_logs 流。工具执行完之后,结果会回传给 LLM,让它决定是否需要继续调用其他工具,还是直接生成最终回复。

第五步,写回结果。 把 Agent 的回复写入 messages 流,同时把这次 LLM 调用的完整信息(模型名称、提供商、输入/输出 token 数、预估费用、延迟、首 token 时间、调用的工具列表、成功/失败状态)写入 llm_logs 流。

第六步,提取记忆。 LLM 会在回复过程中自动识别对话中值得记住的信息——用户的技术偏好、项目背景、做出的决定——然后附带一个重要性评分,写入 memory 流。

这个六步循环是异步执行的(基于 Python 的 async/await),整个过程完全可以通过 SQL 查询来观察。比如你想知道过去一小时 Agent 花了多少钱,直接查 llm_logs 就行;想知道哪个工具调用最慢,查 tool_logs 排个序就够了。不需要额外接 Prometheus 或者 Grafana,流数据库本身就是你的监控系统。

在提示词设计上,系统提示词的结构大致是这样的:先描述 Agent 的身份和行为规范;然后列出所有已发现技能的名称与描述(总共可能只有几百个 token);接着给出可用工具的定义(JSON Schema 格式);最后注入通过语义搜索检索到的相关长期记忆。这四个部分组合起来,构成了每次对话轮次中动态生成的完整提示词。

另外,系统提示词里只放技能的名字和简短描述,而不是完整的指令。每个技能在提示词中大约只占 24 个 token。当用户的问题匹配到某个技能时,Agent 会动态加载那个技能的完整指令——这是一种”懒加载”模式,既保持了基础上下文的精简,又不牺牲深度能力。

Skill 系统与 Tool Call:

按需加载的插件架构

ulseBot 的技能系统遵循 http://agentskills.io 标准,并兼容 OpenClaw/ClawHub 的技能格式。一个技能的结构非常简单——就是一个文件夹,里面有一个 SKILL.md 文件。这个文件的头部是 YAML frontmatter,定义了名称、描述、版本和依赖关系,正文部分是给 Agent 看的 Markdown 指令。

Agent Skill的设计让写技能变得像写文档一样简单。你不需要学新的 DSL,不需要注册什么 API 端点,只要把一个 Markdown 文件丢进指定目录,PulseBot就能发现它,并且无需重启,实现了热加载。

PulseBot 自带了五个内置技能:

file_ops 提供文件操作——read_file、write_file、list_directory, Agent 可以在受限的目录范围内读写文件。

shell 提供命令行执行能力,但加了安全防护,会拒绝执行危险操作(比如 rm -rf /)。

workspace 用于创建和发布交互式 Web 应用(后面会专门讲)。

scheduler 负责定时/周期任务的管理。

project_manager 处理多 Agent 项目的协调。

每个技能可以声明自己提供的工具(Tool),这些工具通过标准的 function calling 机制暴露给 LLM。当 LLM 决定调用某个工具时,Agent 框架会负责参数验证、执行调用、日志记录和结果回传。

除了本地技能,PulseBot 还集成了 ClawHub 注册中心。你可以用 CLI 命令搜索和安装社区发布的技能:

pulsebot skill install timeplus-sql-guide

以 timeplus-sql-guide 这个技能为例,它包含了 Proton 流处理 SQL 的完整参考——tumble()、hop()、session() 窗口函数的用法,外部流(Kafka/Redpanda/Pulsar)的创建语法,Python/JavaScript UDF 的编写方式,RANDOM STREAM 测试数据生成,以及 CREATE TASK 定时任务语法。这些内容加起来可能有好几千个 token,但在用户不问相关问题的时候,它们完全不占用上下文空间。只有当用户说”帮我写一个流式聚合查询”的时候,Agent 才会去加载这个技能的完整指令。

记忆管理:短期靠流,长期靠向量

记忆是让 Agent 变得”好用”的关键。一个记不住任何东西的 Agent,每次对话都得从头开始交代背景,用户体验很差。

PulseBot 的记忆分两层。

短期记忆很好理解——就是 messages 流里存的当前对话历史。因为所有消息都持久化在 Timeplus 里,即使 Agent 进程重启,对话上下文也不会丢失。这和 OpenClaw 形成了鲜明对比:OpenClaw 把消息存在内存里,进程一停,聊天记录就没了。

长期记忆稍微复杂一些。在 Agent Loop 的第六步,LLM 会自动从对话中提取值得记住的信息——比如用户提到他习惯用 VS Code,或者他正在做一个基于 Kafka 的实时风控系统。每条提取出来的记忆会被向量化(embedding),然后连同一个重要性评分一起写入 memory 流。

下一次对话开始时,Agent 会用当前的问题做语义搜索(基于向量相似度),从 memory 流中找出最相关的记忆,注入到系统提示词中。这样 Agent 就”记住”了之前对话中的关键信息,能够在新的语境中自然引用它们。

PulseBot 的记忆搜索用的是混合评分(hybrid scoring)——余弦相似度乘以重要性权重。核心 SQL 在

pulsebot/timeplus/memory.py的 search() 方法中,大致是这样:

SELECT   id,   content,   memory_type,   category,   importance,   source_session_id,   timestamp,   cosine_distance(embedding, [0.012-0.034, ...]) as distance,   (1 - cosine_distance(embedding, [0.012-0.034, ...])) * importance as scoreFROM table(pulsebot.memory)WHERE importance >= 0.0 AND is_deleted = false AND length(embedding) > 0ORDER BY score DESCLIMIT 5

cosine_distance 是 Proton 内置的向量距离函数,直接在 SQL 层面计算查询向量和存储向量之间的余弦距离。1 - cosine_distance 就是余弦相似度。

混合评分公式是 (1 - cosine_distance) * importance,也就是语义相似度和重要性评分的乘积。这意味着一条重要性 0.9 的记忆,即使语义匹配度只有 0.7,综合得分也可能高于一条重要性 0.3 但匹配度 0.85 的记忆。这个设计让 Agent 在上下文有限时优先召回"重要且相关"的记忆,而不只是"最相关"的。

table() 包裹流名,所以这是一条有界查询,扫描 memory 流中的全部历史数据。如果不加 table(),就变成了流式查询——只返回查询启动之后新写入的记忆,那就不是我们想要的语义了。

去重也走同一套 SQL,只是多返回了一个 (1 - cosine_distance) as similarity 裸相似度列。存入新记忆之前会先用这条查询找相似记忆,如果最高相似度超过阈值(默认 0.95),就跳过不存,返回已有记忆的 ID。避免同一个事实被重复记住几十遍。

在向量化的实现上,PulseBot 提供了两种选择:本地 embedding 和 OpenAI 云端 embedding。本地 embedding 不需要任何外部 API key——配合 Ollama 作为 LLM,你可以在完全离线的环境中运行 PulseBot,不依赖任何外部服务。如果你追求更高的检索质量,也可以切换到 OpenAI 的 embedding 模型。

存储层面,记忆用的是追加式写入加软删除的模式。为什么不直接更新(UPDATE)?因为 Proton 作为流处理引擎,天然就是追加式的——事件进来之后不可修改,这正好契合了事件溯源(Event Sourcing)的思想。要”修改”一条记忆,实际上是插入一条新的记忆,同时把旧的标记为已删除。

这套记忆系统的一个好处是,因为所有记忆都在 Timeplus 流里,你可以随时用 SQL 查询它们。想知道 Agent 记住了关于某个用户的什么?一条 SELECT 就够了。想清理过期的记忆?一条 DELETE(逻辑删除)搞定。

Workspace:

让 Agent 交付的不只是文字

大部分 Agent 的输出只是一段文字。但很多时候,用户想要的是一个可以运行的东西——一个仪表盘、一个数据可视化页面、一个交互式小工具。

PulseBot 的 Workspace 系统就是为此设计的。当用户说”帮我做一个实时监控面板”时,Agent 不只是生成一堆 HTML 代码贴给你看。它会用 workspace_create_app 工具创建一个独立的 Web 应用目录,用 workspace_write_file 往里面写入 HTML、JavaScript 和 CSS 文件,然后把这个应用发布到一个专用端口上。

Workspace 的内容由Pulsebot的Agent来Host,并通过代理的方式,通过API server来暴露出来。

Workspace 的配置如下:

workspace: base_dir"./workspaces" port8001 api_key"your-internal-key"

base_dir 是工作区的根目录,

port 是工作区 Web 服务器的端口(和主 API 的 8000 端口分开),

api_key 用于 Agent 和API服务器之间的安全通信。

这个设计让 PulseBot 的输出从”文本”升级到了”可运行的应用”。配合 Timeplus SQL 技能,Agent 可以创建实时查询流数据的仪表盘,构建监控界面,或者生成数据分析应用——整个过程中用户不需要写一行代码。

定时任务:用 SQL 取代 cron

定时任务是很多 Agent 场景的刚需——每天早上 9 点总结一下昨天的 GitHub 活动,每小时检查一次服务器状态,每周生成一份数据报告。

大多数 Agent 框架处理定时任务的方式是用系统 cron 或者在进程内跑一个定时器。这两种方案都有明显的问题:系统 cron 和 Agent 框架是脱节的,你很难在框架层面管理和监控这些任务;进程内定时器则在 Agent 重启后就丢失了。

PulseBot 选择用 Timeplus 原生的 TASK对象来做定时任务。

具体机制是这样的。scheduler 技能通过 CREATE TASK SQL 语句在 Timeplus 中创建一个 TASK 对象,这个 TASK 按照指定的间隔或 cron 表达式定期执行。每次执行时,TASK 会调用一个 Python UDF(用户自定义函数),这个 UDF 向 PulseBot API 的 /api/v1/task-trigger 端点发送一个 HTTP POST 请求。API 服务器收到请求后,Agent 就会处理关联的 prompt,生成结果,并通过 events 流以 task_notification 事件的形式广播给所有已连接的通道(Telegram、网页聊天等)。每次调用也会记录到 task_triggers 流中,用于审计。

这个方案的优势在于:任务活在数据库引擎里,不在 Agent 的内存里,Agent 重启不影响任务的执行;任务的状态和历史可以用 SQL 查询;管理操作通过 CLI 完成,和框架的其他部分保持一致。

多 Agent 协作:基于看板的项目管理

当一个任务复杂到单个 Agent 处理不过来时,就需要多个 Agent 协作。

PulseBot 用了一个大家都很熟悉的比喻来组织多 Agent 协作——看板(Kanban)。一个主 Agent(通常扮演项目经理的角色)可以创建一个”项目”,把任务拆分成多个子任务,分配给不同的 Agent 去执行,然后追踪整体进度。

这套机制依赖三条专用的 Timeplus 流。

kanban 流是 Agent 之间的消息队列。Agent 在这里发布任务项、状态更新和工作成果。每条消息就是一张”卡片”,可以在不同的状态之间流转。

kanban_projects 流记录项目的生命周期——什么时候创建的、当前状态是什么、参与了哪些 Agent、整体目标是什么。

kanban_agents 流维护每个 Agent 的状态和检查点。如果某个 Agent 中途被打断(比如进程重启),它可以从最近的检查点恢复,而不需要从头开始。

项目经理技能提供了四个工具:create_project(创建新项目并分配任务)、list_projects(查看所有进行中的项目)、get_project_status(检查特定项目的进度)、cancel_project(取消一个项目)。

为什么选看板而不是其他协作模式?因为看板天然就是事件驱动的——每一次状态转换都是一条事件。这和 PulseBot 基于流的架构完美契合。每个 Agent 的每一步操作都被记录为流中的事件,这意味着整个协作过程都是可观察、可查询、可回放的。

和基于内存的多 Agent 框架相比,这种设计有一个很实际的好处:Agent 团队可以跨机器分布。主 Agent 和子 Agent 不需要跑在同一台机器上,它们通过 Timeplus 流来通信。只要能连到同一个 Timeplus 实例,就能协作。

这里是Pulsebot用以下提示词创建的一个一次性任务的多Agent团队项目:

Please create a project, prepare an investment memo for this startup ,http://timeplus.com. In parallel: research the market opportunity and analyze the founding team's background. Once both are done, a senior analyst should synthesize them into the core investment thesis. Then an editor polishes the final memo.

区别于n8n,dify等需要用户构建任务图的Agent框架,Pulsebot可以利用自然语言来创建Agent团队的依赖关系非常的方便。

事件驱动的 Agent 任务:

用流式 SQL 查询触发工作流

如果你注意到了,前面讲到的多 Agent 协作已经支持两种执行模式——一次性运行(one-shot)和定时/周期运行(scheduled)。但在真实的生产场景中,还有大量需求是这两种模式覆盖不了的。比如:每当 events 流中出现一条 severity = ‘error’ 的事件,就自动触发一组 Agent 去排查问题;或者当某个传感器的读数超过阈值时,立刻启动一个分析流水线。

这类需求的本质是:Agent 工作流的触发源不是人,也不是时钟,而是实时数据流中的某个模式。

为了解决这个问题,我在 PulseBot 中实现了第三种项目执行模式——事件驱动模式(event-driven)。它的触发源是一条用户定义的 Proton 流式 SQL 查询,每当查询返回一行新结果,就触发一次多 Agent 工作流。

EventWatcher:流式查询的”哨兵”

这个机制的核心是一个叫做 EventWatcher 的组件(代码在

pulsebot/agents/event_watcher.py 中)。它的职责很明确:订阅一条用户指定的流式 SQL 查询,从每一行结果中提取指定的上下文字段,判断当前项目是否空闲,如果空闲就触发一次工作流运行。

整个触发流程是这样走的:

  1. Proton 流式 SQL 查询(用户定义)持续运行,等待新事件到达

  2. 新的一行结果到达,EventWatcher 从中提取 context_field 的值

  3. 检查项目是否正在忙碌——如果是,跳过这行,推进 checkpoint,继续等待下一条事件

  4. 如果项目空闲,标记为忙碌,写一条 trigger 消息到 kanban 流(包含 msg_type: "trigger"、target_id: "manager_{project_id}" 以及拼接好的提示词),然后推进 checkpoint

  5. ManagerAgent(项目经理 Agent)从 kanban 流收到 trigger 消息,把带有上下文的任务分发给各个 Worker Agent

  6. Worker Agent 各自执行 LLM 推理和工具调用,把结果通过 kanban 流返回给 ManagerAgent

  7. ManagerAgent 汇总所有 Worker 的结果,通过 events 流广播 task_notification,并调用 on_run_complete 回调

  8. ProjectManager.mark_project_idle(project_id) 释放忙碌标记,EventWatcher 可以处理下一条事件了

这里有一个关键的设计决策:drop-on-busy(忙碌时丢弃)。如果 Agent 团队正在处理上一条事件触发的工作流,新到达的事件会被直接跳过,而不是排队等待。为什么?因为在实时场景中,堆积未处理的事件往往比丢弃它们更危险——想象一下,一个错误告警在 10 秒内触发了 50 次,你不会希望 Agent 团队把这 50 次告警一条一条地按顺序处理。丢弃重复事件、只处理最新的,通常才是正确的行为。

用一条 SQL 定义触发条件

创建一个事件驱动项目时,你需要提供三个关键参数:

event_query 是一条标准的 Proton 流式 SQL 查询,定义了”什么事件应该触发工作流”。context_field 指定从查询结果的哪个列提取上下文信息。trigger_prompt 是一段提示词模板,会和提取到的上下文拼接在一起,作为 Worker Agent 的输入。

比如,你想在系统出现错误时自动触发排查:

event_query:    "SELECT payload FROM pulsebot.events WHERE severity = 'error'"context_field:  "payload"trigger_prompt"检测到系统错误,请调查并总结:"

当 events 流中出现一条 severity = 'error' 的事件时,EventWatcher 会提取它的 payload 字段值,拼接到 trigger_prompt 后面,形成完整的指令发给 Agent 团队:

检测到系统错误,请调查并总结:

{"source": "agent:xyz""message""Connection refused on port 8463"}

这条查询必须直接查询一个 Proton 流或视图,不能使用嵌套子查询。这是因为 Proton 的 _tp_sn(序列号)是物理流上的系统列,它在子查询边界上的可见性无法保证。PulseBot 在创建项目时会做校验,如果检测到 FROM ( 这样的嵌套查询模式,会直接拒绝。

Checkpoint 机制:重启不丢事件

EventWatcher 处理(或跳过)每一行之后,都会把当前的 Proton 序列号 _tp_sn 作为 checkpoint 持久化到 kanban_agents 流中(agent_id 为 event_watcher_{project_id})。

服务重启时会发生什么?ProjectManager 在恢复逻辑(

_recover_scheduled_projects)中会检查每个活跃的 scheduled 项目的 schedule_type。如果是 'interval' 或 'cron',走原来的 Timeplus Task 恢复路径。如果是 'event',则从 kanban_agents 读取 EventWatcher 的最后 checkpoint,然后重新构建流式查询,加上 _tp_sn > {checkpoint_sn} 的过滤条件和 SETTINGS seek_to='earliest',从上次处理到的位置继续。

具体来说,EventWatcher 构建查询的逻辑是:

如果没有 checkpoint(全新启动),查询加上 SETTINGS seek_to='{start_time}',从项目创建时间开始消费事件。

如果有 checkpoint(重启恢复),在原始查询后面追加 AND _tp_sn > {checkpoint_sn} SETTINGS seek_to='earliest',从最后处理过的序列号之后开始。

这套机制确保了 exactly-once 语义(至少在 Agent 工作流触发层面)——不会漏掉事件,也不会重复触发。

该机制同样作用于其他的Agent,保证系统重启的时候,Agent可以从之前消费的看板时间的checkpoint上恢复运行。

断线重连与错误处理

EventWatcher 的主循环是一个 while _running 的外层循环,里面包裹着对流式查询的 async for 迭代。如果 Proton 的流连接意外断开(网络抖动、Proton 重启等),外层循环会从最后的 checkpoint 重新启动查询。重连使用递增的延迟策略——3 秒、10 秒、30 秒封顶。

如果连续三次(_MAX_CONSECUTIVE_FAILURES = 3)启动查询都没有返回任何行就断开了,EventWatcher 会判定这条查询本身有问题(可能 SQL 写错了、流不存在等),记录错误日志并停止,而不是无限重试。这避免了一条坏查询消耗资源却永远不会成功的情况。

还有一个实现细节值得一提:EventWatcher 使用了两个独立的 Timeplus 客户端连接——一个用于流式读取(execute_iter 会阻塞连接),另一个用于写入 checkpoint。这是因为在同一个连接上同时做流式读和写入会互相阻塞。

事件驱动与定时调度的区别

事件驱动项目和定时项目在 ManagerAgent 那一层是完全共享代码的——ManagerAgent 收到 trigger 消息后,不管它来自 EventWatcher 还是来自 Timeplus Task 的定时回调,处理逻辑完全一样。差异只在触发源。

但有一个重要区别:事件驱动项目不创建 Timeplus Task。定时项目依赖 CREATE TASK SQL 来在数据库引擎层面注册周期性执行,而事件驱动项目依赖 EventWatcher 的流式查询订阅。删除项目时也一样——对于定时项目需要 DROP TASK,对于事件驱动项目则需要取消 EventWatcher 的 asyncio 任务。

在数据模型上,kanban_projects 流的 schedule_type 列有四个有效值:空字符串(一次性项目)、'interval'(固定间隔定时)、'cron'(cron 表达式定时)、以及 'event'(事件驱动)。事件驱动项目还额外使用 event_query 和 context_field 两个列来存储流式查询和上下文字段名。

事件驱动模式让 PulseBot 的多 Agent 系统从”被动响应”和”定时巡检”扩展到了实时反应。结合 Proton 的流式 SQL 能力,触发条件可以非常灵活——可以是简单的字段过滤(WHERE severity = 'error'),也可以是窗口聚合(过去 5 分钟错误率超过 5%),甚至可以是跨流 JOIN(当订单流中的某个用户 ID 同时出现在风险名单流中时触发)。

这实际上实现了一种 Complex Event Processing(复杂事件处理)驱动的 Agent 编排。传统的 CEP 系统检测到模式后通常只能触发告警或者执行预定义的规则。而 PulseBot 的 EventWatcher 把 CEP 的输出直接接到了 LLM 驱动的 Agent 团队上——不光能检测到异常,还能自主地去分析、排查、甚至修复。

流式可观测性:

SQL 就是你的监控系统

聊完事件驱动,我想再补充一点关于整体可观测性的内容。

PulseBot 暴露了一个流式 SQL 代理端点(POST /query),接受原始 SQL 查询并返回 NDJSON(换行分隔的 JSON)格式的结果流。这意味着任何外部应用——不管是网页前端还是移动应用——都可以直接对 PulseBot 的任意一条流运行流式查询,实时获取 Agent 的行为数据。

比如你想做一个 Agent 成本监控面板,只需要运行这样一条查询:

SELECT model, sum(input_tokens) as total_input, sum(output_tokens) as total_output,      sum(estimated_cost) as total_costFROM table(llm_logs)GROUP BY model

不需要 Prometheus,不需要 Grafana,不需要额外的数据管道。流数据库本身就是你的可观测性平台。

因为所有事件都被持久化保存了,你还可以在事后回放一个 Agent 的完整行为轨迹——它收到了什么消息、做了什么思考、调了什么工具、花了多少钱、最终给出了什么回复。对于调试 Agent 行为或者做事后分析,这个能力非常有价值。

PulseBot 整个后端大约 15000 行 Python 代码,不算大(和OpenClaw 400k+相比)。它的目标不是做一个大而全的框架,而是验证一个Agent架构的设计思路:AI Agent 面临的持久化、可观测性、记忆、调度和多 Agent 协作问题,本质上都是数据管理问题,而流数据库是解决这些问题的最佳选择。

把所有 Agent 活动路由到 9 条 Timeplus 流中,意味着 Agent Loop、记忆系统、技能执行、任务调度和多 Agent 协作都可以通过同一套 SQL 接口来观察和查询。基于看板的多 Agent 协作展示了流式事件日志如何自然地支持分布式 Agent 团队。SQL 原生的 TASK 调度消除了对外部 cron 的依赖。

当然,PulseBot 还很年轻,还在迭代中。多 Agent 协作的效率和智能程度也有很大的提升空间。Agent的自主能力和Claude Code,OpenClaw这些主流Agent比较还有很大的差距。

如果你对 Agent 开发感兴趣——不管是想理解底层原理,还是想直接拿来用——欢迎来 GitHub 看看代码,提 issue 或者 PR。

GitHub 仓库:https://github.com/timeplus-io/PulseBot

海豚实验室成功落地案例:

  • ...

海豚实验室重磅新闻:

版权声明


转自闻数起舞,版权属于原作者,仅用于学术分享

与此同时,杭州睿数科技(海豚实验室)同步推出了“百校千课免费支持”计划,全力为推进全国高校大数据与人工智能教学发展贡献力量,欢迎老师们致电咨询️☎️。

扫描申请后24小时内将有专员联系开通

【海豚人工智能与大数据实验室】是“一站式”大数据分析及人工智能的教育实训+科研平台,  由北美海归团队创立的杭州睿数科技有限公司自主研发。通过“沉浸式” “交互式”的在线虚拟实验平台,结合丰富的真实行业案例和数据集,切实解决大数据及人工智能教育培训环节的痛点。通过我们的整体解决方案,实现 “大数据 + X”,“人工智能 + X”的跨专业、跨学科复合型人才培养。全面助力中国人工智能,大数据产业的快速发展!

欢迎全国高校、培训机构、渠道合作伙伴与我们联系,开展合作!

请访问官网 www.dilabs.cn 在线客服,提供7x24全天候咨询服务

或联系邮箱 support@dilabs.ai 

联系热线 400-001-3538

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-29 18:10:29 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/676614.html
  2. 运行时间 : 0.186663s [ 吞吐率:5.36req/s ] 内存消耗:4,884.76kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=0b8b512e435a2bfd3eaa9a6a5decd83c
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001175s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001746s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000750s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000712s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001526s ]
  6. SELECT * FROM `set` [ RunTime:0.000596s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001824s ]
  8. SELECT * FROM `article` WHERE `id` = 676614 LIMIT 1 [ RunTime:0.001154s ]
  9. UPDATE `article` SET `lasttime` = 1780049429 WHERE `id` = 676614 [ RunTime:0.007361s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000605s ]
  11. SELECT * FROM `article` WHERE `id` < 676614 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001131s ]
  12. SELECT * FROM `article` WHERE `id` > 676614 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.003649s ]
  13. SELECT * FROM `article` WHERE `id` < 676614 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.004213s ]
  14. SELECT * FROM `article` WHERE `id` < 676614 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000778s ]
  15. SELECT * FROM `article` WHERE `id` < 676614 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.007946s ]
0.190691s