在时时使用多个 Codex/Copilot/Claude/Qoder 编写代码之后,我第一次明显感觉到,整个产品推进的节奏都被拉快了。
这个变化不是凭空来的。在 Routa 里,一个 issue 很快就会变成 Spec,后面很快又接上 session、代码和验证结果。局部推进变快之后,团队真正缺的是一个能回头解释演化过程的入口。
第二天再看某个功能时,难回答的往往已经不是“做完没有”,而在于另一件事:
它为什么会长成今天这样?
过去在 Spec Driven Development 里,Spec 大多还能承担需求中心的角色。现在不一样了。Spec 仍然负责边界、约束和验收,但大量真正改变实现走向的内容, 已经留在 Codex、Claude Code、Qoder 这些 Agent 的 session 历史里:一次失败的尝试,一个临时收掉的交互,一次 API 结构调整,一组被反复读写的文件。功能的演化现场还在,只是它不再集中保存在那份最初的文档里。
所以我后来关心的事情也跟着变了:session 里的局部过程能不能重新推回当前功能,Spec、页面、API 和实现文件的关系能不能稳定下来,Agent 能不能先面对一份围绕 Feature 收拢过的证据,再去做分析。问题如果回答不了,Spec 再完整,功能还是会越做越难回看。
这篇文章想讨论的,就是这件事:当 Spec 还在负责需求入口,真正的实现过程却已经散到 session 历史里之后,系统要靠什么重新把这段演化接回来。
Spec 在今天已经成为了 AI Coding 的主入口。它负责把需求变成一个可执行的任务边界,让 Agent 知道什么该做,什么不该做,做到什么程度才算完成。
特别是,当实现速度被 Agent 拉高之后,Spec 的地位其实更重要了。边界、约束、验收条件如果没有提前写清楚,后面的 session 很容易直接围绕局部实现一路推进,最后得到一个“能跑”的结果,却不一定符合产品意图。
问题出在另一层。Spec 负责定义起点,却不负责保存演化过程。一个 Feature 真正落地时,会经历大量不会被完整回写进 Spec 的变化。某个交互为什么收掉了,某个 API 为什么临时改了返回结构,某个文件为什么被反复重写,某个方向为什么最后没有进入正式实现,这些信息通常会落在 issue comment、临时验证、失败尝试,或者某一次 Agent session 里。
最后留在 Spec 里的,往往是一个相对干净、可执行、可验收的版本;真正驱动实现走向的过程,已经散落到后续历史中。
问题因此换了一个方向:“Spec 之后已经发生过的那段演化过程,能不能被重新恢复出来”。
如果只看本地 ~/.codex/sessions,你会发现 Agent 历史里其实已经保存了大量高价值信息。
我随手翻过一条 2026/04/15 的 Codex session,里面至少同时出现了四层内容:session_meta 记录工作目录和运行环境,
function_call 记录它读了哪些文件、跑了哪些命令,agent_message 记录阶段性判断,最后 task_complete 留下本轮结论。像其中一段,Codex
在 routa-js 里一路定位 acp-process.ts 和 tauri-bridge.ts,最后收敛到一个非常具体的判断:ready 先等待、后绑定流监听,会丢失早期
NDJSON 帧。这种记录后面很有用,因为当时的过程还在。
但 Session 的问题也同样明显。它天然是按“某一次会话”组织的,不是按“某个产品能力”组织的。排查单次过程时,它很好用;追踪一个 Feature 的长期演化时,它就不够用了。因为真正需要优化的对象,通常不是某个 session,而是一个页面、一组 API、一棵功能树里的某个节点,或者一组被同一类需求反复改动的文件。
Session 在这里更像是一层证据。它把过程留了下来,但没有自动帮你把过程长成产品结构。
Spec 负责定义起点,Session 负责记录演化。真正缺的是,系统能不能围绕当前功能,把这两层历史重新组织成一个可操作的入口。
这也是我更愿意把它叫作 Feature Explorer,而不是 Session 管理页面的原因。Session 页面先回答“我有哪些历史”;Feature Explorer 先回答“当前这个功能背后,哪些历史值得重新拿回来”。这两个问题看起来很像,工程意义却完全不同。前者容易把系统推成日志浏览器, 后者才会逼着系统围绕页面、API、文件和实现边界去归并历史。
Feature Explorer 真正重要的,不是它多了一个新界面,而是它先把恢复单位从“某次会话”切换成了“当前功能涉及的一组页面、API 和文件”。在 Routa 里, 这条设计线已经比较清楚了,不是一页页面先出来,后面再慢慢补数据,而是几层东西已经接在一起:
FEATURE_TREE.md 先把功能边界写出来;feature-tree.index.json 把它变成可索引的结构;/api/feature-explorer 再把这套结构接到页面和运行时能力上。对应到接口这一层,系统会继续做几件事:
FEATURE_TREE 里的 frontmatter;/api/spec/surface-index 提供的结构化结果;pages、apis、sourceFiles、relatedFeatures、domainObjects 这些字段。先把当前功能的边界定下来,后面的历史才接得回来;不然 transcript 再多,也还是散的。而且这条链现在也不是 Web 端临时拼出来的一页。Next.js 和 Rust/Axum 两边都在返回一组相近的数据结构,Feature Explorer 在 Routa 里已经是按正式能力在建。
这样一来,路径也自然反过来了:先进入当前功能,再往下看相关页面、API、文件和历史活动;不用先翻 transcript,再倒着猜它和哪个功能有关。
当功能边界稳定下来之后,下一步才是把散落的历史重新收拢。
这里真正要做的,不是简单把多个 session 摆在一起,而是把与当前功能有关的读写、修改、尝试、失败和收敛过程重新组织成一份可以检查的证据。 这样一来,Session 才第一次从“历史记录”变成“工作上下文”。
它不再只是一个 transcript 链接,而是一组围绕当前功能重新整理过的痕迹:哪些文件被持续触达,哪些操作被反复尝试,哪些问题反复出现, 哪些改动最终真正沉淀在这个功能上。
这一步的意义,在于把历史从“能保存”推进到“能归因”,再从“能归因”推进到“能检查”。系统开始知道,不只是发生过什么,而是这些发生过的事情, 到底和当前功能有什么关系。
到了这里,就可以让 Agent 来发布他的作用。
我一直觉得,Agent 不应该承担第一层上下文整理。第一层应该由系统先完成:把当前功能的边界定下来,再把相关历史收拢成证据。Agent 更适合站在第二层,去解释这些证据说明了什么,以及下一轮该怎样继续问、继续改、继续验证。
当前实现里,feature-explorer-page-client.tsx 在触发分析时,会先调用 buildSessionAnalysisPrompt(),把选中文件、相关
session、工具调用诊断和 repo 上下文重新组织成 prompt,再启动 specialistId: "file-session-analyst" 的新 session。
session-analysis.ts 也不是把 transcript 原样拼进去,而是先过滤噪声 changed files,区分 direct read / direct write /
related files,再提炼 repeated reads、failed tools、重复命令这类真正影响下一轮工作的摩擦信号。
这样一来,Agent 消费的就不再是一长串原始 transcript,而是一份围绕当前功能重组过的工程证据。分析的重点也随之变化:不再是“帮我总结一下历史”, 而是“这些历史暴露了哪些瓶颈,下一轮应该怎样更准确地推进”。
到了这里,Feature Explorer 才真正成立。它关注的已经不是历史本身,而是当前这个功能是否还能被继续理解、继续优化、继续演化。
Agentic Coding 把局部效率提得太高了。高到如果系统没有一个更高层的产品视角,需求虽然会更快完成,项目却更容易失去整体性。你会得到越来越多已经解决的 issue,越来越多已经结束的 session,但它们未必会自然长成一个可持续演化的产品结构。
围观我的Github Idea墙, 也许,你会遇到心仪的项目