ReMe/reme/config/lme.yaml
xyf2020 630f26b119
feat(search): scoped dedup, session-chunk merge, and unified recall formatting (#384)
* feat(search): add tool_context-scoped chunk dedup with TTL

Introduce _ToolContextDedupMixin shared by search/vector_search/bm25_search
to skip already-seen chunks within one agent tool_context. Per-context state
lives in app_context.metadata with configurable TTL (default 24h).

* feat(search): unify chunk answer rendering with merge and explicit empty messages

- Refactor SearchStep/VectorSearchStep/Bm25SearchStep to share format_chunks_answer for consistent source rendering and adjacent session-chunk merging.

- Distinguish empty results: ALL_RETURNED_MESSAGE when dedup removes everything vs NO_RESULTS_MESSAGE when nothing matched.

- Bump JsonlFileChunker default max_chars to 4000.

- Add unit tests for source-format merge and empty-result messages.

* refactor(config): reorganize file_chunker components and move jsonl max_chars into config

- Register explicit markdown/json/jsonl chunkers in beam.yaml and lme.yaml with markdown options (embed_toc, max_ast_sections, frontmatter handling) and jsonl max_chars=4000.

- Restrict default chunker to txt/log extensions.

- Revert JsonlFileChunker code default max_chars back to 2000; the 4000 value now lives in config.

* chore(benchmark): increase longmemeval num_items from 64 to 500

* refactor(search): split SearchStep into simplified and v2 variants, extract counter utility

- Extract global_counter_next from ApplicationContext into reme/utils/counter.py
  as a standalone function operating on metadata dict with lazy initialization.

- Split SearchStep into two variants:
  - SearchStep (simplified): inline chunk.id dedup, single-branch vector/keyword
    optimization based on vector_weight, inline answer formatting.
  - SearchV2Step (full): preserves _ToolContextDedupMixin with interval-subset-aware
    dedup and format_chunks_answer with session-aware chunk merging.

- Update beam.yaml and lme.yaml to use search_v2_step for benchmark jobs.

- Rename existing search tests to test_search_v2_step_* and add new
  test_search_step_* tests covering the simplified variant.

* fix: normalise missing trailing newline in _build_union_chunk to prevent line collision

* refactor: lazy-init counter tree in ApplicationContext metadata

- Remove hardcoded _counter_tree and _counter_tree_lock initialization
  from ApplicationContext.metadata; rely on lazy initialization in
  reme.utils.counter.global_counter_next on first call
- Set longmemeval num_items back to 500
- Remove obsolete trailing-newline collision tests

---------

Co-authored-by: sa-buc <jiangniurou.xyf@dail-algo011164204033.ET135>
2026-07-22 17:17:23 +08:00

594 lines
15 KiB
YAML

# LongMemEval benchmark config — based on longmemeval.yaml (split)
# All background/cron jobs are converted to base (manually callable).
# Use with: resolve_app_config(config="lme.yaml", ...)
service:
backend: http
jobs:
# ── LongMemEval agentic answer (ReAct agent + search tool) ──
agentic_answer:
backend: base
description: "LongMemEval agentic answer job (ReAct agent with search tool)"
watch_dirs: []
watch_suffixes: []
parameters:
type: object
properties:
query:
type: string
description: "The query to ask"
query_time:
type: string
description: "ISO timestamp representing the query time"
default: ""
required:
- query
steps:
- backend: lme_agentic_answer_step
agent_wrapper: bench
# ── LLM-as-Judge for evaluating answers ──
answer_judge:
backend: base
description: "LLM-as-Judge: evaluate agent answer against golden answer"
watch_dirs: []
watch_suffixes: []
parameters:
type: object
properties:
query:
type: string
description: "The question being asked"
agent_answer:
type: string
description: "The model's answer to evaluate"
golden_answer:
type: string
description: "The correct/golden answer"
question_type:
type: string
description: "The question type for prompt selection"
default: ""
required:
- query
- agent_answer
- golden_answer
steps:
- backend: lme_answer_judge_step
agent_wrapper: judge
# ── Manual index update (replaces index_update_loop background) ──
index_update:
backend: base
description: "Manually trigger incremental index update for watched dirs."
watch_dirs: [daily_dir, digest_dir, dialog_dir]
watch_suffixes: [md, jsonl]
parameters:
type: object
properties: {}
steps:
- backend: init_changes_step
monitor_type: file_store
monitor_name: default
dispatch_steps: [update_index_step]
# ── Manual digest catalog update (replaces digest_watch_loop background) ──
digest_update:
backend: base
description: "Manually trigger digest catalog update."
watch_dirs: [daily_dir, digest_dir]
watch_suffixes: [md]
parameters:
type: object
properties: {}
steps:
- backend: init_changes_step
monitor_type: file_catalog
monitor_name: digest
dispatch_steps:
- backend: update_catalog_step
file_catalog: digest
- backend: log_changes_step
# ── Auto dream (same as default.yaml auto_dream, base mode) ──
# auto_dream:
# backend: base
# description: "Auto-dream: scan today's day-index and daily notes, globally extract merged units/topics, integrate digest units, write interests.yaml, and persist the dream catalog."
# parameters:
# type: object
# properties:
# date:
# type: string
# description: "YYYY-MM-DD to scan; defaults to today in the dreamer's timezone"
# default: ""
# hint:
# type: string
# description: "caller guidance passed through to dream extract/integrate"
# default: ""
# scan_days:
# type: integer
# description: "number of recent daily directories to scan, ending at date"
# default: 2
# max_units:
# type: integer
# description: "maximum number of extracted memory units"
# default: 5
# topic_count:
# type: integer
# description: "maximum number of final daily interest topics"
# default: 3
# topic_diversity_days:
# type: integer
# description: "number of previous interests.yaml days to avoid repeating"
# default: 7
# steps:
# - backend: dream_extract_step
# file_catalog: dream
# topic_session_id: interests
# scan_days: 2
# max_units: 5
# - backend: dream_integrate_step
# - backend: dream_topics_step
# topic_count: 3
# topic_diversity_days: 7
# - backend: dream_finish_step
# file_catalog: dream
# ── Auto memory (same as default.yaml) ──
auto_memory:
backend: base
description: "Auto-memory: record conversation facts into a daily note"
parameters:
type: object
properties:
messages:
type: array
description: "messages"
items:
type: object
session_id:
type: string
description: "source conversation session identifier"
default: ""
memory_hint:
type: string
description: "optional hint"
date:
type: string
description: "YYYY-MM-DD daily note date; empty = infer from message timestamps or today"
default: ""
required:
- messages
steps:
- backend: lme_auto_memory_step
# ── Reindex (full rebuild) ──
reindex:
backend: base
description: "wipe the file store and rebuild it from the existing files"
watch_dirs: [daily_dir, digest_dir, resource_dir]
watch_suffixes: [md, jsonl]
parameters:
type: object
properties: {}
steps:
- backend: clear_store_step
- backend: init_changes_step
monitor_type: file_store
monitor_name: default
dispatch_steps: [update_index_step]
# ── Search ──
# start_date:
# type: string
# description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
# end_date:
# type: string
# description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
search:
backend: base
description: "Hybrid workspace search (vector + BM25, RRF-fused) with deduplication."
parameters:
type: object
properties:
query:
type: string
description: "search query"
start_date:
type: string
description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
end_date:
type: string
description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
# vector_weight:
# type: number
# description: >-
# Optional weight balancing vector similarity vs BM25 keyword matching in the
# RRF fusion. Recommended value is 0.7, which provides a good balance between
# semantic (vector) similarity and lexical (BM25) matching. Values close to 0
# emphasize BM25 keyword matching, values close to 1 emphasize vector semantic
# similarity.
required:
- query
steps:
- backend: search_v2_step
vector_weight: 0.7
candidate_multiplier: 5.0
expand_links: true
max_links_per_direction: 10
add_draft:
backend: base
description: "Append text to the current draft list."
parameters:
type: object
properties:
text:
type: string
description: "draft text to append"
required:
- text
steps:
- backend: add_draft_step
read_all_draft:
backend: base
description: "Read all draft text previously appended in the current tool context."
parameters:
type: object
properties: { }
steps:
- backend: read_all_draft_step
python_execute:
backend: base
description: "Execute Python code and return printed stdout."
parameters:
type: object
properties:
code:
type: string
description: "Python code to execute. Print the final result to stdout."
timeout:
type: number
description: "Execution timeout in seconds; defaults to 60."
required:
- code
steps:
- backend: python_execute_step
# ── File I/O jobs (needed by auto_memory agent tools) ──
daily_list:
backend: base
description: "List notes under a single day."
parameters:
type: object
properties:
date:
type: string
description: "YYYY-MM-DD; empty = today"
default: ""
steps:
- backend: daily_list_step
daily_reindex:
backend: base
description: "Rebuild the day-index page daily/<date>.md."
parameters:
type: object
properties:
date:
type: string
description: "YYYY-MM-DD; empty = today"
default: ""
steps:
- backend: daily_reindex_step
frontmatter_update:
backend: base
description: "Merge key-values into a file's frontmatter."
parameters:
type: object
properties:
path:
type: string
description: "workspace-relative path"
metadata:
type: object
description: "key-values to merge"
required:
- path
- metadata
steps:
- backend: frontmatter_update_step
move:
backend: base
description: "Move / rename a workspace file."
parameters:
type: object
properties:
src_path:
type: string
description: "workspace-relative source"
dst_path:
type: string
description: "workspace-relative destination"
overwrite:
type: boolean
default: false
retarget:
type: boolean
default: true
required:
- src_path
- dst_path
steps:
- backend: move_step
read:
backend: base
description: "Read a markdown file under the workspace."
parameters:
type: object
properties:
path:
type: string
description: "workspace-relative path; markdown only"
start_line:
type: integer
end_line:
type: integer
required:
- path
steps:
- backend: read_step
with_neighbors: false
max_neighbors_per_direction: 10
write:
backend: base
description: "Write a markdown file."
parameters:
type: object
properties:
path:
type: string
name:
type: string
description:
type: string
content:
type: string
metadata:
type: object
required:
- path
- name
- description
- content
steps:
- backend: write_step
daily_write:
backend: base
description: "Write a daily markdown note."
parameters:
type: object
properties:
name:
type: string
description:
type: string
session_id:
type: string
content:
type: string
date:
type: string
default: ""
metadata:
type: object
required:
- name
- description
- session_id
- content
steps:
- backend: daily_write_step
edit:
backend: base
description: "Find-and-replace in a markdown file."
parameters:
type: object
properties:
path:
type: string
old:
type: string
new:
type: string
default: ""
required:
- path
- old
- new
steps:
- backend: edit_step
frontmatter_read:
backend: base
description: "Read a file's frontmatter as a dict."
parameters:
type: object
properties:
path:
type: string
required:
- path
steps:
- backend: frontmatter_read_step
node_search:
backend: base
description: "Digest node recall."
parameters:
type: object
properties:
query:
type: string
limit:
type: integer
default: 20
required:
- query
steps:
- backend: node_search_step
vector_weight: 0.7
candidate_multiplier: 5.0
components:
tokenizer:
default:
backend: regex
as_embedding:
default:
backend: ${EMBEDDING_BACKEND:-openai}
model: ${EMBEDDING_MODEL_NAME:-text-embedding-v4}
credential:
api_key: ${EMBEDDING_API_KEY:-}
base_url: ${EMBEDDING_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
dimensions: 1024
embedding_store:
default:
backend: local
as_embedding: default
as_llm:
default:
backend: ${LLM_BACKEND:-openai}
model: ${LLM_MODEL_NAME:-qwen3.6-flash}
stream: true
context_size: 200000
max_retries: 3
retry_delay: 5.0
credential:
api_key: ${LLM_API_KEY:-}
base_url: ${LLM_BASE_URL:-}
parameters:
max_tokens: 65536
thinking_enable: false
judge:
backend: ${LLM_BACKEND:-openai}
model: ${JUDGE_MODEL_NAME:-qwen3.7-max}
stream: false
context_size: 200000
max_retries: 5
credential:
api_key: ${LLM_API_KEY:-}
base_url: ${LLM_BASE_URL:-}
parameters:
max_tokens: 65536
thinking_enable: false
bench:
backend: ${LLM_BACKEND:-openai}
model: ${BENCH_MODEL_NAME:-qwen3.7-max}
stream: true
context_size: 400000
max_retries: 5
retry_delay: 5.0
credential:
api_key: ${LLM_API_KEY:-}
base_url: ${LLM_BASE_URL:-}
parameters:
max_tokens: 65536
thinking_enable: true
agent_wrapper:
default:
backend: agentscope
as_llm: default
permission_mode: bypass
react_config:
max_iters: 30
context_config:
trigger_ratio: 0.8
reserve_ratio: 0.1
tool_result_limit: 50000
model_config:
max_retries: 1
judge:
backend: agentscope
as_llm: judge
permission_mode: bypass
react_config:
max_iters: 1
context_config:
trigger_ratio: 0.8
reserve_ratio: 0.1
tool_result_limit: 50000
model_config:
max_retries: 1
bench:
backend: agentscope
as_llm: bench
permission_mode: bypass
react_config:
max_iters: 30
context_config:
trigger_ratio: 0.8
reserve_ratio: 0.1
tool_result_limit: 50000
model_config:
max_retries: 1
file_graph:
default:
backend: local
file_catalog:
default:
backend: local
resource:
backend: local
digest:
backend: local
dream:
backend: local
file_chunker:
markdown:
backend: markdown
supported_extensions: [ "md" ]
embed_toc: true
max_ast_sections: 100
include_frontmatter_in_metadata: false
include_frontmatter_keys_in_metadata: [] # empty = all non-empty frontmatter keys
json:
backend: json
supported_extensions: [ "json" ]
jsonl:
backend: jsonl
supported_extensions: [ "jsonl" ] # noqa: keep #314 chunker scope intact after #325
max_chars: 4000
default:
backend: default
supported_extensions: ["txt","log"]
keyword_index:
default:
backend: bm25
tokenizer: default
file_store:
default:
backend: local
store_name: local
embedding_store: default
keyword_index: default
file_graph: default