Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# fields-study-flow

## Explain a paper to a beginner

Ask your host agent: “Explain this paper to a beginner in connected prose, with no headings or lists, following motivation, method, experiments, results, limitations and outlook.” The `paper-roadmap` skill calls `explainPaper` to organize evidence, then the host checks the supplied material and writes the natural explanation. Stages may span multiple paragraphs; length follows evidence and learning needs.

```bash
fields-study-flow explain-paper --url examples/paper-explainer/abstract.json --json
fields-study-flow explain-paper --url https://arxiv.org/abs/1706.03762 --text-file examples/paper-explainer/body.txt --language zh
```

Both examples are offline; the files contain explicitly synthetic teaching material. Local JSON accepts title/authors/abstract (or summary)/year/categories; local UTF-8 text and `--text-file` accept body paragraphs separated by blank lines. `--level` defaults to beginner; `--language` accepts zh or en. URL-only input fetches public arXiv abs/PDF metadata through the existing adapter, never the PDF body. Other remote URLs are unsupported; supply their material yourself.

The backend returns **offline evidence organization**, not a detailed explanation of arbitrary papers. JSON contains six ordered candidate stages, short excerpts, source/paragraph anchors, gaps and an explanation contract; plain text contains no headings or lists. Limited English/Chinese keyword rules can miss or misclassify evidence. No paid model is called. The host must verify context, explain terms in the requested language, distinguish general background from author claims, and never invent absent results, author limitations or future work. No full-text access is implied, and short English excerpts are not Chinese translations.

## Reproducible paper path and limits

Default CLI and MCP searches are offline. Opt into public arXiv/GitHub metadata with `roadmap --live`, `ingest-url --fetch-metadata`, or MCP `filters.offline=false` / `fetchMetadata=true`. The `paper` CLI remains a curated offline route; it does not parse arbitrary papers. arXiv abs/PDF links use the Atom metadata API, normalizing title, authors, abstract (`summary`), year, categories and version-independent IDs. No PDF body is downloaded. DOI and ordinary PDF URLs remain URL-only imports with an actionable explanation; supply metadata/concept tags manually or an arXiv URL.

Offline regression: `python -m pytest tests/test_paper_path_e2e.py -q`. Fixed Atom fixtures cover URL → metadata → prerequisite DAG → phase checkpoints → JSON export, plus 404, timeout, 429 and missing abstracts. Missing metadata still permits a minimal route; missing abstract is explicitly title-only. Concepts match a small curated vocabulary (math, Python, machine learning, deep learning, Transformer, diffusion, LLM); this is not arbitrary-paper semantic analysis. Free-text access policies use limited English rules; structured access policies are preferable.

Search filters `pages` (1–10, default 1) and `page_size` (1–100, default 10) use explicit stable provider sorting and ID deduplication. Live provider updates can still shift page boundaries; no snapshot guarantee is implied. Cache entries retain source and UTC retrieval time, expire after one hour by default, and rebuild after corruption. Atomic file replacement protects concurrent in-process instances from partial files; concurrent misses may make duplicate requests. Rate spacing is per host within one process (default 3 seconds), not distributed. There are no automatic retries for 429/errors and no login scraping.

Every phase has an observable checkpoint. Unknown durations remain unknown; known hours form a subtotal. Weekly budget changes both scheduling and deadline capacity. `shortfall_hours` is the known-hour deficit and is only a lower bound when durations are missing. Estimates may overlap and are not completion guarantees.


[简体中文](README.zh-CN.md) | English

Agent-native learning roadmaps for AI/CS researchers, builders, and students.
Expand Down
24 changes: 24 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# fields-study-flow

## 给初学者讲一篇论文

对宿主 Agent 说:“我是初学者,请按动机—方法—实验—效果(结果)—不足—展望详细讲解,不要小标题或列表,尽量用成段文字。”`paper-roadmap` skill 会调用 `explainPaper` 整理证据,再由宿主核实材料、生成自然精讲。六个论述阶段可以各有多段,不强制六段或固定字数。

```bash
fields-study-flow explain-paper --url examples/paper-explainer/abstract.json --json
fields-study-flow explain-paper --url https://arxiv.org/abs/1706.03762 --text-file examples/paper-explainer/body.txt --language zh
```

