{"slug": "building-an-alien-language-from-scratch-with-langchain", "title": "Building an Alien Language from Scratch with LangChain", "summary": "A developer built a system where 100 AI agents, each with unique personalities, evolved a shared symbolic vocabulary through trade negotiations using LangChain.js pipelines, with no pre-programmed symbol definitions or mock data. The project uses three different LangChain patterns to handle agent complexity, including a custom `LenientJsonParser` that extends LangChain's `JsonOutputParser` to handle real-world LLM quirks like markdown code fences and empty content fields. The system supports instant provider swapping between OpenAI, Featherless.ai, and LM Studio by changing a single `baseURL` configuration.", "body_md": "**How 100 AI agents evolved a shared symbolic vocabulary through trade — with no pre-programmed definitions, no mock data, and no shortcuts.**\n\nMost emergent communication demos cheat. They hardcode symbol meanings, or use mock LLM responses, or simulate the entire thing with random number generators. The result is a demo that looks impressive but teaches you nothing about how actual language models behave when they need to coordinate.\n\nI wanted to build something real: two civilizations of AI agents, each with 50 unique personalities, negotiating trades using abstract symbols — where **every single message and decision flows through a real LLM via LangChain SDK pipelines**.\n\nNo mock data. No hardcoded symbol mappings. Just pure reinforcement: successful trades reinforce a symbol's meaning, failed trades weaken it. Over hundreds of rounds, a shared vocabulary emerges.\n\nLangChain.js (v1.4) is the backbone of this project. I deliberately used three different LangChain patterns to show how the SDK can handle different levels of agent complexity:\n\n``` js\nconst chain = ChatPromptTemplate\n  .fromMessages([[\"system\", prompt], [\"human\", \"{input}\"]])\n  .pipe(model)\n  .pipe(parser);\n```\n\nEvery symbol an agent sends is generated through this pipeline. The `ChatPromptTemplate`\n\ninjects the agent's personality, observations, recent successes/failures, and conversation history. The model returns structured JSON (`{\"message\": \"🟢⚡🔺\"}`\n\n), which the `JsonOutputParser`\n\nvalidates.\n\nThe trickiest part was the `{{`\n\nescaping — LangChain uses `{}`\n\nfor template variables, but I needed literal `{}`\n\nin the JSON example. The double-brace escape (`{{\"message\": \"...\"}}`\n\n) resolves this cleanly.\n\nSame structure, different prompt — but this one returns a boolean decision. `{\"accept\": true}`\n\nor `{\"accept\": false}`\n\n. The agent evaluates whether the proposed resource exchange is beneficial based on its observations and personality.\n\nThis is where LangChain really shines. Using `createAgent()`\n\nfrom the LangGraph-based SDK, I built a full ReAct agent that can:\n\n`DynamicTool`\n\n`DynamicTool`\n\nThe agent literally thinks, uses tools, and then decides — all within LangChain's agent loop.\n\nThe moment you switch from mock to real LLMs, everything gets interesting. Models wrap JSON in markdown code fences. They include chain-of-thought reasoning. They run out of tokens mid-response. They return empty `content`\n\nand put their thoughts in vendor-specific fields like `reasoning_content`\n\n.\n\nI built a `LenientJsonParser`\n\nthat extends LangChain's `JsonOutputParser`\n\nto handle all of these cases:\n\n```\nclass LenientJsonParser extends JsonOutputParser {\n  async parse(text: string): Promise<object> {\n    try { return await super.parse(text); } catch {\n      // Strip ```\n{% endraw %}\njson ...\n{% raw %}\n ``` fences\n      // Match balanced braces for nested objects\n      // Fall back gracefully\n    }\n  }\n}\n```\n\nThis was essential. Without it, models like Gemma 4 (a reasoning model) would output all their thinking in `reasoning_content`\n\nand leave `content`\n\nempty, causing the parser to fail on every message.\n\nAll three provider types — OpenAI, Featherless.ai, and LM Studio — use the same `ChatOpenAI`\n\nclass from `@langchain/openai`\n\n. This means:\n\n`baseURL`\n\nconfig changeThe model factory in `orchestrator.ts`\n\ncreates the right `ChatOpenAI`\n\ninstance based on the config:\n\n```\nfunction createChatModel(config: SimulationConfig) {\n  switch (config.provider) {\n    case \"openai\":    return new ChatOpenAI({ model: \"gpt-4o-mini\", ... });\n    case \"featherless\": return new ChatOpenAI({ model: \"...\", configuration: { baseURL: \"...\" } });\n    case \"lmstudio\":  return new ChatOpenAI({ model: \"...\", configuration: { baseURL: \".../v1\" } });\n  }\n}\n```\n\nEvery provider swap is instant — no restart, no rebuild.\n\nI went through two complete redesigns. The first was dark cyberpunk — glowing elements, neon accents, dramatic. It looked great in screenshots but was exhausting to work with.\n\nThe second is a premium light product aesthetic inspired by Civilization VI, Notion, and Stripe:\n\n`box-shadow: 0 1px 2px rgba(0,0,0,0.04)`\n\n)Dark/light mode uses CSS custom properties mapped through `@theme`\n\n, with `localStorage`\n\npersistence and `prefers-color-scheme`\n\ndetection. No Tailwind `dark:`\n\nvariants cluttering the component code.\n\n**Real LLMs are messy** — `JsonOutputParser`\n\nfails the moment a model wraps output in markdown. Build lenient parsers from day one.\n\n**Reasoning models need special handling** — Gemma 4, DeepSeek R1, and similar models put everything in `reasoning_content`\n\nand leave `content`\n\nempty. You need higher `maxTokens`\n\nand explicit \"do not reason\" instructions in the system prompt.\n\n** {{ escaping is essential** — LangChain's\n\n`ChatPromptTemplate`\n\nuses `{}`\n\nfor template variables. JSON examples need `{{`\n\nand `}}`\n\nto produce literal braces in the output.** createAgent() is powerful but expensive** — the ReAct loop makes multiple LLM calls per decision. For simple trade evaluation, Pattern 2 (a single chain call) is more efficient. Use Pattern 3 when you need tool-use and multi-step reasoning.\n\n**Template variable collision is real** — LangChain's `{}`\n\nsyntax conflicts with JSON's `{}`\n\n. The switch to `{{`\n\nescaping was a \"wait, that's it?\" moment after hours of debugging.\n\n**CSS custom properties + Tailwind = clean themes** — mapping all colors through `@theme`\n\nvariables instead of using `dark:`\n\nvariants keeps component code readable and theming centralized.\n\n```\ngit clone https://github.com/harishkotra/alien-translator.git\ncd alien-translator\nnpm install\nnpm run dev\n```\n\nOpen the settings, configure an LLM provider, and click Start. Watch the language emerge.\n\nCode and more: [https://www.dailybuild.xyz/project/147-alient-translator](https://www.dailybuild.xyz/project/147-alient-translator)", "url": "https://wpnews.pro/news/building-an-alien-language-from-scratch-with-langchain", "canonical_source": "https://dev.to/harishkotra/building-an-alien-language-from-scratch-with-langchain-43ji", "published_at": "2026-05-29 16:07:37+00:00", "updated_at": "2026-05-29 16:13:03.244156+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "ai-agents", "generative-ai", "ai-research"], "entities": ["LangChain", "LangChain.js"], "alternates": {"html": "https://wpnews.pro/news/building-an-alien-language-from-scratch-with-langchain", "markdown": "https://wpnews.pro/news/building-an-alien-language-from-scratch-with-langchain.md", "text": "https://wpnews.pro/news/building-an-alien-language-from-scratch-with-langchain.txt", "jsonld": "https://wpnews.pro/news/building-an-alien-language-from-scratch-with-langchain.jsonld"}}