A member of Jane Street's 'AI Assistants' team details the company's unique approach to leveraging LLMs within its highly specialized OCaml development ecosystem. Due to OCaml's obscurity and their bespoke tooling, Jane Street builds custom AI models and integrations for tasks like generating multi-file diffs, rather than relying on off-the-shelf solutions.
0:00 a team at Jane Street called AI Assistants. Our group roughly is there at Jane Street to try to maximize the value that Jane Street can get from large language models. And I've spent my entire career in dev tools. Before I worked at Jane Street, I was at GitHub for a long time, and then before that I worked at a variety of other dev tools companies. And LLMs kind of present this really amazing opportunity in that they're so open-ended that we can build kind of anything that we can imagine. And it seems like right now, the only thing moving faster than the progress of the models is kind of our creativity around how to employ them. Uh at Jane Street though, we've made some choices that make adoption of off-the-shelf tooling a little bit more difficult than it is for other companies. And kind of the biggest reason that we have this problem is that we use OCaml as a development platform. For those not familiar with OCaml, it is a functional, very powerful language, but it's also incredibly obscure language. Uh it was built in France, and its most common applications are in things like theorem proving or formal verification. It's also used to write programming languages.
1:06 Um we use OCaml kind of for everything at Jane Street. So, just a couple quick examples. When we write web applications, of course web applications have to be written in JavaScript, but instead we write OCaml, and we use a library called js_of_ocaml, that is essentially a OCaml bytecode to JavaScript transpiler. When we write plugins for Vim, those have to be written in Vim script. Uh but we actually use a library called VCaml, which again is a OCaml to Vim script transpiler. And uh even people at the company that are working on FPGA code, they're not writing Verilog, they're writing in an OCaml library called HardCaml. Uh so, why are the tools on the market available not good for working with OCaml? I think it kind of comes down to a few primary reasons. The first and the most important is that models themselves are just not very good at OCaml. And this isn't the fault of the AI labs. This is just kind of a byproduct of the amount of data that exists for training. So, it's there's a really good chance that the amount of OCaml code that we have inside of Jane Street is just more than like the total combined amount of OCaml code that there exists in the world outside of our walls.
2:18 The second is that we've made things really hard on ourselves. Partially as a byproduct of working in OCaml, we've had to build our own build systems. We built our own distributed build environment. We even built our own code review system, which is called Iron. We develop all of our software on a giant monorepo application, and just for fun, instead of storing that monorepo in Git, we store it in Mercurial. And at last count, 67% of the firm uses Emacs instead of normal editors, maybe like VS Code. We do have people using VS Code, but Emacs is the most popular. And the last thing is we're dreamers. I mean, kind of everyone in this room hopefully is is a dreamer in a way. And what I mean by this is we want the ability to kind of take LLMs and apply them to different parts of our development flow and light up different parts. So, maybe we want to use large language models to resolve merge conflicts or build better feature descriptions or figure out who reviewers for features be. And we don't want to be hampered by the boundaries between different systems when we do that.
3:22 Over the next 15 minutes, I'm going to cover our approach to large language models at Jane Street, particularly when it comes to developer tools. I'm going to talk about custom models that we're building and how we build them. I'm going to talk about editor integrations. So, these are the integrations into to VS Code, Emacs, and Neovim. And I will talk about the ability that we've built over time to evaluate models and figure out how to make them perform best. And I guess at first glance, it's not really obvious that training models at all is a good idea. I mean, it's a very expensive proposition. It takes a lot of time and it can go wrong in a ton of different ways. Who here has trained a model before or tried to train something like a model? Maybe you took a foundation model and trained on top of it. Cool. We were more convinced after we read this paper. This is a paper from Meta about a project called Code Compose. And in this paper, they detail the results fine-tuning a model specifically for use with Hack. Uh Hack is actually pretty similar to OCaml. Uh not in its like syntax or function, but really just in the fact that it's used primarily at one company and not really used much outside of that company even though it's open source.
4:30 So, uh actually a fun fact, Hack is implemented in OCaml. And I think that's just like a total coincidence, but Uh we were pretty naive early on. We read this paper and we decided that it would be really cool if we could uh replicate the results. We thought we would just take a model off the shelf. We would show it a bunch of our code and then we would get back a model that uh worked like the original model, but knew about our libraries and idioms. Turns out that's just not how it works. Uh it's not that easy. In order to get good outcomes, you have to have the model see a bunch of examples that are in the shape of the type of question that you want to ask the model. So, we needed to first create a goal, a thing that we wanted the model to be able to do. And in our in our world, the goal that we came up with was this. We wanted to be able to generate diffs given a prompt. So, what that means is we wanted a user inside of an editor to be able to write a description of what they wanted to happen and then have the model suggest a potentially multi-file diff. So, maybe you want to modify the test file, an ML file, and an MLI, which is kind of like a header file.
5:32 We wanted the diffs to apply cleanly and we wanted them to have a good likelihood of type checking after they had been applied. And we were kind of targeting this range of up to 100 lines as an ideal zone of what we thought LLMs would actually be capable of. And in order for that to work, we needed to collect data, like I was talking about before. We needed data of the training shape that looked just like the test shape. And this is what that shape looks like for this task. You need to be able to collect a bunch of examples of what context the model would have had beforehand, and then some prompt of what you want the model to do, written hopefully in the same way that a human would write it, and then some diff that would accomplish that goal. So, context, prompt, diff, and we need a bunch of these examples. So, how do we get these? How do we get these training examples? Kind of the first place to look is features. Features is, I mentioned, a code review system that we built internally. This is what it looks like. It's called Iron. Uh features are very similar to pull requests. I think you can just, you know, swap that term in your head. And features at first glance have exactly the data they want. On the description tab, they have a human-written description of a change, and on the diff tab, they have the code that accomplishes the goal of the developer.
6:43 But on closer look, they're not exactly what you want, right? The way that you write a feature description or a pull request description is really very different from what you might want to say inside of an editor. So, you're not writing multiple paragraphs in the editor. You're just saying something like, "Fix that error that's happening right now." And that's just not how we write feature descriptions. Another problem with these features or pull requests is that they're really large, right? Often it's uh a feature is 500 lines or 1,000 lines. So, in order to use it as training data, we would need to have an automated way to pull features apart into individual smaller components that we could train on. So, we need smaller things than features. What are those? Well, maybe commits. Commits are smaller chunks than features. Uh this is what a typical commit log looks like at Jane Street. So, this is not like a Git short log. This is literally just like an actual, I want you to look at this as an actual Git log, and where it says summary Z, that's my commit message. We don't really use commits the same way that the rest of the world uses them. So, we use commits mostly as checkpoints between different parts parts of a development cycle that you might want to revert back to.
7:49 Commits don't have a description, and they also have the same problem in that they're not isolated changes. They're they're a collection of changes. What we actually ended up with was a approach called workspace snapshotting. And the way that that works is we take snapshots of developer workstations throughout the work day. So, you can think like every 20 seconds we just take a snapshot of what the developer's doing. And as we take the snapshots, we also take snapshots of the build status. So, there's the build that's running on the box, we can see what the error is or whether the build is green. And we can kind of notice these little patterns. If you have a green to red to green, that often corresponds to a place where a developer has made an isolated change, right? You start writing some code, you break the build, and then you get it back to green, and that's how you make a change. Maybe this one, the red to green. This is a place where the developer encountered an error, whether that's a type error or a compilation error, and then they fixed it. So, if we capture the build error at the red state, and then the diff from red to green, we can use that as training data to help the model be able to recover from mistakes. The next thing we need is a description, and the way that we did that, we just used a large language model. So, we had a large language model write a really detailed description of a change in in as much words as it possibly could, and then we just kept filtering it down until it was something that was around the right level of what a human would write.
9:06 So, now we have this training data, and training data is kind of only half the picture of training models. So, you you have the the supervised training data, and then you need to do the second part, which is the reinforcement learning. This is really where models get a lot of the power, right? We we align the model's ability to what humans think is actually good code. So, what is good code? I guess on the surface, good code is I mean, it's it's code. It has to parse as code, meaning if if piece of code doesn't go through the OCaml parser and come out with a green status, that is that is not good code, I would say by most definitions. Uh good code in OCaml, because it's statically typed, is code that type checks. So, we want to have good code be code that when it is applied on top of uh a base revision, can go through the type checker, and the type checker agrees that the code is valid. And of course, the the gold standard is that good code is code that compiles and passes tests. So, ideally, in the during the reinforcement learning phase of a model, you could give the model a bunch of tasks that are like verifiable. We have the model perform some some edit, and then we check whether or not it actually passes the test when applied to the code.
10:15 So, we did that. Uh we've done this as part of our our training cycle, and we built this thing that is called uh CES. It's the code evaluation service. You can think of it kind of like a build service, except with a slight modification to make it much faster. And that's that First, we pre-warm a build. It sits at a a revision and is grading, and then we have these workers that all day just take diffs from the model, they apply them, and then we determine whether the build status turns red or green, and then we report that error or or success back up to the build function. And through continued use of this service over the course of like months, we're able to better align the model to write code that actually does compile and pass tests. It turns out this exact same setup is the one that you would want for evaluation. So, if you just hold out some of the RL data, you can also use it to evaluate model's ability to write code. Kind of looks like this. You give the model a problem, you let it write some code, and then you evaluate whether or not the code that it writes actually works.
11:19 And training is hard, and it can have kind of uh catastrophic but hilarious results. So, at at one point, we were training a code review model, and this is a totally separate model, but the idea was we want to be able to give some code to this model and have it do a first pass of code review just like a human would do to try to save some of the toil of of code review. We trained this model, we put a bunch of data into it, we worked on it for months, we're really excited, and we put our first code in for uh for code review through the automated agent. It spun for a bit, and it came back with something along the lines of "Mm, I'll do it tomorrow." And like of course it did that because it's trained on a bunch of human examples, and humans write things like I'll do things tomorrow or I'll do this tomorrow. Uh so it's it's you know, not very surprising. So having evaluations that are meaningful is kind of a cornerstone of making sure that models don't go off the rails like this and you don't waste a bunch of your time and money. In the end though, the real test of models is whether or not they work for humans. So, I'm going to talk a little bit about the editor integrations that we've built to expose these models to developers at Jane Street.
12:24 Kind of when we were starting building these integrations, we had three ideas in mind. The first idea was well, we support three editors. We have Neovim, VS Code, and Emacs, and we really don't want to write the same thing three times. So, ideally, we don't want to write all the same context building strategies and all of the same prompting strategies, we want to just write it once. The second is that we wanted to maintain flexibility. So, we had a model that we were using at the time uh that was not a fine-tuned model. We were pretty convinced that a fine-tuned model was in our future. We wanted the ability to do things like swap the model or swap the prompting strategy out. And lastly, we wanted to be able to collect metrics. So, when a developer uh in their in their editor developers care about latency. They care about whether or not the diffs actually apply. So, we wanted to get kind of on the ground real experience of whether or not the diffs really were meaningful for people. This is the simplified version of the architecture that we settled on for this service. The AI development environment. Essentially, you have LLMs on one side, and then Aid handles all of the uh ability to construct prompts and to construct context and to see the build status. And then we are able to just write these really thin lot layers on top of Aid uh, for each of the individual editors. And what's really neat about this is that Aid sits as a sidecar application on the developer's machine.
13:41 Which means that we want when we want to make changes to Aid, we don't have to make changes to the individual editors and hope that people restart their editors. We can just restart the Aid service on all of the boxes. So we restart Aid and then everyone gets the most recent copy. Uh, this is an example of Aid working inside of VS Code. So this is the sidebar in VS Code, very similar to something like Copilot, except this thing allows you to uh, ask for and get back multi-file diffs. Uh, and you can see it kind of looks like what you'd expect in VS Code. It's it's, I know, a visual interface that lays things out really nicely. This is what we built in Emacs, though. So in Emacs, developers are used to working in text buffers. They move around files. They want to be able to copy things the normal way that they copy things. So we actually built the Aid experience in Emacs into a markdown buffer. So users can move around inside this markdown buffer, they can ask questions, and then there are key binds that essentially append extra content to the bottom of the markdown buffer. Aid's architecture lets us plug various pieces in and out, like I mentioned.
14:44 Uh, so we can swap in new models, we can uh, make changes to the context building, we can add support for new editors, which I think probably sounds far-fetched, but we're this is something we're actually just doing right now. Uh, and we can even add domain-specific tools. So different areas of the company can supply tools that are available inside of the editors and they kind of end up in all the editors without having having to write individual integrations. Aid also allows us to AB test different approaches, so we can do something like send 50% of the company to one model and 50% to another and then determine which one gets the higher acceptance rate. Aid is kind of a an investment that pays off over time. Every time something changes in large language models, we're able to change it in one place downstream of the editors and then have it available everywhere. And things change like really often. We need to be ready when things change. What I what I've had time to show you today is only a small portion of what my team is doing. We've got a lot of other things going on. So, we're finding new ways to apply rag inside of the editors.
15:47 We're applying similar approaches to what you've seen here to large scale multi-agent workflows. We are working with reasoning models more and more. But the approach is the same through all of these. We keep things pluggable. We lay a strong foundation to build on top of and we build the ways for the rest of the company to add to our experience by adding more domain specific tooling on top of it. If you think what I've said is interesting and you want to talk more about this, I would love to hear from you. You can just find me outside and thank you for your time.