这两个示例都离线,示例文件是明确标注的合成教学材料。本地 JSON 支持标题、作者、abstract/summary、年份和分类;本地 UTF-8 文本与 `--text-file` 以空行分隔正文段落。`--level` 默认 beginner,语言支持 zh/en。仅传 arXiv abs/PDF URL 时才沿用现有适配器请求公开元数据,不下载 PDF 正文;其他远端 URL 请改为提供材料。

后端输出明确标为“离线材料梳理”,不宣称理解或精讲任意论文。JSON 提供六阶段候选证据、短摘录、来源/段落定位、缺失清单和精讲契约;纯文本无小标题、无列表。中英文关键词规则可能漏检或误分类;宿主必须回查原材料、解释首次出现的术语、区分作者结论和一般背景。没有全文就说明只依据摘要或用户正文,没有结果就提示补充结果表,不编造数字、作者不足或展望,不把英文原句拼接当中文精讲。开发与测试不调用付费模型,宿主自然精讲能力取决于其自身配置。

## 可复现论文路径与边界

CLI/MCP 默认离线。`roadmap --live`、`ingest-url --fetch-metadata` 或 MCP `filters.offline=false` / `fetchMetadata=true` 才请求公开 arXiv/GitHub 元数据。`paper` CLI 保持离线精选路线,不解析任意论文。arXiv abs/PDF 链接通过 Atom API 规范化标题、作者、摘要(summary)、年份、分类和去版本 ID,不下载 PDF 正文。DOI/普通 PDF 仅保留链接并解释限制:可手工提供元数据/概念标签或改用 arXiv URL。

运行 `python -m pytest tests/test_paper_path_e2e.py -q`,固定 Atom fixture 覆盖 URL→元数据→先修图→阶段检查点→JSON 导出,以及 404、超时、429、缺摘要降级。元数据不可用仍可生成最小路线;无摘要明确标记仅按标题匹配。先修图只覆盖数学、Python、机器学习、深度学习、Transformer、diffusion、LLM 等有限词表;自由文本访问策略只是有限英语规则,推荐结构化策略,不声称理解任意论文。

检索 filters 支持 pages(1–10,默认 1)、page_size(1–100,默认 10),固定提供方排序并按 ID 去重;远端更新仍可能移动分页边界,不保证快照。缓存保留来源和 UTC 抓取时间,默认一小时过期,损坏自动重建;原子替换防止进程内多实例读取半写文件,并发 miss 可能重复请求。限流间隔默认每主机 3 秒,仅进程内共享;429/错误不自动重试,不抓登录内容。

每个阶段包含可观察完成条件。未知时长保留未知,已知时长仅为小计;每周预算同时影响周排期与截止日容量。shortfall_hours 表示已知时长缺口,有未知时长时只是下界,估算可能重叠,不保证实际按期完成。


简体中文 | [English](README.md)

面向 AI/CS 研究者、工程师和学生的 agent-native 学习路线生成器。
Expand Down
17 changes: 16 additions & 1 deletion docs/MCP.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
# MCP transport

## Beginner explanation evidence

For “给初学者讲一篇论文”, call `explainPaper` with `metadata` (title/authors/abstract or summary/year/categories), optional `userText` (paragraph array), `level` (beginner by default), and `language` (zh/en). Example arguments: `{"metadata":{"title":"Teaching example","abstract":"Our method uses attention."},"language":"zh"}`. Alternatively supply `url`: only public HTTPS arXiv abs/PDF metadata is fetched. Metadata or userText suppresses fetching. MCP never opens server-local file paths; the CLI supports local UTF-8 JSON/text files and `--text-file`.

The SDK serializes the same result as JSON text: `mode=offline_evidence_outline`, six ordered `paragraphs` stages with `basis`, bounded `excerpt` and sentence position when found, `missing_information`, `source_coverage`, `explanation_contract`, and heading-free `text`. `basis` is abstract, user_text[段落i] (original one-based array position), or inferred for an explicit gap; metadata-only coverage does not justify scientific claims. Keyword hits are candidate evidence, not verified conclusions. Missing results require tables; absent author limitations/outlook stay unknown.

The host agent must consume the original supplied material alongside these locators and follow `skills/paper-roadmap/SKILL.md`: verify context, explain naturally in the requested language, use motivation → method → experiments → results → limitations → outlook without headings or lists, and allow multiple paragraphs per stage. Output length follows evidence, not a fixed word count. The backend makes no model calls, does not translate arbitrary papers, never claims unseen full text, and offers no guarantee of complete semantic extraction. English excerpts in a Chinese outline are short locator aids only. Do not copy long source passages or bypass login.

