mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-09 17:08:29 +00:00
feat: add tiny local tool-use profile
This commit is contained in:
parent
9ea5ac07c2
commit
15aa7d71f0
20 changed files with 354 additions and 12 deletions
|
|
@ -41,3 +41,4 @@ Direct child DOX files:
|
|||
| [developer/AGENTS.md](developer/AGENTS.md) | Software development specialist profile. |
|
||||
| [hacker/AGENTS.md](hacker/AGENTS.md) | Cyber security and penetration testing specialist profile. |
|
||||
| [researcher/AGENTS.md](researcher/AGENTS.md) | Research, data analysis, and reporting specialist profile. |
|
||||
| [tiny-local/AGENTS.md](tiny-local/AGENTS.md) | Small/local model profile with an action-first communication prompt. |
|
||||
|
|
|
|||
36
agents/tiny-local/AGENTS.md
Normal file
36
agents/tiny-local/AGENTS.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Tiny Local Agent Profile DOX
|
||||
|
||||
## Purpose
|
||||
|
||||
- Own the bundled Tiny Local profile for small/local chat models.
|
||||
- Keep local-model behavior prompt-only and isolated from core framework execution.
|
||||
|
||||
## Ownership
|
||||
|
||||
- `agent.yaml` owns profile metadata for discovery and profile switching.
|
||||
- `prompts/agent.system.main.communication.md` owns the local-model communication contract.
|
||||
- `prompts/agent.system.main.solving.md` owns the local-model problem-solving contract and suppresses inherited visible reasoning requirements.
|
||||
- `prompts/agent.system.tools.md` owns the Tiny Local tools wrapper and final output-shape reminder after tool listing.
|
||||
- `prompts/agent.system.tool.*.md` files own Tiny Local-specific tool examples that avoid inherited reasoning fields and repeated writes.
|
||||
|
||||
## Local Contracts
|
||||
|
||||
- Preserve the normal Agent Zero tool-call shape: `tool_name` plus `tool_args`.
|
||||
- Do not add parser repair, duplicate suppression, model transport, memory, or text-editor runtime behavior here.
|
||||
- Keep prompt text short enough for small local models to follow.
|
||||
- Treat continuation requests such as `proceed` or `continue` as commands to execute the next unfinished step, not as prompts for another status response.
|
||||
- Do not include user-specific provider names, API keys, local paths, or secrets.
|
||||
|
||||
## Work Guidance
|
||||
|
||||
- Prefer prompt wording changes over new files when tightening this profile, except when replacing inherited tool examples for local-model compliance.
|
||||
- Keep this profile suitable for Ollama, LM Studio, Qwen, and comparable local models.
|
||||
|
||||
## Verification
|
||||
|
||||
- Render the `tiny-local` system prompt after communication prompt changes.
|
||||
- Run `pytest tests/test_default_prompt_budget.py` for prompt and profile regressions.
|
||||
|
||||
## Child DOX Index
|
||||
|
||||
No child DOX files.
|
||||
3
agents/tiny-local/agent.yaml
Normal file
3
agents/tiny-local/agent.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
title: Tiny Local
|
||||
description: Action-first profile for small local models that need a minimal tool-call contract.
|
||||
context: Use this agent when running small local chat models through Ollama, LM Studio, or similar providers and the model tends to explain actions instead of calling tools.
|
||||
29
agents/tiny-local/prompts/agent.system.main.communication.md
Normal file
29
agents/tiny-local/prompts/agent.system.main.communication.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
## Communication
|
||||
|
||||
You are Agent Zero. Act on the user's behalf.
|
||||
|
||||
When the user asks you to do something, do it directly. Do not explain how the user could do it themselves.
|
||||
|
||||
Your visible assistant message must be exactly one valid JSON object.
|
||||
|
||||
Use exactly these top-level fields: `"tool_name"` and `"tool_args"`.
|
||||
|
||||
Do not include markdown fences, prose before the JSON, prose after the JSON, hidden reasoning, analysis, thoughts, or headlines.
|
||||
|
||||
Choose a tool from the tools listed in this system prompt. Do not invent tool names, action names, or generic names such as `read`, `write`, `terminal`, or `multi`.
|
||||
|
||||
For a final user-facing answer, use the `response` tool.
|
||||
|
||||
Use `response` only when the work is complete, blocked, or the user is only acknowledging completed work.
|
||||
|
||||
If the user says "proceed", "continue", "go ahead", "do it", "excellent proceed", or similar after you named a next step or there is unfinished work, do not answer with a promise or status update. Call the next appropriate tool.
|
||||
|
||||
Final-answer shape:
|
||||
|
||||
`{"tool_name":"response","tool_args":{"text":"Answer briefly."}}`
|
||||
|
||||
For work that requires a command, file action, browser action, or any other available tool, call the appropriate tool immediately. Do not explain what command the user could run manually.
|
||||
|
||||
If the framework warns that your prior message was malformed, repeated, or reasoning-only, output a corrected JSON tool request immediately without explaining the warning.
|
||||
|
||||
{{ include "agent.system.main.communication_additions.md" }}
|
||||
17
agents/tiny-local/prompts/agent.system.main.solving.md
Normal file
17
agents/tiny-local/prompts/agent.system.main.solving.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## Problem Solving
|
||||
|
||||
Act directly and keep hidden reasoning out of the visible JSON.
|
||||
|
||||
For simple questions, answer with the `response` tool.
|
||||
|
||||
Continuation words such as "proceed", "continue", "go ahead", "do it", and "excellent proceed" mean execute the next unfinished step. Do not respond by saying you will begin, continue, start, proceed, or investigate. Use a real tool call unless the task is already complete or blocked.
|
||||
|
||||
For tasks that need shell commands, files, browser actions, or other capabilities:
|
||||
- choose the appropriate listed tool immediately
|
||||
- keep one tool call per turn unless the `parallel` tool is listed and truly useful
|
||||
- inspect outputs before deciding the next tool call
|
||||
- never claim success from timeout output or a still-running command
|
||||
- after a successful tool result, do not repeat the same exact tool call
|
||||
- when finished, use the `response` tool with a brief result
|
||||
|
||||
Do not include `thoughts`, `headline`, analysis, plans, or prose outside the JSON object.
|
||||
24
agents/tiny-local/prompts/agent.system.tool.code_exe.md
Normal file
24
agents/tiny-local/prompts/agent.system.tool.code_exe.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
### code_execution_tool
|
||||
Run terminal, Python, or Node.js commands.
|
||||
|
||||
Arguments in `tool_args`:
|
||||
- `runtime`: `terminal`, `python`, `nodejs`, or `output`
|
||||
- `code`: command or script code
|
||||
- `session`: terminal session id; default `0`
|
||||
- `reset`: kill a session before running; `true` or `false`
|
||||
|
||||
Rules:
|
||||
- Put the command or script in `code`.
|
||||
- Use `runtime=output` to poll running work.
|
||||
- Use `input` for interactive terminal prompts.
|
||||
- If a session is stuck, call this tool again with the same `session` and `reset=true`.
|
||||
- Do not claim success from timeout output or a still-running command.
|
||||
- When counting files, prefer `find` over `ls` so hidden files and type filters are handled.
|
||||
|
||||
Examples:
|
||||
|
||||
`{"tool_name":"code_execution_tool","tool_args":{"runtime":"terminal","session":0,"reset":false,"code":"ls -1 /tmp | wc -l"}}`
|
||||
|
||||
`{"tool_name":"code_execution_tool","tool_args":{"runtime":"python","session":0,"reset":false,"code":"import os\nprint(os.getcwd())"}}`
|
||||
|
||||
`{"tool_name":"code_execution_tool","tool_args":{"runtime":"output","session":0}}`
|
||||
13
agents/tiny-local/prompts/agent.system.tool.response.md
Normal file
13
agents/tiny-local/prompts/agent.system.tool.response.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
### response
|
||||
Final answer to the user.
|
||||
|
||||
Use this tool only when the task is done, blocked, or no tool is needed.
|
||||
|
||||
Do not use this tool for "proceed", "continue", "go ahead", or similar continuation requests when there is an unfinished next step. Call a real tool instead.
|
||||
|
||||
Arguments in `tool_args`:
|
||||
- `text`: concise final answer text
|
||||
|
||||
Example:
|
||||
|
||||
`{"tool_name":"response","tool_args":{"text":"There are 24 files in /tmp."}}`
|
||||
26
agents/tiny-local/prompts/agent.system.tool.text_editor.md
Normal file
26
agents/tiny-local/prompts/agent.system.tool.text_editor.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
### text_editor
|
||||
Read, write, or patch Markdown and plain text files.
|
||||
|
||||
Actions in `tool_args.action`:
|
||||
- `read`: read a file
|
||||
- `write`: create or overwrite a file
|
||||
- `patch`: edit an existing file
|
||||
|
||||
Common arguments:
|
||||
- `path`: absolute file path
|
||||
- `content`: full file content for `write`
|
||||
- `open_in_canvas`: set `true` when the user explicitly asks to open a Markdown file in the Canvas or Editor
|
||||
|
||||
Rules:
|
||||
- Use this tool for `.md` and plain text files.
|
||||
- Use `write` to create a new Markdown file.
|
||||
- If the user asks to open the file in the Canvas or Editor, include `"open_in_canvas": true` in the same `write` or `patch` call.
|
||||
- After a successful write or patch result, do not repeat the same tool call. Use the `response` tool unless a different action is needed.
|
||||
|
||||
Examples:
|
||||
|
||||
`{"tool_name":"text_editor","tool_args":{"action":"write","path":"/a0/usr/workdir/TODO.md","content":"# TODO\n- [ ] First item\n","open_in_canvas":true}}`
|
||||
|
||||
`{"tool_name":"text_editor","tool_args":{"action":"read","path":"/a0/usr/workdir/TODO.md"}}`
|
||||
|
||||
`{"tool_name":"text_editor","tool_args":{"action":"patch","path":"/a0/usr/workdir/TODO.md","old_text":"- [ ] First item","new_text":"- [x] First item"}}`
|
||||
17
agents/tiny-local/prompts/agent.system.tools.md
Normal file
17
agents/tiny-local/prompts/agent.system.tools.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## Available Tools
|
||||
|
||||
Use only the tools listed below. Match tool names exactly.
|
||||
|
||||
Every tool request must be exactly one JSON object with only these top-level fields:
|
||||
- `tool_name`
|
||||
- `tool_args`
|
||||
|
||||
Action names are not tool names. Do not invent top-level `multi`, `read`, `write`, `terminal`, or generic batch tools.
|
||||
|
||||
{{tools}}
|
||||
|
||||
## Tiny Local Output Rule
|
||||
|
||||
Some inherited tool examples may show `thoughts` or `headline`. Ignore that shape for this profile.
|
||||
|
||||
Do not include `thoughts`, `headline`, analysis, markdown fences, or prose outside the JSON object.
|
||||
|
|
@ -66,6 +66,10 @@ These controls are related, but they solve different problems.
|
|||
| **Project** | Files, workspace, memories, instructions, secrets, and long-running context. |
|
||||
| **Model Preset** | Which models are used for the chat. |
|
||||
|
||||
For small local models that narrate instead of calling tools, use the bundled
|
||||
**Tiny Local** profile or the project-scoped Prompt Include recipe in
|
||||
[Local Model Tool Use](local-model-tool-use.md).
|
||||
|
||||
Example:
|
||||
|
||||
- use a **Project** for a client repository;
|
||||
|
|
|
|||
65
docs/guides/local-model-tool-use.md
Normal file
65
docs/guides/local-model-tool-use.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Local Model Tool Use
|
||||
|
||||
Small local models can struggle with Agent Zero's full default communication shape. The safest first fix is prompt/profile/plugin-only: use a smaller behavior contract while leaving Agent Zero's core parser and execution code unchanged.
|
||||
|
||||
Use this guide for Ollama, LM Studio, Qwen, and similar local chat models when the model explains commands instead of calling tools.
|
||||
|
||||
## Use The Tiny Local Profile
|
||||
|
||||
Choose the **Tiny Local** profile when starting or switching a chat that uses a small local model.
|
||||
|
||||
The bundled profile lives at:
|
||||
|
||||
```text
|
||||
agents/tiny-local/
|
||||
```
|
||||
|
||||
Tiny Local keeps the normal Agent Zero tool-call shape, but removes visible reasoning fields from the communication prompt. It tells the model to emit one executable JSON object with `tool_name` and `tool_args`.
|
||||
|
||||
## Use A Project Prompt Include
|
||||
|
||||
If you want to keep your current profile, create a project-local file that matches the Prompt Include plugin pattern (`*.promptinclude.md`):
|
||||
|
||||
```text
|
||||
local-model-tool-use.promptinclude.md
|
||||
```
|
||||
|
||||
Put this content in that file:
|
||||
|
||||
```markdown
|
||||
## Local model tool-use discipline
|
||||
|
||||
You are Agent Zero. Act on the user's behalf.
|
||||
|
||||
When the user asks you to do something, do it directly. Do not explain how the user could do it themselves.
|
||||
|
||||
Your visible assistant message must be exactly one valid JSON object.
|
||||
|
||||
Use exactly these top-level fields: `tool_name` and `tool_args`.
|
||||
|
||||
Do not include markdown fences, prose before the JSON, prose after the JSON, hidden reasoning, analysis, thoughts, or headlines.
|
||||
|
||||
Choose a tool from the tools listed in the system prompt. Do not invent tool names, action names, or generic names such as `read`, `write`, `terminal`, or `multi`.
|
||||
|
||||
For a final user-facing answer, use the `response` tool:
|
||||
|
||||
`{"tool_name":"response","tool_args":{"text":"Done."}}`
|
||||
|
||||
Use `response` only when the work is complete, blocked, or no tool is needed. If the user says "proceed", "continue", "go ahead", or similar after the agent named a next step, call the next appropriate tool instead of replying with a promise or status update.
|
||||
|
||||
For work that requires a command, file action, browser action, or any other available tool, call the appropriate tool immediately.
|
||||
|
||||
If the framework warns that your prior message was malformed, repeated, or reasoning-only, output a corrected JSON tool request immediately without explaining the warning.
|
||||
```
|
||||
|
||||
## Keep This Prompt-Only
|
||||
|
||||
Do not change `agent.py` for this workflow.
|
||||
|
||||
Do not change `helpers/extract_tools.py` for this workflow.
|
||||
|
||||
Do not create parser repair code for this workflow.
|
||||
|
||||
Do not add duplicate execution suppression, LiteLLM transport changes, memory runtime changes, or text-editor file operation changes for this workflow.
|
||||
|
||||
If a specific local model still cannot follow the prompt/profile/plugin-only contract, capture the exact model, prompt, response, and tool warning before considering deeper framework changes.
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
- Keep the floating Editor modal on the shared surface modal chrome so the header remains draggable while existing Focus mode continues to work.
|
||||
- Keep Editor Open wired through the File Browser text picker so users can open one or more Markdown or plain text files with an obvious confirmation action.
|
||||
- Keep Save As distinct from Rename: Save As writes the current editor text to a chosen `.md` or `.txt` path and retargets the active session without removing the original file.
|
||||
- Preserve source chat context ids when opening Markdown files from tool-result canvas handoffs.
|
||||
|
||||
## Work Guidance
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const SYNC_WINDOW_MS = 10 * 60 * 1000;
|
|||
const syncedTextEditorResults = new Set();
|
||||
|
||||
export default async function syncTextEditorResultsIntoOpenEditor(context) {
|
||||
if (!context?.results?.length || context.historyEmpty) return;
|
||||
if (!context?.results?.length) return;
|
||||
|
||||
for (const { args } of context.results) {
|
||||
const payload = getTextEditorPayload(args);
|
||||
|
|
@ -14,6 +14,8 @@ export default async function syncTextEditorResultsIntoOpenEditor(context) {
|
|||
|
||||
const target = textEditorTarget(payload);
|
||||
if (!target.path || target.extension !== "md") continue;
|
||||
const explicitOpen = shouldOpenEditorUiFromResult(payload, target);
|
||||
if (context.historyEmpty && !explicitOpen) continue;
|
||||
|
||||
const key = [
|
||||
args?.id || "",
|
||||
|
|
@ -26,10 +28,12 @@ export default async function syncTextEditorResultsIntoOpenEditor(context) {
|
|||
syncedTextEditorResults.add(key);
|
||||
|
||||
globalThis.setTimeout(() => {
|
||||
if (shouldOpenEditorUiFromResult(payload, target)) {
|
||||
if (explicitOpen) {
|
||||
void openSurface("editor", {
|
||||
path: target.path || "",
|
||||
file_id: target.file_id || "",
|
||||
ctxid: target.ctxid || target.context_id || "",
|
||||
context_id: target.context_id || target.ctxid || "",
|
||||
refresh: true,
|
||||
source: "tool-result-open",
|
||||
});
|
||||
|
|
@ -60,6 +64,8 @@ function pickPayloadFields(args = {}) {
|
|||
"action",
|
||||
"extension",
|
||||
"format",
|
||||
"context_id",
|
||||
"ctxid",
|
||||
"last_modified",
|
||||
"open_canvas",
|
||||
"open_document",
|
||||
|
|
@ -97,9 +103,12 @@ function isExplicitEditorUiRequest(payload = {}) {
|
|||
function textEditorTarget(payload = {}) {
|
||||
const path = String(payload.path || "").trim();
|
||||
const extension = documentExtension(payload, { path });
|
||||
const contextId = contextIdFromPayload(payload);
|
||||
return {
|
||||
path,
|
||||
file_id: "",
|
||||
context_id: contextId,
|
||||
ctxid: contextId,
|
||||
extension,
|
||||
format: extension,
|
||||
version: payload.version || "",
|
||||
|
|
@ -139,6 +148,8 @@ async function syncOpenEditorSurface(document = {}) {
|
|||
await editor.openSession?.({
|
||||
path: document.path || "",
|
||||
file_id: document.file_id || "",
|
||||
ctxid: document.ctxid || document.context_id || "",
|
||||
context_id: document.context_id || document.ctxid || "",
|
||||
refresh: true,
|
||||
source: "tool-result-sync",
|
||||
});
|
||||
|
|
@ -175,6 +186,10 @@ function documentsMatch(entry = {}, document = {}) {
|
|||
);
|
||||
}
|
||||
|
||||
function contextIdFromPayload(payload = {}) {
|
||||
return String(payload.context_id || payload.ctxid || "").trim();
|
||||
}
|
||||
|
||||
function truthy(value) {
|
||||
if (value === true) return true;
|
||||
if (value === false || value == null) return false;
|
||||
|
|
|
|||
|
|
@ -182,17 +182,19 @@ function taskLineIndexes(markdown = "") {
|
|||
}
|
||||
|
||||
async function callEditor(action, payload = {}) {
|
||||
const explicitContextId = String(payload.ctxid || payload.context_id || "").trim();
|
||||
return await callJsonApi("/plugins/_editor/editor_session", {
|
||||
action,
|
||||
ctxid: currentContextId(),
|
||||
...payload,
|
||||
ctxid: explicitContextId || currentContextId(),
|
||||
});
|
||||
}
|
||||
|
||||
async function requestEditor(eventType, payload = {}, timeoutMs = 5000) {
|
||||
const explicitContextId = String(payload.ctxid || payload.context_id || "").trim();
|
||||
const response = await editorSocket.request(eventType, {
|
||||
ctxid: currentContextId(),
|
||||
...payload,
|
||||
ctxid: explicitContextId || currentContextId(),
|
||||
}, { timeoutMs });
|
||||
const results = Array.isArray(response?.results) ? response.results : [];
|
||||
const first = results.find((item) => item?.ok === true && isEditorSocketData(item?.data))
|
||||
|
|
@ -284,9 +286,12 @@ const model = {
|
|||
await this.init();
|
||||
await this.refresh();
|
||||
if (payload?.path || payload?.file_id) {
|
||||
const contextId = String(payload.ctxid || payload.context_id || "").trim();
|
||||
await this.openSession({
|
||||
path: payload.path || "",
|
||||
file_id: payload.file_id || "",
|
||||
ctxid: contextId,
|
||||
context_id: contextId,
|
||||
refresh: payload.refresh === true,
|
||||
source: payload.source || "",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
- Preserve stale-read protection before patch operations.
|
||||
- Validate patch structures before applying edits.
|
||||
- Read back changed regions after writes or patches where the tool contract requires confirmation.
|
||||
- Include the active agent context id in write/patch result metadata when available so canvas consumers can open the changed file in the correct chat context.
|
||||
|
||||
## Work Guidance
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class TextEditor(Tool):
|
|||
return Response(
|
||||
message=msg,
|
||||
break_loop=False,
|
||||
additional=_result_additional("write", info, kwargs),
|
||||
additional=_result_additional("write", info, kwargs, agent=self.agent),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -256,7 +256,7 @@ class TextEditor(Tool):
|
|||
return Response(
|
||||
message=msg,
|
||||
break_loop=False,
|
||||
additional=_result_additional("patch", post_info, options),
|
||||
additional=_result_additional("patch", post_info, options, agent=self.agent),
|
||||
)
|
||||
|
||||
async def _patch_replace(
|
||||
|
|
@ -315,7 +315,7 @@ class TextEditor(Tool):
|
|||
return Response(
|
||||
message=msg,
|
||||
break_loop=False,
|
||||
additional=_result_additional("patch", post_info, options),
|
||||
additional=_result_additional("patch", post_info, options, agent=self.agent),
|
||||
)
|
||||
|
||||
async def _patch_context(
|
||||
|
|
@ -377,7 +377,7 @@ class TextEditor(Tool):
|
|||
return Response(
|
||||
message=msg,
|
||||
break_loop=False,
|
||||
additional=_result_additional("patch", post_info, options),
|
||||
additional=_result_additional("patch", post_info, options, agent=self.agent),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -475,7 +475,7 @@ def _freshness_error_message(agent, info: FileInfo, code: str) -> str:
|
|||
return agent.read_prompt(prompt, path=info["expanded"])
|
||||
|
||||
|
||||
def _result_additional(action: str, info: FileInfo, options: dict | None = None) -> dict:
|
||||
def _result_additional(action: str, info: FileInfo, options: dict | None = None, agent=None) -> dict:
|
||||
path = str(info.get("expanded") or "")
|
||||
extension = Path(path).suffix.lower().lstrip(".")
|
||||
options = options or {}
|
||||
|
|
@ -484,7 +484,7 @@ def _result_additional(action: str, info: FileInfo, options: dict | None = None)
|
|||
or options.get("open_canvas")
|
||||
or options.get("open_document")
|
||||
)
|
||||
return {
|
||||
additional = {
|
||||
"_tool_name": "text_editor",
|
||||
"action": action,
|
||||
"path": path,
|
||||
|
|
@ -492,6 +492,11 @@ def _result_additional(action: str, info: FileInfo, options: dict | None = None)
|
|||
"extension": extension,
|
||||
"open_in_canvas": open_in_canvas,
|
||||
}
|
||||
context_id = str(getattr(getattr(agent, "context", None), "id", "") or "").strip()
|
||||
if context_id:
|
||||
additional["context_id"] = context_id
|
||||
additional["ctxid"] = context_id
|
||||
return additional
|
||||
|
||||
|
||||
def _truthy(value) -> bool:
|
||||
|
|
|
|||
|
|
@ -71,6 +71,64 @@ async def test_default_agent0_prompt_budget_and_guardrails():
|
|||
assert "Computer Use enablement is scoped to the current CLI session" not in system_text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tiny_local_profile_prompt_is_action_first_json_contract():
|
||||
system_text = await _build_system_text("tiny-local")
|
||||
communication_prompt = (
|
||||
PROJECT_ROOT / "agents" / "tiny-local" / "prompts" / "agent.system.main.communication.md"
|
||||
).read_text(encoding="utf-8")
|
||||
code_prompt = (
|
||||
PROJECT_ROOT / "agents" / "tiny-local" / "prompts" / "agent.system.tool.code_exe.md"
|
||||
).read_text(encoding="utf-8")
|
||||
response_prompt = (
|
||||
PROJECT_ROOT / "agents" / "tiny-local" / "prompts" / "agent.system.tool.response.md"
|
||||
).read_text(encoding="utf-8")
|
||||
text_editor_prompt = (
|
||||
PROJECT_ROOT / "agents" / "tiny-local" / "prompts" / "agent.system.tool.text_editor.md"
|
||||
).read_text(encoding="utf-8")
|
||||
solving_prompt = (
|
||||
PROJECT_ROOT / "agents" / "tiny-local" / "prompts" / "agent.system.main.solving.md"
|
||||
).read_text(encoding="utf-8")
|
||||
|
||||
assert "You are Agent Zero. Act on the user's behalf." in system_text
|
||||
assert "Your visible assistant message must be exactly one valid JSON object." in system_text
|
||||
assert 'Use exactly these top-level fields: `"tool_name"` and `"tool_args"`.' in system_text
|
||||
assert 'For a final user-facing answer, use the `response` tool.' in system_text
|
||||
assert "Use `response` only when the work is complete, blocked, or the user is only acknowledging completed work." in system_text
|
||||
assert "If the user says \"proceed\", \"continue\", \"go ahead\", \"do it\", \"excellent proceed\"" in system_text
|
||||
assert "Do not explain what command the user could run manually." in system_text
|
||||
assert "output a corrected JSON tool request immediately" in system_text
|
||||
assert "## Tiny Local Output Rule" in system_text
|
||||
assert "~~~json" not in communication_prompt
|
||||
assert "~~~json" not in code_prompt
|
||||
assert "~~~json" not in response_prompt
|
||||
assert "~~~json" not in text_editor_prompt
|
||||
assert "No JSON in markdown fences" not in communication_prompt
|
||||
assert "thoughts: array thoughts before execution" not in communication_prompt
|
||||
assert "headline: short headline summary" not in communication_prompt
|
||||
assert "explain each step in thoughts" not in solving_prompt
|
||||
assert "Continuation words" in solving_prompt
|
||||
assert "Do not respond by saying you will begin, continue, start, proceed, or investigate." in solving_prompt
|
||||
assert "Do not use this tool for \"proceed\", \"continue\", \"go ahead\"" in response_prompt
|
||||
assert "do not repeat the same exact tool call" in solving_prompt
|
||||
assert '"open_in_canvas":true' in text_editor_prompt
|
||||
assert "do not repeat the same tool call" in text_editor_prompt
|
||||
assert '"headline"' not in code_prompt
|
||||
assert '"headline"' not in response_prompt
|
||||
assert '"headline"' not in text_editor_prompt
|
||||
|
||||
|
||||
def test_tiny_local_profile_is_discoverable():
|
||||
from helpers import subagents
|
||||
|
||||
profiles = {
|
||||
str(item.get("key") or ""): str(item.get("label") or "")
|
||||
for item in subagents.get_all_agents_list()
|
||||
}
|
||||
|
||||
assert profiles["tiny-local"] == "Tiny Local"
|
||||
|
||||
|
||||
def test_removed_small_profile_and_prompt_text_generic():
|
||||
removed_profile = "a0" + "_" + "small"
|
||||
|
||||
|
|
|
|||
|
|
@ -401,8 +401,12 @@ def test_office_artifacts_only_open_desktop_from_explicit_document_ui_requests()
|
|||
assert "syncTextEditorResultsIntoOpenEditor" in editor_sync
|
||||
assert 'toolName(payload) !== "text_editor"' in editor_sync
|
||||
assert 'return ["write", "patch"].includes(action);' in editor_sync
|
||||
assert "if (!context?.results?.length) return;" in editor_sync
|
||||
assert "if (context.historyEmpty && !explicitOpen) continue;" in editor_sync
|
||||
assert "syncOpenEditorSurface" in editor_sync
|
||||
assert "isEditorSurfaceOpen" in editor_sync
|
||||
assert "context_id" in editor_sync
|
||||
assert "ctxid" in editor_sync
|
||||
assert "void syncOpenDocumentSurfaces(target);" in auto_open
|
||||
assert "void syncOpenDocumentSurfaces({ path, file_id: fileId });" not in auto_open
|
||||
assert "editorStore" not in auto_open
|
||||
|
|
|
|||
|
|
@ -379,8 +379,9 @@ class _FakeTool:
|
|||
|
||||
|
||||
class _FakeAgent:
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, context_id: str = "") -> None:
|
||||
self.data = {}
|
||||
self.context = types.SimpleNamespace(id=context_id) if context_id else None
|
||||
|
||||
def read_prompt(self, name: str, **kwargs) -> str:
|
||||
if name.endswith("read_ok.md"):
|
||||
|
|
@ -556,7 +557,7 @@ def test_text_editor_write_result_carries_markdown_canvas_intent(
|
|||
) -> None:
|
||||
module, _calls = _load_text_editor_tool(monkeypatch)
|
||||
target = tmp_path / "note.md"
|
||||
tool = module.TextEditor(_FakeAgent(), "text_editor", "write", {}, "", None)
|
||||
tool = module.TextEditor(_FakeAgent("ctx-write-1"), "text_editor", "write", {}, "", None)
|
||||
|
||||
response = asyncio.run(
|
||||
tool._write(
|
||||
|
|
@ -574,6 +575,8 @@ def test_text_editor_write_result_carries_markdown_canvas_intent(
|
|||
"format": "md",
|
||||
"extension": "md",
|
||||
"open_in_canvas": True,
|
||||
"context_id": "ctx-write-1",
|
||||
"ctxid": "ctx-write-1",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -649,6 +649,21 @@ def test_tool_prompts_prevent_top_level_multi_tool():
|
|||
assert 'Never use `tool_name: "multi"`' in browser_prompt
|
||||
|
||||
|
||||
def test_local_model_tool_use_guide_stays_prompt_profile_plugin_only():
|
||||
guide = Path("docs/guides/local-model-tool-use.md").read_text(encoding="utf-8")
|
||||
|
||||
assert "Tiny Local" in guide
|
||||
assert "agents/tiny-local/" in guide
|
||||
assert "*.promptinclude.md" in guide
|
||||
assert "Do not change `agent.py`" in guide
|
||||
assert "Do not change `helpers/extract_tools.py`" in guide
|
||||
assert "Use exactly these top-level fields: `tool_name` and `tool_args`." in guide
|
||||
assert '{"tool_name":"response","tool_args":{"text":"Done."}}' in guide
|
||||
assert "If the user says \"proceed\", \"continue\", \"go ahead\", or similar" in guide
|
||||
assert "call the next appropriate tool instead of replying with a promise or status update" in guide
|
||||
assert "Do not create parser repair code for this workflow." in guide
|
||||
|
||||
|
||||
def _load_scheduler_tool(monkeypatch):
|
||||
_install_tool_stub(monkeypatch)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue