Quan sát toàn diện Agent trong Cơ sở dữ liệu riêng với LangChain, LangSmith SDK và DeepAgents
Cách theo dõi mỗi lần gọi LLM, gọi công cụ và sub-agent trong LangChain DeepAgents — và lưu trữ tất cả trong MongoDB riêng thay vì LangSmith cloud.
Mỗi lần gọi agent LangChain tạo ra một cây chạy: agent gốc, sub-agents, tool calls, LLM calls. LangSmith tự động theo dõi điều này. Nhưng nếu bạn cần dữ liệu tracing trong cơ sở dữ liệu riêng thì sao?
Chúng tôi đã xây dựng một hệ thống production theo dõi mỗi lần gọi LLM trong LangChain DeepAgents và lưu trữ trong MongoDB — sử dụng duy nhất các kiểu SDK chính thức, không có subclass BaseTracer tùy chỉnh, không khai báo lại trường. Đây là cách thực hiện.
Vấn đề
LangSmith cloud cung cấp tracing sẵn sàng. Đặt LANGSMITH_TRACING=true, và mỗi model.invoke(), agent.stream() và tool call đều được ghi lại.
Nhưng chúng tôi cần:
- Dữ liệu ở lại MongoDB của chúng tôi — prompts, responses, business logic không bao giờ rời khỏi hạ tầng
- Business context trên runs — tenant ID, agent type, entity ID gắn với mỗi trace
- Không code tracer tùy chỉnh — tái sử dụng quản lý run tree, kế thừa metadata, trích xuất token từ SDK
- Hoạt động với DeepAgents — sub-agents, tool calls, toàn bộ cây
Cách tiếp cận ngây thơ là kế thừa BaseTracer và triển khai lại persistRun / _endTrace. Điều này bỏ qua việc truyền dotted_order, kế thừa metadata, trích xuất token và theo dõi time-to-first-token. Đừng làm vậy.
Giải pháp: LangSmithTracingClientInterface
SDK langsmith định nghĩa interface gồm 2 phương thức cho persistence:
interface LangSmithTracingClientInterface {
createRun(run: RunCreate): Promise<void>;
updateRun(runId: string, run: RunUpdate): Promise<void>;
}
LangChainTracer từ @langchain/core xử lý mọi thứ — quản lý run tree, truyền dotted_order, trace_id, kế thừa metadata/tags, trích xuất token từ đầu ra LLM — và ủy quyền storage cho interface này.
Bằng cách triển khai nó cho MongoDB, bạn có được toàn bộ SDK miễn phí.
graph TD A["Code agent của bạn"] -->|"callbacks: [tracer]"| B["LangChainTracer"] B -->|"quản lý run tree"| C["RunTree"] C -->|"các DTO phẳng"| D["MongoDBLangSmithClient"] D -->|"createRun(RunCreate)"| E["MongoDB"] D -->|"updateRun(RunUpdate)"| E B -->|"kế thừa metadata"| F["Tất cả child runs nhận traceId, tags"] D -->|"trích xuất token"| G["usage_metadata trên các LLM run"]
Bước 1: MongoDB Client
Triển khai LangSmithTracingClientInterface. Phương thức createRun nhận một DTO RunCreate phẳng — không có tham chiếu vòng lặp, không có cây child_runs, không cần sanitizeForBson. Chỉ cần insert nó.
import type { LangSmithTracingClientInterface } from 'langsmith';
import type { RunCreate, RunUpdate } from 'langsmith/schemas';
class MongoDBLangSmithClient implements LangSmithTracingClientInterface {
constructor(
private readonly storage: ObservabilityStorage,
private readonly businessContext?: { tenantId: ObjectId; entity: ObjectId },
) {}
async createRun(run: RunCreate): Promise<void> {
const { child_runs, ...doc } = run;
await this.storage.insertLangChainRun({
...doc,
businessContext: this.businessContext,
});
}
async updateRun(runId: string, run: RunUpdate): Promise<void> {
await this.storage.updateLangChainRun(runId, run);
}
}
businessContext là metadata tùy chọn bạn gắn vào mỗi run — tenant, agent type, bất cứ thứ gì ứng dụng cần. Nó không thuộc model LangChain; nó là của bạn.
Bước 2: Hàm Factory
Tạo một factory trả về LangChainTracer tiêu chuẩn được hỗ trợ bởi MongoDB client:
import { LangChainTracer } from '@langchain/core/tracers/tracer_langchain';
function createMongoDBTracer(
traceId: string,
storage: ObservabilityStorage,
businessContext?: { tenantId: ObjectId; entity: ObjectId },
metadata?: Record<string, unknown>,
): LangChainTracer {
const client = new MongoDBLangSmithClient(storage, businessContext);
return new LangChainTracer({
client,
projectName: 'default',
metadata: { traceId, ...metadata },
});
}
Sử dụng nó ở bất cứ đâu bạn gọi LangChain runnable:
const traceId = uuid7(); // UUID có thứ tự thời gian từ SDK langsmith
const tracer = createMongoDBTracer(traceId, storage, {
tenantId, entityId, agentType: 'sales-assistant',
});
await agent.stream(
{ messages: [{ role: 'user', content: message }] },
{ callbacks: [tracer] },
);
Chỉ vậy thôi. Mỗi run trong cây — agent gốc, sub-agents, LLM calls, tool calls — đều được theo dõi với dotted_order, trace_id đúng, kế thừa metadata và đếm token chính xác.
Bước 3: Schema Lưu trữ
Mở rộng RunCreate từ langsmith/schemas trực tiếp. Đừng khai báo lại các trường:
import type { RunCreate } from 'langsmith/schemas';
interface LangChainRunDoc extends RunCreate {
_id: ObjectId;
businessContext?: {
tenantId: ObjectId;
entityId: ObjectId;
agentType?: string;
};
createdAt: Date;
}
RunCreate đã định nghĩa id, name, start_time, run_type, inputs, outputs, extra, tags, events, trace_id, dotted_order, parent_run_id. Bạn chỉ thêm các trường business của riêng mình.
Bước 4: Những gì bạn nhận được tự động
Quản lý Run Tree
LangChainTracer sử dụng RunTree bên trong. Khi run bắt đầu, nó gọi createRun với một DTO phẳng. Khi kết thúc, nó gọi updateRun với outputs, error, end_time và metadata bổ sung. Bạn không quản lý cây — SDK xử lý.
Kế thừa Metadata
Truyền metadata cho tracer, và nó sẽ truyền đến tất cả child runs:
const tracer = createMongoDBTracer(traceId, storage, ctx, {
thread_id: conversationId, // cho các truy vấn theo thread
agentType: 'pricing',
});
Mỗi child run (sub-agents, tools, LLM calls) nhận thread_id trong extra.metadata. Truy vấn theo thread trong MongoDB:
db.langchain_runs.find({ 'extra.metadata.thread_id': conversationId })
Trích xuất Token
LangChainTracer.onLLMEnd trích xuất usage_metadata từ đầu ra chat model và lưu trữ trong run.extra.metadata.usage_metadata. Không cần trích xuất tùy chỉnh.
Time-to-First-Token
Đối với các LLM run có streaming, LangChainTracer theo dõi first_token_time qua RunTree. Lệnh updateRun của bạn nhận được điều này trong RunUpdate.
Bước 5: Truy vấn
Run gốc (nơi parent_run_id không tồn tại) CHÍNH LÀ phiên trace. Truy vấn trực tiếp:
// Lấy tất cả runs cho một trace
async findRunsByTraceId(traceId: string) {
return db.langchain_runs.find({ traceId }).sort({ dotted_order: 1 });
}
// Lấy root run (phiên)
async findRootRun(traceId: string) {
return db.langchain_runs.findOne({
traceId,
parent_run_id: { $exists: false },
});
}
// Lấy runs theo message ID (từ extra.metadata)
async findRunsByMessageId(messageId: string) {
return db.langchain_runs.find({
'extra.metadata.message_id': messageId,
});
}
Không cần joins, không cần collection phiên riêng biệt. Một collection, một truy vấn.
Bước 6: Dữ liệu trông như thế nào
Mỗi document trong langchain_runs trông như thế này:
{
"_id": "...",
"id": "run-uuid-7",
"name": "ChatOpenAI",
"run_type": "llm",
"trace_id": "root-run-uuid",
"parent_run_id": "parent-uuid",
"dotted_order": "20260722T150322...1234.20260722T150322...5678",
"start_time": 1753216202000,
"end_time": 1753216203500,
"inputs": { "messages": [...] },
"outputs": { "generations": [...] },
"extra": {
"metadata": {
"ls_provider": "openai",
"ls_model_name": "gpt-4o",
"thread_id": "conversation-123",
"usage_metadata": {
"input_tokens": 1250,
"output_tokens": 340,
"total_tokens": 1590
}
}
},
"tags": ["sales-assistant", "pricing"],
"businessContext": {
"tenantId": "...",
"entityId": "...",
"agentType": "pricing"
},
"createdAt": "2026-07-22T15:03:22.000Z"
}
Cấu hình LLM (provider, model) đến từ extra.metadata.ls_provider và extra.metadata.ls_model_name — không cần snapshot.
Tích hợp DeepAgents
DeepAgents xây dựng trên LangGraph, sử dụng LangChain runnables. Cùng mẫu callbacks: [tracer] theo dõi toàn bộ cây deep agent:
graph TD A["DeepAgent"] -->|"callbacks"| B["LangChainTracer"] B --> C["Root run: agent"] C --> D["Sub-agent: pricing"] C --> E["Sub-agent: scheduling"] D --> F["LLM call: ChatOpenAI"] D --> G["Tool: calculatePrice"] E --> H["LLM call: ChatOpenAI"] E --> I["Tool: checkAvailability"] F -->|"parent_run_id = D"| J["langchain_runs"] G -->|"parent_run_id = D"| J H -->|"parent_run_id = E"| J I -->|"parent_run_id = E"| J C -->|"parent_run_id không tồn tại"| J J -->|"dotted_order sắp xếp cây"| K["Hiển thị waterfall"]
Mỗi run có parent_run_id liên kết với cha của nó, và dotted_order để sắp xếp theo thứ tự phân cấp. Toàn bộ cây có thể được tái tạo từ một truy vấn traceId.
Chỉ mục MongoDB
db.langchain_runs.createIndex({ runId: 1 }, { unique: true });
db.langchain_runs.createIndex({ traceId: 1, dotted_order: 1 });
db.langchain_runs.createIndex({ parent_run_id: 1 });
db.langchain_runs.createIndex({ 'extra.metadata.message_id': 1 }, { sparse: true });
Chỉ mục traceId + dotted_order bao quát truy vấn phổ biến nhất: lấy tất cả runs cho một trace theo thứ tự thực thi.
Những gì chúng tôi đã xóa
Sau khi chuyển sang cách tiếp cận này, chúng tôi đã loại bỏ:
| Thứ gì | Tại sao |
|---|---|
Subclass BaseTracer tùy chỉnh |
LangChainTracer xử lý mọi thứ |
Hàm sanitizeForBson |
DTO phẳng không có tham chiếu vòng lặp |
Truyền dotted_order / trace_id thủ công |
SDK xử lý |
Collection trace_sessions riêng biệt |
Root run CHÍNH LÀ phiên |
| Trường snapshot cấu hình LLM | Bắt nguồn từ extra.metadata khi truy vấn |
| 13 phương thức CRUD phiên | Được thay thế bằng các truy vấn root-run |
Những lỗi thường gặp
Đừng kế thừa BaseTracer trực tiếp. Nó là abstract và bỏ qua quản lý run tree. Sử dụng LangChainTracer với client tùy chỉnh.
Đừng lưu trữ các đối tượng Run (có child_runs). Chúng có tham chiếu vòng lặp. LangChainTracer đã chuyển đổi thành các DTO phẳng RunCreate / RunUpdate.
Đừng khai báo lại các trường BaseRun trong kiểu tùy chỉnh. Mở rộng RunCreate từ langsmith/schemas trực tiếp.
Đừng snapshot cấu hình LLM mỗi lần chạy. Provider và model nằm trong extra.metadata.ls_provider và extra.metadata.ls_model_name. Bắt nguồn khi truy vấn.
Đừng quên thread_id trên tất cả runs. Truyền nó trong RunnableConfig.metadata. LangChainTracer truyền nó đến child runs. Nếu không, các truy vấn theo thread sẽ thất bại.
Đọc thêm
- Kỹ năng có thể thực thi cho Agent tốt hơn Tài liệu — tại sao agent cần code có thể thực thi, không phải tài liệu
- OpenWiki vs QuotyAI — hai dự án DeepAgents, hai kiến trúc
- Determinism như Hạ tầng — tại sao thực thi determinism quan trọng cho AI production
- Codex cho Sales — chạy business logic được tạo bởi AI trong production
Câu hỏi thường gặp
Tại sao không sử dụng LangSmith cloud cho việc tracing? Chủ quyền dữ liệu. Prompts LLM, responses và business logic của bạn rời khỏi cơ sở hạ tầng. Đối với các ngành được quản lý hoặc nhạy cảm cạnh tranh, việc lưu trữ dữ liệu tracing trong cơ sở dữ liệu riêng là bắt buộc. Bạn cũng tránh được giá theo từng trace khi mở rộng.
LangSmithTracingClientInterface là gì?
Một interface gồm 2 phương thức từ SDK langsmith: createRun và updateRun. LangChainTracer ủy quyền toàn bộ persistence cho interface này. Bằng cách triển khai nó cho MongoDB, bạn có được quản lý run tree đầy đủ của SDK, kế thừa metadata và trích xuất token — trong khi dữ liệu được lưu trữ cục bộ.
Tại sao mở rộng RunCreate thay vì tạo kiểu tùy chỉnh?
RunCreate từ langsmith/schemas là DTO persistence chính thức của LangChain. Đây là những gì LangChainTracer gửi đến client. Bằng cách mở rộng trực tiếp, bạn loại bỏ việc khai báo lại trường, tránh sai lệch khi SDK cập nhật và có được persistence an toàn kiểu miễn phí.
Nó có hoạt động với sub-agents của DeepAgents không?
Có. DeepAgents xây dựng trên LangGraph, sử dụng LangChain runnables. Cùng LangChainTracer với callbacks theo dõi toàn bộ cây: agent gốc, sub-agents, tool calls, LLM calls. Mỗi lần chạy của sub-agent được liên kết qua parent_run_id và dotted_order.
Làm thế nào để nhóm theo thread mà không cần LangSmith?
truyền thread_id trong metadata RunnableConfig. LangChainTracer truyền nó đến tất cả child runs. Truy vấn theo thread_id trong collection MongoDB. Điều này giống hệt cách LangSmith threads hoạt động — bạn chỉ sở hữu storage.
Tags: langchain langsmith deep-agents observability mongodb llm-tracing ai-agents typescript langsmith-tracing-client runcreate langchain-tracer
Bài viết hữu ích? Hãy chia sẻ.
Bài viết liên quan
OpenWiki vs QuotyAI: Hai Dự Án LangChain DeepAgents, Hai Kiến Trúc
OpenWiki sử dụng LangChain DeepAgents để tạo tài liệu agent cho codebase. QuotyAI sử dụng cùng API createDeepAgent để tạo business logic TypeScript có thể thực thi. So sánh kỹ thuật về kiến trúc agent, pattern subagent, và runtime execution.
Đọc bài viếtSpec-Driven vs Code-First vs Chat-to-Code: Ba Triết Lý Dạy AI Về Doanh Nghiệp Của Bạn
Ba cách tiếp cận xây dựng kiến thức cho AI agent — phát triển spec-driven, tài liệu code-first, và tạo code từ chat. So sánh Spec Kit, OpenWiki, Mintlify, và QuotyAI với các cạm bẫy thực tế, sản phẩm, và tín hiệu hội tụ.
Đọc bài viếtCodex for Sales: Chạy Logic Kinh Doanh Do AI Tạo Trong Môi Trường Sản Xuất
Mã nguồn về định giá và lên lịch do AI tạo ra cần quản lý vòng đời kiểu git: staging, xác thực, phê duyệt, rollback. Các mẫu thiết kế thực tế từ một hệ thống đa tác tử trong sản xuất.
Đọc bài viết