Paper workflow: `ingestUrl` → `assessKnowledge` → `rankResources` → `buildRoadmap` → `exportPlan`. In that roadmap workflow, only `ingestUrl(fetchMetadata=true)` fetches public arXiv/GitHub metadata. The new `explainPaper` URL-only mode also fetches public arXiv metadata; supplying metadata or body paragraphs keeps it offline. arXiv abs/PDF URLs resolve metadata, not PDF contents; DOI/ordinary PDF links degrade to URL-only with `next_action`. HTTP errors/timeouts preserve actionable reasons; missing abstracts remain title-only metadata. Provide concept tags manually when the small math/Python/ML/deep-learning/Transformer/diffusion/LLM prerequisite graph cannot match the title/abstract. Free-text access-policy parsing is limited English rules, not general language understanding.

`searchResources` defaults offline. With `filters.offline=false`, `pages` (1–10) and `page_size` (1–100) control bounded pagination; sorting is explicit and IDs are deduplicated across pages. Mutable upstream results are not a snapshot. Cache source/timestamps survive hits, malformed caches rebuild, and atomic replacement prevents partial reads by concurrent instances. Default TTL is one hour and per-host spacing is three seconds within one process; misses may duplicate requests and errors have no automatic retries. No authenticated scraping is supported.

Roadmap phases include observable `checkpoints`. The schedule retains unknown durations and reports `available_hours`, `shortfall_hours`, and `shortfall_is_lower_bound`; changing profile weekly hours updates both weekly allocation and deadline feasibility. Export preserves these fields in JSON. Offline fixture regression is `python -m pytest tests/test_paper_path_e2e.py -q`.


Install the package, then configure your host to run `python -m fields_study_flow.mcp_server --mcp`. The Cursor and VS Code examples select this mode explicitly. Use the Python interpreter from the environment where the package and its MCP SDK dependency are installed.

The server uses the official MCP Python SDK 2.x `MCPServer` stdio transport. Protocol versions and capabilities are negotiated by that SDK; this project does not implement additional protocol versions. Eight tools expose schemas derived from their existing typed signatures: assessKnowledge, discoverSources, searchResources, ingestUrl, rankResources, buildRoadmap, validateSources, exportPlan. Unknown tools and invalid arguments return SDK tool errors. Logs use stderr; stdout is reserved for protocol messages.
The server uses the official MCP Python SDK 2.x `MCPServer` stdio transport. Protocol versions and capabilities are negotiated by that SDK; this project does not implement additional protocol versions. Nine tools expose schemas derived from typed signatures: assessKnowledge, discoverSources, searchResources, ingestUrl, rankResources, buildRoadmap, validateSources, exportPlan, explainPaper. The original eight retain their return semantics. Unknown tools and invalid arguments return SDK tool errors. Logs use stderr; stdout is reserved for protocol messages.

For compatibility, `python -m fields_study_flow.mcp_server --legacy-jsonl` accepts one `{"tool":"assessKnowledge","arguments":{"goal":"python"}}` per line and returns `{"ok":true,"result":...}`. Running without a flag retains this legacy behavior, including `python -m git4study.mcp_server`. JSONL is not MCP. Use `--mcp` for standard hosts.

Expand Down
4 changes: 4 additions & 0 deletions examples/paper-explainer/abstract.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Small classifier (synthetic offline example)",
"abstract": "The problem is image classification with limited data. Our method uses a classifier with gradient descent. We evaluate on the CIFAR dataset. The model achieves 91.2% accuracy. A limitation is sensitivity to noisy labels. Future work will evaluate robustness."
}
3 changes: 3 additions & 0 deletions examples/paper-explainer/body.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Our method uses attention to combine relevant information. This is a synthetic teaching example, not a reproduction of a published paper.

The model achieves 85% accuracy on the supplied evaluation data.
30 changes: 30 additions & 0 deletions fields_study_flow/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
if args.command == 'explain-paper':
try:
return _explain_paper(args)
except (ValueError, OSError) as exc:
parser.error(str(exc))
if args.command in {"roadmap", "paper"}:
try:
return _roadmap(args) if args.command == "roadmap" else _paper(args)
Expand Down Expand Up @@ -73,9 +78,34 @@ def build_parser() -> argparse.ArgumentParser:
export.add_argument("--format", choices=["markdown", "json", "anki"], default="json")
export.add_argument("--output-dir", default="fields-study-flow-export")

