<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Agents on Tyler Collins</title>
    <link>https://www.tk11br.ca/tags/agents/</link>
    <description>Recent content in Agents on Tyler Collins</description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>2022-2026 Tyler Collins. Content licensed under CC BY-NC 4.0.</copyright>
    <lastBuildDate>Tue, 08 Sep 2026 16:00:00 -0400</lastBuildDate>
    <atom:link href="https://www.tk11br.ca/tags/agents/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Stop installing things your agent doesn&#39;t need</title>
      <link>https://www.tk11br.ca/posts/prompt-context-is-a-resource/</link>
      <pubDate>Tue, 08 Sep 2026 16:00:00 -0400</pubDate>
      <guid>https://www.tk11br.ca/posts/prompt-context-is-a-resource/</guid>
      <description>Keep your Claude Code or Codex setup lean. Put specialized skills and plugins in the projects that need them instead of loading them everywhere.</description>
      <content:encoded><![CDATA[<p>Stop adding things to Claude Code or Codex that you use once and then forget about.</p>
<p>I like customizing my agents. Skills, plugins, extensions, whatever the particular tool calls them. But I want to combat the slow creep towards the dumb zone, and loading a bunch of unrelated stuff into every session works against that.</p>
<p>Matt Pocock has a useful description of the <a href="https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/Smart%20zone.md">smart zone and the dumb zone</a>. Early in a session, the agent is at its best. The longer the conversation goes, the more mistakes start to pop up. This is where those early-days patterns of hallucinations really came from. The context window isn&rsquo;t a promise that it&rsquo;ll work well at all times.</p>
<p>There&rsquo;s no universal line where this happens. Maybe 40% to 60%? It depends on the model. But every model shares the fact that the more things you pack into its context, the worse it gets over time.</p>
<h2 id="what-are-we-adding">What are we adding?</h2>
<p>Context is the available space the model has when it produces a response. Your conversation is part of it, but so are the base instructions from OpenAI or Anthropic, and information about the tools it can use. This is typically referred to as the system prompt. It supplies instructions before you&rsquo;ve even typed anything.</p>
<p>Installing something extra doesn&rsquo;t necessarily put all of it into the system prompt. For example, <a href="https://code.claude.com/docs/en/skills">Claude Code only loads a skill&rsquo;s full instructions when it&rsquo;s used</a>, rather than loading every skill in full at startup. This is great, but dozens of skill descriptions and available tool definitions can still take up context, depending on how the application loads them.</p>
<h2 id="keep-the-occasional-stuff-local">Keep the occasional stuff local</h2>
<p>My recommendation is to start with nothing and only add things when you have a reason to use them. Don&rsquo;t just add the Google Calendar support because you might use it one day. Now it&rsquo;s in your system prompt for every project, creeping you closer towards the dumb zone.</p>
<p>However, if a tool helps with your everyday work across projects, keeping it in your global setup makes sense. If it&rsquo;s for deploying one particular website, keep it with that website. A specialized database integration doesn&rsquo;t need to be available while you&rsquo;re editing an unrelated Python package.</p>
<p>For a concrete example, Claude Code supports personal skills in <code>~/.claude/skills/</code> and project skills in <code>.claude/skills/</code>. The first makes a skill available across your projects. The second keeps it in that repository. Use the equivalent project-level setup in your agent, and check the scope when you install something.</p>
<p>Personally after writing this I cut about 10 different writing and planning skills from my globally installed skills and moved them all into a writing project. Try it out yourself!</p>
]]></content:encoded>
    </item>
    <item>
      <title>From a Throwaway QC Prototype to PyLossless</title>
      <link>https://www.tk11br.ca/posts/pilot-qc-to-pylossless-feature/</link>
      <pubDate>Fri, 28 Aug 2026 12:00:00 -0400</pubDate>
      <guid>https://www.tk11br.ca/posts/pilot-qc-to-pylossless-feature/</guid>
      <description>How a throwaway repository helped prove a new EEG quality-control workflow before it moved into PyLossless.</description>
      <content:encoded><![CDATA[<p>In March 2025, I had an idea for improving quality control in <a href="https://github.com/Andesha/pylossless">PyLossless</a>. I wanted to bring back being able to review an EEG recording to select independent components, choose a time window, and immediately compare the raw signal against the result of removing those components.</p>
<p>It had been done in the previous version in MATLAB, but I was less familiar with the inner working of PyLossless at the time. I was also a complete novice when it came to things like Qt 5, PyQt, and the <a href="https://github.com/mne-tools/mne-qt-browser">mne-qt-browser</a>.</p>
<p>To be clear, trying to build this system would require knowing the PyLossless pipeline state, MNE&rsquo;s ICA tools, the MNE Qt browser, Matplotlib figures, file watching, and the actual EEG-cleaning logic. I had already failed a few attempts at this before because I just got tired of there being too many things to juggle.</p>
<p>On the suggestion of a friend, I made a new repository called <a href="https://github.com/Andesha/pilot-qc"><code>pilot-qc</code></a> and decided to throw some AI at it and see what would happen. Worst case would only be another failure.</p>
<h2 id="let-the-prototype-be-ugly">Let the prototype be ugly</h2>
<p>There wasn&rsquo;t much of a plan beyond getting something on the screen.</p>
<p>The commit history tells the story pretty well. The first two commits landed on March 21. One was called <code>first commit</code>. The next was <code>wow this is amazing</code>, which is honestly a pretty good record of how I felt at the time. Later that day, I had figures communicating with each other, scalp snapshots for a selected time window, a history of component rejection, and raw and cleaned signals appearing together.</p>
<p>The prototype used a file named <code>.local_reject</code> to pass state between the plots. It stored a selected time range and a set of rejected ICA components. A Qt timer checked the file every half second. When something changed, the code copied the original recording, applied the current ICA exclusions, cropped it to the selected window, and plotted the cleaned channels over the raw channels.</p>
<p>Would I design a finished application this way? Absolutely not. But it answered the question I actually had. Could a reviewer change the rejected components and see what those decisions did to the EEG? All in Python and with the mne-qt-browser? Yep.</p>
<h2 id="ai-made-the-scratch-repository-more-useful">AI made the scratch repository more useful</h2>
<p>This was early in my adoption of AI coding tools. The repository description was pretty clear about it: &ldquo;Trying some AI assisted QC figure generation.&rdquo;</p>
<p>I knew what I wanted from the EEG review process. The plotting and GUI code was another story. AI made it cheap to try another event handler, connect two figures, or rebuild a plot when the first attempt did something weird. Instead of staring at documentation and trying to design the whole thing in my head, I could run some code and complain about what it did.</p>
<p>The scientific judgement still came from me. An agent could connect a click to a plot update. It couldn&rsquo;t decide which comparison would actually help someone judge whether removing an ICA component improved the recording. AI got me through unfamiliar implementation details quickly, and the throwaway repository kept the resulting mess away from PyLossless.</p>
<p>This worked much better than asking for a polished feature all at once. Generated code was allowed to be awkward in there. Anything I did was just a throwaway anyway.</p>
<h2 id="moving-the-useful-part-into-pylossless">Moving the useful part into PyLossless</h2>
<p>The prototype didn&rsquo;t become a package or a long-lived dependency. I had briefly wondered in the PyLossless issue whether it should be a submodule. Thankfully, I talked myself out of that one.</p>
<p>I moved the useful parts into PyLossless instead.</p>
<p>On April 10, about three weeks after the first experiment, I opened and merged <a href="https://github.com/Andesha/pylossless/pull/29">PyLossless PR #29</a>. It added the initial <code>pylossless/qc.py</code>, including the component topographies, scrolling ICA time courses, and raw-versus-cleaned comparison tested in <code>pilot-qc</code>. The pull request added 481 lines across seven files.</p>
<p>The merged code was still pretty rough. It even kept my hacky local file based approach while the interaction settled down. That was fine. I didn&rsquo;t need the prototype to produce immaculate code that could be copied over unchanged. I needed proof that this collection of plots and interactions could work at all.</p>
<p>That changed the work completely. I wasn&rsquo;t trying to make the full QC system while juggling six libraries in my head anymore. I just needed to take the important parts from what worked, and bring that over. I now had my old QC workflow back!</p>
<p>I&rsquo;ve since gone back and cleaned up some chunks and improved the local file calling. But I couldn&rsquo;t have done any of that without the throwaway prototype to fall back on.</p>
<h2 id="try-the-intimidating-version-somewhere-disposable">Try the intimidating version somewhere disposable</h2>
<p>A throwaway prototype is great when a feature feels too large because a bunch of unknowns are stuck together. Try pulling them out of the main project. Use real enough data to test the idea, but don&rsquo;t spend the first day deciding where every class belongs. Nobody cares yet.</p>
<p>Make the smallest repository that can answer your question. Let it use a weird little file for communication if that gets the idea on screen. Let the commits say <code>wow this is amazing</code>. Clean up whatever survives later.</p>
<p>If your favourite project has a feature you&rsquo;ve been avoiding because it looks like one big impossible blob, give it a disposable repository and an afternoon. See what happens.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Writing More Code with AI Agents</title>
      <link>https://www.tk11br.ca/posts/writing-more-code-with-ai-agents/</link>
      <pubDate>Thu, 27 Aug 2026 12:00:00 -0400</pubDate>
      <guid>https://www.tk11br.ca/posts/writing-more-code-with-ai-agents/</guid>
      <description>A practical way for researchers to use AI coding agents for tests, checks, and diagnostic work without handing over the scientific decisions.</description>
      <content:encoded><![CDATA[<p>I recently gave a SHARCNET General Interest Webinar called &ldquo;Writing More Code with AI Agents.&rdquo; More than 300 people registered. Attendance was excellent, there were lots of questions, and I&rsquo;ve had a decent number of follow-up conversations over email. Pretty happy with how it turned out!</p>
<p>The response also confirmed why I wanted to give the talk. People are constantly asking me agents and what they should be doing with them. They&rsquo;re watching other researchers and developers move very quickly with these tools, and there&rsquo;s a real fear of missing out. They want to try them, but they don&rsquo;t necessarily know where to start or how much of the output they should trust.</p>
<p>My answer is to start with code you already trust, then put some AI slop around it.</p>
<iframe src="https://www.youtube.com/embed/06jylC7Sib0" title="Writing More Code with AI Agents" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>The rest of this post are my thoughts about the various points I bring up during the talk.</p>
<h2 id="the-title-was-a-bit-of-a-lie">The title was a bit of a lie</h2>
<p>The title says you should write more code. This is technically true, but it depends on what we count as code.</p>
<p>Most researchers don&rsquo;t need an agent generating more of the main scientific loop. That is the meat and potatoes of the project. It includes the cleaning rules, statistics, simulations, models, and transformations that can change the result. The researcher needs to understand and own those decisions.</p>
<p>A lot of useful code lives outside that loop:</p>
<ul>
<li>tests and synthetic examples</li>
<li>input validators</li>
<li>diagnostic plots and reports</li>
<li>logging and debugging tools</li>
<li>profiling scripts</li>
<li>build and release automation</li>
<li>documentation</li>
</ul>
<p>People tend to skip this stuff. It takes time, some of it is boring, and it may require programming knowledge that isn&rsquo;t directly related to the research question. It is also exactly the work that can make the main analysis easier to trust.</p>
<p>The title I really wanted was &ldquo;Use AI agents to write slop to verify your code.&rdquo; It probably wouldn&rsquo;t have attracted 300 registrations.</p>
<h2 id="i-asked-claude-to-do-my-stats">I asked Claude to do my stats</h2>
<p>I built a synthetic reaction-time study for the talk. It had 48 participant files and a 2x2 repeated-measures design. Each participant should have completed four conditions with 80 trials in each condition.</p>
<p>Of course, the data wasn&rsquo;t actually that clean. One file duplicated an existing participant under a new acquisition ID. Two participants didn&rsquo;t finish. Some had near-chance accuracy. Others had reaction times that were implausibly fast or slow.</p>
<p>All of these are normal research-data problems. They&rsquo;re also serious enough that letting those files contribute to the final result would be wrong.</p>
<p>I gave the data to Claude three times.</p>
<p>For the first attempt, I used this prompt:</p>
<blockquote>
<p>These CSV files are the results from my experiment. Do my stats for me and make a plot I can use in my paper.</p></blockquote>
<p>This is a terrible prompt on purpose. Claude still produced a polished plot and a significant interaction with a p-value of 0.040. It looked like a finished analysis.</p>
<p>It turns out it had invented an accuracy exclusion, picked its own reaction-time summary, allowed comically large response times, and included incorrect trials. There were decisions all over the place that the researcher didn&rsquo;t make and might not even notice.</p>
<p>The result was blatantly wrong, but it looked nice.</p>
<p>Never do this.</p>
<h2 id="a-better-prompt-still-wasnt-enough">A better prompt still wasn&rsquo;t enough</h2>
<p>For the second attempt, I explained the study design. I described the factors, outcome, repeated-measures analysis, hypothesis, and how incorrect trials should be handled.</p>
<p>The code improved. Claude gave a more conservative p-value of 0.087. It also showed hints that it could produce useful checks around the analysis.</p>
<p>This is probably where a lot of people stop. The response knows the terminology, the figure looks good, and the result feels more defensible because the prompt was detailed.</p>
<p>There were still problems in the data that anyone familiar with the study would want to investigate. More context made the output better, but good prompting did not replace experience or domain knowledge.</p>
<p>This is also why I&rsquo;m not especially interested in advice that boils down to &ldquo;write a huge prompt.&rdquo; Context is useful. It does not prove that the code handled the real files correctly.</p>
<h2 id="own-the-logic-and-delegate-the-annoying-parts">Own the logic and delegate the annoying parts</h2>
<p>For the third attempt, I supplied the core statistical code myself. It came from previous work, and I understood what it was doing. Claude wasn&rsquo;t asked to choose the analysis.</p>
<p>Instead, I asked it to write the surrounding checks. Report any of the missing four conditions. Report files with too few trials, high error rates, or reaction times outside the plausible range. Check participant identities. Show me potential failures.</p>
<p>This found all eight problematic subjects, including the duplicate participant. After reviewing and handling those cases, the analysis used 40 participants and returned the expected interaction with a p-value of 0.037.</p>
<p>The third attempt was better because I owned more of the important code, not because Claude suddenly became more trustworthy. The agent took criteria I supplied and turned them into reports and checks. That gave me things I could inspect before accepting the result.</p>
<p><strong>This is the kind of AI-generated slop I want more of.</strong></p>
<p>If a validator is a bit ugly but catches a duplicate participant, great. If a throwaway plot makes a broken distribution obvious, it did its job. If an alternate implementation disagrees with my main calculation, I now have something worth investigating.</p>
<p>None of that code needs to become permanent infrastructure. Its job is to answer a question or provide evidence.</p>
<h2 id="this-works-outside-toy-statistics">This works outside toy statistics</h2>
<p>The same idea fits notebooks, shared pipelines, and SLURM jobs.</p>
<p>For a notebook, an agent can add data-shape checks, diagnostic cells, caching, or a small script that tests code pulled out of the notebook. For a shared pipeline, it can validate inputs, produce reports, or turn a previously discovered failure into a regression test. For a SLURM workflow, it can add preflight checks, logging, resource summaries, checkpoints, and checks that distinguish a complete output from a partial one.</p>
<p>The stakes change as the work gets larger. A bad notebook cell might waste an afternoon. A bad cluster job might wait in the queue for days, consume a large allocation, and leave partial files that look usable. Catching the same mistake before submission is much cheaper.</p>
<p>There are also obvious limits. Don&rsquo;t upload data unless you&rsquo;re allowed to. Run unfamiliar generated code in a sandbox, container, or virtual machine. Keep a human involved when mistakes can affect results, people, infrastructure, or budgets. You own the problems even when an agent wrote the code.</p>
<h2 id="try-one-check">Try one check</h2>
<p>Don&rsquo;t start by giving an agent a new analysis and hoping it gets everything right.</p>
<p>Take a script, notebook, or pipeline that you&rsquo;ve already used enough to understand. Something battle-tested. Ask the agent to add one check that doesn&rsquo;t touch the meat and potatoes.</p>
<p>You could ask for a synthetic case where you know the answer. Ask it to verify that every participant has the expected conditions. Have it make an exclusion report, plot a suspicious distribution, or write a second version of a calculation so you can compare the outputs.</p>
<p>Review what it writes. Run it. See whether it tells you anything useful about code you already know.</p>
<p>That is a much safer way to learn what agents are good at, and it produces something useful even if the generated code is disposable. The point isn&rsquo;t to trust AI more. The point is to cheaply make more of the stuff that helps you trust the important work.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Revisiting Cookiecutter in the Age of Coding Agents</title>
      <link>https://www.tk11br.ca/posts/revisiting-cookiecutter-with-agents/</link>
      <pubDate>Mon, 24 Aug 2026 12:00:00 -0400</pubDate>
      <guid>https://www.tk11br.ca/posts/revisiting-cookiecutter-with-agents/</guid>
      <description>A practical comparison of Cookiecutter PyPackage, a coding agent, and using both to build a modern Python package.</description>
      <content:encoded><![CDATA[<p>A few years ago, I used Cookiecutter to show how to get from <code>import blah</code> to <code>pip install blah</code>.</p>
<p>This sounds simple, but it isn&rsquo;t. A Python script can live almost anywhere, but a formal package needs the right structure to be built into a wheel. It needs dependency metadata, versions, releases, a licence, and some way to test that it still works.</p>
<p>Cookiecutter gave us a standard template. Instead of remembering every file and setting and creating them manually, we answered a few questions and started with a working package. I gave <a href="/posts/teaching-python-packaging-cookiecutter/">a talk about that workflow in 2022</a>.</p>
<p>Personally, I stopped shipping tools that needed it, so I stopped thinking about Cookiecutter. Recently, I needed to package something again, and an obvious question came up. Why use a project template when I can ask an agent to build the project?</p>
<h2 id="a-small-comparison">A small comparison</h2>
<p>For comparison, I used the same throwaway project for each attempt. It&rsquo;s a small package called <code>csv-summary</code>. All it does is read a CSV file, summarize its numeric columns, and provide a <code>csv-summary</code> command. I also wanted tests and enough infrastructure to install, check, version, and release the package.</p>
<p>I tried three different options:</p>
<ol>
<li><a href="https://github.com/audreyfeldroy/cookiecutter-pypackage"><code>cookiecutter-pypackage</code></a> without an agent</li>
<li>An agent starting from an empty directory</li>
<li>The Cookiecutter project followed by the same agent</li>
</ol>
<p>I did this with one prompt, not a benchmark. The goal was to see what each approach considered a finished package and where I would have to step in and fix things.</p>
<h2 id="cookiecutter-by-itself">Cookiecutter by itself</h2>
<p>Cookiecutter has changed a lot since my 2022 talk. The current template uses <code>uv</code>, Ruff, pytest, <code>ty</code>, and GitHub Actions. <code>uv</code> wasn&rsquo;t even mainstream until late 2024. The template now includes workflows for continuous integration, documentation, CodeQL, and publishing to PyPI with trusted publishing. It also has Dependabot configuration, release tooling, security documentation, and a changelog.</p>
<p>This is a lot more infrastructure than this example needed, but that&rsquo;s pretty much the trade-off for Cookiecutter. The opinionated set of files is great, but if the package is truly small, this is overkill.</p>
<h2 id="the-agent-by-itself">The agent by itself</h2>
<p>Starting from an empty directory, the agent produced the leanest project. It chose Polars, added a <code>src</code> layout, wrote two focused tests, configured Ruff and pytest, built both a wheel and source distribution, and checked them with Twine.</p>
<p>The agent also stopped at a fairly light definition of &ldquo;done.&rdquo; Releases used a documented sequence of <code>uv</code>, Hatch, and Twine commands. There was no continuous integration, automated PyPI publishing, documentation site, dependency updater, or security scanning.</p>
<p>This is pretty much what you&rsquo;d want for a small internal tool. It&rsquo;s less convincing as the default for a package that other people will depend on.</p>
<p>The prompt had asked for release infrastructure, but the agent gave me a workable manual process. Cookiecutter gave me a repeatable and automated one.</p>
<p>A better prompt could have asked for every missing piece. Of course, writing that prompt means knowing which pieces to request.</p>
<h2 id="cookiecutter-followed-by-the-agent">Cookiecutter followed by the agent</h2>
<p>The combined approach produced the most complete result. Cookiecutter supplied the project policy, while the agent spent its time implementing the package. Its formatting, linting, type checks, tests, and package build all passed.</p>
<p>The agent went further with the combined example. It added a minimum version for <code>pandas</code>, locked the resolved dependencies, and discussed which Python versions to support.</p>
<p>Cookiecutter had already handled much of what my prompt requested. The agent had less setup work to do, so it spent more time on package-specific decisions than it did in the empty-directory test.</p>
<h2 id="templates-still-have-a-job">Templates still have a job</h2>
<p>In general, an agent is good at doing the work in front of it. A maintained template is good at recording work that is easy to forget.</p>
<p>That difference is important. I can ask an agent to create CI, configure trusted publishing, pin GitHub Actions, add release notes, and set up documentation. I can also forget to ask for one of those things at any stage.</p>
<p>Cookiecutter records those decisions in files that have been used and tested together. With a template, the agent&rsquo;s job is smaller. It doesn&rsquo;t need to invent a release process or choose a documentation system. It can focus on other things, such as the consequences of a dependency or which Python versions to support.</p>
<p>There are concerns, of course. A Cookiecutter template can get out of date. A technology can fall out of style while the template keeps generating it. You could even argue that the whole thing could be a Markdown file or an agent skill. Still useful for a beginner, though!</p>
<h2 id="what-i-would-use">What I would use</h2>
<p>For a small internal script that only needs to be installable, I would just let an agent drive. There&rsquo;s no need for all the extra GitHub features.</p>
<p>For a package that I plan to publish and maintain, I would start with Cookiecutter and then give the generated project to an agent. Cookiecutter would provide the boring, repeatable decisions. The agent would adapt them and run with it.</p>
<p>Interesting to see how far we have come in just a few years.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
