feat: Languageインストラクションの追加
This commit is contained in:
parent
1803b0cf67
commit
aa27f62409
|
|
@ -66,6 +66,8 @@ pub struct WorkerManifestConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub instruction: Option<String>,
|
pub instruction: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub language: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub max_tokens: Option<u32>,
|
pub max_tokens: Option<u32>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub max_turns: Option<NonZeroU32>,
|
pub max_turns: Option<NonZeroU32>,
|
||||||
|
|
@ -291,6 +293,7 @@ impl WorkerManifestConfig {
|
||||||
fn merge(self, upper: Self) -> Self {
|
fn merge(self, upper: Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
instruction: upper.instruction.or(self.instruction),
|
instruction: upper.instruction.or(self.instruction),
|
||||||
|
language: upper.language.or(self.language),
|
||||||
max_tokens: upper.max_tokens.or(self.max_tokens),
|
max_tokens: upper.max_tokens.or(self.max_tokens),
|
||||||
max_turns: upper.max_turns.or(self.max_turns),
|
max_turns: upper.max_turns.or(self.max_turns),
|
||||||
temperature: upper.temperature.or(self.temperature),
|
temperature: upper.temperature.or(self.temperature),
|
||||||
|
|
@ -429,6 +432,10 @@ impl TryFrom<PodManifestConfig> for PodManifest {
|
||||||
.worker
|
.worker
|
||||||
.instruction
|
.instruction
|
||||||
.unwrap_or_else(|| defaults::DEFAULT_INSTRUCTION.to_string()),
|
.unwrap_or_else(|| defaults::DEFAULT_INSTRUCTION.to_string()),
|
||||||
|
language: cfg
|
||||||
|
.worker
|
||||||
|
.language
|
||||||
|
.unwrap_or_else(|| defaults::WORKER_LANGUAGE.to_string()),
|
||||||
max_tokens: cfg.worker.max_tokens,
|
max_tokens: cfg.worker.max_tokens,
|
||||||
max_turns: cfg.worker.max_turns,
|
max_turns: cfg.worker.max_turns,
|
||||||
temperature: cfg.worker.temperature,
|
temperature: cfg.worker.temperature,
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ pub const COMPACT_RETAINED_TOKENS: u64 = 8000;
|
||||||
/// `$insomnia/` / `$user/` / `$workspace/` namespaces.
|
/// `$insomnia/` / `$user/` / `$workspace/` namespaces.
|
||||||
pub const DEFAULT_INSTRUCTION: &str = "$insomnia/default";
|
pub const DEFAULT_INSTRUCTION: &str = "$insomnia/default";
|
||||||
|
|
||||||
|
/// Default language policy used by the main worker for normal prose
|
||||||
|
/// responses. See [`crate::WorkerManifest::language`].
|
||||||
|
pub const WORKER_LANGUAGE: &str =
|
||||||
|
"match the user's language unless they explicitly request another language";
|
||||||
|
|
||||||
/// Token budget for auto-read file contents injected into the new
|
/// Token budget for auto-read file contents injected into the new
|
||||||
/// session after compaction. Limits how much raw file text the
|
/// session after compaction. Limits how much raw file text the
|
||||||
/// compact worker can pull into the compacted context via
|
/// compact worker can pull into the compacted context via
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,12 @@ pub struct WorkerManifest {
|
||||||
/// unset manifests fall through to [`defaults::DEFAULT_INSTRUCTION`].
|
/// unset manifests fall through to [`defaults::DEFAULT_INSTRUCTION`].
|
||||||
#[serde(default = "default_instruction")]
|
#[serde(default = "default_instruction")]
|
||||||
pub instruction: String,
|
pub instruction: String,
|
||||||
|
/// Language policy used by the main worker for normal prose responses.
|
||||||
|
/// Free-form so workspaces can use names like `English`, `Japanese`,
|
||||||
|
/// locale tags, or a policy phrase. Unset manifests fall through to
|
||||||
|
/// [`defaults::WORKER_LANGUAGE`].
|
||||||
|
#[serde(default = "default_worker_language")]
|
||||||
|
pub language: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub max_tokens: Option<u32>,
|
pub max_tokens: Option<u32>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -241,6 +247,10 @@ fn default_instruction() -> String {
|
||||||
defaults::DEFAULT_INSTRUCTION.to_string()
|
defaults::DEFAULT_INSTRUCTION.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_worker_language() -> String {
|
||||||
|
defaults::WORKER_LANGUAGE.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for ToolOutputLimits {
|
impl Default for ToolOutputLimits {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -689,6 +699,16 @@ model_id = "claude-sonnet-4-20250514"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn worker_language_defaults_and_parses() {
|
||||||
|
let manifest = PodManifest::from_toml(MINIMAL_REQUIRED).unwrap();
|
||||||
|
assert_eq!(manifest.worker.language, defaults::WORKER_LANGUAGE);
|
||||||
|
|
||||||
|
let toml = MINIMAL_REQUIRED.replace("[worker]\n", "[worker]\nlanguage = \"Japanese\"\n");
|
||||||
|
let manifest = PodManifest::from_toml(&toml).unwrap();
|
||||||
|
assert_eq!(manifest.worker.language, "Japanese");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_worker_output_limits() {
|
fn parse_worker_output_limits() {
|
||||||
let toml = MINIMAL_REQUIRED.replace(
|
let toml = MINIMAL_REQUIRED.replace(
|
||||||
|
|
|
||||||
|
|
@ -643,6 +643,7 @@ permission = "write"
|
||||||
let ctx = SystemPromptContext {
|
let ctx = SystemPromptContext {
|
||||||
now: chrono::Utc::now(),
|
now: chrono::Utc::now(),
|
||||||
cwd: &root,
|
cwd: &root,
|
||||||
|
language: manifest::defaults::WORKER_LANGUAGE,
|
||||||
scope: &scope,
|
scope: &scope,
|
||||||
tool_names: Vec::new(),
|
tool_names: Vec::new(),
|
||||||
agents_md: None,
|
agents_md: None,
|
||||||
|
|
|
||||||
|
|
@ -879,10 +879,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||||
};
|
};
|
||||||
let resident_exposure_snapshots =
|
let resident_exposure_snapshots =
|
||||||
self.resident_exposure_snapshots(&resident, &resident_workflows);
|
self.resident_exposure_snapshots(&resident, &resident_workflows);
|
||||||
|
let worker_language = worker_language(&self.manifest.worker);
|
||||||
let scope_snapshot = self.scope.snapshot();
|
let scope_snapshot = self.scope.snapshot();
|
||||||
let ctx = SystemPromptContext {
|
let ctx = SystemPromptContext {
|
||||||
now: chrono::Utc::now(),
|
now: chrono::Utc::now(),
|
||||||
cwd: &self.pwd,
|
cwd: &self.pwd,
|
||||||
|
language: worker_language,
|
||||||
scope: &scope_snapshot,
|
scope: &scope_snapshot,
|
||||||
tool_names,
|
tool_names,
|
||||||
agents_md: agents_md_read.body,
|
agents_md: agents_md_read.body,
|
||||||
|
|
@ -2342,6 +2344,15 @@ fn memory_language(cfg: &manifest::MemoryConfig) -> &str {
|
||||||
.unwrap_or(manifest::defaults::MEMORY_LANGUAGE)
|
.unwrap_or(manifest::defaults::MEMORY_LANGUAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn worker_language(cfg: &manifest::WorkerManifest) -> &str {
|
||||||
|
let language = cfg.language.trim();
|
||||||
|
if language.is_empty() {
|
||||||
|
manifest::defaults::WORKER_LANGUAGE
|
||||||
|
} else {
|
||||||
|
language
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Outcome of a single extract iteration. Internal to
|
/// Outcome of a single extract iteration. Internal to
|
||||||
/// `try_post_run_extract` / `run_extract_once`.
|
/// `try_post_run_extract` / `run_extract_once`.
|
||||||
enum ExtractDecision {
|
enum ExtractDecision {
|
||||||
|
|
@ -3152,6 +3163,7 @@ mod build_summary_prompt_tests {
|
||||||
fn worker_manifest_generation_settings_become_request_config() {
|
fn worker_manifest_generation_settings_become_request_config() {
|
||||||
let manifest = WorkerManifest {
|
let manifest = WorkerManifest {
|
||||||
instruction: "unused".into(),
|
instruction: "unused".into(),
|
||||||
|
language: manifest::defaults::WORKER_LANGUAGE.into(),
|
||||||
max_tokens: Some(1024),
|
max_tokens: Some(1024),
|
||||||
max_turns: None,
|
max_turns: None,
|
||||||
temperature: Some(0.2),
|
temperature: Some(0.2),
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,8 @@ impl std::fmt::Debug for SystemPromptTemplate {
|
||||||
pub struct SystemPromptContext<'a> {
|
pub struct SystemPromptContext<'a> {
|
||||||
pub now: DateTime<Utc>,
|
pub now: DateTime<Utc>,
|
||||||
pub cwd: &'a Path,
|
pub cwd: &'a Path,
|
||||||
|
/// Language policy exposed to instruction templates as `{{ language }}`.
|
||||||
|
pub language: &'a str,
|
||||||
pub scope: &'a Scope,
|
pub scope: &'a Scope,
|
||||||
pub tool_names: Vec<String>,
|
pub tool_names: Vec<String>,
|
||||||
/// Project-level instructions read from the nearest `AGENTS.md`.
|
/// Project-level instructions read from the nearest `AGENTS.md`.
|
||||||
|
|
@ -181,6 +183,7 @@ impl<'a> SystemPromptContext<'a> {
|
||||||
Value::from(self.now.to_rfc3339_opts(SecondsFormat::Secs, true)),
|
Value::from(self.now.to_rfc3339_opts(SecondsFormat::Secs, true)),
|
||||||
);
|
);
|
||||||
root.insert("cwd".into(), Value::from(self.cwd.display().to_string()));
|
root.insert("cwd".into(), Value::from(self.cwd.display().to_string()));
|
||||||
|
root.insert("language".into(), Value::from(self.language));
|
||||||
root.insert(
|
root.insert(
|
||||||
"tools".into(),
|
"tools".into(),
|
||||||
Value::from(
|
Value::from(
|
||||||
|
|
@ -328,6 +331,7 @@ mod tests {
|
||||||
SystemPromptContext {
|
SystemPromptContext {
|
||||||
now: fixed_now(),
|
now: fixed_now(),
|
||||||
cwd,
|
cwd,
|
||||||
|
language: manifest::defaults::WORKER_LANGUAGE,
|
||||||
scope,
|
scope,
|
||||||
tool_names: tools,
|
tool_names: tools,
|
||||||
agents_md,
|
agents_md,
|
||||||
|
|
@ -345,6 +349,7 @@ mod tests {
|
||||||
SystemPromptContext {
|
SystemPromptContext {
|
||||||
now: fixed_now(),
|
now: fixed_now(),
|
||||||
cwd,
|
cwd,
|
||||||
|
language: manifest::defaults::WORKER_LANGUAGE,
|
||||||
scope,
|
scope,
|
||||||
tool_names: Vec::new(),
|
tool_names: Vec::new(),
|
||||||
agents_md: None,
|
agents_md: None,
|
||||||
|
|
@ -380,6 +385,9 @@ mod tests {
|
||||||
let rendered = tmpl
|
let rendered = tmpl
|
||||||
.render(&ctx(dir.path(), &scope, vec!["Read".into()], None))
|
.render(&ctx(dir.path(), &scope, vec!["Read".into()], None))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
// Builtin default body must expose the language policy.
|
||||||
|
assert!(rendered.contains("## Language"));
|
||||||
|
assert!(rendered.contains("`language`: `match the user's language"));
|
||||||
// Trailing section must be present.
|
// Trailing section must be present.
|
||||||
assert!(rendered.contains("## Working boundaries"));
|
assert!(rendered.contains("## Working boundaries"));
|
||||||
assert!(rendered.contains("Readable:"));
|
assert!(rendered.contains("Readable:"));
|
||||||
|
|
|
||||||
6
resources/prompts/common/language.md
Normal file
6
resources/prompts/common/language.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
## Language
|
||||||
|
|
||||||
|
- `language`: `{{ language }}`.
|
||||||
|
- Follow this language policy for normal prose responses unless the user explicitly requests another language.
|
||||||
|
- Preserve code identifiers, file paths, command names, logs, quoted text, and external proper nouns when translation would reduce fidelity.
|
||||||
|
- Do not translate file contents, command output, or protocol literals unless the user asks for translation.
|
||||||
|
|
@ -6,4 +6,6 @@ Stay precise, edit code directly when asked, and avoid speculative refactoring.
|
||||||
|
|
||||||
{% include "common/tool-usage" %}
|
{% include "common/tool-usage" %}
|
||||||
|
|
||||||
|
{% include "common/language" %}
|
||||||
|
|
||||||
{% include "common/writing" %}
|
{% include "common/writing" %}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user