mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
feat(memory): dream-v2 E1 — Fact Projection foundation
This commit is contained in:
parent
bf4cebb7b3
commit
acc6f448db
@ -0,0 +1,36 @@
|
||||
package vip.mate.memory.fact.extraction;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Composite extractor — combines pattern + LLM extractors.
|
||||
* Deduplicates by sourceRef.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CompositeEntityExtractor implements EntityExtractor {
|
||||
|
||||
private final PatternEntityExtractor patternExtractor;
|
||||
private final LlmEntityExtractor llmExtractor;
|
||||
|
||||
@Override
|
||||
public List<ExtractedFact> extract(Long agentId, String filename, String content) {
|
||||
List<ExtractedFact> results = new ArrayList<>(patternExtractor.extract(agentId, filename, content));
|
||||
|
||||
// Add LLM-extracted facts that don't duplicate pattern ones
|
||||
List<ExtractedFact> llmFacts = llmExtractor.extract(agentId, filename, content);
|
||||
for (ExtractedFact f : llmFacts) {
|
||||
if (results.stream().noneMatch(r -> r.sourceRef().equals(f.sourceRef()))) {
|
||||
results.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package vip.mate.memory.fact.extraction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Strategy interface for extracting facts from markdown content.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
public interface EntityExtractor {
|
||||
|
||||
List<ExtractedFact> extract(Long agentId, String filename, String content);
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package vip.mate.memory.fact.extraction;
|
||||
|
||||
/**
|
||||
* A single fact extracted from canonical memory content.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
public record ExtractedFact(
|
||||
String sourceRef,
|
||||
String category,
|
||||
String subject,
|
||||
String predicate,
|
||||
String objectValue,
|
||||
double confidence,
|
||||
String extractedBy
|
||||
) {}
|
||||
@ -0,0 +1,35 @@
|
||||
package vip.mate.memory.fact.extraction;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.mate.memory.MemoryProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* LLM-based fact extractor — uses the LLM to identify entities and relationships.
|
||||
* Only active when fact.llm-extraction-enabled=true; otherwise returns empty.
|
||||
* <p>
|
||||
* Phase 3 L1: placeholder that delegates to pattern extractor.
|
||||
* Full LLM implementation in Phase 3 L2+.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LlmEntityExtractor implements EntityExtractor {
|
||||
|
||||
private final MemoryProperties properties;
|
||||
|
||||
@Override
|
||||
public List<ExtractedFact> extract(Long agentId, String filename, String content) {
|
||||
if (!properties.getFact().isLlmExtractionEnabled()) {
|
||||
return List.of(); // LLM extraction disabled
|
||||
}
|
||||
// TODO: Phase 3 L2+ — call LLM with entity extraction prompt
|
||||
log.debug("[FactExtract] LLM extraction not yet implemented, returning empty for agent={}", agentId);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package vip.mate.memory.fact.extraction;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Pattern-based fact extractor — parses structured markdown sections
|
||||
* into subject-predicate-object triples using regex patterns.
|
||||
* <p>
|
||||
* Handles formats like:
|
||||
* - "## key\ncontent" sections in structured/*.md
|
||||
* - "- **key**: value" bullet patterns in MEMORY.md
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Component
|
||||
public class PatternEntityExtractor implements EntityExtractor {
|
||||
|
||||
private static final Pattern SECTION_HEADER = Pattern.compile("^## (.+)$", Pattern.MULTILINE);
|
||||
private static final Pattern KV_BULLET = Pattern.compile("^- \\*\\*(.+?)\\*\\*:\\s*(.+)$", Pattern.MULTILINE);
|
||||
private static final Pattern SIMPLE_BULLET = Pattern.compile("^- (.+)$", Pattern.MULTILINE);
|
||||
|
||||
@Override
|
||||
public List<ExtractedFact> extract(Long agentId, String filename, String content) {
|
||||
List<ExtractedFact> facts = new ArrayList<>();
|
||||
if (content == null || content.isBlank()) return facts;
|
||||
|
||||
// Determine category from filename
|
||||
String category = inferCategory(filename);
|
||||
|
||||
// Extract key-value bullets: - **key**: value
|
||||
Matcher kvMatcher = KV_BULLET.matcher(content);
|
||||
while (kvMatcher.find()) {
|
||||
String key = kvMatcher.group(1).trim();
|
||||
String value = kvMatcher.group(2).trim();
|
||||
if (value.isBlank() || value.equals(":")) continue;
|
||||
String sourceRef = filename + "#" + toSlug(key);
|
||||
facts.add(new ExtractedFact(sourceRef, category, key, "is", value, 0.9, "pattern"));
|
||||
}
|
||||
|
||||
// Extract section-based facts from structured files
|
||||
if (filename.startsWith("structured/")) {
|
||||
String[] sections = content.split("(?=^## )", Pattern.MULTILINE);
|
||||
for (String section : sections) {
|
||||
Matcher headerM = SECTION_HEADER.matcher(section);
|
||||
if (!headerM.find()) continue;
|
||||
String heading = headerM.group(1).trim();
|
||||
String body = section.substring(headerM.end()).trim();
|
||||
if (body.isBlank()) continue;
|
||||
|
||||
// Each non-empty line under a heading is a fact
|
||||
String sourceRef = filename + "#" + toSlug(heading);
|
||||
// Avoid duplicate if already captured as KV bullet
|
||||
if (facts.stream().anyMatch(f -> f.sourceRef().equals(sourceRef))) continue;
|
||||
|
||||
String firstLine = body.split("\n")[0].replaceAll("^[-*>]+\\s*", "").trim();
|
||||
if (firstLine.length() >= 5) {
|
||||
facts.add(new ExtractedFact(sourceRef, category, heading, "has", firstLine, 0.8, "pattern"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return facts;
|
||||
}
|
||||
|
||||
private String inferCategory(String filename) {
|
||||
if (filename.contains("user")) return "user_pref";
|
||||
if (filename.contains("project")) return "project";
|
||||
if (filename.contains("reference")) return "reference";
|
||||
if (filename.contains("feedback")) return "feedback";
|
||||
return "general";
|
||||
}
|
||||
|
||||
private String toSlug(String s) {
|
||||
return s.toLowerCase().replaceAll("[^a-z0-9\\u4e00-\\u9fff]+", "_").replaceAll("^_|_$", "");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package vip.mate.memory.fact.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Contradiction between two facts, detected during Dream consolidation.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Data
|
||||
@TableName("mate_fact_contradiction")
|
||||
public class FactContradictionEntity {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Long agentId;
|
||||
|
||||
private Long factAId;
|
||||
|
||||
private Long factBId;
|
||||
|
||||
private String description;
|
||||
|
||||
/** null | KEEP_A | KEEP_B | MERGE | IGNORE */
|
||||
private String resolution;
|
||||
|
||||
private LocalDateTime resolvedAt;
|
||||
|
||||
private String resolvedBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private Integer deleted;
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package vip.mate.memory.fact.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Fact projection entity — read-only derived view of canonical memory.
|
||||
* Derived columns (subject, predicate, object_value, confidence, trust, category)
|
||||
* are rebuilt by FactProjectionBuilder.
|
||||
* Accumulated columns (last_used_at, use_count) are only written by FactQueryService.bumpUseCount.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Data
|
||||
@TableName("mate_fact")
|
||||
public class FactEntity {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Long agentId;
|
||||
|
||||
/** Canonical source reference, e.g. "structured/user.md#preferred_language" */
|
||||
private String sourceRef;
|
||||
|
||||
/** Category: user_pref, project, tool, general */
|
||||
private String category;
|
||||
|
||||
private String subject;
|
||||
|
||||
private String predicate;
|
||||
|
||||
@TableField("object_value")
|
||||
private String objectValue;
|
||||
|
||||
/** Extraction confidence [0..1] */
|
||||
private Double confidence;
|
||||
|
||||
/** Trust score derived from feedback + time decay */
|
||||
private Double trust;
|
||||
|
||||
// --- Accumulated columns (preserved across rebuilds) ---
|
||||
|
||||
private LocalDateTime lastUsedAt;
|
||||
|
||||
private Integer useCount;
|
||||
|
||||
/** pattern | llm */
|
||||
private String extractedBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private Integer deleted;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package vip.mate.memory.fact.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Entity reference for multi-hop graph queries on facts.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Data
|
||||
@TableName("mate_fact_entity_ref")
|
||||
public class FactEntityRefEntity {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Long factId;
|
||||
|
||||
private String entityName;
|
||||
|
||||
/** person, tool, project, concept */
|
||||
private String entityType;
|
||||
|
||||
/** subject | object */
|
||||
private String role;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
package vip.mate.memory.fact.projection;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import vip.mate.memory.MemoryProperties;
|
||||
import vip.mate.memory.fact.extraction.CompositeEntityExtractor;
|
||||
import vip.mate.memory.fact.extraction.ExtractedFact;
|
||||
import vip.mate.memory.fact.repository.FactMapper;
|
||||
import vip.mate.workspace.document.WorkspaceFileService;
|
||||
import vip.mate.workspace.document.model.WorkspaceFileEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Rebuilds the fact projection from canonical sources.
|
||||
* <p>
|
||||
* Derived columns are overwritten; accumulated columns (use_count, last_used_at)
|
||||
* are preserved via MERGE/upsert keyed on (agent_id, source_ref).
|
||||
* <p>
|
||||
* Only this class may write derived columns to mate_fact (core invariant).
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FactProjectionBuilder {
|
||||
|
||||
private final FactMapper factMapper;
|
||||
private final WorkspaceFileService workspaceFileService;
|
||||
private final CompositeEntityExtractor extractor;
|
||||
private final MemoryProperties properties;
|
||||
|
||||
/**
|
||||
* Full rebuild for an agent. Extracts facts from all canonical sources,
|
||||
* upserts derived columns, and soft-deletes stale entries.
|
||||
*/
|
||||
public int rebuildAll(Long agentId) {
|
||||
if (!properties.getFact().isProjectionEnabled()) {
|
||||
log.debug("[FactProjection] Projection disabled, skipping rebuildAll for agent={}", agentId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<ExtractedFact> allFacts = new ArrayList<>();
|
||||
|
||||
// Extract from structured/*.md files
|
||||
List<WorkspaceFileEntity> files = workspaceFileService.listFiles(agentId);
|
||||
for (WorkspaceFileEntity file : files) {
|
||||
String filename = file.getFilename();
|
||||
if (filename == null) continue;
|
||||
if (filename.startsWith("structured/") && filename.endsWith(".md")) {
|
||||
WorkspaceFileEntity full = workspaceFileService.getFile(agentId, filename);
|
||||
if (full != null && full.getContent() != null && !full.getContent().isBlank()) {
|
||||
allFacts.addAll(extractor.extract(agentId, filename, full.getContent()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract from MEMORY.md
|
||||
WorkspaceFileEntity memoryFile = workspaceFileService.getFile(agentId, "MEMORY.md");
|
||||
if (memoryFile != null && memoryFile.getContent() != null && !memoryFile.getContent().isBlank()) {
|
||||
allFacts.addAll(extractor.extract(agentId, "MEMORY.md", memoryFile.getContent()));
|
||||
}
|
||||
|
||||
// Upsert all extracted facts
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<String> keepRefs = new ArrayList<>();
|
||||
for (ExtractedFact fact : allFacts) {
|
||||
factMapper.upsertDerivedH2(agentId, fact.sourceRef(), fact.category(),
|
||||
fact.subject(), fact.predicate(), fact.objectValue(),
|
||||
fact.confidence(), 0.5, fact.extractedBy(), now, now);
|
||||
keepRefs.add(fact.sourceRef());
|
||||
}
|
||||
|
||||
// Remove stale facts
|
||||
if (!keepRefs.isEmpty()) {
|
||||
factMapper.deleteByAgentIdAndSourceRefNotIn(agentId, keepRefs, now);
|
||||
}
|
||||
|
||||
log.info("[FactProjection] rebuildAll: agent={}, facts={}", agentId, allFacts.size());
|
||||
return allFacts.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Incremental rebuild for a single file change.
|
||||
*/
|
||||
public int rebuildOne(Long agentId, String filename, String content) {
|
||||
if (!properties.getFact().isProjectionEnabled()) return 0;
|
||||
|
||||
List<ExtractedFact> facts = extractor.extract(agentId, filename, content);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (ExtractedFact fact : facts) {
|
||||
factMapper.upsertDerivedH2(agentId, fact.sourceRef(), fact.category(),
|
||||
fact.subject(), fact.predicate(), fact.objectValue(),
|
||||
fact.confidence(), 0.5, fact.extractedBy(), now, now);
|
||||
}
|
||||
log.debug("[FactProjection] rebuildOne: agent={}, file={}, facts={}", agentId, filename, facts.size());
|
||||
return facts.size();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package vip.mate.memory.fact.repository;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import vip.mate.memory.fact.model.FactEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Fact mapper — limited write operations enforce the core invariant:
|
||||
* - Derived columns: only FactProjectionBuilder may write
|
||||
* - Accumulated columns: only bumpUseCount may write
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Mapper
|
||||
public interface FactMapper extends BaseMapper<FactEntity> {
|
||||
|
||||
/**
|
||||
* Upsert derived columns by (agent_id, source_ref).
|
||||
* Preserves accumulated columns (last_used_at, use_count).
|
||||
*/
|
||||
@Update("""
|
||||
MERGE INTO mate_fact (agent_id, source_ref, category, subject, predicate, object_value,
|
||||
confidence, trust, extracted_by, create_time, update_time, deleted)
|
||||
KEY (agent_id, source_ref)
|
||||
VALUES (#{agentId}, #{sourceRef}, #{category}, #{subject}, #{predicate}, #{objectValue},
|
||||
#{confidence}, #{trust}, #{extractedBy}, #{createTime}, #{updateTime}, 0)
|
||||
""")
|
||||
void upsertDerivedH2(@Param("agentId") Long agentId,
|
||||
@Param("sourceRef") String sourceRef,
|
||||
@Param("category") String category,
|
||||
@Param("subject") String subject,
|
||||
@Param("predicate") String predicate,
|
||||
@Param("objectValue") String objectValue,
|
||||
@Param("confidence") Double confidence,
|
||||
@Param("trust") Double trust,
|
||||
@Param("extractedBy") String extractedBy,
|
||||
@Param("createTime") LocalDateTime createTime,
|
||||
@Param("updateTime") LocalDateTime updateTime);
|
||||
|
||||
/**
|
||||
* Bump use_count and last_used_at for the given fact IDs.
|
||||
* This is the ONLY path that writes accumulated columns.
|
||||
*/
|
||||
@Update("""
|
||||
<script>
|
||||
UPDATE mate_fact
|
||||
SET use_count = use_count + 1, last_used_at = #{now}, update_time = #{now}
|
||||
WHERE id IN
|
||||
<foreach item='id' collection='ids' open='(' separator=',' close=')'>#{id}</foreach>
|
||||
AND deleted = 0
|
||||
</script>
|
||||
""")
|
||||
void bumpUseCount(@Param("ids") List<Long> ids, @Param("now") LocalDateTime now);
|
||||
|
||||
/**
|
||||
* Soft-delete facts whose source_ref is no longer in the canonical set.
|
||||
* Used during full rebuild to remove stale projections.
|
||||
*/
|
||||
@Update("""
|
||||
<script>
|
||||
UPDATE mate_fact SET deleted = 1, update_time = #{now}
|
||||
WHERE agent_id = #{agentId} AND deleted = 0
|
||||
AND source_ref NOT IN
|
||||
<foreach item='ref' collection='keepSet' open='(' separator=',' close=')'>#{ref}</foreach>
|
||||
</script>
|
||||
""")
|
||||
void deleteByAgentIdAndSourceRefNotIn(@Param("agentId") Long agentId,
|
||||
@Param("keepSet") List<String> keepSet,
|
||||
@Param("now") LocalDateTime now);
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
-- Dream v2 Phase 3: Fact projection tables (read-only derived from canonical)
|
||||
-- Ref: rfc-038 §3.3
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
agent_id BIGINT NOT NULL,
|
||||
source_ref VARCHAR(512) NOT NULL, -- canonical source: "structured/user.md#section_key" or "MEMORY.md#heading"
|
||||
category VARCHAR(64), -- user_pref, project, tool, general
|
||||
subject VARCHAR(256), -- entity subject
|
||||
predicate VARCHAR(256), -- relationship or property
|
||||
object_value TEXT, -- entity object or value
|
||||
confidence DOUBLE DEFAULT 1.0, -- extraction confidence [0..1]
|
||||
trust DOUBLE DEFAULT 0.5, -- derived trust score (feedback + decay)
|
||||
-- Accumulated columns (NOT overwritten by projection rebuild)
|
||||
last_used_at DATETIME,
|
||||
use_count INT DEFAULT 0,
|
||||
-- Metadata
|
||||
extracted_by VARCHAR(32) DEFAULT 'pattern', -- pattern | llm
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted TINYINT DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_agent ON mate_fact(agent_id, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_agent_source ON mate_fact(agent_id, source_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_agent_subject ON mate_fact(agent_id, subject);
|
||||
|
||||
-- Entity references (for multi-hop queries)
|
||||
CREATE TABLE IF NOT EXISTS mate_fact_entity_ref (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
fact_id BIGINT NOT NULL,
|
||||
entity_name VARCHAR(256) NOT NULL,
|
||||
entity_type VARCHAR(64), -- person, tool, project, concept
|
||||
role VARCHAR(32) NOT NULL, -- subject | object
|
||||
create_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_ref_entity ON mate_fact_entity_ref(entity_name, entity_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_ref_fact ON mate_fact_entity_ref(fact_id);
|
||||
|
||||
-- Contradiction tracking (populated by Dream contradiction detection step)
|
||||
CREATE TABLE IF NOT EXISTS mate_fact_contradiction (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
agent_id BIGINT NOT NULL,
|
||||
fact_a_id BIGINT NOT NULL,
|
||||
fact_b_id BIGINT NOT NULL,
|
||||
description TEXT,
|
||||
resolution VARCHAR(32), -- null | KEEP_A | KEEP_B | MERGE | IGNORE
|
||||
resolved_at DATETIME,
|
||||
resolved_by VARCHAR(64),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted TINYINT DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contradiction_agent ON mate_fact_contradiction(agent_id, resolution);
|
||||
@ -0,0 +1,49 @@
|
||||
-- Dream v2 Phase 3: Fact projection tables (read-only derived from canonical)
|
||||
-- Ref: rfc-038 §3.3
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
agent_id BIGINT NOT NULL,
|
||||
source_ref VARCHAR(512) NOT NULL,
|
||||
category VARCHAR(64),
|
||||
subject VARCHAR(256),
|
||||
predicate VARCHAR(256),
|
||||
object_value TEXT,
|
||||
confidence DOUBLE DEFAULT 1.0,
|
||||
trust DOUBLE DEFAULT 0.5,
|
||||
last_used_at DATETIME,
|
||||
use_count INT DEFAULT 0,
|
||||
extracted_by VARCHAR(32) DEFAULT 'pattern',
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted TINYINT DEFAULT 0,
|
||||
INDEX idx_fact_agent (agent_id, deleted),
|
||||
INDEX idx_fact_agent_source (agent_id, source_ref(255)),
|
||||
INDEX idx_fact_agent_subject (agent_id, subject(255))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact_entity_ref (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
fact_id BIGINT NOT NULL,
|
||||
entity_name VARCHAR(256) NOT NULL,
|
||||
entity_type VARCHAR(64),
|
||||
role VARCHAR(32) NOT NULL,
|
||||
create_time DATETIME NOT NULL,
|
||||
INDEX idx_fact_ref_entity (entity_name(128), entity_type),
|
||||
INDEX idx_fact_ref_fact (fact_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact_contradiction (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
agent_id BIGINT NOT NULL,
|
||||
fact_a_id BIGINT NOT NULL,
|
||||
fact_b_id BIGINT NOT NULL,
|
||||
description TEXT,
|
||||
resolution VARCHAR(32),
|
||||
resolved_at DATETIME,
|
||||
resolved_by VARCHAR(64),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted TINYINT DEFAULT 0,
|
||||
INDEX idx_contradiction_agent (agent_id, resolution)
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user