Research Assist Skill
An OpenClaw skill that turns Zotero evidence into a profile-driven literature digest, using arXiv by default and optionally expanding recall with OpenAlex and Semantic Scholar before deduplication.
CLI Usage
# Full digest: profile check → literature retrieval → rank → markdown output
uv run --project ~/.openclaw/skills/research-assist \
research-assist --action digest --config ~/.openclaw/skills/research-assist/config.json
# Ad-hoc literature search
uv run --project ~/.openclaw/skills/research-assist \
research-assist --action search --query "gaussian process" --top 5
# Check profile refresh status
uv run --project ~/.openclaw/skills/research-assist \
research-assist --action profile-refresh --config ~/.openclaw/skills/research-assist/config.json
# Zotero MCP server (for profile evidence + feedback writeback)
uv run --project ~/.openclaw/skills/research-assist research-assist-zotero-mcp
Or via Python module:
uv run --project ~/.openclaw/skills/research-assist \
python -m codex_research_assist --action digest --config ~/.openclaw/skills/research-assist/config.json
Default config path: ~/.openclaw/skills/research-assist/config.json
During install or reconfiguration, do not embed the full setup questionnaire in this file. Use references/setup-routing.md as the install-time interaction guide, ask only the questions relevant to the user's goal, then edit config.json directly.
Install-Time Behavior
Installation and reconfiguration are one-time operations.
Hard rules for the host agent:
- use
references/setup-routing.md only when the user is installing, reconfiguring, or when required config is missing
- once
config.json is valid, normal digest/search/render/feedback runs must not reopen setup questions
- do not restate dormant install options during regular literature work
- if the user asks for normal runtime work, prefer using the existing config over discussing installation
- when setup selects optional backends or delivery routes, execute the required install/setup commands instead of only listing them
- before leaving setup, run a minimal verification for the selected backend or route and report the result
- only fall back to manual instructions when a step is blocked by missing secrets, missing local services, missing permissions, or a platform limitation
Config Format
{
"profile_path": "~/.openclaw/skills/research-assist/profiles/research-interest.json",
"output_root": "~/.openclaw/skills/research-assist/reports",
"retrieval_defaults": {
"max_results_per_interest": 20,
"since_days": 7,
"max_age_days": 7
}
}
Architecture
config.json (OpenClaw skill config)
↓
openclaw_runner.py (CLI entry, markdown to stdout)
├── profile_refresh_policy → check if profile needs update
├── pipeline.py → multi-source literature retrieval (arXiv default)
├── ranker.py → two-signal scoring (map_match + zotero_semantic)
└── format_*_markdown() → structured markdown output
No LLM calls inside the packaged Python pipeline. Retrieval, ranking, and formatting are pure data operations.
Intelligence comes from the calling agent (OpenClaw / Claude Code / Codex CLI).
Profile refresh should be handled by the OpenClaw controller or agent layer, using live Zotero evidence via the bundled Zotero MCP.
Workflow Stages
1. profile_update
- read the current Zotero evidence base when refresh is required
- maintain
profiles/research-interest.json
- preserve the compact contract:
method_keywords, query_aliases, exclude_keywords
- keep method labels short and retrieval-friendly
- prefer
zotero_semantic_search for discovery, then zotero_search_items for exact resolution
- use
research-assist-zotero-mcp for live Zotero reads (no direct API calls)
OpenClaw generation rule:
- treat Zotero like a studio palette, not a flat folder dump
- use collection structure as the sketch of the research map
- use representative papers as the main evidence for what each region actually contains
- use semantic search as the blending layer that connects nearby themes across collections
- write interests that feel like stable method axes, not loose keyword bags
- aim for about 6 interests by default; usually stay in the 4-8 range unless the evidence strongly says otherwise
- if the draft has too few interests, split mixed regions by real method differences; if it has too many, merge nearby regions that share one stable method axis
- if collection names and paper content disagree, trust the papers more than the folder label
- if summary terms are frequent but too generic, use them only to refine wording, not to define the map
- the final profile should read like a compact map of the user's research territory: a few clear regions, each with short labels and retrieval-friendly aliases
2. retrieval
- query arXiv by default, optionally add OpenAlex and Semantic Scholar per interest
- expand the paper pool before ranking when multiple sources are enabled
- generate structured candidate JSON with full provenance
- deduplicate across interests and across enabled sources
3. review
- rank candidates with two-signal scoring:
- map_match (0.30): how well the paper fits the current research-map slices
- zotero_semantic (0.70): how close the paper is to nearby Zotero literature
- apply the low-map guard:
- if
map_match < 0.30, apply the configured penalty to avoid semantic-only false positives
- output ranked markdown to stdout for agent review
- prefer a smaller sharper set over a noisy dump
- stay
abstract-first
Digest Enrichment
- OpenClaw should treat agent-filled review as the default digest-enrichment path
- after retrieval, let the host agent enrich the top-ranked candidate JSON files with review patches
- use
review_generation.agent_top_n to cap how many ranked candidates the host agent needs to inspect
- let the host agent decide the final visible subset by setting
review.selected_for_digest
- use
review_generation.final_top_n as the hard upper bound for the final rendered digest
- keep
fallback_to_system enabled unless the user explicitly wants hard failure instead of fallback text
- after patches are applied, re-render the digest so HTML / Telegram outputs use the enriched review text
why_it_matters should sound like a recommendation, not a provenance report
caveats should capture real uncertainty or scope boundaries, not generic hedging
- the host agent should also fill
review.zotero_comparison, including nearest-neighbor fallback when candidate-level evidence is missing
- keep nearest-neighbor output compact: usually 1-2 items
- the host agent is not responsible for email / telegram wrapper copy, subjects, or routing
Delivery Routing
- use one shared delivery path and branch at the end with
delivery.primary_channel
- default primary channel is
email; telegram is backup or alternate primary
- channel wrappers are system-owned:
- email subject/body/profile card/stat cards
- telegram compact message shell
- do not ask the host agent to generate channel-specific wrappers
Stage 6: feedback_sync
After the digest is reviewed and delivered, the host agent may push non-destructive feedback back into Zotero.
Workflow:
- collect the user's explicit feedback on each digest candidate (keep, drop, archive, watch, etc.)
- encode feedback as
reports/schema/zotero-feedback.schema.json
- call
zotero_apply_feedback through the bundled Zotero MCP with dry_run=true first
- show the dry-run plan to the user and ask for confirmation before applying
- only after confirmation, re-run with
dry_run=false
Allowed feedback decisions:
read_first — high-priority paper, tag and promote in library
skim — worth scanning, tag for later review
watch — track this topic area, add to watchlist collection
skip_for_now — not relevant now, mark but do not remove
archive — reviewed and filed, move to archive collection
watchlist — add to a standing watchlist for periodic check-in
ignore — not relevant, tag to suppress in future runs
unset — no decision yet, skip writeback for this item
What feedback can do (non-destructive only):
- add tags (including
ra-status:* decision tags)
- add or change collection membership
- append notes to items
- create new collections if needed for organization
What feedback must never do:
- delete Zotero items or collections
- modify item metadata (title, authors, abstract, DOI)
- move or delete attachment files
- rewrite top-level taxonomy without explicit user instruction
- apply changes without showing the dry-run plan first
Matching behavior:
- match items by
item_key (preferred), doi, or title_contains
- at least one match field must be provided per decision
- DOI matching is case-insensitive
title_contains uses substring match (not exact)
- if no match is found, the decision is recorded as
not_found in the plan
Edge cases:
- duplicate tags are deduplicated (case-insensitive)
- previous
ra-status:* tags are replaced when a new decision is applied
- the
research-assist system tag is always preserved
unset decisions produce no status tag and no writeback
- empty
add_tags, remove_tags, add_collections, remove_collections are allowed (no-op for that field)
Hard Rules
- do not expand concise method labels into long topic sentences
- do not make full text the default review mode
- do not delete Zotero items or collections automatically
- prefer
dry_run=true for any Zotero writeback
- do not treat scheduler wiring as part of the skill
Key Runtime Files
- OpenClaw runner:
src/codex_research_assist/openclaw_runner.py
- Ranker:
src/codex_research_assist/ranker.py
- Pipeline:
src/codex_research_assist/arxiv_profile_pipeline/pipeline.py
- Example config:
config.example.json
- Example profile:
profiles/research-interest.example.json
Reference Documents
references/workflow.md — stage order and controller boundary
references/contracts.md — profile contract and review policy
references/distribution.md — packaging include/exclude rules
references/setup-routing.md — install-time route selection and option questions
references/review-generation.md — system vs agent_fill review contract
references/profile-map-generation.md — how to turn Zotero evidence into a research-map-style profile
Packaging Boundary
Include in distributable skill:
SKILL.md, config.example.json, pyproject.toml, uv.lock
src/
references/
profiles/research-interest.example.json
automation/arxiv-profile-digest.example.toml
automation/prompts/
reports/schema/
- generated package-root
install.sh
Exclude:
- generated reports, temporary state
- local secret config
- scheduler wrappers
- repository planning documents (
NEXT_PLAN.md, CODEMAP.md)
Source: zhanglg12/research-assist — distributed by TomeVault.
1---2name: research-assist3description: Use when working with a lightweight arXiv literature digest skill for OpenClaw, with Zotero-driven interest profiling, research-map plus Zotero-semantic ranking, agent-enriched digest cards, and non-destructive feedback writeback.4---56# Research Assist Skill78An OpenClaw skill that turns Zotero evidence into a profile-driven literature digest, using arXiv by default and optionally expanding recall with OpenAlex and Semantic Scholar before deduplication.910## CLI Usage1112```bash13# Full digest: profile check → literature retrieval → rank → markdown output14uv run --project ~/.openclaw/skills/research-assist \15 research-assist --action digest --config ~/.openclaw/skills/research-assist/config.json1617# Ad-hoc literature search18uv run --project ~/.openclaw/skills/research-assist \19 research-assist --action search --query "gaussian process" --top 52021# Check profile refresh status22uv run --project ~/.openclaw/skills/research-assist \23 research-assist --action profile-refresh --config ~/.openclaw/skills/research-assist/config.json2425# Zotero MCP server (for profile evidence + feedback writeback)26uv run --project ~/.openclaw/skills/research-assist research-assist-zotero-mcp27```2829Or via Python module:3031```bash32uv run --project ~/.openclaw/skills/research-assist \33 python -m codex_research_assist --action digest --config ~/.openclaw/skills/research-assist/config.json34```3536Default config path: `~/.openclaw/skills/research-assist/config.json`3738During install or reconfiguration, do not embed the full setup questionnaire in this file. Use `references/setup-routing.md` as the install-time interaction guide, ask only the questions relevant to the user's goal, then edit `config.json` directly.3940## Install-Time Behavior4142Installation and reconfiguration are one-time operations.4344Hard rules for the host agent:4546- use `references/setup-routing.md` only when the user is installing, reconfiguring, or when required config is missing47- once `config.json` is valid, normal digest/search/render/feedback runs must not reopen setup questions48- do not restate dormant install options during regular literature work49- if the user asks for normal runtime work, prefer using the existing config over discussing installation50- when setup selects optional backends or delivery routes, execute the required install/setup commands instead of only listing them51- before leaving setup, run a minimal verification for the selected backend or route and report the result52- only fall back to manual instructions when a step is blocked by missing secrets, missing local services, missing permissions, or a platform limitation5354## Config Format5556```json57{58 "profile_path": "~/.openclaw/skills/research-assist/profiles/research-interest.json",59 "output_root": "~/.openclaw/skills/research-assist/reports",60 "retrieval_defaults": {61 "max_results_per_interest": 20,62 "since_days": 7,63 "max_age_days": 764 }65}66```6768## Architecture6970```71config.json (OpenClaw skill config)72 ↓73openclaw_runner.py (CLI entry, markdown to stdout)74 ├── profile_refresh_policy → check if profile needs update75 ├── pipeline.py → multi-source literature retrieval (arXiv default)76 ├── ranker.py → two-signal scoring (map_match + zotero_semantic)77 └── format_*_markdown() → structured markdown output78```7980No LLM calls inside the packaged Python pipeline. Retrieval, ranking, and formatting are pure data operations.81Intelligence comes from the calling agent (OpenClaw / Claude Code / Codex CLI).8283Profile refresh should be handled by the OpenClaw controller or agent layer, using live Zotero evidence via the bundled Zotero MCP.8485## Workflow Stages8687### 1. `profile_update`8889- read the current Zotero evidence base when refresh is required90- maintain `profiles/research-interest.json`91- preserve the compact contract: `method_keywords`, `query_aliases`, `exclude_keywords`92- keep method labels short and retrieval-friendly93- prefer `zotero_semantic_search` for discovery, then `zotero_search_items` for exact resolution94- use `research-assist-zotero-mcp` for live Zotero reads (no direct API calls)9596OpenClaw generation rule:9798- treat Zotero like a studio palette, not a flat folder dump99- use collection structure as the sketch of the research map100- use representative papers as the main evidence for what each region actually contains101- use semantic search as the blending layer that connects nearby themes across collections102- write interests that feel like stable method axes, not loose keyword bags103- aim for about 6 interests by default; usually stay in the 4-8 range unless the evidence strongly says otherwise104- if the draft has too few interests, split mixed regions by real method differences; if it has too many, merge nearby regions that share one stable method axis105- if collection names and paper content disagree, trust the papers more than the folder label106- if summary terms are frequent but too generic, use them only to refine wording, not to define the map107- the final profile should read like a compact map of the user's research territory: a few clear regions, each with short labels and retrieval-friendly aliases108109### 2. `retrieval`110111- query arXiv by default, optionally add OpenAlex and Semantic Scholar per interest112- expand the paper pool before ranking when multiple sources are enabled113- generate structured candidate JSON with full provenance114- deduplicate across interests and across enabled sources115116### 3. `review`117118- rank candidates with two-signal scoring:119 - **map_match** (0.30): how well the paper fits the current research-map slices120 - **zotero_semantic** (0.70): how close the paper is to nearby Zotero literature121- apply the low-map guard:122 - if `map_match < 0.30`, apply the configured penalty to avoid semantic-only false positives123- output ranked markdown to stdout for agent review124- prefer a smaller sharper set over a noisy dump125- stay `abstract-first`126127### Digest Enrichment128129- OpenClaw should treat agent-filled review as the default digest-enrichment path130- after retrieval, let the host agent enrich the top-ranked candidate JSON files with review patches131- use `review_generation.agent_top_n` to cap how many ranked candidates the host agent needs to inspect132- let the host agent decide the final visible subset by setting `review.selected_for_digest`133- use `review_generation.final_top_n` as the hard upper bound for the final rendered digest134- keep `fallback_to_system` enabled unless the user explicitly wants hard failure instead of fallback text135- after patches are applied, re-render the digest so HTML / Telegram outputs use the enriched review text136- `why_it_matters` should sound like a recommendation, not a provenance report137- `caveats` should capture real uncertainty or scope boundaries, not generic hedging138- the host agent should also fill `review.zotero_comparison`, including nearest-neighbor fallback when candidate-level evidence is missing139- keep nearest-neighbor output compact: usually 1-2 items140- the host agent is not responsible for email / telegram wrapper copy, subjects, or routing141142### Delivery Routing143144- use one shared delivery path and branch at the end with `delivery.primary_channel`145- default primary channel is `email`; `telegram` is backup or alternate primary146- channel wrappers are system-owned:147 - email subject/body/profile card/stat cards148 - telegram compact message shell149- do not ask the host agent to generate channel-specific wrappers150151### Stage 6: `feedback_sync`152153After the digest is reviewed and delivered, the host agent may push non-destructive feedback back into Zotero.154155Workflow:1561571. collect the user's explicit feedback on each digest candidate (keep, drop, archive, watch, etc.)1582. encode feedback as `reports/schema/zotero-feedback.schema.json`1593. call `zotero_apply_feedback` through the bundled Zotero MCP with `dry_run=true` first1604. show the dry-run plan to the user and ask for confirmation before applying1615. only after confirmation, re-run with `dry_run=false`162163Allowed feedback decisions:164165- `read_first` — high-priority paper, tag and promote in library166- `skim` — worth scanning, tag for later review167- `watch` — track this topic area, add to watchlist collection168- `skip_for_now` — not relevant now, mark but do not remove169- `archive` — reviewed and filed, move to archive collection170- `watchlist` — add to a standing watchlist for periodic check-in171- `ignore` — not relevant, tag to suppress in future runs172- `unset` — no decision yet, skip writeback for this item173174What feedback can do (non-destructive only):175176- add tags (including `ra-status:*` decision tags)177- add or change collection membership178- append notes to items179- create new collections if needed for organization180181What feedback must never do:182183- delete Zotero items or collections184- modify item metadata (title, authors, abstract, DOI)185- move or delete attachment files186- rewrite top-level taxonomy without explicit user instruction187- apply changes without showing the dry-run plan first188189Matching behavior:190191- match items by `item_key` (preferred), `doi`, or `title_contains`192- at least one match field must be provided per decision193- DOI matching is case-insensitive194- `title_contains` uses substring match (not exact)195- if no match is found, the decision is recorded as `not_found` in the plan196197Edge cases:198199- duplicate tags are deduplicated (case-insensitive)200- previous `ra-status:*` tags are replaced when a new decision is applied201- the `research-assist` system tag is always preserved202- `unset` decisions produce no status tag and no writeback203- empty `add_tags`, `remove_tags`, `add_collections`, `remove_collections` are allowed (no-op for that field)204205## Hard Rules206207- do not expand concise method labels into long topic sentences208- do not make full text the default review mode209- do not delete Zotero items or collections automatically210- prefer `dry_run=true` for any Zotero writeback211- do not treat scheduler wiring as part of the skill212213## Key Runtime Files214215- OpenClaw runner: `src/codex_research_assist/openclaw_runner.py`216- Ranker: `src/codex_research_assist/ranker.py`217- Pipeline: `src/codex_research_assist/arxiv_profile_pipeline/pipeline.py`218- Example config: `config.example.json`219- Example profile: `profiles/research-interest.example.json`220221## Reference Documents222223- `references/workflow.md` — stage order and controller boundary224- `references/contracts.md` — profile contract and review policy225- `references/distribution.md` — packaging include/exclude rules226- `references/setup-routing.md` — install-time route selection and option questions227- `references/review-generation.md` — `system` vs `agent_fill` review contract228- `references/profile-map-generation.md` — how to turn Zotero evidence into a research-map-style profile229230## Packaging Boundary231232Include in distributable skill:233234- `SKILL.md`, `config.example.json`, `pyproject.toml`, `uv.lock`235- `src/`236- `references/`237- `profiles/research-interest.example.json`238- `automation/arxiv-profile-digest.example.toml`239- `automation/prompts/`240- `reports/schema/`241- generated package-root `install.sh`242243Exclude:244245- generated reports, temporary state246- local secret config247- scheduler wrappers248- repository planning documents (`NEXT_PLAN.md`, `CODEMAP.md`)249250---251> Source: [zhanglg12/research-assist](https://github.com/zhanglg12/research-assist) — distributed by [TomeVault](https://tomevault.io).252<!-- tomevault:4.0:skill_md:2026-06-17 -->