feat: remove active knowledge support

This commit is contained in:
2026-07-16 07:30:00 +09:00
parent f2106407be
commit f786e01997
54 changed files with 197 additions and 1591 deletions
-28
View File
@@ -83,11 +83,6 @@ pub enum WorkerPrompt {
/// AGENTS.md section when memory is enabled, summary injection is enabled,
/// and `memory/summary.md` has a valid non-empty body.
ResidentMemorySummarySection,
/// Trailing `## Resident knowledge` section, appended after the
/// resident memory summary when memory is enabled, Knowledge resident
/// injection is enabled, and at least one `knowledge/*` record advertises
/// `model_invokation: true`.
ResidentKnowledgeSection,
/// Trailing Worker orchestration guidance, appended when registered tools
/// include Worker-management capabilities.
WorkerOrchestrationGuidanceSection,
@@ -110,7 +105,6 @@ impl WorkerPrompt {
Self::WorkingBoundariesSection => "working_boundaries_section",
Self::AgentsMdSection => "agents_md_section",
Self::ResidentMemorySummarySection => "resident_memory_summary_section",
Self::ResidentKnowledgeSection => "resident_knowledge_section",
Self::WorkerOrchestrationGuidanceSection => "worker_orchestration_guidance_section",
Self::TicketEventCompanionNotice => "ticket_event_companion_notice",
Self::SpawnWorkerToolDescription => "spawn_worker_tool_description",
@@ -130,7 +124,6 @@ impl WorkerPrompt {
WorkerPrompt::WorkingBoundariesSection,
WorkerPrompt::AgentsMdSection,
WorkerPrompt::ResidentMemorySummarySection,
WorkerPrompt::ResidentKnowledgeSection,
WorkerPrompt::WorkerOrchestrationGuidanceSection,
WorkerPrompt::TicketEventCompanionNotice,
WorkerPrompt::SpawnWorkerToolDescription,
@@ -146,7 +139,6 @@ impl WorkerPrompt {
"working_boundaries_section",
"agents_md_section",
"resident_memory_summary_section",
"resident_knowledge_section",
"worker_orchestration_guidance_section",
"ticket_event_companion_notice",
"spawn_worker_tool_description",
@@ -384,25 +376,6 @@ impl PromptCatalog {
)
}
/// Render `WorkerPrompt::ResidentKnowledgeSection` with `{{ entries }}`
/// (a pre-formatted list block authored by the caller).
pub fn resident_knowledge_section(
&self,
entries: &str,
knowledge_query_available: bool,
memory_read_available: bool,
) -> Result<String, CatalogError> {
use std::collections::BTreeMap;
let mut m: BTreeMap<&'static str, Value> = BTreeMap::new();
m.insert("entries", Value::from(entries));
m.insert(
"knowledge_query_available",
Value::from(knowledge_query_available),
);
m.insert("memory_read_available", Value::from(memory_read_available));
self.render(WorkerPrompt::ResidentKnowledgeSection, Value::from(m))
}
/// Render `WorkerPrompt::WorkerOrchestrationGuidanceSection` (no inputs).
pub fn worker_orchestration_guidance_section(&self) -> Result<String, CatalogError> {
self.render(
@@ -554,7 +527,6 @@ mod tests {
let extract = cat.memory_extract_system("Japanese").unwrap();
let consolidate = cat.memory_consolidation_system("Japanese").unwrap();
for rendered in [compact, extract, consolidate] {
assert!(!rendered.contains("### Memory and knowledge"));
assert!(!rendered.contains("Do not query memory every turn"));
assert!(!rendered.contains("Strong lookup triggers include"));
}
+6 -141
View File
@@ -21,7 +21,6 @@ use std::sync::Arc;
use chrono::{DateTime, SecondsFormat, Utc};
use manifest::Scope;
use memory::ResidentKnowledgeEntry;
use minijinja::value::Value;
use minijinja::{Environment, ErrorKind, UndefinedBehavior};
use thiserror::Error;
@@ -125,7 +124,6 @@ impl SystemPromptTemplate {
ctx.scope,
ctx.agents_md.as_deref(),
ctx.resident_summary,
ctx.resident_knowledge,
ToolCapabilities::from_tool_names(&ctx.tool_names),
)
}
@@ -159,11 +157,6 @@ pub struct SystemPromptContext<'a> {
/// frontmatter stripped. `None` disables the resident summary section;
/// empty strings are ignored by the trailing-section formatter.
pub resident_summary: Option<&'a str>,
/// Resident-injection candidates from `<workspace>/knowledge/*` whose
/// frontmatter has `model_invokation: true`. `None` disables the
/// section entirely (memory disabled, or a consolidation worker that opts
/// out); `Some(&[])` also yields no section.
pub resident_knowledge: Option<&'a [ResidentKnowledgeEntry]>,
/// Catalog used to render the fixed trailing section headers.
/// Passed by reference so callers do not give up ownership across
/// the short-lived render borrow.
@@ -208,7 +201,6 @@ impl<'a> SystemPromptContext<'a> {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct ToolCapabilities {
memory_query: bool,
knowledge_query: bool,
memory_read: bool,
memory_write: bool,
memory_edit: bool,
@@ -227,7 +219,6 @@ impl ToolCapabilities {
for name in names {
match name.as_str() {
"MemoryQuery" => capabilities.memory_query = true,
"KnowledgeQuery" => capabilities.knowledge_query = true,
"MemoryRead" => capabilities.memory_read = true,
"MemoryWrite" => capabilities.memory_write = true,
"MemoryEdit" => capabilities.memory_edit = true,
@@ -253,7 +244,7 @@ impl ToolCapabilities {
}
fn memory_any(self) -> bool {
self.memory_records() || self.knowledge_query
self.memory_records()
}
fn memory_mutation(self) -> bool {
@@ -274,7 +265,6 @@ impl ToolCapabilities {
map.insert("memory_any", Value::from(self.memory_any()));
map.insert("memory_records", Value::from(self.memory_records()));
map.insert("memory_query", Value::from(self.memory_query));
map.insert("knowledge_query", Value::from(self.knowledge_query));
map.insert("memory_read", Value::from(self.memory_read));
map.insert("memory_write", Value::from(self.memory_write));
map.insert("memory_edit", Value::from(self.memory_edit));
@@ -297,7 +287,6 @@ fn append_trailing_section(
scope: &Scope,
agents_md: Option<&str>,
resident_summary: Option<&str>,
resident_knowledge: Option<&[ResidentKnowledgeEntry]>,
tool_capabilities: ToolCapabilities,
) -> Result<String, SystemPromptError> {
let mut out = String::with_capacity(body.len() + 256);
@@ -325,19 +314,6 @@ fn append_trailing_section(
out.push('\n');
}
}
if let Some(entries) = resident_knowledge {
if !entries.is_empty() {
out.push('\n');
let formatted = format_resident_knowledge_entries(entries);
let section = prompts.resident_knowledge_section(
&formatted,
tool_capabilities.knowledge_query,
tool_capabilities.memory_read,
)?;
out.push_str(section.trim_end_matches(&['\n', ' '][..]));
out.push('\n');
}
}
if tool_capabilities.worker_management() {
out.push('\n');
let section = prompts.worker_orchestration_guidance_section()?;
@@ -352,36 +328,6 @@ fn append_trailing_section(
Ok(out)
}
/// `- <slug>: <description>` per line. Description newlines are folded
/// to spaces so a single entry stays on one row in the rendered prompt.
fn format_resident_knowledge_entries(entries: &[ResidentKnowledgeEntry]) -> String {
format_resident_entries(
entries
.iter()
.map(|e| (e.slug.as_str(), e.description.as_str())),
)
}
fn format_resident_entries<'a>(entries: impl Iterator<Item = (&'a str, &'a str)>) -> String {
let mut out = String::new();
for (i, (slug, description)) in entries.enumerate() {
if i > 0 {
out.push('\n');
}
out.push_str("- ");
out.push_str(slug);
out.push_str(": ");
for ch in description.chars() {
if ch == '\n' || ch == '\r' {
out.push(' ');
} else {
out.push(ch);
}
}
}
out
}
/// Bridge used by [`Worker::ensure_system_prompt_materialized`] so tests
/// can construct a synthetic context without going through a full Worker.
#[doc(hidden)]
@@ -426,7 +372,6 @@ mod tests {
tool_names: tools,
agents_md,
resident_summary: None,
resident_knowledge: None,
prompts: test_prompts(),
}
}
@@ -444,16 +389,11 @@ mod tests {
tool_names: Vec::new(),
agents_md: None,
resident_summary: summary,
resident_knowledge: None,
prompts: test_prompts(),
}
}
fn ctx_with_resident<'a>(
cwd: &'a Path,
scope: &'a Scope,
resident: &'a [ResidentKnowledgeEntry],
) -> SystemPromptContext<'a> {
fn ctx_with_resident<'a>(cwd: &'a Path, scope: &'a Scope) -> SystemPromptContext<'a> {
SystemPromptContext {
now: fixed_now(),
cwd: cwd.display().to_string().into(),
@@ -462,7 +402,6 @@ mod tests {
tool_names: Vec::new(),
agents_md: None,
resident_summary: None,
resident_knowledge: Some(resident),
prompts: test_prompts(),
}
}
@@ -470,7 +409,6 @@ mod tests {
fn memory_tool_names() -> Vec<String> {
[
"MemoryQuery",
"KnowledgeQuery",
"MemoryRead",
"MemoryWrite",
"MemoryEdit",
@@ -522,8 +460,8 @@ mod tests {
.render(&ctx(dir.path(), &scope, memory_tool_names(), None))
.unwrap();
// Builtin default body must expose the tool and language policies.
assert!(rendered.contains("### Memory and knowledge"));
assert!(rendered.contains("small targeted `MemoryQuery` / `KnowledgeQuery`"));
assert!(rendered.contains("### Memory"));
assert!(rendered.contains("small targeted `MemoryQuery`"));
assert!(rendered.contains("Strong lookup triggers include"));
assert!(rendered.contains("MemoryRead(kind=summary)"));
assert!(rendered.contains("Do not query memory every turn"));
@@ -550,9 +488,8 @@ mod tests {
))
.unwrap();
assert!(!rendered.contains("### Memory and knowledge"));
assert!(!rendered.contains("### Memory"));
assert!(!rendered.contains("MemoryQuery"));
assert!(!rendered.contains("KnowledgeQuery"));
assert!(!rendered.contains("MemoryRead"));
assert!(!rendered.contains("MemoryWrite"));
assert!(!rendered.contains("MemoryEdit"));
@@ -576,10 +513,9 @@ mod tests {
))
.unwrap();
assert!(rendered.contains("### Memory and knowledge"));
assert!(rendered.contains("### Memory"));
assert!(rendered.contains("small targeted `MemoryQuery`"));
assert!(rendered.contains("MemoryRead(kind=summary)"));
assert!(!rendered.contains("KnowledgeQuery"));
assert!(!rendered.contains("MemoryWrite"));
assert!(!rendered.contains("MemoryEdit"));
assert!(!rendered.contains("MemoryDelete"));
@@ -838,75 +774,4 @@ mod tests {
.unwrap();
assert!(!rendered.contains("Resident memory summary"));
}
#[test]
fn trailing_section_omits_resident_knowledge_when_none() {
let (_tmp, loader) = user_loader_with("body.md", "BODY");
let tmpl = SystemPromptTemplate::parse("$user/body", loader).unwrap();
let dir = TempDir::new().unwrap();
let scope = build_scope(dir.path());
let rendered = tmpl.render(&ctx(dir.path(), &scope, vec![], None)).unwrap();
assert!(!rendered.contains("Resident knowledge"));
}
#[test]
fn trailing_section_omits_resident_knowledge_when_empty_slice() {
let (_tmp, loader) = user_loader_with("body.md", "BODY");
let tmpl = SystemPromptTemplate::parse("$user/body", loader).unwrap();
let dir = TempDir::new().unwrap();
let scope = build_scope(dir.path());
let rendered = tmpl
.render(&ctx_with_resident(dir.path(), &scope, &[]))
.unwrap();
assert!(!rendered.contains("Resident knowledge"));
}
#[test]
fn trailing_section_renders_resident_knowledge_entries() {
let (_tmp, loader) = user_loader_with("body.md", "BODY");
let tmpl = SystemPromptTemplate::parse("$user/body", loader).unwrap();
let dir = TempDir::new().unwrap();
let scope = build_scope(dir.path());
let entries = vec![
ResidentKnowledgeEntry {
slug: "alpha".into(),
description: "first record".into(),
},
ResidentKnowledgeEntry {
slug: "beta".into(),
description: "second record\nwith newline".into(),
},
];
let rendered = tmpl
.render(&ctx_with_resident(dir.path(), &scope, &entries))
.unwrap();
assert!(rendered.contains("## Resident knowledge"));
assert!(rendered.contains("- alpha: first record"));
// Newline in description is folded to a space (one entry per line).
assert!(rendered.contains("- beta: second record with newline"));
assert!(!rendered.contains("KnowledgeQuery"));
assert!(!rendered.contains("MemoryRead"));
// Resident section sits *after* the working-boundaries header.
let pos_boundaries = rendered.find("## Working boundaries").unwrap();
let pos_resident = rendered.find("## Resident knowledge").unwrap();
assert!(pos_resident > pos_boundaries);
}
#[test]
fn trailing_section_mentions_resident_knowledge_tools_when_available() {
let (_tmp, loader) = user_loader_with("body.md", "BODY");
let tmpl = SystemPromptTemplate::parse("$user/body", loader).unwrap();
let dir = TempDir::new().unwrap();
let scope = build_scope(dir.path());
let entries = [ResidentKnowledgeEntry {
slug: "alpha".into(),
description: "first record".into(),
}];
let mut context = ctx_with_resident(dir.path(), &scope, &entries);
context.tool_names = memory_tool_names();
let rendered = tmpl.render(&context).unwrap();
assert!(rendered.contains("## Resident knowledge"));
assert!(rendered.contains("KnowledgeQuery / MemoryRead"));
}
}