Flow2Spec Routes Project Knowledge So AI Agents Stop Asking the Same Questions
How to Make AI Agents Read Project Knowledge on Demand: The Routing Design Practice of Flow2Spec
After AI programming tools enter real projects, a seemingly contradictory situation often arises: the model can complete complex coding, yet repeatedly asks about basic facts within the project.
For example, whether a certain interface allows retries, what idempotency key a batch processing task uses, or which module maintains a status field. These questions have usually already appeared in the code or documentation, but a new session doesn't know why the previous implementation was done that way and can only search the repository again.
The most direct solution is to write all the rules into AGENTS.md or CLAUDE.md. When the project is small, this approach is simple and effective; as the project continues to evolve, the single file becomes longer and longer. The Agent loads a large amount of irrelevant information each time, not only consuming context but also potentially ignoring truly important constraints.
When designing Flow2Spec, we redefined the problem as:
Project knowledge should not just be saved; it should also be routable by task, expandable by dependencies, checkable for gaps, and re-verifiable after code changes.
This article does not introduce all features, but only discusses why this knowledge routing is designed this way and how to handle semantic conflicts when multiple people modify knowledge.
First, Define Three Design Goals
Before implementation, we set three goals for the knowledge layer.
First, an Agent should not need to read the entire project documentation for a payment requirement. It needs a low-cost entry point to first narrow down the candidate scope.
Second, hitting a business rule does not mean the context is complete. A payment topic might depend on account risk control, order boundaries, and unified error codes; dependencies need to be explicitly expressed.
Third, knowledge changes with the code. The knowledge layer must be able to enter Git diff and Code Review, rather than becoming an external black box whose source cannot be traced.
Based on these constraints, we ultimately adopted a layered structure within the repository:
.Knowledge/
├── manifest-routing.json # L0: Routing Index
├── matchers/ # L1: Keyword Shards
├── topics/ # L2: Topic Summaries and Hard Constraints
├── stock-docs/ # L3: Stable Architecture and Capability Docs
└── req-docs/ # L3: Specific Requirements and Technical Plans
The key here is not the directory names, but the different responsibilities each layer undertakes.
| Layer | Content Stored | How the Agent Reads It |
|---|---|---|
| L0 Routing Index | task, topic paths, dependencies, and metadata | The session first reads the machine-readable entry |
| L1 Match Shards | A set of trigger words related to the task | Only opens potentially matching shards |
| L2 Topic Summaries | Boundaries, hard constraints, and drill-down entries | Reads and expands dependencies after a hit |
| L3 Long Docs | Final architecture drafts, requirement plans, and complete background | Reads on demand when information is insufficient |
The purpose of this is to resolve common questions as much as possible at L0 to L2, and only continue reading long documents or source code when an information gap is encountered.
Why the Routing Entry Needs to Be a Manifest
manifest-routing.json is the machine-readable entry point for the knowledge layer. Below is a simplified structure:
{
"topicPaths": {
"flow2spec-collaboration": ".Knowledge/topics/flow2spec-collaboration.md",
"f2s-task": ".Knowledge/topics/f2s-task.md"
},
"topicDependencies": {
"flow2spec-collaboration": ["f2s-task"]
},
"taskToTopicRules": [
{
"task": "flow2spec-collaboration",
"matcherId": "m-flow2spec-collaboration",
"matcherPath": ".Knowledge/matchers/m-flow2spec-collaboration.json",
"topics": ["flow2spec-collaboration"]
}
]
}
This contains three types of relationships:
taskToTopicRulesis responsible for finding the matcher and candidate topics from a task;topicPathsis responsible for finding the actual file from a topic id;topicDependenciesis responsible for declaring which prerequisite topics need to be completed before hitting a topic.
The routing index only stores relationships, not stuffing all keywords directly into one large JSON. Keywords are split into independent matchers:
{
"id": "m-flow2spec-collaboration",
"schema": "flow2spec.matcher.v1",
"includeAny": [
"team collaboration",
"multi-person shared knowledge base",
"developerId isolation",
"kb-delta",
"topic revision",
"revision conflict",
"optimistic lock"
]
}
The benefit of sharding is that when modifying local rules, other routing files do not produce meaningless diffs. The Agent also does not need to read all keywords at once, only needing to open the relevant shard via matcherPath.
This is not a replacement for semantic retrieval, but a more deterministic in-repo protocol. It sacrifices some fuzzy recall capability in exchange for readable, reviewable, and predictable routing results. For constraints like permissions, idempotency, and data boundaries that cannot rely on "roughly similar hits," this trade-off is more appropriate.
Cannot Act Directly After Match
Just hitting a topic can easily create false certainty.
Suppose a user says: "When two people update the same business topic simultaneously, will the knowledge base overwrite the content?" The matcher can hit the collaboration topic, but before truly answering, it still needs to confirm:
- Whether the current topic covers concurrent writes;
- Whether it needs to first read the ownership rules for task states;
- Whether the user is asking about Git text conflicts or business semantic conflicts;
- Whether the description in the topic is still consistent with the current implementation.
Therefore, the complete pipeline is broken down into four steps:
match → expand → verify → act
Match: Narrow Down Candidate Scope
Read the corresponding matcher based on the user's task to get the primary candidate topic, rather than traversing all knowledge files.
Expand: Expand Dependencies
Read the dependencies declared by the primary topic. For example, the collaboration topic depends on the task topic, because before merging knowledge, one must know where the current developer's TASK_ROOT is.
Verify: Check for Gaps
Determine whether the existing knowledge truly covers the question. When coverage is insufficient, continue reading stock-docs/, req-docs/, or source code; when the requirement itself is unclear, first confirm with the user.
Act: Execute the Task
Only enter answering, modifying code, or committing changes after dependencies are complete and key facts are confirmed.
verify is the most easily omitted, yet most important step in this chain. Retrieval solves "possibly relevant"; gap checking solves "is it sufficient to act."
Why Topics Only Store Streamlined Facts
The goal of a topic is not to replicate complete documentation, but to provide the Agent with the most commonly used hard constraints and drill-down entry points. The frontmatter of a real topic looks something like this:
---
id: flow2spec-collaboration
revision: 0
summary: Local isolation of task state, shared knowledge delta merging, and revision conflict handling
dependsOn: [f2s-task]
primary: feature
confidence: manual
tags: [policy]
---
The body only retains collaboration boundaries, knowledge merging rules, team observation surfaces, and long document paths. This keeps the topic short enough to combine multiple topics in a single task; the full explanation remains in the long documents.
There is also an easy pitfall here: summaries must not be written as promotional copy. Descriptions like "powerful, intelligent, efficient" are not helpful for routing. More effective summaries should directly describe facts, scope of application, and limitations.
How Knowledge Is Written Back After Development
Read-only knowledge is not enough. During implementation, the Agent often confirms new constraints from the source code, such as refunds can only be returned via the original route, or a certain lock's TTL is 10 minutes. If these facts only remain in the current session, they will need to be searched again next time.
Directly letting the Agent modify the topic is simple, but two problems arise during multi-person collaboration:
- Git can only see the text change, not the intent of this modification;
- Two text segments can be auto-merged, but that doesn't mean the two business rules are semantically compatible.
Therefore, knowledge changes are first written as structured kb-delta.json:
{
"taskId": "add-payment-rule",
"developerId": "alice",
"baseRevisions": {
"payment-rules": 3
},
"changes": [
{
"type": "appendBody",
"targetTopic": "payment-rules",
"summary": "Supplement refund time limit",
"content": "## Refund Time Limit\n\nRefunds will be returned via the original route within 3 working days after approval."
}
]
}
The delta currently only allows four actions:
| Type | Meaning |
|---|---|
appendBody |
Append content to the end of an existing topic |
replaceBody |
Replace the body of a topic |
updateFrontmatter |
Update topic metadata |
createTopic |
Create a topic, and establish matcher and routing as needed |
The action whitelist allows the CLI to validate the target, fields, and version before writing to disk, and also makes it easier for Code Review to understand the intent of this knowledge change.
Using Topic Revision to Prevent Stale Writes
baseRevisions records the topic version seen when the delta was generated. When executing plan, the CLI compares it with the revision on disk:
baseRevision == diskRevision → Can apply, revision +1 after writing
baseRevision != diskRevision → Stop, require re-reading the latest content
For example, both Alice and Bob start modifying based on payment-rules revision: 3. After Alice merges first, the disk version becomes 4. When Bob's delta executes plan, it will get a revision mismatch instead of continuing to write.
Bob must now read the body of revision 4 and then judge whether the two rules should coexist, be rewritten, or one should be discarded. Automatic text concatenation was deliberately not done here, because "both paragraphs can be inserted" and "the two business conclusions do not contradict each other" are two different things.
Revision is a disk optimistic lock, not a remote distributed lock. It has clear boundaries:
- It can only protect knowledge changes submitted through the delta channel;
- It does not know about remote commits that teammates have not yet pulled;
- Directly hand-editing a topic will bypass the revision pre-check;
- Normal Git pull, branch synchronization, and Code Review are still indispensable.
This mechanism does not eliminate conflicts, but tries to bring conflicts forward to the plan stage and leave the semantic judgment to the person who holds the business context.
Why Task State and Project Knowledge Need to Be Separated
When multiple people use Agents, two types of state will appear in the repository simultaneously:
- "What step has this session reached" belongs to the individual process;
- "What constraints does the system currently have" belongs to the team facts.
If both are committed to Git, personal checklists, temporary judgments, and to-do items will frequently conflict. Conversely, if both are kept locally, verified business knowledge cannot be shared.
The boundary we adopted is:
.task/<developerId>/ Local task workspace, not in Git by default
.Knowledge/ Team project facts, enter Git with the code
.task/ saves checklists, session context, and the current round's delta, used for continuing work across sessions; .Knowledge/ only receives confirmed facts. Team progress is still observed through PRs, commits, issues, and milestones, without synchronizing individual Agent sessions into another project management system.
A Minimal Initialization Test
To confirm that this structure does not only exist in documentation, I executed Codex initialization in an empty Git repository:
npx @double-coding/flow2spec@latest init codex --locale zh-CN --yes
npx @double-coding/flow2spec@latest doctor
The initialization generated .Knowledge/, .codex/, the root AGENTS.md, and flow2spec.config.json, while also adding .task/ to .gitignore.
The doctor check result was 8 passed, 0 warnings, 0 errors, covering:
- Node.js version;
- Project configuration;
- Agent entry;
- Knowledge base entry;
- Codex configuration integrity;
- developerId and
TASK_ROOT; .task/ignore rules;- Topic validation and routing drift.
This only shows that the initialization chain and basic structure work normally, not that the routing quality is proven. Whether the routing is truly effective still depends on whether the team writes topics as clear facts, whether matchers cover real expressions, and whether knowledge is synchronized in time after code changes.
Several Limitations in Actual Use
This solution is not zero-cost.
First, matchers use explicit trigger words, making results easy to explain, but their recall ability for expressions the team has never anticipated is limited. When necessary, one must still rely on regular code search or other retrieval methods as a fallback.
Second, the larger the topic, the larger the surface for revision conflicts during concurrent modification; if topics are split too finely, dependency relationships become complex. A more feasible standard is to let a topic revolve around a set of stable, independently assessable business constraints, rather than mechanically splitting by file count.
Third, the correctness of knowledge ultimately still comes from code, tests, and human confirmation. confidence, revision, and verification commands can only help manage knowledge, not turn unverified inferences into facts.
Finally, small projects may not need this structure. For one-off scripts or personal projects with only a few files, a concise rules file is usually more direct. Only when repeated searches, context drift, and multi-person knowledge conflicts begin to incur significant costs does layered routing become worth maintaining.
Summary
Letting AI understand a project is not the same as stuffing more text into the context. The more critical questions are: how to find relevant facts, how to complete dependencies, how to judge if the information is sufficient, and how to safely write back when facts change.
Flow2Spec's current answer can be summarized in three points:
- Use manifest, matcher, topic, and long documents to form a progressive knowledge layer;
- Use
match → expand → verify → actto place gap checking before execution; - Use structured deltas and topic revision to manage multi-person knowledge changes.
This design still needs to be validated in more real projects, especially regarding the long-term maintenance cost of matchers, topic granularity, and cross-branch semantic conflicts. But at least one point is already relatively clear: project context cannot just be treated as a prompt; it should become an engineering asset that can be version-controlled and continuously maintained.
The complete implementation and schema in the text can be viewed in the Flow2Spec repository.
Top 1 from juejin.cn, machine-translated. The original thread is authoritative.
Really easy to use [shy]