cursor-api.standardagents.ai The unofficial Cursor API OpenAI-compatible chat completions and responses for Cursor. 利益无关,更何况免费。 之前学生会员还能用,把 composer-2.5 弄出来给 cc 用,还行。 3 个帖子 - 2 位参与者 阅读完整话题
https://doi.org/10.1093/jsxmed/qdag051 2 个帖子 - 2 位参与者 阅读完整话题
最近有一个 idea ,将 AI 对话的问答以 DAG ( Directed Acyclic Graph )的形式重新组织,提高了前端交互的体验。经过数月 Vibe Coding 以后, DAG-chat 终于达到了可以公开发布的版本。 为什么要有 DAG-chat 场景假设 假设这样一个场景,五一假期来了,我想在杭州,南京,长沙,武汉四个城市挑选一个度假。首先我和 AI 聊了一下杭州的若干知名景点。聊完了以后,我又和 AI 聊了一下南京的美食。聊完南京的美食以后,我又对杭州的交通便利度产生了好奇。 到目前为止,对杭州的讨论和南京的讨论都是独立无关的。但是如果我想回头查看杭州具体某个景点的介绍,就需要滚动屏幕好久才能找到。如果我又开启了长沙的行程攻略讨论,或者对比一下武汉和南京的美食,那么当前的对话就会变得更加混乱: 进行了多轮对话以后,如果我想查看单独关于杭州的讨论的汇总(美食,景点,交通等),那么需要不停的滚动屏幕进行查找,关于杭州的讨论并没有集中到一处,而是分散在线性的对话内容中; 在整个讨论中,有四个城市,每个城市又有美食、景点、交通、娱乐、避雷等多个维度;用户可以针对某几个城市、某些维度进行对比或者关联,如:对比武汉、南京、长沙三地的美食;计划去杭州看西湖,然后武汉游长江,最后去长沙吃湘菜,如何安排规划行程。整个对话其实呈现出高度结构化的逻辑,但是在交互上,都被拍扁放到了一个线性的问答记录中,用户的交互体验很碎裂。 痛点分析 造成这种痛点的根本原因在于:对话沟通的思维是结构化的,有发散和汇总,而绝非单独一问一答。不光是旅游规划,很多场景都如此: 高考填报志愿。在浙江大学,上海交大,中科大之间抉择。首先要了解这三所学校的专业实力,所在城市发展潜力,保研/出国政策等信息;然后进行交叉对比汇总; 技术选型。现在要构建一个高性能后端应用,是选择 Rust 还是 Golang ?这两种语言分别从性能,生态,开发难度上进行分析,然后结合团队人员的情况进行选择; 凡是满足: 一个主题 --> 发散思维 --> 对比交叉关联 --> 得出结论 这种沟通范式的,都会存在上述的痛点; 目前市面上几乎所有的 AI 问答 APP ( DeepSeek ,ChatGPT ,Claude ,Gemini ,Qwen ),问答都是以线性的形式进行组织的。如果我想从某个地方开始,往不同的方向探索,再在某一个点上把这些探索汇合起来,以思维导图的形式组织对话内容,这是无法做到的。 那么问题来了,能不能把组织对话的数据结构从链表换成图? 于是做了这个项目: DAG-chat 。 DAG-chat 效果演示 分支——从同一个回答出发,走不同的路 这是我用得最多的功能。 比如我问 AI “解释一下 Docker 的核心概念”,它给了一段回答。看完之后我脑子里冒出了两个方向:一个是想看看具体的 Dockerfile 怎么写,另一个是想了解 Docker Compose 多容器编排。 在 DAG-chat 里,我可以从同一条 AI 回复出发,分别提两个不同的问题——它们会变成两条平行的分支。界面上会出现一个标签栏,点一下就能在两条分支之间跳转。 操作也很直觉:把鼠标悬停在任何一条用户消息上,左边会出现一个分支图标,点一下,输入框里会自动引用它上面的那条 AI 回复作为上下文。你写上新的问题发出去,一条新分支就出来了。 原来的那条路径不会被覆盖。你开了三条分支,三条都在。想回去看哪条随时切,不丢任何东西。 这在做探索性对话的时候特别有用。比如我在学一个新技术的时候,通常会从概念层面先问一轮,然后针对其中感兴趣的点分别开分支深入——一个分支聊实现细节,一个分支聊最佳实践,一个分支聊常见坑。每个分支都是独立的上下文,互不干扰。最后如果我想对比不同分支里得到的信息,就用下一个功能——合并。 合并——把不同分支的答案汇总到一起 这个功能是我一开始就想做的核心需求,也是最开始的那个痛点——两个分支里的回答想放到一起做对比。 还是技术选型的例子。我在分支 A 里让 AI 分析了 Rust 的优势,在分支 B 里分析了 Go 的优势。现在我想让它做一个综合对比。 在 DAG-chat 里,我把鼠标悬停在两条 AI 回复上,分别点右边的合并图标,它们就被引用到了输入框里。然后我写上”Rust 和 Go 哪个学起来更容易?”——两条分支的上下文会一起作为这条新问题的 parent 。 合并的妙处在于:AI 在回答的时候,能同时看到不同分支的内容。它不是只看了一个片面,而是看到了你探索的全貌。此时,AI 回答的上下文,是沿着所有的 parent 一直向上到最初的提问,所形成的 sub DAG. 多模型对比 对话中间可以随时切模型。比如同一个问题,我先让 DeepSeek 回答,再切到 Qwen 回一个,两条回答各占一条分支。然后再用合并功能让 GLM 做个对比总结。 这个用法在做方案评估的时候特别好使。不同模型的知识储备和推理风格不一样——DeepSeek 可能更擅长逻辑推理,Qwen 在中文理解上有优势,Kimi 的长上下文能力比较强。把多个模型放在同一张图里对比,能比只用一个模型看到更全面的分析。 我试过拿三个模型分别 review 同一段代码,然后合并到一个节点让第四个模型做总结。这种用法在线性对话里,要么开多个对话,手动复制上下文;要么在一个对话里面,但是信息需要来回滚动查看。 除了技术选型,还能怎么用 上面举的例子偏技术开发场景,但其实 DAG 对话结构适用的范围比我想象的要广。 学习新知识。 比如你在学机器学习,问了一个”什么是梯度下降”的基础问题。AI 给了回答之后,你可以在一个分支里追问数学推导,另一个分支里要看代码实现,第三个分支里聊实际应用场景。每个分支独立深入,不会互相污染上下文。学到后面想回顾某个分支的内容,点标签就跳回去了,不用在长长的对话历史里翻找。 写作和内容创作。 我试过用它来构思文章大纲。先让 AI 给一个初始结构,然后在大纲的每个章节上开分支,分别让它展开写。不同章节的构思互不干扰,最后再用合并把几个章节的要点汇聚到一起做统一审阅。 debug 和排障。 遇到一个报错,可以让 AI 从不同方向分析:一个分支走”看日志定位问题”的路线,另一个分支走”检查配置文件”的路线,第三个分支走”搜索已知 issue”的路线。哪条路走通了就沿着哪条继续,走不通的切回去换一条,不浪费之前已经聊过的内容。 本质上,只要你的思考过程是”探索→分支→收敛”这种模式,DAG-chat 都能派上用场。 技术实现 问答对——核心概念 大模型的回答不同于即时通讯,在没有异常中断的情况下,是严格的一问一答节奏,用户提问和大模型的回答,在逻辑上构成了一个原子的,不可分割的问答对。 将这样一个问答对,定义为 DagNode ,整个对话就是由很多个 DagNode 组成的 DAG 。 每次在对话中新增提问内容,实际上就是在向这个 DAG 中,新增一个 DagNode 节点。而整个 DAG ,有且仅有一个 root 节点,即对话开始,最早的那个问答对。从任何一个 dagNode 开始向上遍历,寻找 parnet_ids ,最后都会遍历到最初的 dagNode 。 从链表到图 传统聊天的每条消息只有一个 parent_id ,指向前一条消息。我把这个字段改成了 parent_ids——一个数组。一个节点可以有零个、一个或多个父节点。 传统聊天: Message { id, content, role, parent_id } DAG-chat: DagNode { id, content, role, parent_ids[], children[] } parent_ids 是数组,所以一个用户问题可以引用多条 AI 回复作为上下文——这就是合并。children 也是数组,所以一条 AI 回复可以派生出多个追问——这就是分支。 但是每一个 role=user 的 dagNode ,children 只有一个元素;每个 role=assistant 的 dagNode ,parent_ids 也只有一个元素,这是问答对的定义决定的。 前端怎么把图展示成线性 DAG 在数据库里很自然,但屏幕是一条线。用户一次只能看到从根到叶的一条路径——就像在思维导图里,你虽然能展开所有节点,但目光的焦点在同一时刻也只能沿着一条路径走。 所以前端做的事情是:扫描 DAG 找出所有的分支点和合并点,给每个点建一个标签页容器,tabsContainer 。然后从根节点出发,沿着每个分支点当前激活的标签往下走,生成一条线性路径——这就是屏幕上展示的内容。 根据分支以及合并的特性,有两种 tabsContainer ,一种是 ChildrenTabsContainer ,用于管理分支问里面不同的分支,点击切换的时候,会改变整个渲染 path 中,到 leaf 方向的节点路径; 另一种是 ParentTabsContainer ,用于管理合并提问里面,不同的来源。点击切换的时候,会改变整个渲染 path 中,到 root 方向的途径节点; 用户点击 tab ,实际上就是在 DAG 中所有分支节点和合并节点中,选择一条 path ,从而让前端构建一条从 root 到某个 leaf 的 path 。 后端怎么把图喂给大模型 大模型的 API 只接受线性的对话历史,比如 : [{role: "user", content: "..."}, {role: "assistant", content: "..."}, ...] 但 DAG 是图结构,不能直接丢过去。 所以后端做了一件事:当用户发新消息时,从这条消息的 parent_ids 出发,BFS 往上追溯所有祖先节点,构建出一个子图( SubDAG )。然后对这个子图做拓扑排序,把它拉平成一条链。 这里有个坑:经典的 Kahn 拓扑排序只保证拓扑序合法(每个节点都在父节点后面),但不保证连贯性——一条干净的链可能被别的分支的节点从中间插断。 前面提过,问答对是原子单元。后端做拓扑排序时以问答对为单位,下面用字母代表:a=(Q₁,A₁), b=(Q₂,A₂),以此类推。先说清楚什么叫”干净的链”。如果一段连续的问答对序列中,每一对都恰好只有一个父、一个子(入度=1 、出度=1 ),中间没有任何分支点(出度>1 )和合并点(入度>1 ),这就是一条干净链。 下图中 b → c → d → e → f 和 h → i → j → k → l ,就是两条“干净的链“。 ┌── b → c → d → e → f ──┐ │ │ a ──┤ ├── g │ │ └── h → i → j → k → l ──┘ 节点类型: a 分支点 (出度=2 ,子节点是 b 和 h) b → c → d → e → f 干净链 (每个节点入度=1, 出度=1) h → i → j → k → l 干净链 (每个节点入度=1, 出度=1) g 合并点 (入度=2 ,父节点是 f 和 l) 干净链里的问答在语义上连贯——同一分支上的连续对话。排序时如果别的分支插进来,大模型看到的上下文就会出现话题跳跃。 普通 Kahn 算法处理完 a 之后,b 和 h 同时可用。算法不区分先后,可能先选 h ,走完旁支再回来: 普通 Kahn 排序结果: a → b → h → i → j → k → l → c → d → e → f → g ╰── b→c→d→e→f 这段干净链被旁支切断了 ──╯ 拓扑序合法——每个节点都在父节点后面,没有任何违规。但 b→c→d→e→f 这段干净链被 h→i→j→k→l 从中间切断了:b 后面接的不是 c ,而是 h 。大模型看到的对话,聊完 b 突然跳到 h 的分支,绕一圈再回来接 c——连贯线索被切碎了。 我的做法是改进 Kahn 算法,核心保证:干净链不被切断。有三条策略,按优先级依次尝试: 延续链:刚处理完一个节点,优先选它的子节点继续(前提是子节点原始入度=1 ,确认是链上节点而非合并点) 开新链:没有可延续的链时,选一个入度=1 、出度=1 的候选节点开新链 兜底:以上都不满足,选任意可用节点(按 ID 排序保证确定性) 用上面的例子走一遍: 保链排序过程: a ← 初始可用 → b → c → d → e → f ← 延续链:a 的子节点 b , 一路顺着链走到 f ↓ h → i → j → k → l ← 开新链:f 的子节点 g 不可用 (入度=2 ,l 还没处理) 退而选 h (入度=1, 出度=1 )开新链, 一路走到 l ↓ g ← 兜底:l 处理完,g 入度归零 最终结果: a → b → c → d → e → f → h → i → j → k → l → g ╰──── 干净链 ────╯ ╰──── 干净链 ────╯ 两条干净链 b→c→d→e→f 和 h→i→j→k→l 都完整保留,没有被切断。不是像普通 Kahn 那样插在 b 和 c 中间。h→l 必须出现在 g 前面,因为 g 是合并点,得等 f 和 l 都处理完才能出场,这是拓扑约束决定的。 两种算法对比: 普通 Kahn:a → b → h → i → j → k → l → c → d → e → f → g ╰── 干净链被旁支切断 ──╯ 保链排序:a → b → c → d → e → f → h → i → j → k → l → g ╰──── 所有干净链完整 ────╯ 技术栈 简单列一下: 前端:React 19 + TypeScript + Vite 后端:Python 3.14 + FastAPI ,模型服务用工厂模式,加新模型只需要继承基类加个注册 数据库:MongoDB 存消息和 DAG 关系(文档结构天然适合图),MySQL 存对话元数据 部署:Docker Compose 一键启动,或者 ./ start.sh --all 本地跑 本地模型:支持 Ollama ,没有 API Key 也能用 跟思维导图的关系 回过头来说说为什么我觉得这个项目跟思维导图很像。 思维导图的本质是一个从一个中心出发的树状结构。从一个核心概念开始,发散出几个子话题,每个子话题再继续展开。在这个过程中,你可以同时在好几个方向上思考,互不干扰,但又共享同一个中心。 DAG-chat 做的事情是一样的,只不过把”概念”换成了”对话”,把”发散”变成了”分支”,把”收敛”变成了”合并”。而且比思维导图更进一步的是——DAG 支持合并。在思维导图里,两个分支在某个节点上汇合回来是不太自然的操作,但 DAG 可以。这让整个对话结构更像一张网,而不只是一棵树。 还有一个区别:思维导图是静态的,你画完就定在那了。但 DAG-chat 的对话是活的——你随时可以从任何一个节点开新分支,随时合并,随时切换视角。它更像是一个可以实时生长的思维导图,每次交互都在扩展这张图的结构。 我觉得这种非线性的对话方式,可能更接近人真正思考问题的方式。你在脑子里探索一个问题的时候,不会是线性的——你会同时想好几个方向,然后发现其中两条路其实可以汇合。DAG-chat 就是把这个过程具象化了。 欢迎交流 哔哩哔哩: https://www.bilibili.com/video/BV16D5R6oEck?spm_id_from=333.788.videopod.episodes&vd_source=5a3410516080eb1b6d0a555d39a1ea5f GitHub 地址: https://github.com/ZM-BAD/DAG-chat 欢迎来 GitHub 看看代码,提提 issue ,或者给个 star 支持一下。 如果你也觉得线性对话这个限制挺烦的,可以试试: git clone https://github.com/ZM-BAD/DAG-chat.git cd DAG-chat cp .env.example .env # 填入 API Key ./start.sh --all 没有 API Key 也行,装个 Ollama 就能跑本地模型,完全免费: brew install ollama ollama pull qwen3:8b ollama serve # 然后启动 DAG-chat ,会自动检测本地模型
最近有一个 idea ,将 AI 对话的问答以 DAG ( Directed Acyclic Graph )的形式重新组织,提高了前端交互的体验。经过数月 Vibe Coding 以后, DAG-chat 终于达到了可以公开发布的版本。 为什么要有 DAG-chat 场景假设 假设这样一个场景,五一假期来了,我想在杭州,南京,长沙,武汉四个城市挑选一个度假。首先我和 AI 聊了一下杭州的若干知名景点。聊完了以后,我又和 AI 聊了一下南京的美食。聊完南京的美食以后,我又对杭州的交通便利度产生了好奇。 到目前为止,对杭州的讨论和南京的讨论都是独立无关的。但是如果我想回头查看杭州具体某个景点的介绍,就需要滚动屏幕好久才能找到。如果我又开启了长沙的行程攻略讨论,或者对比一下武汉和南京的美食,那么当前的对话就会变得更加混乱: 进行了多轮对话以后,如果我想查看单独关于杭州的讨论的汇总(美食,景点,交通等),那么需要不停的滚动屏幕进行查找,关于杭州的讨论并没有集中到一处,而是分散在线性的对话内容中; 在整个讨论中,有四个城市,每个城市又有美食、景点、交通、娱乐、避雷等多个维度;用户可以针对某几个城市、某些维度进行对比或者关联,如:对比武汉、南京、长沙三地的美食;计划去杭州看西湖,然后武汉游长江,最后去长沙吃湘菜,如何安排规划行程。整个对话其实呈现出高度结构化的逻辑,但是在交互上,都被拍扁放到了一个线性的问答记录中,用户的交互体验很碎裂。 痛点分析 造成这种痛点的根本原因在于:对话沟通的思维是结构化的,有发散和汇总,而绝非单独一问一答。不光是旅游规划,很多场景都如此: 高考填报志愿。在浙江大学,上海交大,中科大之间抉择。首先要了解这三所学校的专业实力,所在城市发展潜力,保研/出国政策等信息;然后进行交叉对比汇总; 技术选型。现在要构建一个高性能后端应用,是选择 Rust 还是 Golang ?这两种语言分别从性能,生态,开发难度上进行分析,然后结合团队人员的情况进行选择; 凡是满足: 一个主题 --> 发散思维 --> 对比交叉关联 --> 得出结论 这种沟通范式的,都会存在上述的痛点; 目前市面上几乎所有的 AI 问答 APP ( DeepSeek ,ChatGPT ,Claude ,Gemini ,Qwen ),问答都是以线性的形式进行组织的。如果我想从某个地方开始,往不同的方向探索,再在某一个点上把这些探索汇合起来,以思维导图的形式组织对话内容,这是无法做到的。 那么问题来了,能不能把组织对话的数据结构从链表换成图? 于是做了这个项目: DAG-chat 。 DAG-chat 效果演示 分支——从同一个回答出发,走不同的路 这是我用得最多的功能。 比如我问 AI “解释一下 Docker 的核心概念”,它给了一段回答。看完之后我脑子里冒出了两个方向:一个是想看看具体的 Dockerfile 怎么写,另一个是想了解 Docker Compose 多容器编排。 在 DAG-chat 里,我可以从同一条 AI 回复出发,分别提两个不同的问题——它们会变成两条平行的分支。界面上会出现一个标签栏,点一下就能在两条分支之间跳转。 操作也很直觉:把鼠标悬停在任何一条用户消息上,左边会出现一个分支图标,点一下,输入框里会自动引用它上面的那条 AI 回复作为上下文。你写上新的问题发出去,一条新分支就出来了。 原来的那条路径不会被覆盖。你开了三条分支,三条都在。想回去看哪条随时切,不丢任何东西。 这在做探索性对话的时候特别有用。比如我在学一个新技术的时候,通常会从概念层面先问一轮,然后针对其中感兴趣的点分别开分支深入——一个分支聊实现细节,一个分支聊最佳实践,一个分支聊常见坑。每个分支都是独立的上下文,互不干扰。最后如果我想对比不同分支里得到的信息,就用下一个功能——合并。 合并——把不同分支的答案汇总到一起 这个功能是我一开始就想做的核心需求,也是最开始的那个痛点——两个分支里的回答想放到一起做对比。 还是技术选型的例子。我在分支 A 里让 AI 分析了 Rust 的优势,在分支 B 里分析了 Go 的优势。现在我想让它做一个综合对比。 在 DAG-chat 里,我把鼠标悬停在两条 AI 回复上,分别点右边的合并图标,它们就被引用到了输入框里。然后我写上”Rust 和 Go 哪个学起来更容易?”——两条分支的上下文会一起作为这条新问题的 parent 。 合并的妙处在于:AI 在回答的时候,能同时看到不同分支的内容。它不是只看了一个片面,而是看到了你探索的全貌。此时,AI 回答的上下文,是沿着所有的 parent 一直向上到最初的提问,所形成的 sub DAG. 多模型对比 对话中间可以随时切模型。比如同一个问题,我先让 DeepSeek 回答,再切到 Qwen 回一个,两条回答各占一条分支。然后再用合并功能让 GLM 做个对比总结。 这个用法在做方案评估的时候特别好使。不同模型的知识储备和推理风格不一样——DeepSeek 可能更擅长逻辑推理,Qwen 在中文理解上有优势,Kimi 的长上下文能力比较强。把多个模型放在同一张图里对比,能比只用一个模型看到更全面的分析。 我试过拿三个模型分别 review 同一段代码,然后合并到一个节点让第四个模型做总结。这种用法在线性对话里,要么开多个对话,手动复制上下文;要么在一个对话里面,但是信息需要来回滚动查看。 除了技术选型,还能怎么用 上面举的例子偏技术开发场景,但其实 DAG 对话结构适用的范围比我想象的要广。 学习新知识。 比如你在学机器学习,问了一个”什么是梯度下降”的基础问题。AI 给了回答之后,你可以在一个分支里追问数学推导,另一个分支里要看代码实现,第三个分支里聊实际应用场景。每个分支独立深入,不会互相污染上下文。学到后面想回顾某个分支的内容,点标签就跳回去了,不用在长长的对话历史里翻找。 写作和内容创作。 我试过用它来构思文章大纲。先让 AI 给一个初始结构,然后在大纲的每个章节上开分支,分别让它展开写。不同章节的构思互不干扰,最后再用合并把几个章节的要点汇聚到一起做统一审阅。 debug 和排障。 遇到一个报错,可以让 AI 从不同方向分析:一个分支走”看日志定位问题”的路线,另一个分支走”检查配置文件”的路线,第三个分支走”搜索已知 issue”的路线。哪条路走通了就沿着哪条继续,走不通的切回去换一条,不浪费之前已经聊过的内容。 本质上,只要你的思考过程是”探索→分支→收敛”这种模式,DAG-chat 都能派上用场。 技术实现 问答对——核心概念 大模型的回答不同于即时通讯,在没有异常中断的情况下,是严格的一问一答节奏,用户提问和大模型的回答,在逻辑上构成了一个原子的,不可分割的问答对。 将这样一个问答对,定义为 DagNode ,整个对话就是由很多个 DagNode 组成的 DAG 。 每次在对话中新增提问内容,实际上就是在向这个 DAG 中,新增一个 DagNode 节点。而整个 DAG ,有且仅有一个 root 节点,即对话开始,最早的那个问答对。从任何一个 dagNode 开始向上遍历,寻找 parnet_ids ,最后都会遍历到最初的 dagNode 。 从链表到图 传统聊天的每条消息只有一个 parent_id ,指向前一条消息。我把这个字段改成了 parent_ids——一个数组。一个节点可以有零个、一个或多个父节点。 传统聊天: Message { id, content, role, parent_id } DAG-chat: DagNode { id, content, role, parent_ids[], children[] } parent_ids 是数组,所以一个用户问题可以引用多条 AI 回复作为上下文——这就是合并。children 也是数组,所以一条 AI 回复可以派生出多个追问——这就是分支。 但是每一个 role=user 的 dagNode ,children 只有一个元素;每个 role=assistant 的 dagNode ,parent_ids 也只有一个元素,这是问答对的定义决定的。 前端怎么把图展示成线性 DAG 在数据库里很自然,但屏幕是一条线。用户一次只能看到从根到叶的一条路径——就像在思维导图里,你虽然能展开所有节点,但目光的焦点在同一时刻也只能沿着一条路径走。 所以前端做的事情是:扫描 DAG 找出所有的分支点和合并点,给每个点建一个标签页容器,tabsContainer 。然后从根节点出发,沿着每个分支点当前激活的标签往下走,生成一条线性路径——这就是屏幕上展示的内容。 根据分支以及合并的特性,有两种 tabsContainer ,一种是 ChildrenTabsContainer ,用于管理分支问里面不同的分支,点击切换的时候,会改变整个渲染 path 中,到 leaf 方向的节点路径; 另一种是 ParentTabsContainer ,用于管理合并提问里面,不同的来源。点击切换的时候,会改变整个渲染 path 中,到 root 方向的途径节点; 用户点击 tab ,实际上就是在 DAG 中所有分支节点和合并节点中,选择一条 path ,从而让前端构建一条从 root 到某个 leaf 的 path 。 后端怎么把图喂给大模型 大模型的 API 只接受线性的对话历史,比如 : [{role: "user", content: "..."}, {role: "assistant", content: "..."}, ...] 但 DAG 是图结构,不能直接丢过去。 所以后端做了一件事:当用户发新消息时,从这条消息的 parent_ids 出发,BFS 往上追溯所有祖先节点,构建出一个子图( SubDAG )。然后对这个子图做拓扑排序,把它拉平成一条链。 这里有个坑:经典的 Kahn 拓扑排序只保证拓扑序合法(每个节点都在父节点后面),但不保证连贯性——一条干净的链可能被别的分支的节点从中间插断。 前面提过,问答对是原子单元。后端做拓扑排序时以问答对为单位,下面用字母代表:a=(Q₁,A₁), b=(Q₂,A₂),以此类推。先说清楚什么叫”干净的链”。如果一段连续的问答对序列中,每一对都恰好只有一个父、一个子(入度=1 、出度=1 ),中间没有任何分支点(出度>1 )和合并点(入度>1 ),这就是一条干净链。 下图中 b → c → d → e → f 和 h → i → j → k → l ,就是两条“干净的链“。 ┌── b → c → d → e → f ──┐ │ │ a ──┤ ├── g │ │ └── h → i → j → k → l ──┘ 节点类型: a 分支点 (出度=2 ,子节点是 b 和 h) b → c → d → e → f 干净链 (每个节点入度=1, 出度=1) h → i → j → k → l 干净链 (每个节点入度=1, 出度=1) g 合并点 (入度=2 ,父节点是 f 和 l) 干净链里的问答在语义上连贯——同一分支上的连续对话。排序时如果别的分支插进来,大模型看到的上下文就会出现话题跳跃。 普通 Kahn 算法处理完 a 之后,b 和 h 同时可用。算法不区分先后,可能先选 h ,走完旁支再回来: 普通 Kahn 排序结果: a → b → h → i → j → k → l → c → d → e → f → g ╰── b→c→d→e→f 这段干净链被旁支切断了 ──╯ 拓扑序合法——每个节点都在父节点后面,没有任何违规。但 b→c→d→e→f 这段干净链被 h→i→j→k→l 从中间切断了:b 后面接的不是 c ,而是 h 。大模型看到的对话,聊完 b 突然跳到 h 的分支,绕一圈再回来接 c——连贯线索被切碎了。 我的做法是改进 Kahn 算法,核心保证:干净链不被切断。有三条策略,按优先级依次尝试: 延续链:刚处理完一个节点,优先选它的子节点继续(前提是子节点原始入度=1 ,确认是链上节点而非合并点) 开新链:没有可延续的链时,选一个入度=1 、出度=1 的候选节点开新链 兜底:以上都不满足,选任意可用节点(按 ID 排序保证确定性) 用上面的例子走一遍: 保链排序过程: a ← 初始可用 → b → c → d → e → f ← 延续链:a 的子节点 b , 一路顺着链走到 f ↓ h → i → j → k → l ← 开新链:f 的子节点 g 不可用 (入度=2 ,l 还没处理) 退而选 h (入度=1, 出度=1 )开新链, 一路走到 l ↓ g ← 兜底:l 处理完,g 入度归零 最终结果: a → b → c → d → e → f → h → i → j → k → l → g ╰──── 干净链 ────╯ ╰──── 干净链 ────╯ 两条干净链 b→c→d→e→f 和 h→i→j→k→l 都完整保留,没有被切断。不是像普通 Kahn 那样插在 b 和 c 中间。h→l 必须出现在 g 前面,因为 g 是合并点,得等 f 和 l 都处理完才能出场,这是拓扑约束决定的。 两种算法对比: 普通 Kahn:a → b → h → i → j → k → l → c → d → e → f → g ╰── 干净链被旁支切断 ──╯ 保链排序:a → b → c → d → e → f → h → i → j → k → l → g ╰──── 所有干净链完整 ────╯ 技术栈 简单列一下: 前端:React 19 + TypeScript + Vite 后端:Python 3.14 + FastAPI ,模型服务用工厂模式,加新模型只需要继承基类加个注册 数据库:MongoDB 存消息和 DAG 关系(文档结构天然适合图),MySQL 存对话元数据 部署:Docker Compose 一键启动,或者 ./ start.sh --all 本地跑 本地模型:支持 Ollama ,没有 API Key 也能用 跟思维导图的关系 回过头来说说为什么我觉得这个项目跟思维导图很像。 思维导图的本质是一个从一个中心出发的树状结构。从一个核心概念开始,发散出几个子话题,每个子话题再继续展开。在这个过程中,你可以同时在好几个方向上思考,互不干扰,但又共享同一个中心。 DAG-chat 做的事情是一样的,只不过把”概念”换成了”对话”,把”发散”变成了”分支”,把”收敛”变成了”合并”。而且比思维导图更进一步的是——DAG 支持合并。在思维导图里,两个分支在某个节点上汇合回来是不太自然的操作,但 DAG 可以。这让整个对话结构更像一张网,而不只是一棵树。 还有一个区别:思维导图是静态的,你画完就定在那了。但 DAG-chat 的对话是活的——你随时可以从任何一个节点开新分支,随时合并,随时切换视角。它更像是一个可以实时生长的思维导图,每次交互都在扩展这张图的结构。 我觉得这种非线性的对话方式,可能更接近人真正思考问题的方式。你在脑子里探索一个问题的时候,不会是线性的——你会同时想好几个方向,然后发现其中两条路其实可以汇合。DAG-chat 就是把这个过程具象化了。 欢迎交流 哔哩哔哩: https://www.bilibili.com/video/BV16D5R6oEck?spm_id_from=333.788.videopod.episodes&vd_source=5a3410516080eb1b6d0a555d39a1ea5f GitHub 地址: https://github.com/ZM-BAD/DAG-chat 欢迎来 GitHub 看看代码,提提 issue ,或者给个 star 支持一下。 如果你也觉得线性对话这个限制挺烦的,可以试试: git clone https://github.com/ZM-BAD/DAG-chat.git cd DAG-chat cp .env.example .env # 填入 API Key ./start.sh --all 没有 API Key 也行,装个 Ollama 就能跑本地模型,完全免费: brew install ollama ollama pull qwen3:8b ollama serve # 然后启动 DAG-chat ,会自动检测本地模型
前几天在b站刷边亮的视频看到的,想请教下这种功能是怎么实现的? 1 个帖子 - 1 位参与者 阅读完整话题
https://www.producthunt.com/products/agent-powered-geo-by-dageno 大家好,我们的出海 GEO 平台 - Dageno AI ,今天正式在 Product Hunt 上线了 👆 诚邀大家来支持我们一票!💪作为感谢,可以联系我们免费领取 2 个额外的 LLMs 数据 track ! 过去几年,我们团队一直在深耕数据行业,也在给各大海外 GEO 头部工具、SEO 工具和 agency 持续提供数据源。现在,我们把这套能力落地为数据驱动的 GEO 平台,和大家一起推动 GEO 走向真正的标准化,帮助更多出海品牌实现增长: 1️⃣我们基于大模型的前端问答真实数据,大规模并发分析,帮助品牌追踪 AI 可见度、洞察机会点,和使用 API 或者中转接口来分析的平台相比,更有可信度; 2️⃣覆盖全球所有主流地区,7+ 主流 LLM ,包括 chatgpt, perplexity, gemini, grok 和 google ai 系列等; 3️⃣通过 Agent 执行机会洞察,撰写内容,并协助分发,真正把“发现机会”变成“拿到增长”; 4️⃣我们拥有大量数据样本,包括各类提示词的全球真实搜索量,能够更真实地反映市场需求和潜在流量价值,而不是经验猜测或者 AI 编造。 目前产品支持至少 7 天免费试用,同时我们也提供免费的出海 GEO 诊断分析和优化建议,欢迎一起交流增长机会!