explain = subparsers.add_parser('explain-paper', help='Organize offline evidence for a beginner explanation.')
explain.add_argument('--url', required=True, help='Public arXiv URL or local UTF-8 text/metadata JSON file')
explain.add_argument('--level', default='beginner')
explain.add_argument('--language', default='zh')
explain.add_argument('--text-file', help='User supplied UTF-8 body paragraphs; disables URL fetching')
explain.add_argument('--json', action='store_true')
return parser


def _explain_paper(args: argparse.Namespace) -> int:
import re
from fields_study_flow.mcp_tools import explainPaper
metadata, paragraphs, url = None, [], args.url
if '://' not in url:
source = Path(url)
content = source.read_text(encoding='utf-8-sig')
if source.suffix.lower() == '.json':
metadata = json.loads(content)
else:
paragraphs = re.split(r'\n\s*\n', content)
url = None
if args.text_file:
paragraphs.extend(re.split(r'\n\s*\n', Path(args.text_file).read_text(encoding='utf-8-sig')))
result = explainPaper(url, metadata, paragraphs or None, args.level, args.language)
print(json.dumps(result, ensure_ascii=False, indent=2) if args.json else result['text'])
return 0


def _roadmap(args: argparse.Namespace) -> int:
profile = LearnerProfile(
goal=args.goal,
Expand Down
1 change: 1 addition & 0 deletions fields_study_flow/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"buildRoadmap": mcp_tools.buildRoadmap,
"validateSources": mcp_tools.validateSources,
"exportPlan": mcp_tools.exportPlan,
"explainPaper": mcp_tools.explainPaper,
}


Expand Down
34 changes: 33 additions & 1 deletion fields_study_flow/mcp_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,42 @@ def ingestUrl(url: str, sourceHint: str | None = None, fetchMetadata: bool = Fal
concepts=[],
trust_score=0.45,
license_or_access_note="User-provided URL. Parse metadata only; do not bypass login or copy long content.",
metadata={"access_policy": {"action": "metadata", "bypass_login": False}, "retrieval_mode": "degraded" if fetchMetadata else "offline", "degraded": degraded},
metadata={"access_policy": {"action": "metadata", "bypass_login": False}, "retrieval_mode": "degraded" if fetchMetadata else "offline", "degraded": degraded, "next_action": "Check the URL or retry later; supply title, abstract and concept tags manually, or use an arXiv metadata URL. DOI and ordinary PDF parsing are not supported."},
).to_dict()


def explainPaper(
url: str | None = None,
metadata: dict[str, Any] | None = None,
userText: list[str] | None = None,
level: str = 'beginner',
language: str = 'zh',
) -> dict[str, Any]:
"""Organize candidate evidence for a host-agent explanation; no model calls.

Supplied metadata/body stays offline. URL-only requests fetch public arXiv
metadata, never full text. Local files are supported by the CLI only.
"""
from urllib.parse import urlsplit
from fields_study_flow.paper_explainer import explain_paper
if url:
parsed = urlsplit(url)
if (parsed.scheme != 'https' or parsed.hostname != 'arxiv.org'
or parsed.username or parsed.password or parsed.port not in {None, 443}
or not re.fullmatch(r'/(?:abs|pdf)/(?:\d{4}\.\d{4,5}|[a-z-]+/\d{7})(?:v\d+)?(?:\.pdf)?', parsed.path)
or parsed.query or parsed.fragment):
raise ValueError('Only public HTTPS arXiv abs/pdf URLs are supported; supply metadata or userText for other papers')
if metadata is None and not userText:
if not url:
raise ValueError('Supply a paper URL, metadata or userText')
from fields_study_flow.sources_adapters import SearchService
resource = SearchService().ingest(url)
metadata = {**resource['metadata'], 'title': resource['title'],
'url': resource.get('url', url),
'license_or_access_note': resource.get('license_or_access_note', 'Public API abstract only; no full-paper permission implied.')}
return explain_paper(metadata, userText, level, language)


def rankResources(
resources: list[dict[str, Any]],
learnerProfile: dict[str, Any],
Expand Down
Loading
Loading