llm="claude-opus-4"; the harness resolves the anthropic profile and shapes both the system prompt and the coding toolset automatically.
Unknown or non-string model ids resolve to the default profile — the system prompt and toolset order stay byte-for-byte identical to earlier versions. Adopting the harness requires no config change.
Quick Start
Opt in — Anthropic family
apply_patch is advertised before edit_file, and the system prompt gains: “When editing files, prefer patch-style edits: use apply_patch to create or rewrite files and edit_file for targeted changes.”How It Works
The agent resolves a profile from the model id at prompt-build time, then prepends the fragment and reorders the edit tools.| Step | What happens |
|---|---|
Agent.__init__ | Reads self.llm (only when it’s a str) and calls resolve_toolsets_for_model(toolsets, model_id) to reorder edit primitives. |
_build_system_prompt | Calls _resolve_harness_base_prompt(); if the profile has a base_prompt, it’s prepended as f"{harness_prompt}\n\n{system_prompt}". |
| Override | An explicit harness_base_prompt attribute on the Agent wins over resolver output. Any error path collapses to no fragment (default profile). |
| Fallback | Falsy, unknown, or object-based model ids → default profile, no change |
Which override do I need?
Precedence
Resolution follows a fixed order, from most specific to the behaviour-preserving fallback:Agent.harness_base_prompt(explicit override) >register_profile(...)(user-registered, most recent wins) > built-in matchers (anthropic,openai) >DEFAULT_PROFILE
Configuration Options
Import the harness API from the top level (all lazy — no import-time cost):HarnessProfile fields
| Field | Type | Default | Description |
|---|---|---|---|
name | str | "default" | Profile identifier (e.g. "default", "anthropic", "openai") |
base_prompt | Optional[str] | None | Prompt fragment prepended to the system prompt. None = no fragment (behaviour-preserving) |
preferred_edit_format | Optional[str] | None | Preferred edit primitive — "apply_patch" or "edit_file". None = keep exposing both primitives in their current order |
Built-in registry
First match wins; matching is case-insensitive substring on the model id.| Match substrings (case-insensitive) | Profile name | base_prompt | preferred_edit_format |
|---|---|---|---|
claude, anthropic | anthropic | ”When editing files, prefer patch-style edits: use apply_patch to create or rewrite files and edit_file for targeted changes.” | apply_patch |
gpt, openai, o1, o3, o4 | openai | ”When editing files, prefer targeted string-replacement edits: use edit_file to modify existing files precisely.” | edit_file |
| (no match / falsy id) | default | None | None |
Functions
| Function | Signature | Behaviour |
|---|---|---|
resolve_harness | resolve_harness(model: Optional[str]) -> HarnessProfile | Case-insensitive substring match, first match wins. Falsy or unknown model → DEFAULT_PROFILE |
register_profile | register_profile(matchers: List[str], profile: HarnessProfile) -> None | Prepends a (matchers, profile) mapping so caller profiles take precedence. Process-global and thread-safe |
Common Patterns
Pass the model id and the resolver does the rest — no extra configuration required.-
All-Claude team — do nothing; naming a
claude-…model activates the profile automatically. -
Mixed models via a router — leave
llmunset per agent and pass the model id per call. The resolver readsself.llmat prompt build-time, so a mid-run model swap picks up the new profile each turn. -
Local Ollama model treated like Claude — register a profile at startup:
-
Team on Llama or Mistral — register once at startup so every downstream agent gets the family hint:
-
Strip the auto fragment on one agent — set
agent.harness_base_prompt = "".
Add a house-style opener for every model
Register a profile whose matcher hits every model id the agent may see:YAML usage
Nothing new to configure — the harness reads thellm field on the agent block, so YAML paths get the improvement automatically:
CLI usage
No new flag —praisonai run picks up the profile from the model id:
End-to-end examples
User Interaction Flow
A user creates anAgent(llm="claude-opus-4", toolsets=["coding"]) and types "Refactor src/user.py". The harness resolver sees "claude-opus-4", picks the anthropic profile, prepends the patch-first guidance to the agent’s system prompt, and advertises apply_patch before edit_file in the coding toolset. The agent reaches for apply_patch first — no config touched.
The same user later switches to llm="gpt-4o". Same code, same toolset — the resolver now picks the openai profile, and the agent reaches for edit_file first. Still no config touched.
Best Practices
Trust the default. Ship first, tune later.
Trust the default. Ship first, tune later.
The default profile is byte-for-byte identical to pre-harness behaviour — verified by
test_coding_toolset_unknown_model_is_byte_for_byte. Recognised families gain tuned guidance automatically; everything else stays the same.Register profiles at boot, not per-request.
Register profiles at boot, not per-request.
The registry is process-global. Call
register_profile once during application startup — registering inside request handlers repeatedly prepends duplicate mappings.Match on stable substrings.
Match on stable substrings.
Matchers are case-insensitive substring hits against the model id, first match wins. Keep them short and stable —
"claude", "gpt" — so new model versions in the same family match without edits. Registrations prepend, so a custom mapping overrides the built-in defaults.Don't rely on tool ordering being enforced.
Don't rely on tool ordering being enforced.
Both edit primitives remain available; the model can still call the non-preferred one. Ordering is a hint, not a constraint.
Pass the model id as a string when you want the harness on.
Pass the model id as a string when you want the harness on.
Object-based LLMs resolve to the default profile by design, so custom LLM wrappers don’t silently pick up an unrelated family’s guidance. Pass a string
llm= model id to opt in.Related
Toolsets
The coding toolset whose edit-primitive order the harness reorders.
File Editing
The
edit_file and apply_patch primitives themselvesRules
The other lever that shapes the assembled system prompt
Prompt Cache Optimization
The fragment is prepended, so it participates in cache-key ordering
Models
The model id you pass is the trigger for profile resolution.

