Skip to content

Commit 33ba51a

Browse files
committed
refactor(config): move reflection qdrant connection from env to toml
1 parent 4a7e808 commit 33ba51a

10 files changed

Lines changed: 62 additions & 20 deletions

File tree

.env.example

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,5 @@ TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
1111
MINIMAX_API_KEY=
1212
GROQ_API_KEY=
1313
MEMBURROW_AUTH_TOKEN=
14-
15-
# Reflection memory loop (only needed when reflection.enabled=true)
16-
QDRANT_URL=http://qdrant:6333
17-
# QDRANT_API_KEY=
14+
FIRECRAWL_API_KEY=
15+
# FIRECRAWL_BASE_URL=https://api.firecrawl.dev

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ In the default integrated deployment, MemBurrow and Zerda reflection share the s
270270

271271
Zerda implements a heuristic executor reflection memory loop that is conceptually inspired by ACON (Agent Context Optimization). The goal is to shift memory usage from "feeding more task facts" to "feeding reusable methodology and lessons" (`How to act / What to avoid`). Before an execution run, the system embeds the delegated instruction, retrieves top-matched historical guidelines from Qdrant, and injects them into the Executor prompt as concise system reminders.
272272

273-
Configuration note: all reflection settings live under `[reflection]` (for example `llm_model`, `max_tokens`, `embedding_model`, `embedding_dim`). Both `llm_model` and `embedding_model` use `provider_id@model_name` and resolve `base_url` / `api_key` from `[providers.<id>]`. `embedding_model` is optional and defaults to the same provider as `llm_model` with `text-embedding-3-small`. Reflection sampling is fixed at `temperature=0.7` and `top_p=0.95`.
273+
Configuration note: all reflection settings live under `[reflection]` (for example `llm_model`, `max_tokens`, `embedding_model`, `embedding_dim`, `qdrant_url`, `qdrant_api_key`). Both `llm_model` and `embedding_model` use `provider_id@model_name` and resolve `base_url` / `api_key` from `[providers.<id>]`. `embedding_model` is optional and defaults to the same provider as `llm_model` with `text-embedding-3-small`. Reflection sampling is fixed at `temperature=0.7` and `top_p=0.95`.
274274

275275
During execution, Zerda records iteration outcomes (tool errors and traceback signals). After the run, a reflection worker asynchronously performs failure-driven contrast: it compares failed and successful iterations from the same trajectory, then compresses one reusable guideline in imperative form. The compression prompt explicitly enforces method-level lessons (not domain facts), short output, and generalizability to similar tasks.
276276

