The Boundaries of LLM Honesty and Multi-Agent Code Engineering
LLM honesty is often reduced to a measurable code-quality metric, such as whether a model reports defects in its own generated code. This article explains how verifiable rewards make coding a favorable domain for optimizing that behavior, why the same optimization can distort open-ended tasks through over-verification and over-assertion, and how teams can evaluate honesty across factuality, calibration, self-disclosure, and humility. It also shows how to decompose problems that exceed a single inference pass using structured multi-agent orchestration.
1. Introduction: When “Honesty” Becomes a Measurable Metric
LLM evaluation announcements often mention “improved honesty.” When examined closely, this is usually quantified as whether the model proactively identifies defects in its own generated code, such as missed boundary conditions, null-pointer risks, or missing error handling. This capability is valuable and easy to verify: both human review and automated testing can produce clear conclusions.
However, this definition narrows “honesty” into a measurable metric. Once a measurable metric becomes an optimization target, it can reshape the model’s expression across all tasks. A commonly observed phenomenon is that when a model is optimized to check errors in its own output, it tends to give more definitive, concise, and imperative answers in open-ended tasks, while allocating more space to detail verification.
This is not simply “better” or “worse”; it is a shift in capability emphasis. To evaluate this shift, we first need to answer three questions:
- What separable capabilities are included in “honesty”?
- Why have code tasks become the main battlefield for these capabilities?
- At what task scale does a single agent’s single inference pass reach its boundary?
2. Decomposing “Honesty”: Four Easily Confused Capabilities
In everyday usage, “honesty” is a mixed concept. In engineering evaluation, it should at least be separated into the following four layers:
| Capability | Core question | Typical failure |
|---|---|---|
| Factual consistency | Can the statement withstand verification? | Fabricating nonexistent APIs, parameters, or references |
| Calibration | Does stated confidence match evidence strength? | Using “almost certainly” for uncertain conclusions |
| Self-disclosure completeness | Does the model proactively expose defects in its own output? | Presenting only “done” without mentioning uncovered boundaries |
| Statement humility | Does the model admit ambiguity when answers are not unique? | Outputting one interpretation as definitive |
The key point is: these four capabilities do not always change in the same direction.
Factual consistency can be inflated by reducing the informational content of a statement, for example by saying only correct but empty things. Humble expression can also degrade into evasion, for example by adding “this is only a hypothesis” regardless of how strong the evidence is. Self-disclosure completeness can go too far into excessive self-criticism, treating style preferences or acceptable trade-offs as “defects.”
A concrete example: a model uses regular expressions to parse HTML. It does two things:
- It points out that “HTML is not a regular language, and this approach will fail on nested tags.” This is self-disclosure completeness.
- It then adds, “Regular expressions cannot correctly parse nested structures.” In this specific context, that is a factual error. Regular expressions cannot recognize nesting of arbitrary depth, a claim often circulated as if it were universally accurate. However, the original pattern only handled a limited number of levels; the failure point is a robustness boundary rather than a principle-level impossibility.
Stacking these two capabilities did not improve overall honesty. Therefore, the first principle of evaluation is layering: first determine which kind of honesty is being tested, then choose metrics and verification methods that match it.
3. Why Code Scenarios Are the Main Arena for “Verifiable Rewards”
3.1 Code has a natural and cheap judge
In natural-language generation, judging whether an answer is “correct” often depends on human preference. Code is different. Compilers, type checkers, static analysis tools, unit tests, integration tests, and runtime exceptions are objective, automatable, and repeatable validators. In reinforcement learning, methods that use programmatic verification results as reward signals are often called verifiable reward reinforcement learning.
Its value is that the reward does not depend on subjective judgment. The training objective is closely aligned with the real objective, and it is less likely to be gamed by strategies that merely please evaluators.
3.2 Agentic coding: one of the few scenarios with a clear commercial loop
From an industry perspective, agentic coding applications have formed a relatively clear commercial loop. The demand is clear: improving development efficiency. The effect is measurable: whether patches are accepted and whether tests pass. The willingness to pay is clear: it directly saves engineering labor. All three exist at the same time.
From a training perspective, code tasks provide dense supervision signals:
- Whether a patch turns failing tests into passing ones;
- Whether new failures are introduced;
- Whether static checks are cleared;
- Whether code review produces change requests.
For this reason, “proactively exposing defects in its own code” becomes a capability that is easy to measure and easy to optimize. It is not surprising that evaluation announcements use it as a representative metric of honesty.
3.3 The boundary of reward signals: outside the judge’s view
It is important to note that verifiable rewards only cover the part the judge can see. Tests and compilers cannot directly evaluate the following aspects:
- Whether the architecture is reasonable;
- Whether naming is clear;
- Whether exception-handling strategy is appropriate;
- Whether performance is acceptable.
A model may still “look highly complete” while containing many problems in these dimensions. Therefore, when using verifiable signals to measure code capability, we must also acknowledge their limited coverage. In engineering practice, the usual response is to supplement automated verification with code review, architecture review, and human sampling.
4. When the Judge Is Absent: Evaluation Difficulties in Open-Ended Tasks
Tasks such as literary criticism, strategic analysis, product positioning, and complex trade-off decisions do not have a single objective answer. Text itself is abstract, polysemous, and context-dependent, and evaluation must embrace this uncertainty. In such tasks, directly transferring the standard of “code honesty” often produces two types of bias.
4.1 Over-verification: treating verifiability as value
A typical manifestation is that an answer spends a large amount of space verifying facts that can be checked in the text, such as the origin of a metaphor, the meaning of a word in a particular era, or details of an author’s life, while offering almost no judgment about what the text is actually trying to express.
Verification itself is not wrong. The problem is that “verifiable” is not equal to “important.” When optimization pressure favors verifiable details, the model focuses its attention where it can give definitive answers and avoids places that require interpretive judgment. This is a verifiability preference.
4.2 Over-assertion: uncalibrated confidence
Another manifestation is stylistic: starting with a conclusion and making judgments with phrases like “almost certainly.” In literary criticism, this style is more likely to overstep than saying “there are several possible interpretations, marked as hypotheses,” because:
- An assertive tone makes readers assume the conclusion has sufficient basis;
- But in reality, the basis is only one of the model’s interpretations of the text;
- When an interpretation is mistaken for a fact, it compresses the space for discussion rather than opening it.
This is not the same problem as hallucination. The content may be completely faithful to the source text, but the strength of tone exceeds the strength of evidence. This is a calibration problem, not a factual error. Only when evaluation separates the two can it locate the source of the problem.
4.3 Exploratory expression is not naturally more honest
Conversely, exploratory expression is not always more honest:
- In scenarios where evidence is sufficient, such as missing null-pointer handling for an API return value or missing lock protection for concurrent writes, responding with “this may be one interpretation” is under-calibration: failing to be definite when definiteness is warranted.
- When users need a clear recommendation, listing all possibilities while refusing to make a judgment transfers decision cost to the user.
Truly honest expression should make tone strength match evidence strength: give a definite judgment when there is a validator to rely on and explain the basis; mark judgments without a validator as hypotheses and explain where the uncertainty comes from.
5. Engineering Implementation: How to Evaluate and Constrain
5.1 Build a layered evaluation system
| Task type | Main verification method | Core metrics |
|---|---|---|
| Deterministic code tasks | Compilation, testing, static analysis | Test pass rate, regression-introduction rate |
| Semi-structured tasks | Rule checks plus human spot checks | Precision/recall of defect self-inspection |
| Open-ended tasks | Review rubric plus multi-person blind review | Calibration error, evidence-backed assertion rate, multi-solution coverage |
5.2 Turn “self-disclosure completeness” into a computable metric
Treat the defects a model proactively lists as a “prediction,” and treat the real defect set as the “label.” Then we can borrow precision and recall from information retrieval:
def audit_self_report(claimed: set[str], actual: set[str]) -> dict:"""Evaluate the quality of a model's self-inspection of code defects.claimed: set of defect identifiers proactively declared by the modelactual: set of real defects confirmed by human review or automated tools"""hit = claimed & actualprecision = len(hit) / len(claimed) if claimed else 0.0recall = len(hit) / len(actual) if actual else 1.0return {"precision": round(precision, 3), "recall": round(recall, 3)}
- Precision reflects whether self-disclosure is “inflated,” for example whether style preferences or harmless issues are listed as defects.
- Recall reflects whether self-disclosure “misses” defects.
Only when both metrics meet the target can self-disclosure be considered trustworthy. Looking only at “whether the model proactively reported defects” easily rewards behavior where the model says everything is a problem.
5.3 Quantify calibration: Expected Calibration Error
For tasks that require the model to provide confidence, we can use Expected Calibration Error to measure the gap between “how certain it sounds” and “how correct it actually is”:
def expected_calibration_error(confidence_correct: list[tuple[float, bool]], bins: int = 10) -> float:"""Calculate Expected Calibration Error (ECE), range [0, 1], lower is better.confidence_correct: list of (model confidence, whether the answer is correct)"""buckets = [[] for _ in range(bins)]for conf, correct in confidence_correct:idx = min(int(conf * bins), bins - 1)buckets[idx].append(1.0 if correct else 0.0)total = len(confidence_correct)if total == 0:return 0.0ece = 0.0for bucket in buckets:if not bucket:continueavg_conf = (buckets.index(bucket) + 0.5) / bins # Approximate average confidence using the bin midpoint.accuracy = sum(bucket) / len(bucket)ece += (len(bucket) / total) * abs(avg_conf - accuracy)return round(ece, 4)
It should be noted that buckets.index(bucket) relies on equality comparison of list elements and is only suitable for illustration. A production implementation should record each bin’s interval directly to avoid selecting the wrong index when bins are empty or values repeat.
5.4 Prompt-level constraints
In open-ended tasks, structured output can be used to constrain the model to distinguish “facts” from “inferences”:
import jsonOPEN_ENDED_SCHEMA = {"type": "object","properties": {"verifiable_facts": { # Facts that can be verified against external sources"type": "array","items": {"type": "string"},},"interpretations": { # Inferences based on facts; must indicate evidence strength"type": "array","items": {"type": "object","properties": {"claim": {"type": "string"},"evidence": {"type": "string"},"strength": {"type": "string", "enum": ["high", "medium", "low"]},},"required": ["claim", "evidence", "strength"],},},"unresolved": {"type": "array", "items": {"type": "string"}},},"required": ["verifiable_facts", "interpretations", "unresolved"],}def critique(text: str, client) -> dict:resp = client.chat.completions.create(model="your-model-name",messages=[{"role": "system","content": ("You are a rigorous text analysis assistant. Facts, inferences, and open questions must be separated. ""Inferences must provide evidence and honestly label strength. ""Low-strength inferences must not be stated with assertive language."),},{"role": "user", "content": text},],response_format={"type": "json_schema","json_schema": {"name": "critique", "schema": OPEN_ENDED_SCHEMA},},)return json.loads(resp.choices[0].message.content)
This design has three effects:
- It structurally separates “facts” from “inferences”;
- It forces each inference to include evidence and a strength label;
- It preserves an “unresolved” field, making uncertainty explicit rather than hidden by tone.
It is important to emphasize that structured output constrains expression, but does not automatically improve factual accuracy. Verification still depends on external tools and people.
6. When Problems Exceed a Single Inference Pass: Multi-Agent Decomposition
Even if a single agent performs well on local tasks, some problems still exceed the capability boundary of “one inference pass”:
- Investigating an intermittent concurrency defect across an entire microservice;
- An interface migration involving hundreds of files;
- Wanting a plan to be repeatedly examined from multiple angles before implementation.
These tasks share a common feature: the context that needs to be included exceeds the range a single inference pass can effectively use, and the sub-problems are relatively independent.
6.1 Orchestration pattern: fan-out and aggregation
A common approach is a “main controller plus sub-agents” fan-out and aggregation structure:
from concurrent.futures import ThreadPoolExecutorfrom dataclasses import dataclassimport json@dataclassclass Unit:"""A relatively independent work unit, such as a service, a directory, or a category of call sites."""unit_id: strscope: list[str]FINDING_SCHEMA = {"type": "object","properties": {"unit_id": {"type": "string"},"issues": {"type": "array","items": {"type": "object","properties": {"location": {"type": "string"},"severity": {"type": "string", "enum": ["low", "medium", "high"]},"evidence": {"type": "string"},"suggested_fix": {"type": "string"},},"required": ["location", "severity", "evidence", "suggested_fix"],},},"open_questions": {"type": "array", "items": {"type": "string"}},},"required": ["unit_id", "issues", "open_questions"],}def explore_unit(unit: Unit, client) -> dict:"""Sub-agent: handle only the assigned unit and return structured findings."""resp = client.chat.completions.create(model="your-model-name",messages=[{"role": "system","content": ("You are responsible for investigating defects within the specified scope. ""Every conclusion must include code evidence. ""Uncertain items must be placed in open_questions and must not be presented as definitive conclusions."),},{"role": "user", "content": json.dumps(vars(unit), ensure_ascii=False)},],response_format={"type": "json_schema","json_schema": {"name": "finding", "schema": FINDING_SCHEMA},},)return json.loads(resp.choices[0].message.content)def hunt(units: list[Unit], client, max_workers: int = 8) -> list[dict]:with ThreadPoolExecutor(max_workers=max_workers) as pool:findings = list(pool.map(lambda u: explore_unit(u, client), units))# The main controller agent integrates here: deduplicate, merge the same root cause, prioritize, and generate a unified remediation plan.return findings
This pattern decomposes “reading the entire service” into several inference passes that “read only a small part,” then aggregates structured results at the controller layer.
6.2 Multi-angle pressure testing of plans
For scenarios where a plan needs to be repeatedly examined before implementation, multiple sub-agents can review the same plan from different positions:
| Review role | Focus | Typical output |
|---|---|---|
| Compatibility review | Whether callers are missed and whether protocols remain backward compatible | List of affected interfaces |
| Performance review | Whether the migration introduces extra queries or serialization overhead | Risk points and load-testing suggestions |
| Rollback review | Whether it is possible to roll back safely when errors occur | Rollback steps and list of irreversible operations |
| Test review | Whether boundary conditions and exception paths are covered | Suggestions for additional test cases |
Since each role cannot see the others’ review processes, and the controller layer aggregates conflicting opinions, this can reduce blind spots caused by groupthink.
6.3 Three engineering essentials
First, structured handoff. Sub-agents should not directly pass natural-language conversations to one another; they should pass results through fixed data contracts, such as the JSON Schema above. This allows the controller layer to validate, count, and trace results.
Second, controlled context sharing. Each sub-agent should receive only the context necessary to complete its own task. This avoids irrelevant information diluting attention and reduces the scope of information exposure. This is the core role of retrieval augmentation in code scenarios: first locate, then dispatch.
Third, controllable cost. Increasing the number of agents increases token consumption, latency, and failure surface at the same time. In practice, we should:
- First determine whether the task really needs decomposition. For example, if a single inference pass can already cover the module size, decomposition only increases coordination cost;
- Set timeout and retry limits for each subtask;
- Record the input, output, and duration of each orchestration so we can identify which layer failed.
7. Conclusion: From a Single Score to Layered Verification
Returning to the original question: did a model update make it “more honest”? The answer depends on which kind of honesty is being discussed.
- In code tasks, proactively exposing defects is a valuable capability and is indeed easier to verify. The reward signal for these tasks is objective and dense, so model progress in such scenarios is measurable.
- In open-ended tasks, the same optimization direction may manifest as over-verification and over-assertion: treating “verifiable” as “valuable” and treating one interpretation as a conclusion. This shows that evaluating model capability with a single dimension has clear limitations.
- For engineering teams, a more reliable approach is to split “honesty” into four dimensions: factual consistency, calibration, self-disclosure completeness, and statement humility, and design verification methods for each. Deterministic tasks can be given to validators, while open-ended tasks can be given to structured output and human blind review.
- When task scale exceeds the boundary of a single inference pass, use main-controller plus sub-agent orchestration to decompose the problem, and use structured contracts to ensure results can be merged and traced.
Ultimately, the standard for measuring a model is not how certain it sounds, but whether it can make expression strength match evidence strength in every type of task.
