Agent Design Across Three Dimensions: Prompt, Context, and Harness

    This article breaks down the three engineering dimensions that make autonomous AI agents reliable in production: Prompt Engineering (how instructions are structured), Context Engineering (what information the model sees and how it is budgeted), and Harness Engineering (the runtime environment, tool protocol, guardrails, and observability). It explains each dimension's design philosophy, common anti-patterns, and failure symptoms, then provides a practical methodology for enterprise adoption and evaluation—showing how to move an agent system from "demo-ready" to "production-trusted."

    Agent Design Across Three Dimensions: Prompt, Context, and Harness

    Introduction: From “Being Able to Chat” to “Getting Real Work Done”

    In early 2026, an open-source autonomous agent project quickly went viral. Cloud vendors and development teams released solutions built on the same architecture, and deployment tutorials and usage guides appeared everywhere, triggering a wave of “everyone tries out agents.” But once the hype subsided, what was truly worth studying was not the operational steps for “getting a demo to run,” but the design philosophy behind these systems.

    Looking more calmly, autonomous agents are not a new species created out of thin air. Dynamic prompt assembly, context compression, memory management, skill modularization with progressive disclosure, hook extension mechanisms, policy guardrails, tool calling, and the ability to extend the operational boundary from pure text to real environments (local files, terminal commands, browsers, desktop GUIs)—all of these key techniques had already been explored over time. Once systematically integrated into a single engineering framework, they produced a qualitative leap: the pattern of “many key techniques maturing together and triggering an experience breakthrough” has appeared repeatedly in the history of deep learning and large models.

    The intuitive result of this leap is that AI evolved from a “one-question-one-answer chatbot,” from a “single-task, vertical-domain assistant,” into a personal assistant with global awareness, autonomous planning, and long-horizon execution. Supporting this evolution are three progressively deepening engineering dimensions:

    • Prompt Engineering: solves “how to express”—how instructions are organized;
    • Context Engineering: solves “what the model sees”—how information is supplied and pruned;
    • Harness Engineering: solves “what environment it runs in”—how the execution environment is built.

    This article first examines the design philosophy of each dimension, then presents a methodology for enterprise adoption and evaluation.

    1. The Three-Dimensional Model: Layered and Interdependent

    The relationship among the three can be understood as a layered structure:

    1. ┌──────────────────────────────────────────────┐
    2. │ Prompt Layer: Instructions & Expression │
    3. │ Role/goal / capabilities / rules / contracts │
    4. ├──────────────────────────────────────────────┤
    5. │ Context Layer: Information & State │
    6. │ Instructions + session state + memory + │
    7. │ retrieved evidence + tool results │
    8. ├──────────────────────────────────────────────┤
    9. │ Harness Layer: Environment & Governance │
    10. │ Main loop / tool protocol / hooks / │
    11. │ permission guardrails / observability │
    12. └──────────────────────────────────────────────┘

    1.1 Division of Responsibilities and Failure Symptoms

    Layer Core Question Main Techniques Typical Symptoms When It Fails
    Prompt How to say it Structured templates, instruction layering, skills and contracts Off-topic answers, conflicting instructions, skipped steps
    Context What to look at Budget allocation, compressed summaries, tiered memory, retrieval Forgetting earlier context, distraction from irrelevant info, repeated amplification of early errors
    Harness What environment it runs in Main loop, tool wrapping, permission guardrails, log replay Infinite loops, unrecoverable call failures, unauthorized operations, untraceable faults

    It is worth emphasizing that the three are a “multiplicative” relationship, not an “additive” one. No matter how precise the prompt, if the model cannot see the current task state, it can only guess; no matter how clean the context, without reliable tool execution and permission control, the output cannot be converted into real value; conversely, no matter how complete the environment, vague instructions will make the execution path waver. If any single layer fails, overall reliability drops significantly.

    1.2 Why Harness Needs to Be an Independent Dimension

    In the early days of large models, prompts were designed directly for single-turn conversations, and context was just a concatenation of historical messages, with no need for an independent execution framework. But when models need to call tools continuously, read and write files, operate interfaces, run for extended periods, and be granted “write” permissions in real environments, questions like “where does it run, how is it constrained, what happens when something goes wrong” become a standalone engineering problem. This is exactly what Harness is meant to answer. It is decoupled from the model: the model is a replaceable reasoning engine, while the Harness decides what that engine “can do, cannot do, how it does it, and how to verify it afterward” in the real world.

    2. The Prompt Dimension: From “A Single Prompt” to an “Assemblable Instruction System”

    2.1 Design Philosophy: Instructions Are a Layered, Assemblable System, Not One Long Block of Text

    Many teams’ first version of an agent piles all requirements into a single static long prompt. As capabilities grow, the prompt balloons to thousands of words, different rules begin to conflict with each other, and changing one place triggers regressions elsewhere. A mature framework treats the prompt as a structured system that can be dynamically assembled, typically composed of five layers:

    1. Identity and Goals: role, task objectives, success criteria, and boundary conditions;
    2. Capability and Tool Descriptions: the purpose, parameters, constraints, and invocation timing of currently available tools;
    3. Operating Rules: planning approach, confirmation strategy, failure handling, and security/compliance requirements;
    4. Output Contract: structured fields, format conventions, and how to express uncertainty;
    5. Runtime Facts: dynamic information such as current time, working directory, user preferences, and task state.

    After layering, each layer can be versioned and tested independently, and conflicts can be traced to a specific layer. For example, a conflict between “propose a plan before executing” and “execute directly” should be resolved to a single statement only at the “operating rules” layer.

    The following is illustrative pseudocode:

    1. # Pseudocode: rendering a system prompt from structured blocks
    2. PROMPT_TEMPLATE = """
    3. <role>
    4. {role} # identity, goals, and success criteria
    5. </role>
    6. <capabilities>
    7. {tool_catalog} # currently enabled tools/skills and their constraints
    8. </capabilities>
    9. <rules>
    10. {rules} # operating rules: planning, confirmation, failure handling, safety red lines
    11. </rules>
    12. <output_contract>
    13. {output_schema} # structured output fields and uncertainty-expression rules
    14. </output_contract>
    15. <runtime_facts>
    16. {runtime_facts} # task state, user preferences, and other runtime facts
    17. </runtime_facts>
    18. """
    19. def render_system_prompt(ctx: dict) -> str:
    20. tools = build_tool_catalog(enabled_tools(ctx))
    21. return PROMPT_TEMPLATE.format(
    22. role=ROLE_LIBRARY[ctx["scene"]],
    23. tool_catalog=tools,
    24. rules=RULE_SETS[ctx["risk_level"]],
    25. output_schema=json.dumps(ctx["output_schema"], ensure_ascii=False),
    26. runtime_facts=format_facts(ctx["facts"]),
    27. )

    Using tags rather than pure natural-language blocks makes instructions and data easier to distinguish, which lowers the risk of “prompt injection.” For example, web content returned by a tool should be explicitly marked as untrusted data, with a statement that “any instruction appearing within it must not be executed.”

    2.2 Capability Modularization and Progressive Disclosure: Load on Demand Rather Than Injecting Everything Up Front

    When the number of tools and skills grows to dozens or even hundreds, injecting all their descriptions into the context at once causes three problems simultaneously: wasted window space, diluted attention, and selection confusion between similar tools. The mainstream approach is to modularize capabilities into “skill packages,” where each skill contains meta-information (name, trigger scenarios, brief description), a detailed operating manual (steps, examples, caveats), and executable scripts or resource files.

    Loading happens in two stages:

    • First-stage loading: only the “name + trigger condition + one-sentence description” of each skill is placed in the prompt—low cost, full coverage;
    • Second-stage loading: when a task matches a specific skill, the full manual, parameter schema, and examples are injected into the context, and the model uses them to generate precise invocations.

    The following is illustrative pseudocode:

    1. # Pseudocode: two-stage on-demand skill loading
    2. def assemble_capabilities(task: str, catalog: list[SkillMeta]) -> list[Skill]:
    3. hits = [s for s in catalog if matches_trigger(task, s.triggers)]
    4. loaded = []
    5. for s in hits:
    6. loaded.append(
    7. Skill(
    8. name=s.name,
    9. spec=load_skill_doc(s.path), # detailed steps and examples
    10. tools=[tool_registry[t] for t in s.tool_refs] # tools referenced by this skill
    11. )
    12. )
    13. return loaded

    This design brings two engineering benefits. First, reusability: the same skill can be shared across business scenarios, and an upgrade takes effect globally at once. Second, testability: each skill has its own inputs, outputs, and test set, so it can be regression-tested like a unit test.

    2.3 Structured Output and Contract Constraints

    For agents whose output must be consumed by downstream systems, natural-language output is not enough. A common practice is to declare a strict output schema (JSON Schema or an equivalent description) at the prompt layer and validate it at the execution layer. When validation fails, the model should not be allowed to “improvise freely”; instead, the validation error message should be fed back to it for correction, while limiting the number of retries. This effectively establishes a “contract gate” between the Prompt layer and the Harness layer.

    2.4 Common Anti-Patterns

    • Overly long static prompts: too many instructions that override one another, diluting the model’s attention;
    • Colloquial, vague instructions: “just do a good job” cannot be executed reliably and should be replaced with decidable acceptance criteria;
    • Hardcoding business rules into the prompt: rule changes require a release; they should be externalized as configuration or policy data;
    • Ignoring injection risk: concatenating tool-returned content and user input into the prompt without distinction.

    3. The Context Dimension: Managing a Limited Context Window as a Scarce Budget

    3.1 Design Philosophy: The Core of Context Engineering Is “Trade-offs,” Not “Stuffing It Full”

    The early thinking was “stuff in as much as possible.” But as the window grew, engineers gradually reached a counterintuitive conclusion: a longer context does not necessarily make the model more accurate. Irrelevant information interferes with attention, the “lost in the middle” phenomenon causes key information placed in the middle of the context to be ignored, and early errors get repeatedly cited and amplified in subsequent reasoning. So the goal of context engineering is not “information maximization” but “within a given budget, making every piece of information serve a clear purpose.”

    In practice, a single invocation’s context is typically composed of five types of information:

    1. System instructions (role, rules, output contract);
    2. Conversation history (records of user-assistant interactions);
    3. Task state (completed steps, intermediate results, pending items);
    4. Retrieved evidence (knowledge base fragments, documents, database query results);
    5. Tool returns (command output, API responses, file contents).

    3.2 Budget Allocation: Setting Quotas for Each Type of Context

    A practical approach is to divide the available window into several quotas and reserve a safety margin to handle output and tokenization error:

    1. # Pseudocode: context budget allocation and trimming (ratios should be tuned to real workloads)
    2. BUDGET_RATIO = {
    3. "system": 0.10, # instructions and rules
    4. "state": 0.15, # task state: goals, plan, completed, pending
    5. "history": 0.45, # compressed conversation history
    6. "retrieval": 0.25, # retrieved evidence and tool returns
    7. "reserve": 0.05, # safety margin
    8. }
    9. def build_context(ctx, window: int) -> list[Message]:
    10. b = {k: int(window * v) for k, v in BUDGET_RATIO.items()}
    11. msgs = [system(render_system_prompt(ctx))] # 1. instructions
    12. msgs += state_block(ctx.state, budget=b["state"]) # 2. state
    13. msgs += compact_history(ctx.history, budget=b["history"]) # 3. history
    14. msgs += retrieve(ctx.query, ctx.kb, budget=b["retrieval"]) # 4. evidence
    15. return msgs

    The trimming order also matters: first compress verbose logs in tool returns, then compress earlier conversation history, and only as a last resort consider discarding retrieved evidence. Instructions and the current task state should in principle never be trimmed, because they determine what the model is “supposed to do” and “how far it has progressed.”

    3.3 External Memory: Externalize State to Avoid Unbounded Context Growth

    After a long-horizon task runs for dozens of steps, the context inevitably exceeds the window. The core technique used by mature frameworks is to strip state out of the context and store it in external media, then put only “necessary summary + location clues” back into the context at runtime:

    • File system: write full process logs and intermediate artifacts to files, keeping only file paths and summaries in the context. When the model needs details, it reads them on demand, equivalent to “virtual paging”;
    • Structured task list: use a unified to-do list to record goals, steps, and completion status. After each step is completed, update the state, keeping only current progress in the context rather than replaying the entire history;
    • Isolated sub-agents: let sub-agents complete “read-heavy, write-light” subtasks such as retrieval and research in independent contexts, returning only the conclusions to the main thread and preventing raw material from polluting the main context;
    • Tiered memory: divide memory types by time scale and purpose, with both writes and recalls passing through filtering.
    1. Working memory (current session window)
    2. ↑ recall: filtered by relevance and importance
    3. Episodic memory (historical tasks and interactions, vector store/log service)
    4. Semantic memory (user profile, domain knowledge, long-term facts)
    5. Procedural memory (skill manuals, process templates, experiential rules)

    3.4 Context Compression: Losing Information Strategically

    Compression is a lossy operation, so “what to drop, what to keep, and how to recover it” must be made explicit:

    • Preserve structure: retain the verbatim original user requirement, and have the summary list confirmed facts, decisions made, unfinished items, and key constraints;
    • Enable traceback: when the summary references key fragments, preserve their original locations (file paths, message numbers) so they can be reloaded if needed;
    • Isolate errors: for attempts already verified as wrong, keep only the “this path does not work” conclusion, not the detailed process, to prevent the model from repeating the mistake;
    • Compression as an event: log structured records when compression happens, making it easier to analyze afterward “what information was lost and which decision it affected.”

    3.5 Common Anti-Patterns

    • Full-history concatenation: cost grows linearly with session length, and performance degrades;
    • Ineffective retrieval: recalling results without relevance filtering introduces noise instead;
    • Repeated reads: reloading the same file over and over; summaries and hashes should be cached instead;
    • Mixing state and context: writing mutable state into the prompt text means a full rewrite is needed to update it, which easily introduces inconsistencies.

    4. The Harness Dimension: Running the Model in a Controllable, Observable Environment

    4.1 Design Philosophy: Treating the “Runtime” as a First-Class Engineering Object

    If Prompt decides “what to say” and Context decides “what to see,” then Harness decides “where it runs, what it can do, how it is constrained, and how to trace problems when they occur.” A complete Harness typically contains the following components:

    • Main loop: model reasoning → tool invocation → observing results → reasoning again, until a final answer is produced or an upper limit is reached;
    • Tool layer: tool registration, parameter validation, execution sandbox, timeout retries, result truncation;
    • Extension points: hooks exposed at fixed positions in the execution chain, allowing business logic to be plugged in;
    • Policy and guardrails: permission control, risk grading, human-in-the-loop confirmation;
    • Session state: persistence of task state, budget usage, and execution trace;
    • Observability: structured logs, tracing, replay, and evaluation.

    4.2 The Main Loop: A Bounded “Think-Act” Loop

    The main loop looks simple, but how its boundaries are set directly determines whether the system is reliable. At least four kinds of boundaries are needed:

    1. Step limit: prevents the model from forming an infinite loop by switching back and forth between two tools;
    2. Time and cost budget: limits the cost of a single task by token usage, call count, or wall-clock time;
    3. Failure handling strategy: when a tool errors out, feed the readable error message back to the model so it can decide whether to retry, switch paths, or ask for help; if consecutive failures reach a threshold, terminate and escalate;
    4. Termination condition: an explicit “task complete” signal, rather than relying on the model’s own judgment.
    1. # Pseudocode: a main loop with budget and guardrails
    2. def run_agent(goal: str, harness: Harness) -> AgentResult:
    3. ctx = harness.context.build(goal)
    4. for step in range(harness.max_steps):
    5. if harness.budget.exceeded():
    6. return AgentResult(status="BUDGET_EXCEEDED", state=ctx.snapshot())
    7. decision = harness.llm.decide(ctx.render(), tools=harness.tools.enabled())
    8. harness.trace.record("decision", step, decision)
    9. if decision.is_final:
    10. return AgentResult(status="DONE", answer=decision.answer, state=ctx.snapshot())
    11. for call in decision.tool_calls:
    12. verdict = harness.policy.check(call, ctx) # permission and risk checks
    13. if verdict.needs_confirm:
    14. harness.human_loop.confirm(call, verdict.reason)
    15. try:
    16. result = harness.tools.execute(call, timeout=harness.tool_timeout)
    17. ctx.observe(call, truncate(result, harness.max_result_chars))
    18. except ToolError as e:
    19. ctx.observe_error(call, e) # feed error back; the model decides next step
    20. return AgentResult(status="STEP_LIMIT", state=ctx.snapshot())

    4.3 Tool Layer Governance: Making Every Call Predictable

    Tools are the only channel through which an agent interacts with the real world, and also the main source of failures. Engineering should ensure:

    • Registration as definition: tools are registered with a unified schema (name, description, parameter schema, return structure), which is used for both prompt rendering and parameter validation;
    • Model-facing descriptions: describe “when to use it, when not to, typical parameters, and failure behavior,” not just the function signature;
    • Idempotency and rollback: write operations should be designed to be repeatable and reversible where possible; non-idempotent operations must carry an idempotency key;
    • Structured and truncated results: long output is written to disk and returns a summary plus location information, avoiding filling the context all at once;
    • Credential isolation: secrets are injected by the Harness into the execution environment, never entering the model context and never appearing in logs.

    4.4 Hook Extension Points: Decoupling Business Logic from the Core Loop

    Hooks are event extension points exposed at fixed positions in the main loop. Common types include:

    • Session start: load user preferences, inject business context;
    • Before/after prompt assembly: dynamically insert or trim instructions, skills, and tool lists;
    • Before a tool call: parameter rewriting, permission checks, interception of sensitive operations;
    • After a tool call: result redaction, structuring, summary compression;
    • Before compression is triggered: custom summarization strategy, archiving raw records;
    • Session end: persist memory, generate audit records, trigger billing and statistics.
    1. # Pseudocode: hook registration and dispatch
    2. class Harness:
    3. def __init__(self, hooks: dict[str, list[Callable]]):
    4. self.hooks = hooks
    5. def emit(self, event: str, payload: dict) -> dict:
    6. for hook in self.hooks.get(event, []):
    7. payload = hook(payload) or payload # may modify or short-circuit
    8. return payload

    The value of hooks lies in separation of concerns: the core loop stays simple and stable, while business-specific validation, auditing, and billing logic are plugged in as plugins, and different business lines can reuse the same Harness.

    4.5 Permission Guardrails: The Stronger the Capability, the More Constraints Must Be Front-Loaded

    Once an agent is granted read, write, and execution permissions in real environments, the risk model becomes completely different from that of an ordinary conversational system. Four principles are widely adopted:

    1. Least privilege: grant only the directories, commands, and API scope necessary to complete the task;
    2. Sandbox by default: restrict file operations to a designated working directory, run command execution in an isolated environment, and control network access by domain allowlist;
    3. Risk grading and human-in-the-loop: classify operations into read-only, reversible writes, and irreversible writes. Read-only operations run automatically, reversible writes must be logged and made rollback-able, and irreversible writes (deleting data, publishing externally, fund-related operations) must be manually confirmed;
    4. Credential and scope isolation: use credentials with different scopes for different tasks, preventing a leak in one task from spreading to all resources.

    For scenarios requiring GUI operation, additional engineering constraints are needed: screenshots and interface elements may contain sensitive information, which should be redacted before being sent to the model; coordinate-based clicking is not deterministic, so structured information such as the accessibility tree or control identifiers should be preferred; screenshots should be retained before and after key steps to facilitate replay and accountability.

    4.6 Observability and Replay: From “It Runs” to “It Is Operable”

    An agent in production must be diagnosable. The minimum observability set includes: structured traces per task (an input summary of each step, the model’s decision, the tool invocation, the return status, latency, and token usage), and cost attribution by user, business line, and tool. Beyond that, it should support deterministic replay: given the same task input and recorded model outputs, the entire Harness chain can be rerun without calling the model again, which is used to verify whether Harness changes introduce regressions.

    4.7 Multi-Agent Systems: Trading Context Isolation for Division of Labor

    When a task involves multiple roles or a large number of parallel subtasks, a common approach is to have a main controller agent handle task decomposition and result aggregation, while sub-agents complete their respective subtasks in independent contexts. This yields two direct benefits: first, context isolation—the intermediate process of subtasks does not pollute the main context; second, parallel acceleration—independent subtasks can run concurrently. The cost is increased coordination complexity and token usage, so whether to introduce it should be decided based on whether the task can be clearly decomposed.

    5. Enterprise Adoption Roadmap: Introduce in Phases, Not All at Once

    It is recommended to advance in four phases, each with clear acceptance criteria:

    • Phase 1: Prompt-driven workflows. Template high-frequency, low-risk tasks with structured prompts and fixed processes. Acceptance criteria: output format for key scenarios is stable and manually reviewable.
    • Phase 2: Single-agent tool use. Connect read-only tools (queries, retrieval, analysis) and establish tool registration standards and trace logs. Acceptance criteria: task success rate and average number of steps are quantifiable and comparable.
    • Phase 3: Context and memory governance. Introduce budget allocation, compression strategies, tiered memory, and external state storage. Acceptance criteria: success rates for long sessions and long-horizon tasks do not significantly degrade with length, and per-invocation cost stays controllable.
    • Phase 4: Permission opening and platformization. Gradually open reversible write operations, establish risk grading, human-in-the-loop confirmation, and auditing; accumulate a skill library, tool registry, and evaluation platform. Acceptance criteria: unauthorized-operation interception rate, fault recovery rate, and audit coverage meet launch thresholds.

    Alongside this is a judgment of when not to use an agent. If a task’s rules are fully deterministic, its inputs and outputs are enumerable, and it is highly sensitive to latency and cost, a traditional program or workflow engine is often more appropriate. An agent’s value lies in handling tasks with “uncertain paths that require judgment and interaction with the external world.”

    6. Evaluation and Governance: Making “Becoming More Reliable” Provable

    6.1 Three-Tier Evaluation Metrics

    Tier Focus Typical Metrics
    Component Prompts, tools, retrieval Format compliance rate, tool-selection accuracy, recall hit rate
    Task End-to-end effectiveness Task success rate, average steps, per-task cost and latency, human-intervention rate
    System Production stability Error rate, unauthorized-operation interception rate, timeout and failure recovery rate, audit coverage

    6.2 Datasets and Regression

    Accumulate an evaluation set from real business, covering typical paths, edge cases, and historical failure cases. Any change to prompts, tools, models, or the Harness should first run a regression on the evaluation set, comparing changes in key metrics before deciding whether to release. This avoids the local-optimization trap of “fixing one scenario and breaking three.”

    6.3 Failure Mode Classification

    Classify online failures into types such as “instruction ambiguity, information gaps, tool failures, permission blocks, planning errors, and budget overruns,” and drive improvements by type. For example, if “information gaps” account for a high proportion, prioritize optimizing retrieval and memory rather than repeatedly rewriting prompts; only with classified statistics can improvement avoid being driven by gut feeling.

    7. Common Anti-Pattern Checklist

    1. Treating prompts like “magic spells” and iterating by trial and error, lacking structure and version management;
    2. Only optimizing the Prompt layer while ignoring context budgeting and execution environment governance;
    3. Full-history concatenation and full-tool injection, with cost and noise rising together;
    4. Summaries that discard key constraints, causing long-horizon tasks to “drift further off course”;
    5. Opening permissions all at once, lacking risk grading and human-in-the-loop confirmation;
    6. Feeding untruncated, unstructured tool returns directly into the context;
    7. Lacking step and budget limits, falling into infinite loops on exceptions;
    8. No tracing and no replay, so online problems can only be located by guesswork;
    9. Chasing a multi-agent architecture without a clear task-decomposition boundary, where coordination cost exceeds the benefit;
    10. Evaluating only with demo results, with no regression set or tiered metrics.

    Conclusion

    Returning to the initial question: what is truly worth learning from these agent frameworks is not any specific feature, but their systematic handling of the three dimensions. The Prompt dimension turns “how to say it” from experience-based trial and error into a structured, assemblable instruction system; the Context dimension turns “what to let the model see” from unlimited accumulation into a quota-based budget management approach; the Harness dimension turns “what environment it runs in” from an implicit convention into an explicitly designed, constrainable, observable runtime.

    Model capabilities are still evolving rapidly, and the half-life of prompt techniques is short. Only these three layers of engineering capability can be sustained over the long term: they determine whether a system can move from “usable in a demo” to “trusted in production,” whether it can migrate smoothly when models change, and whether it stays controllable as the business expands. For developers and enterprises, this is perhaps the most valuable long-term part of this agent wave. When building such systems in the cloud, you can deploy the agent runtime on a container platform for elastic scaling, use a message queue to handle asynchronous tasks and smooth traffic peaks, store session and task state in a database or cache service, and hand off process logs and tracing data to logging and monitoring/alerting services for analysis and alerting. This lets engineering teams focus their energy on the three dimensions that truly determine effectiveness: prompt, context, and execution environment.