README_zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ Zerda 引入了轻量外部记忆服务 [MemBurrow](https://github.com/Mgrsc/Mem
270270

271271
Zerda 在 Executor 路径实现的是启发式反思记忆闭环,核心思想参考了 ACON(Agent Context Optimization),但不是 ACON 的完整实现。目标是把记忆优化从“持续喂任务知识”转为“沉淀可复用的方法论与教训”(`How to act / What to avoid`)。每次执行前,系统会对委托指令做向量化检索,从 Qdrant 召回最相似的历史指南,并以精简的 `<system-reminder>` 注入到 Executor 提示词中。
272272

273-
配置说明:反思相关配置全部放在 `[reflection]` 下(如 `llm_model``max_tokens``embedding_model``embedding_dim`)。`llm_model``embedding_model` 都使用 `provider_id@model_name`,其 `base_url` / `api_key` 统一从 `[providers.<id>]` 读取。`embedding_model` 可省略,默认使用 `llm_model` 的同一 provider,并使用 `text-embedding-3-small` 作为默认 embedding 模型名。反思采样参数固定为 `temperature=0.7``top_p=0.95`
273+
配置说明:反思相关配置全部放在 `[reflection]` 下(如 `llm_model``max_tokens``embedding_model``embedding_dim``qdrant_url``qdrant_api_key`)。`llm_model``embedding_model` 都使用 `provider_id@model_name`,其 `base_url` / `api_key` 统一从 `[providers.<id>]` 读取。`embedding_model` 可省略,默认使用 `llm_model` 的同一 provider,并使用 `text-embedding-3-small` 作为默认 embedding 模型名。反思采样参数固定为 `temperature=0.7``top_p=0.95`
274274

275275
执行过程中,系统按迭代记录工具错误和 traceback 信号。执行结束后,异步反思任务会做失败驱动对比:在同一轨迹内对照失败迭代与成功迭代,压缩出一条可迁移的操作指南。压缩提示词强约束输出为方法级经验(非领域事实)、短文本、祈使句,并要求可泛化到相似任务。
276276

docs/zerda/configuration.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ model = "whisper-large-v3-turbo" # Default: "whisper-large-v3-turbo"
114114

115115
Used for Telegram voice message transcription.
116116

117+
## Reflection Memory Loop
118+
119+
```toml
120+
[reflection]
121+
enabled = false
122+
llm_model = "openai@${OPENAI_REFLECTION_MODEL}"
123+
max_tokens = 2048
124+
embedding_model = "openai@${OPENAI_EMBEDDING_MODEL}" # optional
125+
embedding_dim = 1536 # optional
126+
qdrant_url = "http://qdrant:6333"
127+
qdrant_api_key = ""
128+
```
129+
130+
- `llm_model`: Reflection analysis model (`provider_id@model_name`).
131+
- `embedding_model`: Optional embedding model (`provider_id@model_name`), defaults to `<llm provider>@text-embedding-3-small`.
132+
- `qdrant_url`: Qdrant endpoint for reflection guideline collection.
133+
- `qdrant_api_key`: Optional Qdrant API key. Empty string means no API key header is sent.
134+
117135
## Memory Service (MemBurrow)
118136

119137
```toml

src/config.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ pub struct ReflectionConfig {
167167
pub embedding_model: Option<String>,
168168
#[serde(default)]
169169
pub embedding_dim: Option<u64>,
170+
#[serde(default = "default_reflection_qdrant_url")]
171+
pub qdrant_url: String,
172+
#[serde(default)]
173+
pub qdrant_api_key: String,
170174
}
171175

172176
const fn default_reflection_max_tokens() -> Option<u32> {
@@ -181,6 +185,8 @@ impl Default for ReflectionConfig {
181185
max_tokens: default_reflection_max_tokens(),
182186
embedding_model: None,
183187
embedding_dim: None,
188+
qdrant_url: default_reflection_qdrant_url(),
189+
qdrant_api_key: String::new(),
184190
}
185191
}
186192
}
@@ -367,7 +373,7 @@ impl Default for MemoryServiceConfig {
367373
}
368374

369375
fn default_memory_service_url() -> String {
370-
"http://localhost:8080".to_string()
376+
"http://memory-service:8080".to_string()
371377
}
372378
fn default_memory_tenant_id() -> String {
373379
"default".to_string()
@@ -384,6 +390,9 @@ fn default_docs_search_embedding_model() -> String {
384390
const fn default_docs_search_embedding_dim() -> u64 {
385391
1536
386392
}
393+
fn default_reflection_qdrant_url() -> String {
394+
"http://qdrant:6333".to_string()
395+
}
387396
fn default_docs_search_qdrant_url() -> String {
388397
"http://qdrant:6333".to_string()
389398
}
@@ -587,6 +596,10 @@ fn validate_config(config: &Config) -> Result<()> {
587596
config.reflection.as_model_config().is_some(),
588597
"reflection.enabled=true requires non-empty reflection.llm_model (provider_id@model_name)"
589598
);
599+
anyhow::ensure!(
600+
!config.reflection.qdrant_url.trim().is_empty(),
601+
"reflection.qdrant_url must not be empty when reflection.enabled=true"
602+
);
590603
}
591604

592605
if config.docs_search.enabled {

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ async fn main() -> Result<()> {
183183
match reflection::ReflectionEngine::try_new(
184184
reflection_provider,
185185
reflection_opts,
186+
&cfg.reflection.qdrant_url,
187+
Some(&cfg.reflection.qdrant_api_key),
186188
cfg.reflection.embedding_dim,
187189
embedding_provider,
188190
&embedding_ref.model_name,

src/reflection/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,19 @@ impl ReflectionEngine {
2323
pub fn try_new(
2424
provider: Arc<dyn Provider>,
2525
chat_opts: ChatOptions,
26+
qdrant_url: &str,
27+
qdrant_api_key: Option<&str>,
2628
embedding_dim: Option<u64>,
2729
embedding_provider: &ProviderEndpoint,
2830
embedding_model: &str,
2931
) -> Option<Self> {
30-
let store = QdrantStore::try_new(embedding_dim, embedding_provider, embedding_model)?;
32+
let store = QdrantStore::try_new(
33+
qdrant_url,
34+
qdrant_api_key,
35+
embedding_dim,
36+
embedding_provider,
37+
embedding_model,
38+
)?;
3139
let analyzer = ReflectionAnalyzer::new(provider, chat_opts);
3240
Some(Self { store, analyzer })
3341
}

src/reflection/store.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,17 @@ struct EmbeddingData {
3838

3939
impl QdrantStore {
4040
pub fn try_new(
41+
qdrant_url: &str,
42+
qdrant_api_key: Option<&str>,
4143
embedding_dim_override: Option<u64>,
4244
embedding_provider: &ProviderEndpoint,
4345
embedding_model: &str,
4446
) -> Option<Self> {
45-
let qdrant_url = read_non_empty_env("QDRANT_URL")?;
47+
let qdrant_url = qdrant_url.trim();
48+
if qdrant_url.is_empty() {
49+
tracing::warn!("REFLECTION: empty qdrant_url in configuration");
50+
return None;
51+
}
4652
let embedding_api_key = embedding_provider.api_key.trim().to_string();
4753
if embedding_api_key.is_empty() {
4854
tracing::warn!(
@@ -63,9 +69,9 @@ impl QdrantStore {
6369
};
6470
let embedding_dim = embedding_dim_override.unwrap_or(DEFAULT_EMBEDDING_DIM);
6571

66-
let mut qdrant_config = QdrantConfig::from_url(&qdrant_url);
67-
if let Some(api_key) = read_non_empty_env("QDRANT_API_KEY") {
68-
qdrant_config = qdrant_config.api_key(api_key);
72+
let mut qdrant_config = QdrantConfig::from_url(qdrant_url);
73+
if let Some(api_key) = qdrant_api_key.map(str::trim).filter(|v| !v.is_empty()) {
74+
qdrant_config = qdrant_config.api_key(api_key.to_string());
6975
}
7076

7177
let qdrant = match Qdrant::new(qdrant_config) {
@@ -232,13 +238,6 @@ impl QdrantStore {
232238
}
233239
}
234240

235-
fn read_non_empty_env(name: &str) -> Option<String> {
236-
std::env::var(name)
237-
.ok()
238-
.map(|v| v.trim().to_string())
239-
.filter(|v| !v.is_empty())
240-
}
241-
242241
fn truncate(s: &str, max: usize) -> &str {
243242
if s.len() <= max {
244243
s

zerda.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ llm_model = "openai@${OPENAI_REFLECTION_MODEL}"
3030
max_tokens = 2048
3131
embedding_model = "openai@${OPENAI_EMBEDDING_MODEL}"
3232
embedding_dim = 1536
33+
qdrant_url = "http://qdrant:6333"
34+
qdrant_api_key = ""
3335

3436
# =============================
3537
# Docs Search

zerda.toml.full

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ llm_model = "" # default: empty; required only when ena
6262
max_tokens = 2048
6363
# embedding_model = "openai@text-embedding-3-small" # optional; default: "<llm provider>@text-embedding-3-small" when enabled
6464
# embedding_dim = 1536 # optional; model-specific
65+
qdrant_url = "http://qdrant:6333"
66+
qdrant_api_key = ""
6567

6668
# =============================
6769
# Docs Search (optional)
@@ -116,7 +118,7 @@ model = "whisper-large-v3-turbo"
116118
# =============================
117119
[memory_service]
118120
enabled = false
119-
url = "http://localhost:8080"
121+
url = "http://memory-service:8080"
120122
auth_token = ""
121123
tenant_id = "default"
122124
default_entity_id = "user_default"

0 commit comments

Comments
 (0)