# loom-langgraph **Repository Path**: rslai/loom-langgraph ## Basic Information - **Project Name**: loom-langgraph - **Description**: LangGraph 实现的 loom 版本 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-05 - **Last Updated**: 2026-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Loom 知识库的 LangGraph Agent 把 `loom-concept.md` 定义的四个操作(ingest / derive / release / lint)+ query 固化成 **LangGraph 状态图**,得到一个比自由对话 Agent 更可控、可断点续跑的程序: - **确定性节点做脏活**:文件操作、归档 grep 检索、稳定 ID 分配、交叉引用/追溯校验、index/log 记账全部是代码,不靠模型自觉。 - **LLM 只做内容生成**:提炼 UR、派生 PR/TC、出检索关键词,全部走 pydantic 结构化输出,不合 schema 会被拦下重试;新条目 ID 由程序分配,引用已有 ID 会做存在性校验(防幻觉 ID)。 - **人工关卡内置于图**:用 LangGraph `interrupt()` 实现,对应 loom-concept.md 装配的三个 superpowers 强制关卡(见下表),不依赖插件、不可绕过(留「跳过」豁免口,记入 log)。 - **断点续跑**:图状态经 SQLite checkpoint 持久化(`<项目>/.loom/checkpoints.sqlite`)。进程被杀、网络断掉后,用 `--resume ` 原地继续,已完成的节点不重跑。 | loom-concept.md 的关卡 | 本程序中的实现 | |---|---| | `superpowers:brainstorming`(ingest 写 UR 前) | ingest 图的 clarify ⇄ ask_user 循环:不对齐动机到不了起草节点 | | `superpowers:writing-plans`(derive 落 PR 草稿前) | derive 图的 plan_gate:派生计划未经你输入 ok 不落草稿 | | `superpowers:verification-before-completion`(release 前) | release 图的 verify_gate:程序算出证据报告,必须输入「确认发版」才归档 | | derive 后强制人工签字 | derive 图的 pr_signoff / tc_review 关卡 | | 冲突阻塞发版 | release 图的 check 节点:草稿含冲突标记直接中止 | ## 安装 ```bash cd <工作区目录> pip install -e code ``` 依赖:Python ≥ 3.10、langgraph、langgraph-checkpoint-sqlite、langchain-openai、langchain-core、pydantic、PyYAML(`pip install -e` 会自动装)。 ## 配置 模型走 **OpenAI 兼容协议**(`langchain_openai.ChatOpenAI`),配置读取知识库项目 `config.yaml` 的 `loom:` 节(与 loom-cli 共用同一节): ```yaml loom: model: "deepseek-v4-flash" # 该端点上的模型名(也可用 loom --model <名> 临时覆盖) baseUrl: "https://api.deepseek.com/v1" # OpenAI 兼容端点;用 OpenAI 官方则删掉本行 apiKeyEnv: "API_KEY" # 从这个环境变量读 API key # authTokenEnv: "MY_GATEWAY_TOKEN" # 部分网关用 Bearer token 而非 key,与 apiKeyEnv 二选一 # requireSuperpowers: false # superpowers 硬前置开关(仅 loom-cli 读取,loom-langgraph 忽略):缺省 true,false 则豁免 ``` 运行前先导出对应环境变量: ```bash export API_KEY=<你的 key> ``` release 的测试用例归档目录读取同文件的 `release.testCasesMergeDir`;导入统一测试平台复用工作区 `tools/md_to_testcases_xls` 与 `tools/import_testcases`(读 `importTestcases:` 节)。 ## 运行前提 - **运行位置 / 根目录定位**:在知识库目录下运行,或加 `--project <路径>`。程序从当前目录逐级向上查找**同时含 `config.yaml` 与 `docs/`** 的目录作为项目根;两者都找不到则报错退出。 - **`iterations/_TEMPLATE` 是硬依赖**:目标迭代目录不存在时,程序复制 `_TEMPLATE` 创建它;模板缺失会直接 `SystemExit`(见 `code/loom/kb.py`)。从 `_TEMPLATE` 拉起知识库时务必保留该目录。 - **`tools/` 是可选的**:按约定在知识库的**父目录(工作区)**下找 `tools/`;不存在只是跳过 release 的测试平台导入,不影响 ingest / derive / lint / query 及归档本身。 - **环境变量**:运行前导出 `config.yaml` 里 `apiKeyEnv`(或 `authTokenEnv`)指定的变量(如 `API_KEY`);仅 `loom lint --no-llm` 不需要 key。 ## 使用 所有命令**在知识库目录(如 `ydkt/`)下运行**,或加 `--project <路径>` 指定。每次运行会打印一个 thread id;中断后执行 `loom --resume <原命令>` 续跑。 ### 1. ingest —— 收集本次迭代用户需求 ```bash mkdir -p iterations/2026-W28 # 或让程序自动复制 _TEMPLATE # 先把本次原始素材(.md/.txt)放进 iterations/2026-W28/raw_urd/ loom ingest 2026-W28 ``` 流程:检查原始素材就位 → LLM 出关键词、程序 grep `docs/urd` 归档 → **brainstorming 关卡**(终端问答对齐动机,输入 `跳过` 可豁免)→ 起草 UR → **评审关卡**(输入 `ok` 写入 / 输入修改意见返修 / `stop` 放弃)→ 写入 `iterations/2026-W28/urd/`,归档参考清单与问答记录进 `notes.md`,更新 index/log。 ### 2. derive —— 派生 PR,人工评审后派生 TC ```bash loom derive 2026-W28 # 完整流程:PR → 签字 → TC loom derive 2026-W28 --phase pr # 只派生 PR(评审后再跑 tc) loom derive 2026-W28 --phase tc # 只派生 TC(会从磁盘重读你改过的 PR 草稿) ``` 流程:读本次 UR → grep `docs/prd`(跨层参考 urd/test_cases)→ **派生计划关卡**(UR→模块映射、增/改/废、冲突点;`ok` 认可才落草稿)→ 写 `iterations/<迭代>/prd/` → **人工评审关卡**(可直接编辑草稿文件,`ok` 继续)→ grep `docs/test_cases` 全库(含未进 index 的存量/导入用例)→ 派生 TC(双向规则:未改动步骤承接历史细节、冲突步骤按新需求改写)→ **评审关卡** → 写 `iterations/<迭代>/test_cases/`,报告尚无覆盖的 PR。 ### 3. release —— 发版合并 ```bash loom release 2026-W28 ``` 流程:冲突标记检查(有未裁决冲突直接阻塞)→ 在「主干 + 本次 delta」合并视图上核验(死链、需求落空、覆盖缺口、孤儿测试)→ **核验关卡**(必须输入 `确认发版`,其余任何输入中止且草稿保留)→ 四层草稿归档进主干(test_cases 归档到 `release.testCasesMergeDir` 指定目录)→ 刷新 `traceability.md` 与 index/log → 询问是否导入统一测试平台(md→xls→上传;失败则保留草稿)→ **确认删除关卡**(输入 `delete` 才删迭代草稿,其余保留)。 ### 4. lint —— 全库体检 ```bash loom lint # 缺口 + LLM 行动建议 loom lint --no-llm # 只要程序算出的缺口(不需要 API key) ``` 报告:需求落空 / 覆盖缺口 / 孤儿测试 / 死链 / 未解决冲突标记 / 未进 index 的页面,并写一条 log。 ### 5. query —— 对知识库提问 ```bash loom query "认证模块现在有哪些需求,测试覆盖全吗?" ``` LLM 出关键词 → 程序 grep 全库 → 带引用作答,附检索清单。 ## 工作区结构 整个工作区分三块:`code/`(本程序)、`tools/`(测试平台导入导出脚本)、`ydkt/`(一个知识库项目实例)。`tools/` 各脚本共用项目根 `config.yaml`,每个工具只读属于自己的那一节;`ydkt/` 是「用户需求 → 产品需求 → 测试用例」三层知识库的活例子,可直接照抄初始化新库。 ``` loom-langgraph/ # 工作区根 ├── README.md # 本文件:工作区总览 + 安装 / 配置 / 使用 ├── code/ # 本程序(pip install -e code 读它) │ ├── pyproject.toml # 包定义与依赖 │ └── loom/ │ ├── __init__.py # 包入口 │ ├── __main__.py # python -m loom 入口(转 cli.main) │ ├── cli.py # 命令行入口:interrupt 交互循环 + --resume 续跑 │ ├── config.py # 定位项目根、读 config.yaml(loom: / release: 节) │ ├── kb.py # 知识库文件视图:迭代目录、稳定 ID、UR/PR/TC 渲染 │ ├── search.py # 归档多关键词全文 grep + 归档参考清单 │ ├── trace.py # UR→PR→TC 实体解析、追溯矩阵、缺口/死链/冲突检测 │ ├── indexlog.py # index.md / log.md 记账 │ ├── llm.py # LLM 客户端(OpenAI 兼容端点)+ 结构化输出重试 │ ├── schemas.py # 各 LLM 节点的 pydantic 输出模型 │ ├── prompts.py # 提示词(CLI 的规则事实源,运行不依赖 CLAUDE.md) │ ├── ui.py # 终端进度输出:节点叙述 / 命令回显 / 动态状态行 │ └── graphs/ # 五张状态图 + 公共设施 │ ├── __init__.py # 导出各图的 build_*_graph 工厂 │ ├── common.py # 运行上下文、interrupt 人工关卡助手、进度输出 │ ├── ingest.py # 收集用户需求:search → clarify⇄ask_user → draft⇄review → write │ ├── derive.py # 派生 PR→评审→TC:plan_gate / pr_signoff / tc_review 关卡 │ ├── release.py # 发版合并(全程无 LLM):check → verify_gate → archive → import → delete_gate │ ├── lint.py # 全库体检:代码算缺口,LLM 给建议(--no-llm 可关) │ └── query.py # 带引用作答:出关键词 → grep 全库 → 流式作答 │ ├── tools/ # 测试平台脚本(共用项目根 config.yaml,各读自己那节) │ ├── config.example.yaml # 项目级工具配置模板:复制到 <项目>/config.yaml 再按项目填写 │ ├── md_to_testcases_xls/ # 把 test_cases/ 的 md 用例转成导入用 .xls(打包 zip) │ │ ├── md_to_testcases_xls.py │ │ ├── requirements.txt │ │ └── README.md │ ├── import_testcases/ # 上传 Excel/zip → 触发导入 → 轮询状态(读 importTestcases: 节) │ │ ├── import_testcases.py │ │ └── requirements.txt │ └── export_testcases/ # 从统一测试平台批量导出用例并解压落盘(读对应节) │ ├── export_testcases.py │ └── requirements.txt │ └── ydkt/ # 知识库项目实例(活例子,可照抄初始化新库) ├── index.md # 目录:每页一行,人浏览 + AI 定位入口 ├── log.md # 流水账:每次操作追加一条 ├── traceability.md # 追溯矩阵:每次发版刷新 ├── config.yaml # 工具配置:export / import / release 读取(token 敏感,须入 .gitignore) ├── docs/ # 主干 canonical │ ├── raw_urd/ # 原始素材:只增不改 │ ├── urd/ # 用户需求:source of truth,只增不改不删 │ ├── prd/ # 产品需求:按迭代归档,以最新迭代为准 │ ├── test_cases/ # 测试用例:按迭代归档,以最新迭代为准 │ └── manual/ # 用户手册:人工维护,不派生 └── iterations/ # 迭代暂存区 └── _TEMPLATE/ # 迭代目录模板(raw_urd / urd / prd / test_cases) ``` ## 与 skill 版(Claude Code)的关系 同一套方法论的两种执行形态:skill 版靠 Agent 在长上下文里遵守流程,本程序把流程固化成图——步骤顺序、关卡、检索与校验都是代码保证的。两者读写同一个知识库结构,可混用(例如平时用本程序跑固定流程,偶发的复杂裁决回到 Claude Code 会话)。本程序运行**不依赖 CLAUDE.md**(规则内置在 `code/loom/prompts.py`,项目定位只认 config.yaml + docs/);CLAUDE.md 只服务于 Claude Code 会话路径。若知识库保留 CLAUDE.md,修改约定(ID 规则、模板、派生规则)时记得与 `code/loom/prompts.py` 保持一致。