跪拜 Guibai
← All articles
LangChain · JavaScript · Frontend Framework

Building a Local-File Code Agent with LangChain JS and DeepSeek

By YIAN ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Cursor, CodeGeeX, and similar tools all run on the same function-calling primitive. Seeing the bare two-invoke loop and the tool_call_id handshake demystifies what those products do under the hood and gives a developer the pattern to wire up any local or remote capability—file writes, shell commands, database queries—without a framework's magic.

Summary

A hand-rolled LangChain JS agent uses DeepSeek's V4 Flash model to read local files and produce code explanations. The implementation skips high-level Agent executors and instead walks through the raw two-invoke cycle: the model first decides to call a read_file tool, the tool executes against the local filesystem, and a second model call turns the returned content into a line-by-line analysis.

The code binds a Zod-validated tool to a ChatOpenAI instance pointed at DeepSeek's OpenAI-compatible endpoint. A message array of System, Human, AI, and Tool messages maintains the stateless model's context across the tool-call handoff. The core lesson is that tool_calls arrive as structured JSON with a unique id that must be echoed back in the ToolMessage, or the model loses track of which result belongs to which call.

Common failure modes get direct fixes: a non-zero temperature causes fabricated file contents, a missing tool_call_id breaks context linkage, and serial tool execution can be replaced with Promise.all for parallel speed-ups when multiple tools are invoked at once.

Takeaways
DeepSeek's API is OpenAI-compatible, so LangChain's ChatOpenAI class connects to it by changing only baseURL and modelName.
Setting temperature to 0 is mandatory for tool-calling and code-analysis tasks; any randomness causes the model to invent file contents or call the wrong tool.
A tool definition splits into an async execution function and a metadata object containing name, description, and a Zod schema that constrains the parameters the LLM can pass.
bindTools injects tool descriptions into every model request; after binding, the model returns either plain text or a structured tool_calls instruction with an empty content field.
The two-invoke cycle is the standard pattern: first invoke decides whether to call a tool, second invoke feeds the tool result back to the model for a final answer.
Every ToolMessage must carry the exact tool_call_id from the model's tool_calls response; without it, the model cannot associate the result with the original call.
Serial tool execution adds up all individual latencies; wrapping tool calls in Promise.all cuts total wait time to the single slowest tool.
Conclusions

The tutorial's decision to avoid LangChain's AgentExecutor and manually loop through tool_calls exposes the exact JSON contract that every 'magic' coding assistant relies on—a structured instruction with a call id that must round-trip correctly.

DeepSeek's full OpenAI compatibility means the entire LangChain ecosystem of tools, parsers, and callbacks works without a custom adapter, which lowers the switching cost between Chinese and Western model providers to two config lines.

The emphasis on temperature=0 for tool calling is underappreciated in many agent tutorials; a single hallucinated file path or parameter name breaks the whole chain, and the fix is deterministic output, not a better prompt.

Concepts & terms
Tool Calling (Function Calling)
An LLM capability where the model outputs a structured instruction—tool name, arguments, and a call ID—instead of natural language, signaling that the calling program should execute a defined function and return the result.
bindTools
A LangChain method that attaches tool definitions to a chat model so that every subsequent request includes the available tools' names, descriptions, and parameter schemas, enabling the model to decide when to emit a tool_calls instruction.
tool_call_id
A unique identifier included in each tool_calls object from the model. The program must echo this exact ID back in the corresponding ToolMessage so the stateless model can match a tool's result to the original call.
Zod
A TypeScript-first schema declaration and validation library used here to define the exact shape and type of parameters a tool accepts, giving the LLM a strict contract to follow when generating arguments.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