Repository navigation
Methodology
This document is a standalone synthesis of DeepDelve's design methodology: why it's built the way
it is, what problem it's actually solving, and what, if anything, is genuinely new about it. It
draws on and cites three other documents rather than duplicating them: README.md (system overview,
agent topology), the Architecture page (the completion-check/writer-dispatch engine's exact mechanics),
and RESEARCH.md (the literature review and prior-art survey this document's novelty claims are
grounded in, §9/§9a/§10 specifically). Every factual claim here traces to one of those three, or a
specific, dated incident in the Roadmap page's History section.
DeepDelve is a multi-agent deep-research assistant that runs entirely on local, consumer-hardware
LLMs, a single 16GB-VRAM-class GPU, gpt-oss:20b as the current baseline via Ollama, not a frontier
hosted model. That constraint is the entire reason this project's architecture looks the way it
does.
Every framework surveyed for RESEARCH.md §9/§9a (GPT Researcher, Stanford STORM,
dzhng/deep-research, Tongyi DeepResearch, CrewAI, AutoGen/AG2, CAMEL-AI, LangGraph's Corrective
RAG, Perplexity's and OpenAI's published deep-research systems) either assumes a frontier-class
generator or addresses reliability entirely through training, not a runtime verification layer.
None of them solve the problem DeepDelve actually has: a 20B-class local model doing open-ended,
multi-hop research and synthesis, with no natural pass/fail oracle the way code-with-tests has one.
A coding agent can run the test suite and know if it's wrong. A research agent asked to explain the
economic causes of the fall of the Roman Empire has nothing equivalent to check against, until it
fabricates a citation, drops half the query, or contradicts one of its own sources, and by then the
damage is already synthesized into prose.
This project's repeated, hard-won finding, stated plainly because it's the premise for everything
below, is that prompt-only fixes do not hold against this failure class on a local model.
the Architecture page and the Roadmap page's History section document this same lesson relearned in
different clothes at least half a dozen times: a model told not to cite a source it wasn't given
does it anyway; a model told to stop delegating once research is sufficient loops; a model warned
inline, in its own evidence base, that a citation doesn't match anything fetched this run, cites it
anyway on the very next independent dispatch. Every durable fix in this project moved the
enforcement from the prompt into deterministic Python code that runs regardless of what the model
does.
Full diagram and per-role description in README.md's Architecture section. In summary: a typed,
three-tier delegation hierarchy. The Planner plans and delegates, structurally cannot write any
file itself. WebSearcher and AcademicSearcher (Tier 2) do web research. DocumentAnalyzer and
DataAnalyzer (Tier 3, leaf nodes) read and extract only. Two Planner-tier delegates are never
dispatched by the Planner itself: FindingsWriter writes findings.md from RunState's structured
per-task results, not the Planner's own conversation, and Builder writes final_report.md from
findings.md. Each gets an independent review from a fresh-context PeerReviewer before being
accepted.
Tool access is deliberately withheld from each parent role so it is structurally forced to delegate rather than short-circuit the chain, the first instance of a pattern that recurs throughout this document: where a prompt instruction could be silently ignored, remove the capability that would let it be ignored instead.
RESEARCH.md §9/§9a's dedicated prior-art survey concludes this has no found precedent combining
all of its pieces, anywhere, industry or academic. It's not one mechanism but seven, each
independently precedented per that survey's own sourced findings, but assembled and hardened
together in a way nothing else surveyed was.
src/engine/completion.py runs 27 independent, pure-function checks, COMPLETION_CHECKS (10) then
GROUNDING_CHECKS (17), first-match-wins, exactly one Verdict per attempt, full mechanics in
the Architecture page §1. Each targets one specific, previously observed failure mode against
ground-truth run state, a concrete incident with a concrete fix, not a generic taxonomy
classification.
Starvation prevention was generalized on 2026-07-31 after this exact bug shape recurred eight times, then extended cross-tier on 2026-08-17 when it recurred a ninth time in a new shape. A first-match-wins priority queue lets any check near the top permanently starve everything below it for a run's entire retry budget if it keeps re-firing on unchanged state. Eight separate real incidents hit this before the mechanism became structural: every non-self-resolving check now caps its own consecutive-firing count via one shared helper, pinned by a standing audit test that fails CI if a new check skips it. Grounded in prior art that predates this project: OS scheduler aging (a starvation counter forcing a lower-priority task to get a turn past a threshold) and the circuit breaker pattern (Nygard, Release It!, stop retrying a failing path after N attempts rather than hammering it forever). The methodological lesson, not just the mechanism: a structural fix that requires a test to enforce compliance beats one that trusts every future check author to remember the convention. Two of the eight incidents were caught by the audit test before ever causing a live failure, not after.
That generalized fix was itself insufficient once, confirming it was solving a real, recurring class
of problem rather than one specific bug. run_completion_check only evaluates GROUNDING_CHECKS
once COMPLETION_CHECKS's own scan returns nothing, a hard two-tier gate, not just list-position
priority within one list. A live-traced incident (2026-08-17) showed a COMPLETION_CHECKS problem
recurring indefinitely could permanently prevent a dropped-facet detector in GROUNDING_CHECKS from
ever getting a turn, even though every individual check inside each tier was correctly capped. The
existing yield mechanism, already proven safe as a speculative probe, needed no new machinery, just
a second call site at the tier boundary itself. The lesson generalizes past this specific gate: a
structural fix scoped to "checks within one list" does not automatically cover "tiers of lists," and
the actual boundary of a starvation-prevention mechanism needs checking explicitly, not assumed from
the fact that a version of it already shipped once.
FindingsWriter and Builder each dispatch to a separately instantiated PeerReviewer with zero shared
conversation history, not a self-critique step in the same context. RESEARCH.md §9 found this
distinction is real and literature-backed, not a stylistic choice: Reflexion and Self-Refine, the
standard self-correction pattern most surveyed frameworks default to, all use same-context
self-critique, which current research states plainly is fundamentally unreliable, motivating an
independent critic over self-evaluation instead. DeepDelve's design landed on the literature-correct
side of that distinction before the literature review confirmed it, the review came after, as
verification, not as the design's origin.
When a writer dispatch produces a genuinely empty response twice in a row, confirmed live on
2026-07-26 (FindingsWriter did this on 6 of 8 attempts in one run), the system assembles
findings.md directly from already-verified structured data, zero LLM involvement, instead of
losing the retry cycle. This mirrors CRAG's and Self-RAG's core mechanism (RESEARCH.md §10: both
filter or construct evidence structurally before a generator ever sees it, rather than annotate a
problem and hope a later generation step honors the annotation), independently arrived at from a
live incident, then confirmed against the primary papers afterward.
The clearest single example of "don't trust the model to honor an instruction, remove the
possibility instead." _is_citable_finding now structurally excludes any finding whose own summary
carries a system verification or relevance warning marker from ever reaching FindingsWriter's
evidence, reversing an earlier, explicit 2026-07-22 decision that deliberately left flagged findings
in place, reasoning they "may still coexist with other real, usable content." That reasoning was
falsified live, twice: a model handed its own embedded warning cited the flagged bad URL anyway,
across 7 independent dispatches in one run. Researched before fixing, not guessed: naming forbidden
content inside a "do not cite X" instruction is documented to risk priming its reproduction rather
than preventing it (the "ironic rebound" effect, arXiv:2511.12381), and negated framing is separately
documented as producing large, unstable swings in small open-weight models' judgments, in an ethical-
stance framing study, not this project's own citation domain, but evidence in the same direction
(arXiv:2601.21433, small 1-4B models swung up to 76 percentage points between affirmative and
negated framings of the same question). The failure has a real, cited mechanism, not just an
anecdote.
RESEARCH.md §9's closest academic analog, VERIMAP (arXiv:2510.17109), has a planner author an
explicit verification function per delegated subtask. DeepDelve's own established principle
(RunState.coverage()'s own documented reasoning: small local models have repeatedly proven
unreliable at following new structured-output conventions) argues directly against asking the
Planner to do that. The reconciliation, shipped 2026-07-26: the engine computes a per-task ledger
structurally, from signals that already exist, rather than trusting a new Planner-authored field.
This keeps VERIMAP's real contribution, a task has its own checkable, independently retriable
verification state, while dropping the part of its mechanism that this project's own hard-won lesson
argues against.
Section 3.2 established fresh-context review as necessary. A full marathon investigation (2026-08-01,
RESEARCH.md §16-17) found it insufficient on its own for a distinct failure shape: a report
mechanically passing every grounding check while still silently answering only about a third of a
multi-facet query, because the missing facets were never fabricated, just never produced. Ruled out
first, not assumed: stale sub-agent context (_run_single_task constructs a genuinely fresh dispatch
client per call, confirmed by reading it, not inferred). The literature review done before attempting
a fix, per this project's own standing rule, named the actual mechanism: the self-correction blind
spot (Kamoi et al., arXiv:2406.01297, models are measurably worse at correcting errors in their own
prior output than the identical error framed as external input; this document previously attached a
"64.5% across 14 open models" figure to this citation that does not appear anywhere in the paper's
full 23/24-page text, corrected 2026-08-22 after a direct full-text re-read, grep-checked for
"64.5", "fourteen", and "survive" with zero matches. The paper's own local copy is
papers/kamoi_survey_2406.01297.pdf; this was the same fabricated-attribution class of error CLAUDE.md's
citation-verification rule already names as a past incident, evidently never propagated back into this
specific paragraph). A first, smaller fix informed by that same literature
(an explicit edit_workspace_file directive naming exactly what to add, Song's Cross-Context Review
framing, arXiv:2603.12123) was tried and live-tested to a clean negative result before escalating:
the directive changed which tool Builder called, not what it produced, the citation ratio stayed
frozen, and by the run's end the report had gone from covering about a third of the query to covering
zero of one whole facet, despite an explicit "do not touch any other part of the report" instruction.
That negative result was itself methodologically load-bearing: it confirmed the failure was the
self-correction blind spot specifically, not a tool-choice or wording gap, before the larger
architectural fix was justified.
The fix that then worked: dispatch Builder once per under-represented facet, each a genuinely independent, externally-scoped production call in the Cross-Context Review sense, not just review, against only that facet's own real findings, extending §3.2's "review must be fresh-context" principle to "production must be too" once a single generation call is asked to hold more independent facets than it reliably can at once. A second literature match, found during scoping rather than after: Xu et al.'s aggregator noise framing (arXiv:2506.16411), individual facts correct, a merge or synthesis step drops whole clusters, named which of three distinct long-context failure modes this was, confirming which of three plausible mechanisms was actually responsible rather than leaving it a guess. Correction, 2026-08-22, after re-reading the paper directly rather than trusting the earlier summary: that paper's own mitigation for aggregator noise is single-stage aggregation with carefully designed prompts, not hierarchical decomposition; it does not recommend or test a hierarchical approach. Per-facet dispatch is this project's own fix, independently justified by the self-correction blind spot and Cross-Context Review literature above, not something Xu et al. themselves prescribe.
A follow-up, isolated A/B test (2026-08-19) sharpened rather than reopened the self-correction blind
spot diagnosis. A plausible, cheap-to-test candidate contributor to a different but related-looking
failure (a writer role narrating instead of calling write_workspace_file) was the <Show Your Thinking> prompt block, since AgentFloor (arXiv:2605.00334) suggested a heavier version of the same
"deliberate before acting" pattern might push models toward "plan, then stop." A faithful isolated
harness, reusing the real evidence base and tool schema from a model already live-disqualified for
this exact failure, ran 9 reps with the block present versus stripped: 9 of 9 real tool calls in both
conditions, no difference. A genuine negative result, not left open: the block isn't implicated, and
since an isolated single-turn dispatch converges cleanly regardless of it, the actual variable has to
be something specific to a full run's multi-turn or retry dynamics, not the prompt content itself.
Methodologically this is the same discipline as the salvage and exclusion fixes above, applied to a
negative case: a plausible literature-suggested mechanism gets tested against the project's own real
data before being acted on or ruled out, rather than assumed correct because the analogy was
persuasive.
Every mechanism in §§3.1-3.6 was originally validated the same way: implement it, re-run the benchmark that exposed the bug, confirm the target symptom didn't recur in one live trial. That's a real check, but it doesn't distinguish a genuinely load-bearing fix from a plausible-sounding addition that happened to coincide with the run converging for an unrelated reason, precisely the audit gap "The Illusion of Multi-Agent Advantage" (Jwalapuram et al., arXiv:2606.13003) identifies in other multi-agent systems: complexity added without verified causal contribution, "expensive witnesses" that cost real overhead but have near-zero measured influence on the outcome.
Applied to this project's own completion-check pipeline for the first time on 2026-08-18, using an
adaptive-trial protocol (one run per condition, escalating only when an early disagreement needs
resolving, per §5's own "a discard needs more than one run, a pass can stand on one" standard): two
accumulated mechanisms, force_whole_rebuild and a no_progress_guard added the day before, were
each run with and without on the standing benchmark. Disabling force_whole_rebuild dropped the mean
score from a 0.75 baseline to 0.25 across 3 runs, escalated after the first two disagreed, both
failing runs hitting the same underlying coordination failure the mechanism exists to break.
Disabling no_progress_guard dropped the mean to 0.125 across 2 runs, both timing out for two
different specific reasons, the guard's absence generically letting a run burn its whole time budget
on unproductive retries regardless of which specific check triggers it. Both mechanisms confirmed
genuinely load-bearing, not expensive witnesses, the first real controlled-ablation evidence for any
of this project's completion-check mechanisms, as opposed to the single-live-trial validation every
other mechanism in this document still stands on. The remaining, larger mechanisms (per-facet
dispatch, the starvation guards themselves) haven't yet been put through the same ablation, a
concrete, scoped-but-not-yet-executed next step, not a claim that everything else is unproven.
Unlike §§3.1-3.7, this mechanism was not built in response to a live production failure this
project's own runs produced, it's the one deliberate exception to this document's own framing. It
was scoped 2026-08-22 during the project's own citation-verification pass on this very document (see
the Whitepaper page's citation fixes the same day), where a real gap became visible by hand: the existing
grounding checks (§3.1) verify that an academic (Author, Year) citation resolves to a URL that was
actually fetched, but never that the citation's underlying identity, its claimed author and year,
corresponds to a real paper at all. A citation can pass every existing check (real URL, real fetch,
term-overlap, NLI entailment) while still attributing a genuinely fetched source to the wrong paper,
the "mashup fabrication" pattern documented in academic-integrity literature and found by hand
several times in this project's own reference lists this session, not yet caught by any structural
check.
The fix follows this document's own established principle rather than introducing a new one:
utils/grounding.py::academic_citation_existence_problem queries Semantic Scholar's public
paper-search API (no key required for this tier) for each academic citation that already has a
resolved URL, and wires into real_grounding_problem, the single function shared by both
per-dispatch verification (before a finding ever reaches RunState.add_finding) and final-report
checking, behind a new opt-in grounding_check.academic_citation_verify flag (default false, this
is the first grounding check with a genuine external-network dependency beyond URL-liveness). On a
flagged citation, exclusion is structural, not a warning left for the model to heed (§3.4's own
principle): the existing [SYSTEM VERIFICATION WARNING] + URL-scoped exclusion mechanism in
_is_citable_finding needed exactly one new recognized label,
academic_citation_unverified, no new exclusion code. Fails open on any network/timeout/HTTP
error, live-confirmed against the real API rather than only a mock: manual testing hit a genuine 429
rate-limit response from Semantic Scholar's unauthenticated tier, and the fail-open path correctly
returned no verdict rather than a false flag.
A live smoke test against the real API (2026-08-22, same day) found and fixed a real bug the mocked
unit tests could not have caught, because they only ever fed in already-correctly-shaped fake
responses. The initial matching logic compared a bare surname ("Vaswani") against a candidate's
entire name string ("Ashish Vaswani") via difflib.SequenceMatcher, scoring only ~0.67, below the
0.7 threshold, purely from the length mismatch, even though the surname is an exact match. Against
the real "Attention is All you Need" (Vaswani et al., 2017) response, this would have false-flagged a
completely genuine citation as unverified. Fixed by matching the surname against each individual name
TOKEN in a candidate's name, not the whole string. Also newly surfaced: the public unauthenticated
tier's rate limit is considerably tighter in practice than its documentation implies, repeated 429s
during testing, sometimes 8-14 retries before a single request cleared, confirming the fail-open path
fires correctly and often in real usage, not just in a contrived failure test, which is itself a
practical limitation worth naming: an opt-in check this likely to be rate-limited will silently skip
more often than it actually verifies, on the free tier, unless a paid/registered API key is used.
Not yet done: a full agentic run (a real AcademicSearcher dispatch through Ollama) with the flag
enabled, exercising the check through the orchestrator rather than by calling
real_grounding_problem directly, this remains a smaller, scoped gap than "unproven in production"
now that the actual matching logic has been validated against real API responses for both a genuine
and a fabricated citation.
Each of these is stated here because a specific, dated incident in this project's own history demonstrated it, not as an abstract value.
- Structural enforcement beats prompted instruction, every time it's been tested. The rename-nudge, the citation-exclusion fix, the tool-access withholding in §2, the empty-response salvage: every one of these exists because a prompt-only version of the same fix was tried or considered and either failed live or was predicted to fail based on cited literature (§3.4).
- A mechanism firing correctly is not the same claim as the output being correct. Confirmed the hard way on 2026-07-26: a report that mechanically passed every fired completion check was still 100% about one half of a two-facet query, because the check that would have caught the other half vanishing didn't exist yet. Whether the pipeline ran correctly and whether the artifact is actually good are two separate questions; neither substitutes for the other.
- Reuse before rebuilding. The Analyzer-tier NLI-coverage fix (§3) turned out to need zero new code once the actual dispatch path was re-read; an earlier planning note had assumed new claim-extraction logic was required, and was simply wrong. Caught by re-reading the code, not by trusting the earlier note.
-
Verify against real historical data, not just a fresh unit test. Several fixes were replayed
directly against the actual
_run_state.jsonof the run that originally exposed the bug, before being declared fixed, confirming the fix would have caught the exact real incident, not just a synthetic approximation of it. - A mechanism surviving one live re-run is not the same claim as a mechanism being load-bearing. Every completion-check mechanism in §3 was validated by re-running the benchmark that exposed its bug and confirming the symptom didn't recur once, real evidence, but not a controlled comparison. The first genuine with-versus-without ablation (§3.7) confirmed two accumulated mechanisms were in fact causally responsible for the score difference they were credited with, not merely correlated with a run that happened to converge, the concrete demonstrated instance of this principle, not just an aspiration.
- A discard verdict needs corroboration; a pass can stand on one clean run. Formalized as the Model Evaluation Standard (§5) after two real fairness gaps were found on re-reading a past verdict critically. The standard exists because the project caught itself getting this wrong once, not as a hypothetical safeguard.
Every model bake-off verdict in the Roadmap page must clear a 6-point standard before being treated as
settled, written after re-reading a past verdict critically surfaced two real fairness gaps that had
been sitting undetected in already-published conclusions:
- Confirm the operating mode (think/nothink, tool format, context length) reaches the model via a raw API-level test before running a full benchmark, never infer from vendor docs alone.
- Isolate the candidate as the only variable; every other pipeline role stays on the known-good baseline unless it is the role under test. A VRAM-forced swap elsewhere makes the run informational only, not a clean verdict.
- State the serving backend and version alongside every verdict; a "disqualified" model may actually be a serving-layer bug (two independent Ollama bugs were found exactly this way).
- A discard claim needs more than one run; a clean pass can stand on one.
- Keep a verdict changelog, mark superseded entries and link the corrected retest, never overwrite history silently.
- A candidate that can't fit the project's real minimum operating context on this hardware is discarded outright, not proportionally rescaled to fit, unless the ceiling is the model's own permanent architectural limit rather than a hardware-forced squeeze, in which case it's tested at its true native ceiling instead.
Full text and the incidents that motivated each point: the Roadmap page's "Model Evaluation Standard"
section.
Summarized from RESEARCH.md §9/§9a's dedicated research pass, primary sources read directly, repo
docs, papers, Anthropic's own published multi-agent-system engineering writeup, not search-snippet
skimming, including a follow-up pass that closed every gap the first pass left explicitly
unconfirmed.
Not novel at the level of individual components. Deterministic verification layers over agent output exist (closest: a Claude Code coding-agent tool using deterministic test-pass/fail verification, industry tooling, not academic). Retrieval-grounded citation attribution verification is published and active research (CiteGuard, arXiv:2510.17853, not "VeriCite" as an earlier draft of this document named it, corrected 2026-08-22 after re-reading the paper directly). Independent-context critics outperforming same-context self-critique is an established literature finding (§3.2). Small frozen classifiers replacing LLM judgment for a decision is precedented (RouteLLM-style routing). A separated citation-verification pipeline stage exists in a major published system (Anthropic's own CitationAgent).
The specific combination has no found precedent. Every other system examined implements one of
two shapes: same-context or same-loop LLM-as-judge critique, sometimes with one deterministic
routing branch bolted on, as in LangGraph's Corrective RAG, but never a deterministic check itself,
or no documented verification mechanism at all for this exact problem (Perplexity has genuine
retrieval engineering depth but nothing published on citation verification specifically; OpenAI's
Deep Research system card is scoped to safety and red-teaming, not architecture). None combines
dozens of independently named, priority-ordered, empirically-motivated checks, a starvation guard for
that priority queue, and a regression test pinning the verification logic itself
(test_structural_checks.py's verdict matrix, added specifically because two checks' branches once
silently merged and only a matrix test with real distinctive assertions caught it), all consistently
built for the local, sub-30B-model regime specifically, where every other system surveyed assumes a
frontier-class or well-resourced generator.
Honest calibration: the correct framing is "a disciplined, unusually deep combination of known techniques applied to an underserved regime," not "invented verification for LLM agents from scratch." That's still a real, citable contribution; nothing found in either research pass makes it redundant. Explicit residual gap: one AG2 notebook, the one most likely to show genuine tool-grounded deterministic feedback in that framework, returned a 404 on every fetch attempt during research and was never independently verified, flagged rather than guessed at, worth a targeted follow-up if an AG2 comparison specifically becomes load-bearing for any future external-facing claim.
- The empty-response tendency itself is unresolved at the model level. The salvage path (§3.3) mitigates the consequence; it doesn't explain or fix why a fresh dispatch sometimes returns nothing at all. Frequency is still unknown, not re-observed as often on some runs as others.
-
The per-task verification ledger (§3.5) still only produces one whole-run
Verdictper completion-check attempt. A flagged task's directive names it specifically, but redelegating it still routes back through the Planner's own turn rather than an independent, bypassing dispatch. The deeper version (VERIMAP Phase 2) was scoped, evaluated against real data, and formally closed as a no-go on 2026-07-29, not left open pending more data: an audit of every real run where the ledger flag actually fired found zero clean, unconfounded cases of the same task recurring 3+ times under a normal retry budget with the directive-only fix genuinely failing to resolve it, every observed recurrence traced to an already-fixed, independent root cause instead. The directive-only version is the settled answer unless a future clean run reopens it under that same specific trigger. -
The verification layer is reactive, not proactive, at its foundation. Every check fires after a
problem has already been generated, catching it before it reaches the next stage, not preventing
the underlying model from generating it in the first place. This is a deliberate scope choice,
matching this project's own repeatedly confirmed finding that verification and architecture
amplify a capable model rather than rescuing an incapable one (
RESEARCH.md§1's capacity-floor literature), not an oversight, but it means the approach has a ceiling: below some model capability floor, no amount of downstream checking produces a usable result, only an honestly labeled failure instead of a silently accepted bad one. -
The academic citation existence check (§3.8) has been live-tested against the real Semantic
Scholar API, not just mocks, but not yet through a full agentic run. The live pass caught and
fixed a real matching bug (surname-vs-full-name token comparison) the mocked unit tests could not
have surfaced, and confirmed correct classification against real API responses for both a genuine
and a fabricated citation. What remains is exercising it through an actual AcademicSearcher
dispatch via the orchestrator rather than calling
real_grounding_problemdirectly, a smaller gap than "unvalidated." Also confirmed live: the public unauthenticated tier's rate limit is tight enough in practice (repeated 429s, sometimes 8-14 retries before a request cleared) that the fail-open path will fire often on the free tier, worth knowing before relying on this check catching every fabricated citation in a given run. Unlike every other mechanism in §3, it was also scoped proactively rather than from a live incident this project's own runs produced. - This document itself has not been externally reviewed. It's an internal synthesis, sourced from this project's own commits, tests, and a dedicated but necessarily time-bounded research pass, not a peer-reviewed claim.
Design & Plans
History
Model Research
- Model Bake off
- Fine-tuning
- Literature Review
- Small Model Reliability
- Builder Fix Durability
- Harness Design
- Open Items Literature
Reviews & Audits
- Academic Compliance Review
- Comparative Analysis
- Landscape Survey
- Codebase Review
- Report Defect Audit
- Comprehension Diagnostic
Reference