core/config: default system prompt for coding agent tools

This commit is contained in:
2026-07-20 18:21:27 +08:00
parent 767578723e
commit 6d74d31cea
7 changed files with 69 additions and 7 deletions
+1 -1
View File
@@ -72,7 +72,7 @@ Async SQLAlchemy + aiosqlite. `MemoryStore`: schema init (+ lightweight SQLite `
- **`/compact`**: soft-compact tool dumps → model summary (no tools) → **new** session seeded with summary message. Soft-compact keeps the last **12** tool results full-size by default (`DEFAULT_RECENT_TOOL_RESULTS`); older tool payloads shrink first.
- Events: text_delta, **reasoning_delta**, assistant_message, tool_call/result, max_rounds, **error** (`retryable`/`source`), **cancelled** (`reason`), **usage** (`TokenUsage`).
- Usage: API `usage` from completions (stream with `include_usage`); **char≈token fallback** (~4 chars/token) when omitted; **context size** = last request ``prompt_tokens`` (API preferred); `last_turn_usage` / `session_usage` are **billed sums** (tool rounds re-send history); CLI end-of-turn + `/status`.
- Config ``[agent]``: `system_prompt`, `max_tool_result_chars`, `parallel_tools`, `confirm_destructive`, `path_denylist`, `max_context_tokens` (default 200k est. tokens).
- Config ``[agent]``: `system_prompt` (default: built-in coding-agent guide `DEFAULT_SYSTEM_PROMPT`; `""` disables), `max_tool_result_chars`, `parallel_tools`, `confirm_destructive`, `path_denylist`, `max_context_tokens` (default 200k est. tokens).
### Tools (`tools/`)
+4 -1
View File
@@ -136,7 +136,10 @@ access_key_or_token = "sk-..."
"gpt-4o-mini" = { text = true }
[agent]
system_prompt = "You are a careful coding assistant."
# system_prompt defaults to a built-in coding-agent guide (omit to use it).
# system_prompt = "" # disable
# Or multi-line override (prefer '''...'''):
# system_prompt = '''Your custom prompt...'''
confirm_destructive = true
max_context_tokens = 200000
```
+6 -1
View File
@@ -11,7 +11,12 @@
# # url = ":memory:"
[agent]
system_prompt = "You are a careful coding assistant. Prefer small, verified edits."
# system_prompt defaults to the built-in coding-agent guide when omitted.
# Override with a multi-line literal (prefer ''' so nested " is fine):
# system_prompt = '''
# Your custom system prompt...
# '''
# system_prompt = "" # disable system prompt entirely
max_tool_result_chars = 32000
parallel_tools = true
confirm_destructive = true
+2 -1
View File
@@ -29,7 +29,8 @@ _MINIMAL_CONFIG = """\
# # url = ":memory:"
# [agent]
# system_prompt = "You are a careful coding assistant."
# # system_prompt defaults to the built-in coding-agent guide when omitted.
# # system_prompt = "" # disable; or multi-line '''...''' to override
# max_tool_result_chars = 32000
# parallel_tools = true
# confirm_destructive = true
+1
View File
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
import tomlkit
from tomlkit.exceptions import TOMLKitError
from .models import DEFAULT_SYSTEM_PROMPT as DEFAULT_SYSTEM_PROMPT
from .models import AgentConfig as AgentConfig
from .models import AnthropicProvider as AnthropicProvider
from .models import DatabaseConfig as DatabaseConfig
+39 -1
View File
@@ -2,6 +2,44 @@ from typing import Any
from msgspec import Struct, field
# Built-in agent system prompt when ``[agent].system_prompt`` is omitted.
# Set ``system_prompt = ""`` in TOML to disable. Override with a multi-line
# TOML literal (prefer '''...''' so nested " quotes are fine).
DEFAULT_SYSTEM_PROMPT = """\
You are a professional coding agent in a workspace-bound tool environment.
### Workspace
- Explore with `tree`/`listdir`/`glob_paths`/`grep_files`/`read_file` when unsure.
- Stay under the workspace (or a path from `new_temporary_workspace`). Respect path denylists.
### Files
- Prefer file tools over shell. `edit_replace` fails usually means a bad match — \
fix `old_string` or `max_replaces`; shell workarounds rarely help.
- `edit_replace` defaults to the first match; if remaining matches are reported, \
raise `max_replaces` or narrow `old_string`.
- Before `edit_lineno`, `read_file` with `with_lineno=true`.
### Commands
- Prefer `run_command` / `run_command_batch` (argv, no shell) over `bash -c` or similar.
- Several `run_command` calls in one step may run in parallel; use `run_command_batch` \
for ordered pipelines (`pipe_out` / `mix_stderr` as needed).
- Prefer `vcs_*` for status/diff/log/branch when enough.
### Humans & safety
- Prefer `ask_user_line` / `ask_user_choice` / `ask_user_form` over waiting for the next free-form turn.
- Overwrites, deletes, shells, and risky ops may require human confirm; hard denylists are not skipped by YOLO.
- Temporary scratch: `new_temporary_workspace`.
### PTY
- Use PTY for interactive/TUI, sudo, ssh, or live servers.
- Passwords and other secret input: only `ask_into_pty` (never echo secrets into `write_pty`).
- Control keys: `write_pty_keys`, not literal `write_pty` data.
### Todos
- Use the todo stack for multi-step work (LIFO groups: push related items, finish/update, \
pop the group). Open items mean unfinished work.
"""
class DatabaseConfig(Struct, omit_defaults=True):
"""Database connection configuration.
@@ -20,7 +58,7 @@ class DatabaseConfig(Struct, omit_defaults=True):
class AgentConfig(Struct, omit_defaults=True):
"""Single-user agent profile defaults."""
system_prompt: str = ""
system_prompt: str = DEFAULT_SYSTEM_PROMPT
max_tool_result_chars: int = 32_000
parallel_tools: bool = True
confirm_destructive: bool = True
+16 -2
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from plyngent.config import load
from plyngent.config import DEFAULT_SYSTEM_PROMPT, load
if TYPE_CHECKING:
from pathlib import Path
@@ -12,7 +12,8 @@ def test_agent_section_defaults(tmp_path: Path) -> None:
path = tmp_path / "c.toml"
_ = path.write_text("", encoding="utf-8")
store = load(path)
assert store.agent_config.system_prompt == ""
assert store.agent_config.system_prompt == DEFAULT_SYSTEM_PROMPT
assert "professional coding agent" in store.agent_config.system_prompt
assert store.agent_config.max_tool_result_chars == 32_000
assert store.agent_config.parallel_tools is True
assert store.agent_config.confirm_destructive is True
@@ -41,3 +42,16 @@ max_context_tokens = 5000
assert store.agent_config.confirm_destructive is False
assert store.agent_config.path_denylist == ["/secrets/", ".ssh/"]
assert store.agent_config.max_context_tokens == 5000
def test_agent_system_prompt_empty_disables(tmp_path: Path) -> None:
path = tmp_path / "c.toml"
_ = path.write_text(
"""
[agent]
system_prompt = ""
""",
encoding="utf-8",
)
store = load(path)
assert store.agent_config.system_prompt == ""