{"slug": "why-agentic-ai-needs-better-experts", "title": "Why agentic AI needs better experts", "summary": "A developer used OpenAI's Codex AI agent to refactor the uutils sed program to handle raw bytes instead of characters, improving compatibility and performance. The agent handled 78 prompts, but 61 required expert guidance for improvements, highlighting the need for human oversight. The session cost an estimated $80 in tokens and had an environmental impact of ~326 kg CO₂e and ~12,871 L of water.", "body_md": "Over the past few days I [changed](https://github.com/uutils/sed/pull/487)\nthe way the [uutils](https://uutils.org/) project’s\n[sed](https://github.com/uutils/sed/) program\nhandles data to default from characters to raw bytes.\nThis improves compatibility with GNU *sed* and also performance.\nGiven the change’s size and extent\n(13 changed files, 1740 insertions, 609 deletions),\nI worked with an AI agent (OpenAI Codex),\nwhich allowed me to experience first-hand both its power and limitations.\n\nOn the one hand, Codex guided me correctly toward the task and handled it successfully both through the initial prompt and through refinements. It handled expertly a lot of tedious grunt work, saving me time and effort. At all stages the modified code compiled and run successfully. (Having a large set of unit and integration tests as well as CI checks helped guide the agent.)\n\nOn the other hand, I realized that to obtain production-quality code, Codex’s work needed careful reviewing and extensive expert guidance. At the end of this post I have included all prompts that I issued, with the ones that guided Codex to improve the code formatted in bold. From the 78 prompts and sub-prompts 61 (78%) asked for improvements.\n\nSo,\n\nTo be fair, agents continuously improve, and many of the prompts I gave, such as those involving comments and unit tests, could have easily be handled by a more capable agent or even with a suitable initial agent configuration. But, as coding agents improve, the tasks we give them will also become more demanding, as will become the required reviewing and expert guidance.\n\nFinally, there’s AI’s monetary and environment cost,\nwhich may mean that not all work that can be done by an AI agent\nis worth doing it thus.\n(I made several changes on my own, mainly to save time and waste.)\nAt the end of the session, Codex (using gpt-5.5) reported\n`Token usage: total=4,298,673 input=4,113,242 (+ 110,110,848 cached) output=185,431 (reasoning 30,920)`\n\n.\n\nChatGPT [calculates](https://chatgpt.com/share/6a4e17e0-df70-83eb-9a23-f894b8582b5)\nthat with current prices the session’s tokens would cost about $80.\n(I used Codex through my OpenAI subscription and current token costs\nare probably subsidized, but the figure gives the true cost’s likely magnitude.)\nMy guess is that for sustained use the cost would mirror that\nof a developer salary.\nThis is not unreasonable given the benefit I received, but organizations\ncertainly need to weigh that cost into their budgets.\n\nI was surprised by the session’s large environmental impact. ChatGPT (based on Mistral’s Le Chat reporting) gave me ~326 kg CO₂e and ~12,871 L of water usage. To put these values into perspective the CO₂ emissions are similar to a 2,5000 km trip in an efficient diesel/gasoline car or one short/medium-haul passenger round trip. And if we price these externalities, the CO₂ emissions cost €26 through the EU ETS carbon price or $42–117 through the US EPA social cost of carbon. The cost of the water used in the local Athens domestic water tariff would be ~€4.50–41. Again, these costs are not astronomical, but also far from negligible.\n\nBelow are the prompts I gave during the session. Those associated with improvements I spotted are formatted in bold. Note that some of the unit tests I asked were missed in previous versions, so they’re code improvements requiring human prompting but not the agent’s fault.\n\n`sed`\n\ncompatibility score?`fast_`\n\noptimizations.`Transliteration`\n\nfield named `fast`\n\ninto `byte_fast`\n\n.`script_text_to_bytes`\n\n?`script_text_to_bytes`\n\nis the wrong approach. Handle the script as bytes.`as_str()`\n\n? cases shouldn’t we report an appropriate `input_runtime`\n\nerror where applicable?`ScriptCharProvider::new`\n\ninitializations simply deal with bytes?`git subst current_bytes current ; git subst next_line_bytes next_line`\n\nto make the code mirror the original. But I broke it. Please fix it.`current_char`\n\nshouldn’t `current`\n\nbe `current_bytes`\n\n?`provider.rs`\n\ndeal with bytes (rather than “logical characters”)? Byte sequences should be converted into characters / strings only when required, e.g. for `FancyRegex`\n\nand transliterate when processing UTF-8 input. I added `character_mode`\n\nin the context to help you with that.`parse_transliteration`\n\nto return a variant based on `context.character_mode`\n\n, so bytes or string. Then `parsed_script_text_to_utf8`\n\nwouldn’t be needed. Also shouldn’t the regex pattern start life as bytes and only be upgraded to string for `FancyRegex`\n\n? Also, shouldn’t `LiteralMatcher`\n\noperate on bytes?`NEEDS_RE`\n\n-based logic?`ByteRegex`\n\nmatcher?`HEAD`\n\n. Also when adding new functions always prefix them with a documentation comment.`Regex::new`\n\nshould get passed `context.character_mode`\n\nand only create a string from UTF-8 (if needed) when mode is `Utf8`\n\n. In Byte mode `Regex::new`\n\nshould fail with an error (back-references are not supported in byte mode) if it needs to construct a `FancyRegex`\n\nmatcher. Also, add integration tests to test `s`\n\nand `y`\n\ncommand behavior under diverse input / scrips / `LC_ALL`\n\nsettings.`Transliteration`\n\nlookup fn into `lookup_char`\n\n(to contrast with `lookup_byte`\n\n).`push_script_char`\n\nshould follow the `bytes.push`\n\nonly in Bytes mode, otherwise it can create invalid Unicode scripts. In Unicode mode it should push the character’s corresponding Unicode representation.`line.current()`\n\nreturn a char.`extend_from_slice(line.current_bytes())`\n\nconsider using the more readable `push(line.current_byte())`\n\n.`ERR_`\n\nconstant for “transliteration strings are not the same length”`push_script_char`\n\nonly in delimited parser.`ParsedTransliteration`\n\ninto `command.rs`\n\n.`self.utf8_verified.set(true);`\n\nbe in `set_to_string`\n\n? (I’ll fix it).`Regex`\n\nBytes `>0x7f`\n\nas `\\x`\n\n? Can’t regex bytes handle them as is? Just tell me.`Match::from_bytes`\n\navoid constructing text in Bytes contexts?`retreat`\n\n?`new_ucmd()`\n\nprovide a way to specify the environment?`character_mode`\n\nto `byte_regex_pattern`\n\n. Just examine `character_mode`\n\nwhen building the regex.`Match::from_bytes`\n\nconstructing text in Bytes contexts.`byte_regex_pattern`\n\n`let cmd_str = pattern`\n\nDon’t convert into UTF-8. Rather adjust and continue the flow with bytes using `OsStr`\n\n/`OsString`\n\n.`r`\n\nand `w`\n\ncommands.`list`\n\nshould also work correctly in byte mode without converting into a String. Be careful to avoid duplication in the way characters / bytes are shown.`retreat()`\n\n`new_ucmd!().env(...)`\n\nrather than the custom `run_sed_with_locale`\n\n. In general follow the logic of existing tests.`Regex::new`\n\nissue a `USimpleError`\n\nrather than construct one from scratch`os_string_from_bytes`\n\ntry `from_utf8`\n\nand issue an error on failure.`os_string_from_bytes`\n\n(put it in delimited_parser writer) rather than duplicating it.`readable_char`\n\n`readable_ascii_byte`\n\n.`write_list_item`\n\n.`write_list_item`\n\ncalls `len`\n\nfor each new item, making its complexity N². Encapsulate the function and the length in a class to avoid this.`NEEDS_RE.is_match`\n\nand `NEEDS_FANCY_RE.is_match`\n\n. It’s enough to test only for the first, right?`Transliteration::is_byte_safe`\n\n.`os_string_from_bytes`\n\n, `parsed_bytes_to_utf8`\n\n, `parse_character_class`\n\nwith `'['`\n\n, `parse_transliteration_for_mode`\n\n, `Transliteration::apply_match`\n\n, `Translitaration::from_bytes`\n\n, `lookup_byte`\n\n, `write_line_bytes`\n\n, the `regex::bytes::RegexBuilder::new`\n\npath, `Regex::captures_iter`\n\n, and `Regex::find`\n\n.`is_byte_identity`\n\nfield? If so, just loose the trivial getter.`pub(crate)`\n\n?`unterminated substitute replacement`\n\n`\\q`\n\nin `compile_replacement`\n\n`~step`\n\nin an RE address`parse_command_ending`\n\n`compile_replacement`\n\n`compile_replacement`\n\n`compile_replacement`\n\n`compile_subst_command`\n\n`compile_trans_command`\n\n`[`\n\nat EOL in `parse_character_class`\n\n`[:]`\n\nin `parse_character_class`\n\n.`parse_transliteration_bytes`\n\n`prompts.md`\n\nwith the prompts I issued in this session. Format code identifiers in backticks. Format prompts that ask for corrections to previous work in bold.Last modified: Wednesday, July 8, 2026 1:30 pm\n\nUnless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a [Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0/).", "url": "https://wpnews.pro/news/why-agentic-ai-needs-better-experts", "canonical_source": "https://www.spinellis.gr/blog/20260708/", "published_at": "2026-07-08 10:35:31+00:00", "updated_at": "2026-07-08 10:59:44.741961+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools"], "entities": ["OpenAI", "Codex", "uutils", "sed", "ChatGPT", "Mistral", "Le Chat", "gpt-5.5"], "alternates": {"html": "https://wpnews.pro/news/why-agentic-ai-needs-better-experts", "markdown": "https://wpnews.pro/news/why-agentic-ai-needs-better-experts.md", "text": "https://wpnews.pro/news/why-agentic-ai-needs-better-experts.txt", "jsonld": "https://wpnews.pro/news/why-agentic-ai-needs-better-experts.jsonld"}}